@sixtyninecommerce/admin 0.9.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 (258) hide show
  1. package/README.md +4598 -128
  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 +318 -17
  9. package/dist/catalog/products.js +182 -14
  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 +282 -25
  21. package/dist/catalog/variants.js +173 -25
  22. package/dist/catalog/visibility.d.ts +20 -5
  23. package/dist/catalog/visibility.js +19 -1
  24. package/dist/client.d.ts +116 -9
  25. package/dist/client.js +41 -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 +121 -10
  63. package/dist/fulfillments/index.js +120 -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 +129 -30
  71. package/dist/index.js +47 -9
  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 +40 -0
  87. package/dist/integrations/index.js +24 -0
  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 +114 -0
  91. package/dist/integrations/pipelines.js +46 -0
  92. package/dist/integrations/types.d.ts +322 -0
  93. package/dist/integrations/types.js +8 -0
  94. package/dist/jobs/bulk-action-run.d.ts +11 -2
  95. package/dist/jobs/bulk-action-run.js +4 -5
  96. package/dist/locations/index.d.ts +66 -0
  97. package/dist/locations/index.js +31 -0
  98. package/dist/meta/index.d.ts +162 -0
  99. package/dist/orders/bulk.d.ts +75 -0
  100. package/dist/orders/bulk.js +150 -0
  101. package/dist/orders/cancel-with-money.d.ts +227 -0
  102. package/dist/orders/cancel-with-money.js +153 -0
  103. package/dist/orders/commit-edit.d.ts +7 -1
  104. package/dist/orders/commit-edit.js +5 -1
  105. package/dist/orders/deposit-policy.d.ts +103 -0
  106. package/dist/orders/deposit-policy.js +202 -0
  107. package/dist/orders/drafts.d.ts +800 -0
  108. package/dist/orders/drafts.js +771 -0
  109. package/dist/orders/fields.d.ts +245 -0
  110. package/dist/orders/fields.js +188 -0
  111. package/dist/orders/get.d.ts +29 -19
  112. package/dist/orders/get.js +17 -8
  113. package/dist/orders/index.d.ts +125 -5
  114. package/dist/orders/index.js +35 -2
  115. package/dist/orders/lifecycle.d.ts +128 -0
  116. package/dist/orders/lifecycle.js +158 -0
  117. package/dist/orders/line-price-preview.d.ts +7 -2
  118. package/dist/orders/list.d.ts +222 -0
  119. package/dist/orders/list.js +377 -0
  120. package/dist/orders/manual-discounts.d.ts +11 -2
  121. package/dist/orders/manual-discounts.js +2 -0
  122. package/dist/orders/manual-pricing.d.ts +5 -0
  123. package/dist/orders/order-entry.d.ts +20 -7
  124. package/dist/orders/plan-edit.d.ts +1 -1
  125. package/dist/orders/plan-edit.js +4 -1
  126. package/dist/orders/settings.d.ts +201 -0
  127. package/dist/orders/settings.js +325 -0
  128. package/dist/orders/status.d.ts +159 -13
  129. package/dist/orders/status.js +21 -0
  130. package/dist/orders/surcharges.d.ts +8 -1
  131. package/dist/orders/surcharges.js +3 -2
  132. package/dist/payments/account-credit.d.ts +30 -0
  133. package/dist/payments/account-credit.js +19 -0
  134. package/dist/payments/ar.d.ts +292 -0
  135. package/dist/payments/ar.js +223 -0
  136. package/dist/payments/connected-account.d.ts +47 -0
  137. package/dist/payments/connected-account.js +61 -0
  138. package/dist/payments/credit-noted-fields.d.ts +12 -0
  139. package/dist/payments/credit-noted-fields.js +37 -0
  140. package/dist/payments/index.d.ts +263 -10
  141. package/dist/payments/index.js +133 -1
  142. package/dist/payments/methods.d.ts +332 -0
  143. package/dist/payments/methods.js +363 -0
  144. package/dist/payments/mollie-methods.d.ts +159 -0
  145. package/dist/payments/mollie-methods.js +141 -0
  146. package/dist/payments/payment-links.d.ts +180 -0
  147. package/dist/payments/payment-links.js +97 -0
  148. package/dist/payments/provider-keys.d.ts +150 -0
  149. package/dist/payments/provider-keys.js +118 -0
  150. package/dist/payments/providers.d.ts +86 -11
  151. package/dist/payments/providers.js +29 -0
  152. package/dist/payments/requests.d.ts +144 -0
  153. package/dist/payments/requests.js +92 -0
  154. package/dist/payments/store-credit-fields.d.ts +15 -0
  155. package/dist/payments/store-credit-fields.js +51 -0
  156. package/dist/pickups/index.d.ts +104 -0
  157. package/dist/pickups/index.js +105 -0
  158. package/dist/platform/channels.d.ts +132 -0
  159. package/dist/platform/channels.js +128 -0
  160. package/dist/platform/geo.d.ts +123 -0
  161. package/dist/platform/geo.js +144 -0
  162. package/dist/platform/tax.d.ts +154 -0
  163. package/dist/platform/tax.js +139 -0
  164. package/dist/pos/anomalies.d.ts +158 -0
  165. package/dist/pos/anomalies.js +64 -0
  166. package/dist/pos/assisted-selling.d.ts +79 -0
  167. package/dist/pos/assisted-selling.js +38 -0
  168. package/dist/pos/exchanges.d.ts +103 -0
  169. package/dist/pos/exchanges.js +22 -0
  170. package/dist/pos/gift-cards.d.ts +135 -0
  171. package/dist/pos/gift-cards.js +47 -0
  172. package/dist/pos/index.d.ts +2125 -32
  173. package/dist/pos/index.js +742 -4
  174. package/dist/pos/no-receipt.d.ts +94 -0
  175. package/dist/pos/no-receipt.js +25 -0
  176. package/dist/pos/offline-sales.d.ts +187 -0
  177. package/dist/pos/offline-sales.js +116 -0
  178. package/dist/pos/pay-by-link.d.ts +143 -0
  179. package/dist/pos/pay-by-link.js +57 -0
  180. package/dist/pos/quick-keys.d.ts +103 -0
  181. package/dist/pos/quick-keys.js +45 -0
  182. package/dist/pos/reports.d.ts +360 -0
  183. package/dist/pos/reports.js +62 -0
  184. package/dist/pos/returns.d.ts +217 -0
  185. package/dist/pos/returns.js +53 -0
  186. package/dist/pricing/bulk-coupons.d.ts +139 -0
  187. package/dist/pricing/bulk-coupons.js +112 -0
  188. package/dist/pricing/index.d.ts +177 -4
  189. package/dist/pricing/index.js +176 -1
  190. package/dist/pricing/price-lists.d.ts +604 -0
  191. package/dist/pricing/price-lists.js +600 -0
  192. package/dist/pricing/vouchers.d.ts +103 -0
  193. package/dist/pricing/vouchers.js +69 -0
  194. package/dist/purchasing/alerts.d.ts +89 -0
  195. package/dist/purchasing/alerts.js +62 -0
  196. package/dist/purchasing/cost.d.ts +110 -0
  197. package/dist/purchasing/cost.js +86 -0
  198. package/dist/purchasing/index.d.ts +36 -0
  199. package/dist/purchasing/index.js +16 -0
  200. package/dist/purchasing/purchase-orders.d.ts +548 -0
  201. package/dist/purchasing/purchase-orders.js +491 -0
  202. package/dist/purchasing/replenishment.d.ts +281 -0
  203. package/dist/purchasing/replenishment.js +240 -0
  204. package/dist/purchasing/settings.d.ts +152 -0
  205. package/dist/purchasing/settings.js +181 -0
  206. package/dist/purchasing/suppliers.d.ts +476 -0
  207. package/dist/purchasing/suppliers.js +438 -0
  208. package/dist/quotes/index.d.ts +501 -0
  209. package/dist/quotes/index.js +439 -0
  210. package/dist/reports/index.d.ts +71 -8
  211. package/dist/reports/index.js +74 -11
  212. package/dist/returns/index.d.ts +818 -0
  213. package/dist/returns/index.js +844 -0
  214. package/dist/search/index.d.ts +174 -0
  215. package/dist/search/index.js +125 -0
  216. package/dist/shipping/definitions.d.ts +29 -0
  217. package/dist/shipping/definitions.js +47 -0
  218. package/dist/shipping/fulfillment-profiles.d.ts +174 -0
  219. package/dist/shipping/fulfillment-profiles.js +104 -0
  220. package/dist/shipping/hendriks.d.ts +97 -0
  221. package/dist/shipping/hendriks.js +50 -0
  222. package/dist/shipping/index.d.ts +48 -0
  223. package/dist/shipping/index.js +25 -0
  224. package/dist/shipping/methods.d.ts +198 -0
  225. package/dist/shipping/methods.js +240 -0
  226. package/dist/shipping/myparcel.d.ts +254 -0
  227. package/dist/shipping/myparcel.js +178 -0
  228. package/dist/shipping/parcel-count-suggestion.d.ts +2 -0
  229. package/dist/shipping/parcel-rule.d.ts +6 -5
  230. package/dist/shipping/parcel-rule.js +21 -23
  231. package/dist/shipping/rate-tables.d.ts +179 -0
  232. package/dist/shipping/rate-tables.js +184 -0
  233. package/dist/stock/index.d.ts +10 -3
  234. package/dist/stock/index.js +3 -0
  235. package/dist/stock/locations.d.ts +144 -0
  236. package/dist/stock/locations.js +168 -0
  237. package/dist/stock/transfers.d.ts +216 -43
  238. package/dist/stock/transfers.js +140 -13
  239. package/dist/stored-value/index.d.ts +214 -4
  240. package/dist/stored-value/index.js +152 -0
  241. package/dist/subscriptions/index.d.ts +682 -0
  242. package/dist/subscriptions/index.js +815 -0
  243. package/dist/tax/index.d.ts +76 -1
  244. package/dist/tax/index.js +50 -0
  245. package/dist/tenant/email-style.d.ts +102 -0
  246. package/dist/tenant/email-style.js +72 -0
  247. package/dist/tenant/email-templates.d.ts +66 -0
  248. package/dist/tenant/email-templates.js +54 -0
  249. package/dist/tenant/notifications.d.ts +23 -17
  250. package/dist/tenant/settings-sources.d.ts +1 -1
  251. package/dist/tenant/time-zone.d.ts +17 -0
  252. package/dist/tenant/time-zone.js +11 -0
  253. package/dist/types.d.ts +51 -4
  254. package/dist/users/index.d.ts +3 -0
  255. package/dist/users/index.js +2 -0
  256. package/dist/users/warehouses.d.ts +50 -0
  257. package/dist/users/warehouses.js +51 -0
  258. package/package.json +2 -2
