@sixtyninecommerce/admin 0.8.0 → 0.9.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 (161) hide show
  1. package/README.md +2445 -17
  2. package/dist/api-tokens/index.d.ts +15 -5
  3. package/dist/api-tokens/index.js +1 -0
  4. package/dist/apps/event-subscription.d.ts +59 -0
  5. package/dist/apps/event-subscription.js +32 -0
  6. package/dist/apps/hook-secret.d.ts +22 -0
  7. package/dist/apps/hook-secret.js +36 -0
  8. package/dist/apps/index.d.ts +54 -3
  9. package/dist/apps/index.js +17 -0
  10. package/dist/audit/index.d.ts +7 -0
  11. package/dist/auth/queries.d.ts +5 -1
  12. package/dist/auth/queries.js +6 -0
  13. package/dist/bulk/index.d.ts +323 -0
  14. package/dist/bulk/index.js +162 -0
  15. package/dist/catalog/assets.d.ts +147 -0
  16. package/dist/catalog/assets.js +164 -0
  17. package/dist/catalog/collections.d.ts +109 -0
  18. package/dist/catalog/collections.js +91 -0
  19. package/dist/catalog/facets.d.ts +103 -0
  20. package/dist/catalog/facets.js +100 -0
  21. package/dist/catalog/index.d.ts +115 -0
  22. package/dist/catalog/index.js +21 -0
  23. package/dist/catalog/options.d.ts +89 -0
  24. package/dist/catalog/options.js +88 -0
  25. package/dist/catalog/products-bulk.d.ts +21 -0
  26. package/dist/catalog/products-bulk.js +58 -0
  27. package/dist/catalog/products.d.ts +489 -0
  28. package/dist/catalog/products.js +365 -0
  29. package/dist/catalog/properties.d.ts +234 -0
  30. package/dist/catalog/properties.js +158 -0
  31. package/dist/catalog/types.d.ts +174 -0
  32. package/dist/catalog/types.js +141 -0
  33. package/dist/catalog/variants.d.ts +274 -0
  34. package/dist/catalog/variants.js +258 -0
  35. package/dist/catalog/visibility.d.ts +57 -0
  36. package/dist/catalog/visibility.js +59 -0
  37. package/dist/client.d.ts +171 -3
  38. package/dist/client.js +81 -12
  39. package/dist/compliance/index.d.ts +88 -0
  40. package/dist/compliance/index.js +88 -0
  41. package/dist/customer-data-redaction.d.ts +14 -0
  42. package/dist/customer-data-redaction.js +22 -0
  43. package/dist/customers/index.d.ts +428 -17
  44. package/dist/customers/index.js +322 -15
  45. package/dist/documents/index.d.ts +513 -16
  46. package/dist/documents/index.js +229 -3
  47. package/dist/events/index.d.ts +259 -0
  48. package/dist/events/index.js +147 -0
  49. package/dist/feeds/ai.d.ts +46 -0
  50. package/dist/feeds/ai.js +43 -0
  51. package/dist/feeds/categories.d.ts +50 -0
  52. package/dist/feeds/categories.js +56 -0
  53. package/dist/feeds/configs.d.ts +54 -0
  54. package/dist/feeds/configs.js +85 -0
  55. package/dist/feeds/identifier-fields.d.ts +25 -0
  56. package/dist/feeds/identifier-fields.js +29 -0
  57. package/dist/feeds/index.d.ts +54 -0
  58. package/dist/feeds/index.js +46 -0
  59. package/dist/feeds/preserve.d.ts +38 -0
  60. package/dist/feeds/preserve.js +74 -0
  61. package/dist/feeds/rules.d.ts +42 -0
  62. package/dist/feeds/rules.js +56 -0
  63. package/dist/feeds/types.d.ts +654 -0
  64. package/dist/feeds/types.js +135 -0
  65. package/dist/fulfillments/index.d.ts +360 -0
  66. package/dist/fulfillments/index.js +425 -0
  67. package/dist/heads/index.d.ts +60 -0
  68. package/dist/heads/index.js +45 -0
  69. package/dist/hooks/assignments.d.ts +90 -0
  70. package/dist/hooks/assignments.js +53 -0
  71. package/dist/hooks/index.d.ts +97 -0
  72. package/dist/hooks/index.js +76 -0
  73. package/dist/index.d.ts +84 -9
  74. package/dist/index.js +16 -1
  75. package/dist/jobs/bulk-action-run.d.ts +65 -0
  76. package/dist/jobs/bulk-action-run.js +70 -0
  77. package/dist/locations/index.d.ts +6 -2
  78. package/dist/meta/index.d.ts +32 -0
  79. package/dist/orders/commit-edit.d.ts +123 -0
  80. package/dist/orders/commit-edit.js +142 -0
  81. package/dist/orders/get.d.ts +722 -0
  82. package/dist/orders/get.js +530 -0
  83. package/dist/orders/index.d.ts +105 -20
  84. package/dist/orders/index.js +44 -0
  85. package/dist/orders/line-price-preview.d.ts +297 -0
  86. package/dist/orders/line-price-preview.js +50 -0
  87. package/dist/orders/manual-discounts.d.ts +132 -0
  88. package/dist/orders/manual-discounts.js +95 -0
  89. package/dist/orders/manual-pricing.d.ts +192 -0
  90. package/dist/orders/manual-pricing.js +72 -0
  91. package/dist/orders/order-entry.d.ts +566 -0
  92. package/dist/orders/order-entry.js +129 -0
  93. package/dist/orders/parcel-charge.d.ts +45 -0
  94. package/dist/orders/parcel-charge.js +55 -0
  95. package/dist/orders/plan-edit.d.ts +489 -0
  96. package/dist/orders/plan-edit.js +93 -0
  97. package/dist/orders/status.d.ts +235 -0
  98. package/dist/orders/status.js +96 -0
  99. package/dist/orders/surcharges.d.ts +75 -2
  100. package/dist/orders/surcharges.js +110 -4
  101. package/dist/payments/account-credit.d.ts +171 -0
  102. package/dist/payments/account-credit.js +107 -0
  103. package/dist/payments/index.d.ts +234 -9
  104. package/dist/payments/index.js +90 -0
  105. package/dist/platform/catalog.d.ts +148 -0
  106. package/dist/platform/catalog.js +74 -0
  107. package/dist/platform/entitlements.d.ts +111 -0
  108. package/dist/platform/entitlements.js +86 -0
  109. package/dist/platform/legal-entities.d.ts +139 -0
  110. package/dist/platform/legal-entities.js +126 -0
  111. package/dist/platform/legal-entity-numbering.d.ts +103 -0
  112. package/dist/platform/legal-entity-numbering.js +101 -0
  113. package/dist/platform/organization-membership.d.ts +80 -0
  114. package/dist/platform/organization-membership.js +71 -0
  115. package/dist/platform/organization-settings.d.ts +33 -0
  116. package/dist/platform/organization-settings.js +42 -0
  117. package/dist/platform/organization-sharing.d.ts +172 -0
  118. package/dist/platform/organization-sharing.js +120 -0
  119. package/dist/platform/organizations.d.ts +205 -0
  120. package/dist/platform/organizations.js +180 -0
  121. package/dist/platform/provision-store.d.ts +88 -8
  122. package/dist/platform/provision-store.js +12 -0
  123. package/dist/platform/reports.d.ts +46 -0
  124. package/dist/platform/reports.js +31 -0
  125. package/dist/platform/server-metrics.d.ts +58 -0
  126. package/dist/platform/server-metrics.js +11 -0
  127. package/dist/pos/index.d.ts +567 -0
  128. package/dist/pos/index.js +205 -0
  129. package/dist/pricing/index.d.ts +405 -0
  130. package/dist/pricing/index.js +351 -0
  131. package/dist/provenance/index.d.ts +93 -0
  132. package/dist/provenance/index.js +53 -0
  133. package/dist/reports/index.d.ts +199 -0
  134. package/dist/reports/index.js +154 -0
  135. package/dist/shipping/parcel-count-suggestion.d.ts +52 -0
  136. package/dist/shipping/parcel-count-suggestion.js +73 -0
  137. package/dist/shipping/parcel-rule.d.ts +48 -0
  138. package/dist/shipping/parcel-rule.js +91 -0
  139. package/dist/stock/index.d.ts +166 -0
  140. package/dist/stock/index.js +115 -0
  141. package/dist/stock/transfers.d.ts +520 -0
  142. package/dist/stock/transfers.js +437 -0
  143. package/dist/stored-value/index.d.ts +154 -0
  144. package/dist/stored-value/index.js +130 -0
  145. package/dist/tenant/capabilities.d.ts +10 -0
  146. package/dist/tenant/entitlements.d.ts +35 -0
  147. package/dist/tenant/entitlements.js +15 -0
  148. package/dist/tenant/field-groups.d.ts +64 -0
  149. package/dist/tenant/field-groups.js +34 -0
  150. package/dist/tenant/legal-entity.d.ts +32 -0
  151. package/dist/tenant/legal-entity.js +41 -0
  152. package/dist/tenant/notifications.d.ts +148 -0
  153. package/dist/tenant/notifications.js +79 -0
  154. package/dist/tenant/retention.d.ts +10 -4
  155. package/dist/tenant/settings-sources.d.ts +103 -0
  156. package/dist/tenant/settings-sources.js +92 -0
  157. package/dist/types.d.ts +86 -11
  158. package/dist/uploads/index.d.ts +11 -7
  159. package/dist/users/index.d.ts +104 -0
  160. package/dist/users/index.js +80 -0
  161. package/package.json +3 -2
@@ -0,0 +1,365 @@
1
+ import { ValidationError } from '@sixtyninecommerce/core';
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';
4
+ /** The engine's bound on one `exclude` or `include` call (`STORE_EXCLUSION_BATCH_LIMIT`). */
5
+ export const MAX_STORE_EXCLUSION_PRODUCTS = 500;
6
+ /**
7
+ * The engine's bounds on one `setAvailabilityInStores` or `availabilityInStores` call
8
+ * (`AVAILABILITY_*_LIMIT`).
9
+ */
10
+ export const MAX_AVAILABILITY_PRODUCTS = 100;
11
+ export const MAX_AVAILABILITY_STORES = 50;
12
+ /** The engine's bound on each list of one {@link ProductsModule.setCollections} call. */
13
+ export const MAX_PRODUCT_COLLECTION_IDS = 200;
14
+ export const PRODUCT_FIELDS = /* GraphQL */ `
15
+ id
16
+ ${CATALOGUE_OWNERSHIP_FIELDS}
17
+ createdAt
18
+ updatedAt
19
+ languageCode
20
+ name
21
+ slug
22
+ description
23
+ enabled
24
+ featuredAsset { ${ASSET_REF_FIELDS} }
25
+ assets { ${ASSET_REF_FIELDS} }
26
+ facetValues { ${FACET_VALUE_REF_FIELDS} }
27
+ optionGroups {
28
+ id
29
+ code
30
+ name
31
+ options {
32
+ id
33
+ code
34
+ name
35
+ }
36
+ }
37
+ translations {
38
+ id
39
+ languageCode
40
+ name
41
+ slug
42
+ description
43
+ }
44
+ excludedFromStore
45
+ variantList(options: { take: 1 }) {
46
+ totalItems
47
+ }
48
+ customFields {
49
+ availabilityState
50
+ availableFrom
51
+ preorderLimit
52
+ }
53
+ `;
54
+ export const PRODUCTS_QUERY = /* GraphQL */ `
55
+ query CatalogProducts($options: ProductListOptions) {
56
+ products(options: $options) {
57
+ items { ${PRODUCT_FIELDS} }
58
+ totalItems
59
+ }
60
+ }
61
+ `;
62
+ export const PRODUCT_QUERY = /* GraphQL */ `
63
+ query CatalogProduct($id: ID!) {
64
+ product(id: $id) { ${PRODUCT_FIELDS} }
65
+ }
66
+ `;
67
+ export const CREATE_PRODUCT_MUTATION = /* GraphQL */ `
68
+ mutation CatalogCreateProduct($input: CreateProductInput!) {
69
+ createProduct(input: $input) { ${PRODUCT_FIELDS} }
70
+ }
71
+ `;
72
+ export const UPDATE_PRODUCT_MUTATION = /* GraphQL */ `
73
+ mutation CatalogUpdateProduct($input: UpdateProductInput!) {
74
+ updateProduct(input: $input) { ${PRODUCT_FIELDS} }
75
+ }
76
+ `;
77
+ export const DELETE_PRODUCT_MUTATION = /* GraphQL */ `
78
+ mutation CatalogDeleteProduct($id: ID!) {
79
+ deleteProduct(id: $id) { ${DELETION_FIELDS} }
80
+ }
81
+ `;
82
+ export const CLONE_PRODUCT_MUTATION = /* GraphQL */ `
83
+ mutation CatalogCloneProduct($id: ID!) {
84
+ cloneProduct(id: $id)
85
+ }
86
+ `;
87
+ export const ADD_OPTION_GROUP_MUTATION = /* GraphQL */ `
88
+ mutation CatalogAddOptionGroup($productId: ID!, $optionGroupId: ID!) {
89
+ addOptionGroupToProduct(productId: $productId, optionGroupId: $optionGroupId) { ${PRODUCT_FIELDS} }
90
+ }
91
+ `;
92
+ export const REMOVE_OPTION_GROUP_MUTATION = /* GraphQL */ `
93
+ mutation CatalogRemoveOptionGroup($productId: ID!, $optionGroupId: ID!, $force: Boolean) {
94
+ removeOptionGroupFromProduct(productId: $productId, optionGroupId: $optionGroupId, force: $force) {
95
+ ... on Product { ${PRODUCT_FIELDS} }
96
+ ... on ErrorResult {
97
+ errorCode
98
+ message
99
+ }
100
+ }
101
+ }
102
+ `;
103
+ export const EXCLUDE_PRODUCTS_FROM_STORE_MUTATION = /* GraphQL */ `
104
+ mutation CatalogExcludeProductsFromStore($productIds: [ID!]!) {
105
+ excludeProductsFromStore(productIds: $productIds) { ${PRODUCT_FIELDS} }
106
+ }
107
+ `;
108
+ export const INCLUDE_PRODUCTS_IN_STORE_MUTATION = /* GraphQL */ `
109
+ mutation CatalogIncludeProductsInStore($productIds: [ID!]!) {
110
+ includeProductsInStore(productIds: $productIds) { ${PRODUCT_FIELDS} }
111
+ }
112
+ `;
113
+ export const SET_ORGANIZATION_PRODUCT_AVAILABILITY_MUTATION = /* GraphQL */ `
114
+ mutation CatalogSetOrganizationProductAvailability(
115
+ $productIds: [ID!]!
116
+ $channelIds: [ID!]!
117
+ $available: Boolean!
118
+ ) {
119
+ setOrganizationProductAvailability(
120
+ productIds: $productIds
121
+ channelIds: $channelIds
122
+ available: $available
123
+ ) {
124
+ productId
125
+ channelId
126
+ action
127
+ }
128
+ }
129
+ `;
130
+ export const ORGANIZATION_PRODUCT_AVAILABILITY_QUERY = /* GraphQL */ `
131
+ query CatalogOrganizationProductAvailability($productIds: [ID!]!, $channelIds: [ID!]!) {
132
+ organizationProductAvailability(productIds: $productIds, channelIds: $channelIds) {
133
+ productId
134
+ channelId
135
+ state
136
+ localCopyId
137
+ }
138
+ }
139
+ `;
140
+ export const MAKE_LOCAL_COPY_MUTATION = /* GraphQL */ `
141
+ mutation CatalogMakeLocalCopy($productId: ID!, $skus: [LocalCopySkuInput!]) {
142
+ makeLocalCopy(productId: $productId, skus: $skus) {
143
+ product { ${PRODUCT_FIELDS} }
144
+ originalProductId
145
+ original
146
+ droppedOrganizationPropertyCount
147
+ }
148
+ }
149
+ `;
150
+ export const SET_PRODUCT_COLLECTIONS_MUTATION = /* GraphQL */ `
151
+ mutation CatalogSetProductCollections(
152
+ $productId: ID!
153
+ $collectionIds: [ID!]!
154
+ $fromCollectionIds: [ID!]
155
+ ) {
156
+ setProductCollections(
157
+ productId: $productId
158
+ collectionIds: $collectionIds
159
+ fromCollectionIds: $fromCollectionIds
160
+ ) {
161
+ added
162
+ removed
163
+ unchanged
164
+ notRemoved {
165
+ collectionId
166
+ reason
167
+ }
168
+ }
169
+ }
170
+ `;
171
+ /**
172
+ * Lift the availability custom fields to first-class fields. A server older
173
+ * than the pre-order engine has no such fields and would refuse the document
174
+ * outright, so the defaults below only cover a NULL value, never an absent one.
175
+ */
176
+ export function toProduct(wire) {
177
+ const { variantList, customFields, ...rest } = wire;
178
+ return {
179
+ ...rest,
180
+ variantCount: variantList.totalItems,
181
+ availabilityState: customFields?.availabilityState ?? 'none',
182
+ availableFrom: customFields?.availableFrom ?? null,
183
+ preorderLimit: customFields?.preorderLimit ?? null,
184
+ };
185
+ }
186
+ function toWireInput(input) {
187
+ const { availabilityState, availableFrom, preorderLimit, ...rest } = input;
188
+ const customFields = availabilityCustomFields({
189
+ availabilityState,
190
+ availableFrom,
191
+ preorderLimit,
192
+ });
193
+ return Object.keys(customFields).length > 0 ? { ...rest, customFields } : rest;
194
+ }
195
+ export function productFilter(filter) {
196
+ const out = {};
197
+ if (!filter)
198
+ return out;
199
+ if (filter.ids !== undefined)
200
+ out.id = { in: filter.ids };
201
+ if (filter.name !== undefined)
202
+ out.name = { contains: filter.name };
203
+ if (filter.sku !== undefined)
204
+ out.sku = { contains: filter.sku };
205
+ if (filter.enabled !== undefined)
206
+ out.enabled = { eq: filter.enabled };
207
+ if (filter.facetValueIds !== undefined)
208
+ out.facetValueId = { in: filter.facetValueIds };
209
+ if (filter.availabilityState !== undefined) {
210
+ out.availabilityState = { eq: filter.availabilityState };
211
+ }
212
+ return out;
213
+ }
214
+ function refuse(code, message) {
215
+ return new ValidationError({ code, message, status: 400, service: 'vendure' });
216
+ }
217
+ /** Distinct ids the way the engine counts them: it stores a uuid lower-case. */
218
+ function distinctCount(ids) {
219
+ return new Set(ids.map((id) => String(id).toLowerCase())).size;
220
+ }
221
+ function assertExclusionBatch(productIds) {
222
+ const count = distinctCount(productIds);
223
+ if (count <= MAX_STORE_EXCLUSION_PRODUCTS)
224
+ return;
225
+ throw refuse('STORE_EXCLUSION_INPUT_INVALID', `Exclude or include at most ${String(MAX_STORE_EXCLUSION_PRODUCTS)} products per call; this one named ${String(count)}. Split it into smaller calls. Nothing was sent.`);
226
+ }
227
+ function assertAvailabilityInput(input, verb = 'set') {
228
+ const bounds = [
229
+ ['products', input?.productIds, MAX_AVAILABILITY_PRODUCTS],
230
+ ['stores', input?.channelIds, MAX_AVAILABILITY_STORES],
231
+ ];
232
+ for (const [noun, ids, limit] of bounds) {
233
+ if (!Array.isArray(ids) || ids.length === 0) {
234
+ throw refuse('ORGANIZATION_AVAILABILITY_INPUT_INVALID', `Name at least one of the ${noun}. Nothing was sent.`);
235
+ }
236
+ const count = distinctCount(ids);
237
+ if (count > limit) {
238
+ throw refuse('ORGANIZATION_AVAILABILITY_INPUT_INVALID', `At most ${String(limit)} ${noun} can be ${verb} in one call; this one named ${String(count)}. Split it into smaller calls. Nothing was sent.`);
239
+ }
240
+ }
241
+ if (verb === 'set' && typeof input.available !== 'boolean') {
242
+ throw refuse('ORGANIZATION_AVAILABILITY_INPUT_INVALID', '`available` must be true (offer the products) or false (stop offering them). Nothing was sent.');
243
+ }
244
+ }
245
+ /** The engine's check of each list (`PRODUCT_COLLECTIONS_INPUT_INVALID`), before sending. */
246
+ function assertCollectionLists(lists) {
247
+ for (const [argument, ids] of Object.entries(lists)) {
248
+ if (ids === undefined)
249
+ continue;
250
+ if (!Array.isArray(ids)) {
251
+ throw refuse('PRODUCT_COLLECTIONS_INPUT_INVALID', `${argument} must be a list of collection ids. Nothing was sent.`);
252
+ }
253
+ if (ids.length > MAX_PRODUCT_COLLECTION_IDS) {
254
+ throw refuse('PRODUCT_COLLECTIONS_INPUT_INVALID', `Name at most ${String(MAX_PRODUCT_COLLECTION_IDS)} collections in ${argument}; this call named ${String(ids.length)}. Nothing was sent.`);
255
+ }
256
+ if (distinctCount(ids) !== ids.length) {
257
+ throw refuse('PRODUCT_COLLECTIONS_INPUT_INVALID', `${argument} names a collection more than once. Name each collection once. Nothing was sent.`);
258
+ }
259
+ }
260
+ }
261
+ export function createProductsModule(transport) {
262
+ const write = async (document, field, variables, options) => {
263
+ return toProduct(await fieldOf(transport, document, field, variables, options));
264
+ };
265
+ return {
266
+ async list(options) {
267
+ const filter = productFilter(options?.filter);
268
+ return vendureListPage(transport, PRODUCTS_QUERY, 'products', (skip, take) => ({
269
+ options: {
270
+ skip,
271
+ take,
272
+ ...(Object.keys(filter).length > 0 ? { filter, filterOperator: 'AND' } : {}),
273
+ ...(options?.sort ? { sort: { [options.sort.field]: options.sort.direction } } : {}),
274
+ },
275
+ }), options, toProduct);
276
+ },
277
+ async get(id, options) {
278
+ const product = await fieldOf(transport, PRODUCT_QUERY, 'product', { id }, options);
279
+ return product ? toProduct(product) : null;
280
+ },
281
+ // `async` so every refusal below — including `toWireInput`'s datetime
282
+ // check — arrives as a rejection, never as a synchronous throw a caller's
283
+ // `.catch()` would miss.
284
+ async create(input, options) {
285
+ if (!Array.isArray(input.translations) || input.translations.length === 0) {
286
+ throw new ValidationError({
287
+ code: 'INVALID_PRODUCT_INPUT',
288
+ message: 'A product needs at least one translation (languageCode + name).',
289
+ status: 400,
290
+ service: 'vendure',
291
+ });
292
+ }
293
+ return write(CREATE_PRODUCT_MUTATION, 'createProduct', { input: toWireInput(input) }, options);
294
+ },
295
+ async update(input, options) {
296
+ return write(UPDATE_PRODUCT_MUTATION, 'updateProduct', { input: toWireInput(input) }, options);
297
+ },
298
+ async delete(id, options) {
299
+ assertDeleted('PRODUCT_NOT_DELETED', [
300
+ await fieldOf(transport, DELETE_PRODUCT_MUTATION, 'deleteProduct', { id }, options),
301
+ ]);
302
+ },
303
+ async clone(id, options) {
304
+ return fieldOf(transport, CLONE_PRODUCT_MUTATION, 'cloneProduct', { id }, options);
305
+ },
306
+ addOptionGroup(productId, optionGroupId, options) {
307
+ return write(ADD_OPTION_GROUP_MUTATION, 'addOptionGroupToProduct', { productId, optionGroupId }, options);
308
+ },
309
+ async removeOptionGroup(productId, optionGroupId, options) {
310
+ const result = await fieldOf(transport, REMOVE_OPTION_GROUP_MUTATION, 'removeOptionGroupFromProduct', { productId, optionGroupId, force: options?.force ?? false }, options);
311
+ return toProduct(unwrapResult(result));
312
+ },
313
+ async exclude(productIds, options) {
314
+ assertExclusionBatch(productIds);
315
+ if (productIds.length === 0)
316
+ return [];
317
+ const rows = await fieldOf(transport, EXCLUDE_PRODUCTS_FROM_STORE_MUTATION, 'excludeProductsFromStore', { productIds }, options);
318
+ return rows.map(toProduct);
319
+ },
320
+ async include(productIds, options) {
321
+ assertExclusionBatch(productIds);
322
+ if (productIds.length === 0)
323
+ return [];
324
+ const rows = await fieldOf(transport, INCLUDE_PRODUCTS_IN_STORE_MUTATION, 'includeProductsInStore', { productIds }, options);
325
+ return rows.map(toProduct);
326
+ },
327
+ async setAvailabilityInStores(input, options) {
328
+ assertAvailabilityInput(input);
329
+ return fieldOf(transport, SET_ORGANIZATION_PRODUCT_AVAILABILITY_MUTATION, 'setOrganizationProductAvailability', {
330
+ productIds: input.productIds,
331
+ channelIds: input.channelIds,
332
+ available: input.available,
333
+ }, options);
334
+ },
335
+ async availabilityInStores(input, options) {
336
+ assertAvailabilityInput(input, 'read');
337
+ return fieldOf(transport, ORGANIZATION_PRODUCT_AVAILABILITY_QUERY, 'organizationProductAvailability', { productIds: input.productIds, channelIds: input.channelIds }, options);
338
+ },
339
+ async makeLocalCopy(productId, options) {
340
+ const { skus, ...call } = options ?? {};
341
+ const result = await fieldOf(transport, MAKE_LOCAL_COPY_MUTATION, 'makeLocalCopy', {
342
+ productId,
343
+ skus: skus === undefined
344
+ ? null
345
+ : skus.map(({ productVariantId, sku }) => ({ productVariantId, sku })),
346
+ }, call);
347
+ return { ...result, product: toProduct(result.product) };
348
+ },
349
+ async setCollections(productId, collectionIds, options) {
350
+ const { from, ...call } = options ?? {};
351
+ assertCollectionLists({ collectionIds, fromCollectionIds: from });
352
+ return fieldOf(transport, SET_PRODUCT_COLLECTIONS_MUTATION, 'setProductCollections', {
353
+ productId,
354
+ collectionIds: [...collectionIds],
355
+ fromCollectionIds: from === undefined ? null : [...from],
356
+ }, call);
357
+ },
358
+ bulk(input, options) {
359
+ return bulkProducts(transport, input, options);
360
+ },
361
+ bulkRun(runId, options) {
362
+ return readProductBulkRun(transport, runId, options);
363
+ },
364
+ };
365
+ }
@@ -0,0 +1,234 @@
1
+ import { type Page, type Transport } from '@sixtyninecommerce/core';
2
+ import type { Call } from '../types.js';
3
+ import { type LanguageCode } from './types.js';
4
+ /**
5
+ * The field types a property may have, as the properties plugin stores them.
6
+ * `content_item` and `product` hold a list of ids; the rest hold one value.
7
+ */
8
+ export type PropertyFieldType = string;
9
+ export interface PropertyConfig {
10
+ /** For `enum` / `multi_enum`. */
11
+ options?: Array<{
12
+ value: string;
13
+ label: string;
14
+ }> | null;
15
+ min?: number | null;
16
+ max?: number | null;
17
+ maxLength?: number | null;
18
+ allowedCollectionIds?: string[] | null;
19
+ filterableFieldSlugs?: string[] | null;
20
+ /** Stored verbatim; its keys are the caller's own. */
21
+ productFilter?: unknown;
22
+ }
23
+ export interface Property {
24
+ id: string;
25
+ createdAt: string;
26
+ updatedAt: string;
27
+ /** Unique within the group; the storefront's key for the value. */
28
+ slug: string;
29
+ fieldType: PropertyFieldType;
30
+ /** A filterable property mirrors its values onto a facet (`facetId`). */
31
+ isFilterable: boolean;
32
+ isRequired: boolean;
33
+ position: number;
34
+ config: PropertyConfig | null;
35
+ facetId: string | null;
36
+ translations: Array<{
37
+ id: string;
38
+ languageCode: LanguageCode;
39
+ name: string;
40
+ helpText: string | null;
41
+ }>;
42
+ }
43
+ export interface PropertyGroup {
44
+ id: string;
45
+ createdAt: string;
46
+ updatedAt: string;
47
+ position: number;
48
+ translations: Array<{
49
+ id: string;
50
+ languageCode: LanguageCode;
51
+ name: string;
52
+ }>;
53
+ properties: Property[];
54
+ /**
55
+ * Whether the group, its properties and their values are written from the active channel. False
56
+ * for a workspace group a member store reads on a product it holds from its organization
57
+ * (`values.get`; decision 80): change the group, its properties and its values in the
58
+ * workspace. A `values.set` changing one of its values is refused whole
59
+ * (`MANAGED_BY_ORGANIZATION`); one echoing them unchanged is accepted. `groups.list` answers
60
+ * the store's own groups only, so their `editableHere` is true. Unlike
61
+ * `CatalogueOwnership.editableHere` it ignores the caller's permissions.
62
+ */
63
+ editableHere: boolean;
64
+ }
65
+ /** One stored value. `languageCode` null: a language-neutral value (every non-text type). */
66
+ export interface ProductPropertyValue {
67
+ id: string;
68
+ propertyId: string;
69
+ languageCode: LanguageCode | null;
70
+ /** Stored verbatim — never case-converted by this SDK. */
71
+ value: unknown;
72
+ }
73
+ export interface ProductPropertyGroupValues {
74
+ group: PropertyGroup;
75
+ values: ProductPropertyValue[];
76
+ }
77
+ export interface CreatePropertyGroupInput {
78
+ translations: Array<{
79
+ languageCode: LanguageCode;
80
+ name: string;
81
+ }>;
82
+ position?: number;
83
+ }
84
+ export interface UpdatePropertyGroupInput {
85
+ id: string;
86
+ translations?: Array<{
87
+ languageCode: LanguageCode;
88
+ name: string;
89
+ }>;
90
+ position?: number;
91
+ }
92
+ export interface CreatePropertyInput {
93
+ groupId: string;
94
+ fieldType: PropertyFieldType;
95
+ slug: string;
96
+ isFilterable?: boolean;
97
+ isRequired?: boolean;
98
+ config?: PropertyConfig;
99
+ position?: number;
100
+ translations: Array<{
101
+ languageCode: LanguageCode;
102
+ name: string;
103
+ helpText?: string;
104
+ }>;
105
+ }
106
+ export interface UpdatePropertyInput {
107
+ id: string;
108
+ slug?: string;
109
+ isFilterable?: boolean;
110
+ isRequired?: boolean;
111
+ config?: PropertyConfig;
112
+ position?: number;
113
+ translations?: Array<{
114
+ languageCode: LanguageCode;
115
+ name: string;
116
+ helpText?: string;
117
+ }>;
118
+ }
119
+ export interface SetPropertyValueInput {
120
+ propertyId: string;
121
+ /**
122
+ * The value's language, for a translatable (text) type. Ignored — stored
123
+ * language-neutral — for every other type, whatever is sent.
124
+ */
125
+ languageCode?: LanguageCode | null;
126
+ /** `null` clears it. A single id for a `content_item`/`product` property is stored as a one-item list. */
127
+ value: unknown;
128
+ /** The facet value's label, for a filterable `content_item`/`product` property. */
129
+ displayName?: string;
130
+ /** Per content item, the fields a field-level facet is built from. */
131
+ fieldValues?: Record<string, Record<string, unknown>>;
132
+ }
133
+ /** What {@link CatalogModule.syncContentFacets} did. */
134
+ export interface ContentFacetSyncResult {
135
+ propertyId: string;
136
+ /**
137
+ * How many content items (a `content_item` property) or products (a `product` property) the
138
+ * property's values name. Each was given its current label; 0 when the property names none, and
139
+ * then nothing was written.
140
+ */
141
+ items: number;
142
+ /**
143
+ * The named ids the label source did not answer: a content item that no longer exists, an id
144
+ * that is not one, a product this store does not hold. Their filter values keep the label they
145
+ * have, and a new one is labelled with the id itself.
146
+ */
147
+ unresolved: string[];
148
+ /**
149
+ * How many field-level facets were brought in line with the content items' field values (a
150
+ * `content_item` property whose saved configuration names fields to filter by), else 0.
151
+ */
152
+ fieldFacets: number;
153
+ }
154
+ /**
155
+ * Product properties: typed, grouped attributes a merchant defines once per
156
+ * store and fills per product (the properties plugin). Reading needs
157
+ * `ReadCatalog`; creating `CreateCatalog`, updating (values included)
158
+ * `UpdateCatalog`, deleting `DeleteCatalog`.
159
+ *
160
+ * Refusals of the group and property doors, GraphQL errors mapped by `core`:
161
+ * - `PROPERTY_GROUP_NOT_FOUND` → `NotFoundError` (404): `groups.update`,
162
+ * `create` (its `groupId`), `reorder` (its `groupId`) and `groups.reorder`
163
+ * (any of its ids, all checked before any group moves) name a group this
164
+ * store does not own. An older platform release skips such an id in
165
+ * `groups.reorder` and answers the store's groups as they are.
166
+ * - `PROPERTY_NOT_FOUND` → `NotFoundError` (404): `update` names a property of
167
+ * no group this store owns.
168
+ * - `PROPERTY_FILTERABLE_INVALID` → `ValidationError` (400): `isFilterable` on
169
+ * a field type that cannot carry a facet.
170
+ * - `MANAGED_BY_ORGANIZATION` → `BusinessRuleError` (422): in a store of an
171
+ * organization, any of those doors given a group or property of the
172
+ * workspace's groups it reads (`editableHere` false), `groups.reorder`
173
+ * included: edit it in the workspace.
174
+ *
175
+ * A platform release older than these codes sends them uncoded, as a
176
+ * non-retryable `UpstreamError` (status 500, `GRAPHQL_ERROR`) whose message is
177
+ * the engine's sentence. A delete of a group or property the store does not
178
+ * own is a `ConflictError` (`PROPERTY_GROUP_NOT_DELETED` /
179
+ * `PROPERTY_NOT_DELETED`), or `MANAGED_BY_ORGANIZATION` for the workspace's.
180
+ *
181
+ * `values.set` checks every value before it writes any. It answers
182
+ * `ENTITY_NOT_FOUND` for a product outside this store and for a `propertyId`
183
+ * this store neither owns nor reads. In a store of an organization it
184
+ * ignores an unchanged echo of a workspace property's value, but refuses a
185
+ * change to one with `MANAGED_BY_ORGANIZATION`, and the whole call with it.
186
+ */
187
+ export interface PropertiesModule {
188
+ groups: {
189
+ /** The complete list, as one terminal page — the query takes no paging. */
190
+ list(options?: Call): Promise<Page<PropertyGroup>>;
191
+ get(id: string, options?: Call): Promise<PropertyGroup | null>;
192
+ create(input: CreatePropertyGroupInput, options?: Call): Promise<PropertyGroup>;
193
+ update(input: UpdatePropertyGroupInput, options?: Call): Promise<PropertyGroup>;
194
+ /** Deletes the group AND every property in it, with their values. */
195
+ delete(id: string, options?: Call): Promise<void>;
196
+ /**
197
+ * Pass EVERY group id of the store, in the order wanted. Only the store's own groups:
198
+ * a workspace group (`editableHere` false) or another store's id refuses the whole call.
199
+ */
200
+ reorder(ids: string[], options?: Call): Promise<PropertyGroup[]>;
201
+ };
202
+ create(input: CreatePropertyInput, options?: Call): Promise<Property>;
203
+ update(input: UpdatePropertyInput, options?: Call): Promise<Property>;
204
+ delete(id: string, options?: Call): Promise<void>;
205
+ /** Pass every property id of the group, in the order wanted. */
206
+ reorder(groupId: string, propertyIds: string[], options?: Call): Promise<Property[]>;
207
+ values: {
208
+ /**
209
+ * Every group of the store, each with this product's stored values in every language. For a
210
+ * product the store holds from its organization, the workspace's groups and values come too,
211
+ * with `group.editableHere` false.
212
+ */
213
+ get(productId: string, options?: Call): Promise<ProductPropertyGroupValues[]>;
214
+ /** Upsert the given values; properties not named keep theirs. */
215
+ set(productId: string, values: SetPropertyValueInput[], options?: Call): Promise<void>;
216
+ };
217
+ }
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";
223
+ 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";
227
+ 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";
230
+ export declare const SET_PRODUCT_PROPERTY_VALUES_MUTATION = "\n mutation CatalogSetProductPropertyValues($productId: ID!, $values: [SetProductPropertyValueInput!]!) {\n setProductPropertyValues(productId: $productId, values: $values)\n }\n";
231
+ 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
+ /** `catalog.syncContentFacets`; its contract is on {@link CatalogModule.syncContentFacets}. */
233
+ export declare function syncContentFacets(transport: Transport, propertyId: string, options?: Call): Promise<ContentFacetSyncResult>;
234
+ export declare function createPropertiesModule(transport: Transport): PropertiesModule;