@@ -1,9 +1,14 @@
1
1
  import { NotFoundError, UpstreamError, ValidationError, } from '@sixtyninecommerce/core';
2
- import { ASSET_REF_FIELDS, assertDeleted, availabilityCustomFields, DELETION_FIELDS, FACET_VALUE_REF_FIELDS, fieldOf, vendureListPage, } from './types.js';
2
+ import { ASSET_REF_FIELDS, assertDeleted, availabilityCustomFields, DELETION_FIELDS, FACET_VALUE_REF_FIELDS, fieldOf, MAX_PAGE_SIZE, toAssetRef, vendureListPage, } from './types.js';
3
+ import { findVariants } from './variant-find.js';
3
4
  const PARCEL_PACKING_MODES = ['shared', 'separate', 'none'];
4
5
  /** The engine's bounds for `parcelCapacity` (shipping-calculator's `MAX_PARCEL_CAPACITY`). */
5
6
  const PARCEL_CAPACITY_MIN = 1;
6
7
  const PARCEL_CAPACITY_MAX = 10_000;
8
+ /** The variants {@link VariantsModule.optionSets} reads at most by default. */
9
+ export const DEFAULT_OPTION_SET_LIMIT = MAX_PAGE_SIZE;
10
+ /** The most variants one {@link VariantsModule.optionSets} call reads. */
11
+ export const MAX_OPTION_SET_LIMIT = 100_000;
7
12
  /** The engine's bound on one `revertPrice` call (`MAX_REVERT_VARIANTS`). */
8
13
  export const MAX_REVERT_PRICE_VARIANTS = 500;
9
14
  export const VARIANT_FIELDS = /* GraphQL */ `
@@ -15,9 +20,16 @@ export const VARIANT_FIELDS = /* GraphQL */ `
15
20
  name
16
21
  sku
17
22
  enabled
18
- price
19
- priceWithTax
20
- currencyCode
23
+ storePrice {
24
+ price
25
+ priceWithTax
26
+ currencyCode
27
+ }
28
+ prices {
29
+ currencyCode
30
+ price
31
+ }
32
+ stockOnHand
21
33
  taxCategory {
22
34
  id
23
35
  name
@@ -51,6 +63,18 @@ export const VARIANT_FIELDS = /* GraphQL */ `
51
63
  preorderLimit
52
64
  parcelCapacity
53
65
  parcelPacking
66
+ minOrderQuantity
67
+ salesUnit
68
+ position
69
+ cost
70
+ packageWeightGrams
71
+ packageLengthCm
72
+ packageWidthCm
73
+ packageHeightCm
74
+ backorderPolicy
75
+ backorderLimit
76
+ gtin
77
+ mpn
54
78
  }
55
79
  `;
56
80
  export const VARIANTS_QUERY = /* GraphQL */ `
@@ -61,6 +85,20 @@ export const VARIANTS_QUERY = /* GraphQL */ `
61
85
  }
62
86
  }
63
87
  `;
88
+ /** Two fields a row, for {@link VariantsModule.optionSets}: the id and its options' ids. */
89
+ export const VARIANT_OPTION_SETS_QUERY = /* GraphQL */ `
90
+ query CatalogVariantOptionSets($productId: ID!, $options: ProductVariantListOptions) {
91
+ productVariants(productId: $productId, options: $options) {
92
+ items {
93
+ id
94
+ options {
95
+ id
96
+ }
97
+ }
98
+ totalItems
99
+ }
100
+ }
101
+ `;
64
102
  export const VARIANT_QUERY = /* GraphQL */ `
65
103
  query CatalogVariant($id: ID!) {
66
104
  productVariant(id: $id) { ${VARIANT_FIELDS} }
@@ -112,13 +150,33 @@ export const PRICE_BASIS_QUERY = /* GraphQL */ `
112
150
  }
113
151
  }
114
152
  `;
153
+ /** The variant fields read out of `customFields` as they are, null for an unset one. */
154
+ export const LIFTED_CUSTOM_FIELDS = [
155
+ 'minOrderQuantity',
156
+ 'salesUnit',
157
+ 'position',
158
+ 'cost',
159
+ 'packageWeightGrams',
160
+ 'packageLengthCm',
161
+ 'packageWidthCm',
162
+ 'packageHeightCm',
163
+ 'backorderPolicy',
164
+ 'backorderLimit',
165
+ 'gtin',
166
+ 'mpn',
167
+ ];
115
168
  function isParcelPacking(value) {
116
169
  return PARCEL_PACKING_MODES.includes(value);
117
170
  }
171
+ /** Lift the custom fields to first-class fields. */
118
172
  export function toVariant(wire) {
119
- const { customFields, ...rest } = wire;
173
+ const { customFields, featuredAsset, assets, ...rest } = wire;
174
+ const lifted = Object.fromEntries(LIFTED_CUSTOM_FIELDS.map((key) => [key, customFields?.[key] ?? null]));
120
175
  return {
121
176
  ...rest,
177
+ ...lifted,
178
+ featuredAsset: featuredAsset ? toAssetRef(featuredAsset) : null,
179
+ assets: assets.map(toAssetRef),
122
180
  taxClassKey: customFields?.taxClassKey ?? null,
123
181
  availabilityState: customFields?.availabilityState ?? 'inherit',
124
182
  availableFrom: customFields?.availableFrom ?? null,
@@ -144,13 +202,30 @@ function assertParcelRule(input) {
144
202
  }
145
203
  }
146
204
  function toWireInput(input) {
147
- const { availabilityState, availableFrom, preorderLimit, taxClassKey, parcelCapacity, parcelPacking, priceBasis, ...rest } = input;
205
+ const { availabilityState, availableFrom, preorderLimit, taxClassKey, parcelCapacity, parcelPacking, priceBasis, minOrderQuantity, salesUnit, cost, packageWeightGrams, packageLengthCm, packageWidthCm, packageHeightCm, backorderPolicy, backorderLimit, gtin, mpn, ...rest } = input;
148
206
  void priceBasis;
149
207
  const customFields = availabilityCustomFields({
150
208
  availabilityState,
151
209
  availableFrom,
152
210
  preorderLimit,
153
211
  });
212
+ const fields = {
213
+ minOrderQuantity,
214
+ salesUnit,
215
+ cost,
216
+ packageWeightGrams,
217
+ packageLengthCm,
218
+ packageWidthCm,
219
+ packageHeightCm,
220
+ backorderPolicy,
221
+ backorderLimit,
222
+ gtin,
223
+ mpn,
224
+ };
225
+ for (const [key, value] of Object.entries(fields)) {
226
+ if (value !== undefined)
227
+ customFields[key] = value;
228
+ }
154
229
  if (taxClassKey !== undefined)
155
230
  customFields.taxClassKey = taxClassKey;
156
231
  if (parcelCapacity !== undefined)
@@ -163,6 +238,69 @@ function toWireInput(input) {
163
238
  function refuse(code, message) {
164
239
  return new ValidationError({ code, message, status: 400, service: 'vendure' });
165
240
  }
241
+ const VARIANT_SORT_FIELDS = [
242
+ 'position',
243
+ 'name',
244
+ 'sku',
245
+ 'createdAt',
246
+ 'updatedAt',
247
+ ];
248
+ /**
249
+ * The engine's `ProductVariantListOptions` for {@link VariantListOptions}, without the page
250
+ * window. ONE sort key, never a tie-breaker beside it: graphql-js coerces an input object in the
251
+ * order its TYPE declares the fields, and `ProductVariantSortParameter` declares `id` before every
252
+ * field named here, so `{ position, id }` reaches the engine as `{ id, position }` and sorts by id
253
+ * alone (W10-113 finding). The engine completes the order itself: `productVariants(productId:)`
254
+ * (core's `getVariantsByProductId`) appends `id ASC` after the caller's key, which makes it total,
255
+ * so offset pages neither repeat nor skip a row even where `position` is null.
256
+ */
257
+ export function variantListOptions(options) {
258
+ const sort = options?.sort ?? { field: 'position', direction: 'ASC' };
259
+ if (!VARIANT_SORT_FIELDS.includes(sort.field) ||
260
+ (sort.direction !== 'ASC' && sort.direction !== 'DESC')) {
261
+ throw refuse('INVALID_VARIANT_SORT', `sort must name one of ${VARIANT_SORT_FIELDS.join(', ')} and the direction ASC or DESC; got ${JSON.stringify(sort)}. Nothing was sent.`);
262
+ }
263
+ const term = options?.term?.trim() ?? '';
264
+ return {
265
+ sort: { [sort.field]: sort.direction },
266
+ // `filterOperator` joins the top-level fields, which are exactly these two.
267
+ ...(term === ''
268
+ ? {}
269
+ : {
270
+ filter: { sku: { contains: term }, name: { contains: term } },
271
+ filterOperator: 'OR',
272
+ }),
273
+ };
274
+ }
275
+ /**
276
+ * Every priced input (`price`, or any `prices` entry) must be whole minor units and state its
277
+ * basis, and the basis must be the store's. Read per call, with the call's own options, so a
278
+ * per-call tenant is checked against ITS store rather than the client's default one.
279
+ */
280
+ export async function assertPricesStateBasis(transport, inputs, options) {
281
+ const priced = inputs.filter((input) => input.price !== undefined || input.prices !== undefined);
282
+ if (priced.length === 0)
283
+ return;
284
+ for (const input of priced) {
285
+ const amounts = [
286
+ ...(input.price !== undefined ? [input.price] : []),
287
+ ...(input.prices ?? []).map((entry) => entry.price),
288
+ ];
289
+ const fractional = amounts.find((amount) => !Number.isInteger(amount));
290
+ if (fractional !== undefined) {
291
+ throw refuse('INVALID_PRICE', `price must be a whole number of minor units; got ${String(fractional)}.`);
292
+ }
293
+ if (input.priceBasis !== 'net' && input.priceBasis !== 'gross') {
294
+ throw refuse('PRICE_BASIS_REQUIRED', 'A variant price must state its basis: priceBasis "net" or "gross".');
295
+ }
296
+ }
297
+ const channel = await fieldOf(transport, PRICE_BASIS_QUERY, 'activeChannel', {}, options);
298
+ const storeBasis = channel.pricesIncludeTax ? 'gross' : 'net';
299
+ const wrong = priced.find((input) => input.priceBasis !== storeBasis);
300
+ if (wrong) {
301
+ throw refuse('PRICE_BASIS_MISMATCH', `This store states prices ${storeBasis === 'gross' ? 'including' : 'excluding'} tax, so a price must be sent with priceBasis "${storeBasis}"; got "${String(wrong.priceBasis)}". Convert it before sending — the SDK does not, because the rate depends on each order's zone.`);
302
+ }
303
+ }
166
304
  export function createVariantsModule(transport) {
167
305
  /**
168
306
  * Every priced input must state its basis, and the basis must be the store's.
@@ -170,23 +308,7 @@ export function createVariantsModule(transport) {
170
308
  * against ITS store rather than the client's default one.
171
309
  */
172
310
  const assertPriceBasis = async (inputs, options) => {
173
- const priced = inputs.filter((input) => input.price !== undefined);
174
- if (priced.length === 0)
175
- return;
176
- for (const input of priced) {
177
- if (!Number.isInteger(input.price)) {
178
- throw refuse('INVALID_PRICE', `price must be a whole number of minor units; got ${String(input.price)}.`);
179
- }
180
- if (input.priceBasis !== 'net' && input.priceBasis !== 'gross') {
181
- throw refuse('PRICE_BASIS_REQUIRED', 'A variant price must state its basis: priceBasis "net" or "gross".');
182
- }
183
- }
184
- const channel = await fieldOf(transport, PRICE_BASIS_QUERY, 'activeChannel', {}, options);
185
- const storeBasis = channel.pricesIncludeTax ? 'gross' : 'net';
186
- const wrong = priced.find((input) => input.priceBasis !== storeBasis);
187
- if (wrong) {
188
- throw refuse('PRICE_BASIS_MISMATCH', `This store states prices ${storeBasis === 'gross' ? 'including' : 'excluding'} tax, so a price must be sent with priceBasis "${storeBasis}"; got "${String(wrong.priceBasis)}". Convert it before sending — the SDK does not, because the rate depends on each order's zone.`);
189
- }
311
+ await assertPricesStateBasis(transport, inputs, options);
190
312
  };
191
313
  const mapAll = (rows, what) => rows.map((row) => {
192
314
  if (row === null) {
@@ -209,13 +331,39 @@ export function createVariantsModule(transport) {
209
331
  return mapAll(rows, 'updateProductVariants');
210
332
  };
211
333
  return {
212
- list(productId, options) {
213
- return vendureListPage(transport, VARIANTS_QUERY, 'productVariants', (skip, take) => ({ productId, options: { skip, take } }), options, toVariant);
334
+ async list(productId, options) {
335
+ const listOptions = variantListOptions(options);
336
+ return vendureListPage(transport, VARIANTS_QUERY, 'productVariants', (skip, take) => ({ productId, options: { skip, take, ...listOptions } }), options, toVariant);
337
+ },
338
+ async optionSets(productId, options) {
339
+ const { limit = DEFAULT_OPTION_SET_LIMIT, ...call } = options ?? {};
340
+ if (!Number.isInteger(limit) || limit < 1 || limit > MAX_OPTION_SET_LIMIT) {
341
+ throw refuse('INVALID_OPTION_SET_LIMIT', `limit must be a whole number from 1 to ${String(MAX_OPTION_SET_LIMIT)}; got ${String(limit)}. Nothing was sent.`);
342
+ }
343
+ const combinations = [];
344
+ let totalItems = 0;
345
+ while (combinations.length < limit) {
346
+ const take = Math.min(MAX_PAGE_SIZE, limit - combinations.length);
347
+ const page = await fieldOf(transport, VARIANT_OPTION_SETS_QUERY, 'productVariants',
348
+ // By id alone (unique, so no page repeats a row of another): one key, as above.
349
+ { productId, options: { skip: combinations.length, take, sort: { id: 'ASC' } } }, call);
350
+ totalItems = page.totalItems;
351
+ for (const item of page.items) {
352
+ combinations.push(item.options.map((option) => option.id).sort());
353
+ }
354
+ // A short page is the end, whatever `totalItems` claims: never ask past it.
355
+ if (page.items.length < take || combinations.length >= totalItems)
356
+ break;
357
+ }
358
+ return { combinations, totalItems, truncated: totalItems > combinations.length };
214
359
  },
215
360
  async get(id, options) {
216
361
  const variant = await fieldOf(transport, VARIANT_QUERY, 'productVariant', { id }, options);
217
362
  return variant ? toVariant(variant) : null;
218
363
  },
364
+ find(input, options) {
365
+ return findVariants(transport, input, options);
366
+ },
219
367
  async create(inputs, options) {
220
368
  inputs.forEach(assertParcelRule);
221
369
  await assertPriceBasis(inputs, options);
@@ -1,5 +1,6 @@
1
- import { type Transport } from '@sixtyninecommerce/core';
1
+ import { type Page, type Transport } from '@sixtyninecommerce/core';
2
2
  import type { Call } from '../types.js';
3
+ import { type Product, type ProductListOptions } from './products.js';
3
4
  export type ProductVisibilityMode = 'public' | 'private';
4
5
  /**
5
6
  * Who a product is shown to on the storefront. A product with no row is
@@ -29,12 +30,20 @@ export interface SetProductVisibilityInput {
29
30
  * `USER_INPUT_ERROR` (`ValidationError`): "Product {id} not found in this
30
31
  * channel". A write from here is always `source: manual`, which a later
31
32
  * pipeline import leaves alone.
32
- *
33
- * The Admin API's `productsByVisibility` list is deliberately NOT exposed: it
34
- * counts and pages visibility rows across every store before narrowing to this
35
- * one, so its totals and pages are wrong (recorded as a finding).
36
33
  */
37
34
  export interface VisibilityModule {
35
+ /**
36
+ * This store's products in one mode, with `products.list`'s filter, sort and
37
+ * paging (filtered before paging; `totalItems` counts this store only).
38
+ * `public` lists products with an explicit public row, not every product
39
+ * without one. With no sort, newest product first.
40
+ *
41
+ * Needs an engine whose `productsByVisibility` honours its options: an older
42
+ * one ignored all but the page and counted other stores' products, so it is
43
+ * refused before running (`ValidationError`, `GRAPHQL_VALIDATION_FAILED` or
44
+ * `BAD_REQUEST`) rather than answered with an unfiltered page.
45
+ */
46
+ list(mode: ProductVisibilityMode, options?: ProductListOptions): Promise<Page<Product>>;
38
47
  /** Null when the product has no override (it is public). `NotFoundError` for an unknown product. */
39
48
  get(productId: string, options?: Call): Promise<ProductVisibility | null>;
40
49
  set(input: SetProductVisibilityInput, options?: Call): Promise<ProductVisibility>;
@@ -51,6 +60,12 @@ export interface VisibilityModule {
51
60
  clear(productId: string, options?: Call): Promise<void>;
52
61
  }
53
62
  export declare const PRODUCT_VISIBILITY_QUERY = "\n query CatalogProductVisibility($id: ID!) {\n product(id: $id) {\n id\n visibility { \n productId\n mode\n source\n allowedCustomerGroupIds\n createdAt\n updatedAt\n }\n }\n }\n";
63
+ /**
64
+ * The marker field is selected only so that an engine from before the list
65
+ * honoured its options refuses the document as an unknown field; nothing reads
66
+ * its answer. It must stay the SECOND root field.
67
+ */
68
+ export declare const PRODUCTS_BY_VISIBILITY_QUERY = "\n query CatalogProductsByVisibility($mode: ProductVisibilityMode!, $options: ProductListOptions) {\n productsByVisibility(mode: $mode, options: $options) {\n items { \n id\n \n catalogueOwnership {\n state\n organizationName\n editableHere\n }\n\n createdAt\n updatedAt\n languageCode\n name\n slug\n description\n enabled\n featuredAsset { \n id\n name\n preview\n source\n type\n mimeType\n customFields {\n altText\n }\n }\n assets { \n id\n name\n preview\n source\n type\n mimeType\n customFields {\n altText\n }\n }\n facetValues { \n id\n code\n name\n facet {\n id\n code\n name\n }\n }\n optionGroups {\n id\n code\n name\n customFields {\n isShared\n }\n options {\n id\n code\n name\n }\n translations {\n id\n languageCode\n name\n }\n }\n translations {\n id\n languageCode\n name\n slug\n description\n }\n excludedFromStore\n variantList(options: { take: 1 }) {\n totalItems\n }\n customFields {\n availabilityState\n availableFrom\n preorderLimit\n brand\n condition\n }\n }\n totalItems\n }\n productsByVisibilityHonoursListOptions\n }\n";
54
69
  export declare const SET_VISIBILITY_MUTATION = "\n mutation CatalogSetVisibility($input: SetProductVisibilityInput!) {\n setProductVisibility(input: $input) { \n productId\n mode\n source\n allowedCustomerGroupIds\n createdAt\n updatedAt\n }\n }\n";
55
70
  export declare const BULK_SET_VISIBILITY_MUTATION = "\n mutation CatalogBulkSetVisibility($input: BulkSetProductVisibilityInput!) {\n bulkSetProductVisibility(input: $input) {\n updated\n }\n }\n";
56
71
  export declare const CLEAR_VISIBILITY_MUTATION = "\n mutation CatalogClearVisibility($productId: ID!) {\n clearProductVisibility(productId: $productId)\n }\n";
@@ -1,5 +1,6 @@
1
1
  import { NotFoundError } from '@sixtyninecommerce/core';
2
- import { fieldOf } from './types.js';
2
+ import { PRODUCT_FIELDS, productListOptions, toProduct, } from './products.js';
3
+ import { fieldOf, vendureListPage } from './types.js';
3
4
  const VISIBILITY_FIELDS = /* GraphQL */ `
4
5
  productId
5
6
  mode
@@ -16,6 +17,20 @@ export const PRODUCT_VISIBILITY_QUERY = /* GraphQL */ `
16
17
  }
17
18
  }
18
19
  `;
20
+ /**
21
+ * The marker field is selected only so that an engine from before the list
22
+ * honoured its options refuses the document as an unknown field; nothing reads
23
+ * its answer. It must stay the SECOND root field.
24
+ */
25
+ export const PRODUCTS_BY_VISIBILITY_QUERY = /* GraphQL */ `
26
+ query CatalogProductsByVisibility($mode: ProductVisibilityMode!, $options: ProductListOptions) {
27
+ productsByVisibility(mode: $mode, options: $options) {
28
+ items { ${PRODUCT_FIELDS} }
29
+ totalItems
30
+ }
31
+ productsByVisibilityHonoursListOptions
32
+ }
33
+ `;
19
34
  export const SET_VISIBILITY_MUTATION = /* GraphQL */ `
20
35
  mutation CatalogSetVisibility($input: SetProductVisibilityInput!) {
21
36
  setProductVisibility(input: $input) { ${VISIBILITY_FIELDS} }
@@ -35,6 +50,9 @@ export const CLEAR_VISIBILITY_MUTATION = /* GraphQL */ `
35
50
  `;
36
51
  export function createVisibilityModule(transport) {
37
52
  return {
53
+ list(mode, options) {
54
+ return vendureListPage(transport, PRODUCTS_BY_VISIBILITY_QUERY, 'productsByVisibility', (skip, take) => ({ mode, options: productListOptions(options, skip, take) }), options, toProduct);
55
+ },
38
56
  async get(productId, options) {
39
57
  const product = await fieldOf(transport, PRODUCT_VISIBILITY_QUERY, 'product', { id: productId }, options);
40
58
  if (!product) {
package/dist/client.d.ts CHANGED
@@ -10,47 +10,63 @@ import { type AuthModule } from './auth/index.js';
10
10
  import { type BulkModule } from './bulk/index.js';
11
11
  import { type CatalogModule } from './catalog/index.js';
12
12
  import { type ComplianceModule } from './compliance/index.js';
13
+ import { type ContentAssetsModule } from './content/assets.js';
13
14
  import { type FormsModule } from './content/forms.js';
15
+ import { type ContentItemsModule } from './content/items.js';
16
+ import { type ContentFieldsModule, type ContentPageValuesModule, type ContentTypesModule } from './content/types.js';
14
17
  import { type CustomObjectsModule } from './custom-objects/index.js';
15
18
  import { type CustomersModule } from './customers/index.js';
16
19
  import { type DocumentsModule } from './documents/index.js';
17
20
  import { type EventsModule } from './events/index.js';
18
21
  import { type FeedsModule } from './feeds/index.js';
19
22
  import { type FulfillmentsModule } from './fulfillments/index.js';
23
+ import { type GeoModule } from './geo/index.js';
20
24
  import { type HeadsModule } from './heads/index.js';
21
25
  import { type HooksModule } from './hooks/index.js';
26
+ import { type IntegrationsModule } from './integrations/index.js';
22
27
  import { type JobsModule } from './jobs/index.js';
23
28
  import { type LocationsModule } from './locations/index.js';
24
29
  import { type MetaModule } from './meta/index.js';
25
30
  import { type MetafieldsModule } from './metafields/index.js';
26
31
  import { type OrdersModule } from './orders/index.js';
27
32
  import { type PaymentsModule } from './payments/index.js';
33
+ import { type PickupsModule } from './pickups/index.js';
28
34
  import { type PlatformAppsModule } from './platform/apps.js';
29
35
  import { type PlatformCatalogModule } from './platform/catalog.js';
36
+ import { type PlatformChannelsModule } from './platform/channels.js';
30
37
  import { type PlatformEntitlementsModule } from './platform/entitlements.js';
38
+ import { type PlatformGeoModule } from './platform/geo.js';
31
39
  import { type PlatformLegalEntitiesModule } from './platform/legal-entities.js';
32
40
  import { type PlatformOrganizationsModule } from './platform/organizations.js';
33
41
  import { type PlatformModule } from './platform/provision-store.js';
34
42
  import { type PlatformReportsModule } from './platform/reports.js';
35
43
  import { type PlatformServerMetricsModule } from './platform/server-metrics.js';
44
+ import { type PlatformTaxModule } from './platform/tax.js';
36
45
  import { type PosModule } from './pos/index.js';
37
46
  import { type PricingModule } from './pricing/index.js';
38
47
  import { type ProvenanceModule } from './provenance/index.js';
48
+ import { type PurchasingModule } from './purchasing/index.js';
49
+ import { type QuotesModule } from './quotes/index.js';
39
50
  import { type ReportsModule } from './reports/index.js';
51
+ import { type ReturnsModule } from './returns/index.js';
52
+ import { type SearchModule } from './search/index.js';
40
53
  import { type RedirectsModule } from './seo/redirects.js';
41
- import { type ParcelCountSuggestionMethod } from './shipping/parcel-count-suggestion.js';
42
- import { type ShippingModule } from './shipping/parcel-rule.js';
54
+ import { type ShippingModule } from './shipping/index.js';
43
55
  import { type StockModule } from './stock/index.js';
44
56
  import { type StoredValueModule } from './stored-value/index.js';
57
+ import { type SubscriptionsModule } from './subscriptions/index.js';
45
58
  import { type TaxModule } from './tax/index.js';
46
59
  import { type CapabilitiesModule } from './tenant/capabilities.js';
47
60
  import { type ChannelsModule } from './tenant/channels.js';
61
+ import { type EmailStyleModule } from './tenant/email-style.js';
62
+ import { type EmailTemplatesModule } from './tenant/email-templates.js';
48
63
  import { type TenantEntitlementsReader } from './tenant/entitlements.js';
49
64
  import { type FieldGroupOwnersReader, type FieldGroupSyncSourcesReader } from './tenant/field-groups.js';
50
65
  import { type LegalEntityModule } from './tenant/legal-entity.js';
51
66
  import { type NotificationsModule } from './tenant/notifications.js';
52
67
  import { type RetentionModule } from './tenant/retention.js';
53
68
  import { type SettingsSourcesModule } from './tenant/settings-sources.js';
69
+ import { type TenantTimeZoneReader } from './tenant/time-zone.js';
54
70
  import { type UploadsModule } from './uploads/index.js';
55
71
  import { type UsersModule } from './users/index.js';
56
72
  export interface AdminClientConfig {
@@ -117,13 +133,23 @@ export interface AdminClient {
117
133
  };
118
134
  audit: AuditModule;
119
135
  /**
120
- * Products, variants, options, facets, collections, assets, visibility and
121
- * properties (spec §9.2 `catalog`). Needs the catalog permissions.
136
+ * Products, variants, options, facets, collections, assets, visibility,
137
+ * properties and recommendations (spec §9.2 `catalog`). Needs the catalog
138
+ * permissions.
122
139
  */
123
140
  catalog: CatalogModule;
124
141
  channels: ChannelsModule;
142
+ /**
143
+ * The CMS's content: forms, content types (collections) with their fields, each type's
144
+ * index-page values, the types' items, and the media library (spec §9.2 `content`).
145
+ */
125
146
  content: {
126
147
  forms: FormsModule;
148
+ types: ContentTypesModule;
149
+ fields: ContentFieldsModule;
150
+ pageValues: ContentPageValuesModule;
151
+ items: ContentItemsModule;
152
+ assets: ContentAssetsModule;
127
153
  };
128
154
  /**
129
155
  * Compliance requests to installed apps: data requests, customer and
@@ -141,6 +167,12 @@ export interface AdminClient {
141
167
  * the store's tax, payment or fulfillment provider; `ping` tests one hook.
142
168
  */
143
169
  hooks: HooksModule;
170
+ /**
171
+ * The store's connections to other systems (spec §9.2). `pipelines.drafts` keeps a
172
+ * half-finished import or export set-up on the server for 7 days after its last change, for any
173
+ * admin of the store with pipeline rights, and never holds a credential.
174
+ */
175
+ integrations: IntegrationsModule;
144
176
  /**
145
177
  * Platform events (spec §6.9): `poll`/`pollAll`/`replay`/`head` over the event log (a
146
178
  * machine credential with `ReadPlatformEvents`), `subscribe` to build a typed webhook
@@ -166,6 +198,11 @@ export interface AdminClient {
166
198
  * AI helpers. Channel-scoped throughout — no method takes a channel.
167
199
  */
168
200
  feeds: FeedsModule;
201
+ /**
202
+ * Countries and zones (spec §9.2, ruling G128): platform-wide reads, the same for every store.
203
+ * `mostSpecificZone` (exported beside it) answers the zone provisioning picks for a country.
204
+ */
205
+ geo: GeoModule;
169
206
  /** The store's head registry: every client surface it serves (spec §6.7). */
170
207
  heads: HeadsModule;
171
208
  /** Every long-running operation the platform runs, under one resource (spec §6.11). */
@@ -197,6 +234,12 @@ export interface AdminClient {
197
234
  * recorded against an order with what it stands at (spec §8.2).
198
235
  */
199
236
  payments: PaymentsModule;
237
+ /**
238
+ * Store pickup (click & collect, POS C1/33): the store's pickups, an order's pickup, and
239
+ * "ready". A location's pickup settings are `locations.pickup/setPickup`; the hand-over is the
240
+ * till's.
241
+ */
242
+ pickups: PickupsModule;
200
243
  /**
201
244
  * Point of sale on a machine principal (spec §8.8): register sessions, cash,
202
245
  * tenders, the sale, refunds, X and Z — with no paired tablet. Needs a `pos`
@@ -215,18 +258,54 @@ export interface AdminClient {
215
258
  * writer only; the history is the platform event log.
216
259
  */
217
260
  provenance: ProvenanceModule;
261
+ /**
262
+ * Buying stock from suppliers, per legal entity (purchase-orders spec §10.1): `settings` with
263
+ * its document `numbering`, `suppliers` with their `contacts`, `purchaseOrders` with their
264
+ * `lines`, `replenishment` (reorder rules and suggestions), and `cost` (a variant's cost
265
+ * ledger). Needs the Purchasing plugin on the store.
266
+ */
267
+ purchasing: PurchasingModule;
268
+ /**
269
+ * B2B quotes: list, read, build and price a quote, move it through its statuses, and convert an
270
+ * approved one into an order (keyed); its PDF, its email, the store's quote design and settings
271
+ * (headless W10-35a). `Quote.editable` / `convertible` are the engine's answer to "what can be
272
+ * done now".
273
+ */
274
+ quotes: QuotesModule;
218
275
  /** Tenant reporting in minor units and ISO-8601 UTC (spec §9.2 `reports`, §9.3). */
219
276
  reports: ReportsModule;
277
+ /**
278
+ * Subscription plans (list, read, create, update, products, delete, price breakdown) and the
279
+ * subscriptions sold on them (list, read, a customer's subscriptions and mandates, the store's
280
+ * billing readiness) (headless W10-36), and their lifecycle and billing commands: purchase,
281
+ * pause/resume/skip/reschedule, cancel, payment method replacement, recovery, reconciliation,
282
+ * renewal and settlement retries, attention clearing and price changes, each money command under
283
+ * one idempotency key per attempt (headless W10-114). Statuses are the meta catalog's lower-case
284
+ * words.
285
+ */
286
+ subscriptions: SubscriptionsModule;
287
+ /**
288
+ * Order returns (RMA): one return, an order's returns, what of an order can
289
+ * still be returned, and the plan for settling a return (headless W9-47). The
290
+ * list and the writes are phase 7.
291
+ */
292
+ returns: ReturnsModule;
293
+ /**
294
+ * One search across the store (spec §7 `search.entities`): products, orders, customers and
295
+ * content in one call, each type's best matches already loaded, in relevance order. A type
296
+ * the credential may not read is skipped and named, never refused.
297
+ */
298
+ search: SearchModule;
220
299
  seo: {
221
300
  redirects: RedirectsModule;
222
301
  };
223
302
  /**
224
303
  * Shipping of the ACTIVE channel (spec §9.2 `shipping`): the channel's
225
- * `parcelRule`, and `parcelCountSuggestion` for a set of variant items.
304
+ * `parcelRule`, `parcelCountSuggestion` for a set of variant items, the
305
+ * C.J. Hendriks 3PL settings (`hendriks.config`) and the MyParcel set-up,
306
+ * carriers and parcel senders (`myparcel`).
226
307
  */
227
- shipping: ShippingModule & {
228
- parcelCountSuggestion: ParcelCountSuggestionMethod;
229
- };
308
+ shipping: ShippingModule;
230
309
  /**
231
310
  * Stock per location by state, the ledger with reasons, reason-carrying
232
311
  * adjustments, transfers and allocation release (spec §9.2 `stock`, §8.7).
@@ -238,7 +317,10 @@ export interface AdminClient {
238
317
  * payment on an order.
239
318
  */
240
319
  storedValue: StoredValueModule;
241
- /** The store's own tax classes and their per-country mappings (spec §8.6). */
320
+ /**
321
+ * The store's own tax classes and their per-country mappings (spec §8.6), and the platform-wide
322
+ * tax categories and rates they map onto.
323
+ */
242
324
  tax: TaxModule;
243
325
  /**
244
326
  * Signed direct-to-storage uploads (spec §6.11): reserve a place, PUT the
@@ -269,6 +351,10 @@ export interface AdminClient {
269
351
  retention: RetentionModule;
270
352
  /** Per notification event: on or off, who sends it (spec §8.15), and to whom. */
271
353
  notifications: NotificationsModule;
354
+ /** The look of the store's emails: colours, type, logo and layout (`settings.email`). */
355
+ emailStyle: EmailStyleModule;
356
+ /** The store's email templates, by type: previews rendered as the store would send them. */
357
+ emailTemplates: EmailTemplatesModule;
272
358
  /**
273
359
  * Where each settings group comes from — the store or its organization — and the store's
274
360
  * choice to keep its own values (multi-region spec §5.3).
@@ -279,6 +365,11 @@ export interface AdminClient {
279
365
  * Changing it needs `UpdateSeller` on every store of the entity.
280
366
  */
281
367
  legalEntity: LegalEntityModule;
368
+ /**
369
+ * The active store's IANA time zone as the engine resolves it (`Europe/Amsterdam` when unset
370
+ * or invalid): the zone the API counts calendar days in. Never the browser's.
371
+ */
372
+ timeZone: TenantTimeZoneReader;
282
373
  };
283
374
  /**
284
375
  * Human SuperAdmin surface. Every method refuses an API token or an app
@@ -289,11 +380,22 @@ export interface AdminClient {
289
380
  apps: PlatformAppsModule;
290
381
  /** Catalogue maintenance on a named store (the translation-language repair). */
291
382
  catalog: PlatformCatalogModule;
383
+ /**
384
+ * The codes renames hold against reuse, and their release: a release that must clear files
385
+ * first is finished by the engine itself — see {@link PlatformChannelsModule}.
386
+ */
387
+ channels: PlatformChannelsModule;
292
388
  /**
293
389
  * Which external plugins each store may use: read one store's grants or every store's, and
294
390
  * connect or disconnect a plugin for a store — see {@link PlatformEntitlementsModule}.
295
391
  */
296
392
  entitlements: PlatformEntitlementsModule;
393
+ /**
394
+ * The platform's zones and countries every store shares: create, rename, delete and change
395
+ * the members of a zone; switch a country on or off — see {@link PlatformGeoModule}. Reads
396
+ * are `geo`.
397
+ */
398
+ geo: PlatformGeoModule;
297
399
  /**
298
400
  * The platform's legal entities (multi-region spec §7.1): SuperAdmin list and create; read and
299
401
  * update through every store of an entity — see {@link PlatformLegalEntitiesModule}.
@@ -308,6 +410,11 @@ export interface AdminClient {
308
410
  reports: PlatformReportsModule;
309
411
  /** Basic health of the platform's own servers (CPU, memory, disk, network). */
310
412
  serverMetrics: PlatformServerMetricsModule;
413
+ /**
414
+ * The platform's tax categories and tax rates every store shares: create, change and delete
415
+ * them — see {@link PlatformTaxModule}. Reads are `tax.categories` / `tax.rates`.
416
+ */
417
+ tax: PlatformTaxModule;
311
418
  };
312
419
  /** A derived client on another tenant, sharing this one's principal. */
313
420
  withTenant(tenant: Partial<TenantContext> & {