@sixtyninecommerce/admin 0.10.0 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (302) hide show
  1. package/README.md +5772 -118
  2. package/dist/auth/index.d.ts +7 -1
  3. package/dist/auth/index.js +8 -1
  4. package/dist/catalog/assets.d.ts +120 -8
  5. package/dist/catalog/assets.js +98 -12
  6. package/dist/catalog/collections.d.ts +279 -14
  7. package/dist/catalog/collections.js +246 -15
  8. package/dist/catalog/documents.d.ts +142 -0
  9. package/dist/catalog/documents.js +114 -0
  10. package/dist/catalog/index.d.ts +54 -6
  11. package/dist/catalog/index.js +10 -1
  12. package/dist/catalog/metadata.d.ts +244 -0
  13. package/dist/catalog/metadata.js +220 -0
  14. package/dist/catalog/products.d.ts +159 -19
  15. package/dist/catalog/products.js +88 -13
  16. package/dist/catalog/properties.d.ts +140 -14
  17. package/dist/catalog/properties.js +65 -1
  18. package/dist/catalog/recommendations.d.ts +288 -0
  19. package/dist/catalog/recommendations.js +237 -0
  20. package/dist/catalog/reviews.d.ts +381 -0
  21. package/dist/catalog/reviews.js +325 -0
  22. package/dist/catalog/types.d.ts +17 -1
  23. package/dist/catalog/types.js +10 -0
  24. package/dist/catalog/variant-find.d.ts +136 -0
  25. package/dist/catalog/variant-find.js +239 -0
  26. package/dist/catalog/variants.d.ts +293 -26
  27. package/dist/catalog/variants.js +164 -7
  28. package/dist/catalog/visibility.d.ts +38 -7
  29. package/dist/catalog/visibility.js +19 -1
  30. package/dist/client.d.ts +121 -9
  31. package/dist/client.js +53 -14
  32. package/dist/content/assets.d.ts +285 -0
  33. package/dist/content/assets.js +181 -0
  34. package/dist/content/forms.d.ts +332 -1
  35. package/dist/content/forms.js +140 -4
  36. package/dist/content/items.d.ts +292 -0
  37. package/dist/content/items.js +170 -0
  38. package/dist/content/menus.d.ts +254 -0
  39. package/dist/content/menus.js +70 -0
  40. package/dist/content/strings.d.ts +137 -0
  41. package/dist/content/strings.js +82 -0
  42. package/dist/content/types.d.ts +282 -0
  43. package/dist/content/types.js +100 -0
  44. package/dist/customers/age-verification.d.ts +66 -0
  45. package/dist/customers/age-verification.js +79 -0
  46. package/dist/customers/credit.d.ts +263 -0
  47. package/dist/customers/credit.js +244 -0
  48. package/dist/customers/delivery-preferences.d.ts +120 -0
  49. package/dist/customers/delivery-preferences.js +97 -0
  50. package/dist/customers/delivery-terms.d.ts +44 -0
  51. package/dist/customers/delivery-terms.js +32 -0
  52. package/dist/customers/groups.d.ts +207 -0
  53. package/dist/customers/groups.js +438 -0
  54. package/dist/customers/index.d.ts +462 -37
  55. package/dist/customers/index.js +317 -40
  56. package/dist/customers/invitation-status.d.ts +57 -0
  57. package/dist/customers/invitation-status.js +21 -0
  58. package/dist/customers/notes.d.ts +67 -0
  59. package/dist/customers/notes.js +51 -0
  60. package/dist/customers/notification-recipients.d.ts +97 -0
  61. package/dist/customers/notification-recipients.js +84 -0
  62. package/dist/customers/saved-lists.d.ts +42 -0
  63. package/dist/customers/saved-lists.js +61 -0
  64. package/dist/customers/vat.d.ts +83 -0
  65. package/dist/customers/vat.js +46 -0
  66. package/dist/customers/wire.d.ts +20 -0
  67. package/dist/customers/wire.js +35 -0
  68. package/dist/documents/ar.d.ts +307 -0
  69. package/dist/documents/ar.js +254 -0
  70. package/dist/documents/emails.d.ts +150 -0
  71. package/dist/documents/emails.js +220 -0
  72. package/dist/documents/index.d.ts +744 -21
  73. package/dist/documents/index.js +438 -24
  74. package/dist/documents/pdf.d.ts +2 -0
  75. package/dist/documents/pdf.js +11 -0
  76. package/dist/fulfillments/create.d.ts +167 -0
  77. package/dist/fulfillments/create.js +216 -0
  78. package/dist/fulfillments/index.d.ts +163 -11
  79. package/dist/fulfillments/index.js +135 -6
  80. package/dist/fulfillments/payment-policy.d.ts +64 -0
  81. package/dist/fulfillments/payment-policy.js +140 -0
  82. package/dist/fulfillments/warehouse.d.ts +724 -0
  83. package/dist/fulfillments/warehouse.js +914 -0
  84. package/dist/geo/index.d.ts +119 -0
  85. package/dist/geo/index.js +107 -0
  86. package/dist/index.d.ts +147 -39
  87. package/dist/index.js +51 -12
  88. package/dist/integrations/afas-actions.d.ts +234 -0
  89. package/dist/integrations/afas-actions.js +310 -0
  90. package/dist/integrations/afas-types.d.ts +483 -0
  91. package/dist/integrations/afas-types.js +5 -0
  92. package/dist/integrations/afas.d.ts +234 -0
  93. package/dist/integrations/afas.js +290 -0
  94. package/dist/integrations/connections.d.ts +103 -0
  95. package/dist/integrations/connections.js +55 -0
  96. package/dist/integrations/esp.d.ts +313 -0
  97. package/dist/integrations/esp.js +169 -0
  98. package/dist/integrations/exact-online-types.d.ts +965 -0
  99. package/dist/integrations/exact-online-types.js +6 -0
  100. package/dist/integrations/exact-online.d.ts +784 -0
  101. package/dist/integrations/exact-online.js +1089 -0
  102. package/dist/integrations/index.d.ts +44 -3
  103. package/dist/integrations/index.js +23 -3
  104. package/dist/integrations/pipeline-discoveries.d.ts +65 -0
  105. package/dist/integrations/pipeline-discoveries.js +65 -0
  106. package/dist/integrations/pipeline-keys.d.ts +13 -0
  107. package/dist/integrations/pipeline-keys.js +22 -0
  108. package/dist/integrations/pipeline-runs.d.ts +119 -0
  109. package/dist/integrations/pipeline-runs.js +87 -0
  110. package/dist/integrations/pipelines.d.ts +87 -15
  111. package/dist/integrations/pipelines.js +41 -22
  112. package/dist/integrations/price-import.d.ts +223 -0
  113. package/dist/integrations/price-import.js +124 -0
  114. package/dist/integrations/types.d.ts +367 -3
  115. package/dist/integrations/types.js +4 -3
  116. package/dist/locations/index.d.ts +66 -0
  117. package/dist/locations/index.js +31 -0
  118. package/dist/meta/index.d.ts +172 -0
  119. package/dist/metafields/index.d.ts +8 -1
  120. package/dist/metafields/index.js +10 -0
  121. package/dist/orders/cancel-with-money.d.ts +237 -0
  122. package/dist/orders/cancel-with-money.js +155 -0
  123. package/dist/orders/commit-edit.d.ts +23 -1
  124. package/dist/orders/commit-edit.js +19 -2
  125. package/dist/orders/deposit-policy.d.ts +3 -1
  126. package/dist/orders/drafts.d.ts +800 -0
  127. package/dist/orders/drafts.js +771 -0
  128. package/dist/orders/fields.d.ts +245 -0
  129. package/dist/orders/fields.js +188 -0
  130. package/dist/orders/get.d.ts +29 -19
  131. package/dist/orders/get.js +17 -8
  132. package/dist/orders/index.d.ts +66 -15
  133. package/dist/orders/index.js +25 -2
  134. package/dist/orders/lifecycle.d.ts +128 -0
  135. package/dist/orders/lifecycle.js +158 -0
  136. package/dist/orders/line-price-preview.d.ts +7 -2
  137. package/dist/orders/list.d.ts +243 -0
  138. package/dist/orders/list.js +487 -0
  139. package/dist/orders/manual-discounts.d.ts +13 -5
  140. package/dist/orders/manual-pricing.d.ts +5 -0
  141. package/dist/orders/money-view.d.ts +149 -0
  142. package/dist/orders/money-view.js +99 -0
  143. package/dist/orders/order-entry.d.ts +20 -7
  144. package/dist/orders/plan-edit.d.ts +34 -2
  145. package/dist/orders/plan-edit.js +112 -11
  146. package/dist/orders/settings.d.ts +201 -0
  147. package/dist/orders/settings.js +325 -0
  148. package/dist/orders/status.d.ts +159 -13
  149. package/dist/orders/status.js +21 -0
  150. package/dist/orders/surcharges.d.ts +20 -1
  151. package/dist/orders/surcharges.js +37 -11
  152. package/dist/payments/account-credit.d.ts +46 -0
  153. package/dist/payments/account-credit.js +42 -0
  154. package/dist/payments/ar.d.ts +292 -0
  155. package/dist/payments/ar.js +223 -0
  156. package/dist/payments/connected-account.d.ts +47 -0
  157. package/dist/payments/connected-account.js +61 -0
  158. package/dist/payments/credit-noted-fields.d.ts +19 -0
  159. package/dist/payments/credit-noted-fields.js +69 -0
  160. package/dist/payments/customer-consent.d.ts +30 -0
  161. package/dist/payments/customer-consent.js +49 -0
  162. package/dist/payments/index.d.ts +340 -19
  163. package/dist/payments/index.js +151 -1
  164. package/dist/payments/methods.d.ts +303 -8
  165. package/dist/payments/methods.js +337 -7
  166. package/dist/payments/mollie-methods.d.ts +159 -0
  167. package/dist/payments/mollie-methods.js +141 -0
  168. package/dist/payments/payment-links.d.ts +180 -0
  169. package/dist/payments/payment-links.js +97 -0
  170. package/dist/payments/provider-keys.d.ts +150 -0
  171. package/dist/payments/provider-keys.js +118 -0
  172. package/dist/payments/providers.d.ts +86 -11
  173. package/dist/payments/providers.js +29 -0
  174. package/dist/payments/requests.d.ts +147 -0
  175. package/dist/payments/requests.js +93 -0
  176. package/dist/payments/store-credit-fields.d.ts +15 -0
  177. package/dist/payments/store-credit-fields.js +51 -0
  178. package/dist/pickups/index.d.ts +106 -0
  179. package/dist/pickups/index.js +108 -0
  180. package/dist/platform/channels.d.ts +132 -0
  181. package/dist/platform/channels.js +128 -0
  182. package/dist/platform/email-gateways.d.ts +165 -0
  183. package/dist/platform/email-gateways.js +78 -0
  184. package/dist/platform/geo.d.ts +123 -0
  185. package/dist/platform/geo.js +144 -0
  186. package/dist/platform/tax.d.ts +162 -0
  187. package/dist/platform/tax.js +169 -0
  188. package/dist/pos/anomalies.d.ts +158 -0
  189. package/dist/pos/anomalies.js +64 -0
  190. package/dist/pos/assisted-selling.d.ts +79 -0
  191. package/dist/pos/assisted-selling.js +38 -0
  192. package/dist/pos/exchanges.d.ts +108 -0
  193. package/dist/pos/exchanges.js +22 -0
  194. package/dist/pos/gift-cards.d.ts +135 -0
  195. package/dist/pos/gift-cards.js +47 -0
  196. package/dist/pos/index.d.ts +2370 -58
  197. package/dist/pos/index.js +854 -7
  198. package/dist/pos/no-receipt.d.ts +94 -0
  199. package/dist/pos/no-receipt.js +25 -0
  200. package/dist/pos/offline-sales.d.ts +228 -0
  201. package/dist/pos/offline-sales.js +170 -0
  202. package/dist/pos/pay-by-link.d.ts +143 -0
  203. package/dist/pos/pay-by-link.js +57 -0
  204. package/dist/pos/quick-keys.d.ts +103 -0
  205. package/dist/pos/quick-keys.js +45 -0
  206. package/dist/pos/reports.d.ts +360 -0
  207. package/dist/pos/reports.js +62 -0
  208. package/dist/pos/returns.d.ts +244 -0
  209. package/dist/pos/returns.js +83 -0
  210. package/dist/pricing/bulk-coupons.d.ts +139 -0
  211. package/dist/pricing/bulk-coupons.js +112 -0
  212. package/dist/pricing/index.d.ts +174 -4
  213. package/dist/pricing/index.js +174 -1
  214. package/dist/pricing/price-lists.d.ts +606 -0
  215. package/dist/pricing/price-lists.js +600 -0
  216. package/dist/purchasing/alerts.d.ts +104 -0
  217. package/dist/purchasing/alerts.js +88 -0
  218. package/dist/purchasing/backorders.d.ts +61 -0
  219. package/dist/purchasing/backorders.js +92 -0
  220. package/dist/purchasing/cost.d.ts +110 -0
  221. package/dist/purchasing/cost.js +86 -0
  222. package/dist/purchasing/index.d.ts +54 -0
  223. package/dist/purchasing/index.js +24 -0
  224. package/dist/purchasing/links.d.ts +263 -0
  225. package/dist/purchasing/links.js +192 -0
  226. package/dist/purchasing/purchase-orders.d.ts +551 -0
  227. package/dist/purchasing/purchase-orders.js +491 -0
  228. package/dist/purchasing/receipts.d.ts +396 -0
  229. package/dist/purchasing/receipts.js +287 -0
  230. package/dist/purchasing/replenishment.d.ts +281 -0
  231. package/dist/purchasing/replenishment.js +240 -0
  232. package/dist/purchasing/returns.d.ts +273 -0
  233. package/dist/purchasing/returns.js +229 -0
  234. package/dist/purchasing/settings.d.ts +152 -0
  235. package/dist/purchasing/settings.js +181 -0
  236. package/dist/purchasing/suppliers.d.ts +476 -0
  237. package/dist/purchasing/suppliers.js +438 -0
  238. package/dist/quotes/index.d.ts +518 -0
  239. package/dist/quotes/index.js +439 -0
  240. package/dist/reports/index.d.ts +71 -8
  241. package/dist/reports/index.js +74 -11
  242. package/dist/returns/index.d.ts +675 -21
  243. package/dist/returns/index.js +756 -11
  244. package/dist/search/index.d.ts +76 -1
  245. package/dist/search/index.js +63 -0
  246. package/dist/seo/config.d.ts +128 -0
  247. package/dist/seo/config.js +95 -0
  248. package/dist/seo/redirects.d.ts +125 -0
  249. package/dist/seo/redirects.js +60 -1
  250. package/dist/seo/settings.d.ts +147 -0
  251. package/dist/seo/settings.js +209 -0
  252. package/dist/shipping/definitions.d.ts +29 -0
  253. package/dist/shipping/definitions.js +47 -0
  254. package/dist/shipping/fulfillment-profiles.d.ts +174 -0
  255. package/dist/shipping/fulfillment-profiles.js +104 -0
  256. package/dist/shipping/hendriks.d.ts +97 -0
  257. package/dist/shipping/hendriks.js +50 -0
  258. package/dist/shipping/index.d.ts +54 -0
  259. package/dist/shipping/index.js +27 -0
  260. package/dist/shipping/methods.d.ts +205 -0
  261. package/dist/shipping/methods.js +240 -0
  262. package/dist/shipping/myparcel.d.ts +668 -0
  263. package/dist/shipping/myparcel.js +466 -0
  264. package/dist/shipping/parcel-count-suggestion.d.ts +2 -0
  265. package/dist/shipping/parcel-rule.d.ts +6 -5
  266. package/dist/shipping/parcel-rule.js +21 -23
  267. package/dist/shipping/rate-tables.d.ts +324 -0
  268. package/dist/shipping/rate-tables.js +390 -0
  269. package/dist/shipping/ship-to-countries.d.ts +75 -0
  270. package/dist/shipping/ship-to-countries.js +47 -0
  271. package/dist/stock/by-location.d.ts +86 -0
  272. package/dist/stock/by-location.js +94 -0
  273. package/dist/stock/index.d.ts +18 -4
  274. package/dist/stock/index.js +7 -0
  275. package/dist/stock/locations.d.ts +144 -0
  276. package/dist/stock/locations.js +168 -0
  277. package/dist/stock/settings.d.ts +106 -0
  278. package/dist/stock/settings.js +103 -0
  279. package/dist/stock/transfers.d.ts +386 -52
  280. package/dist/stock/transfers.js +222 -17
  281. package/dist/stored-value/index.d.ts +312 -11
  282. package/dist/stored-value/index.js +223 -4
  283. package/dist/subscriptions/index.d.ts +682 -0
  284. package/dist/subscriptions/index.js +815 -0
  285. package/dist/tax/display-policy.d.ts +105 -0
  286. package/dist/tax/display-policy.js +84 -0
  287. package/dist/tax/index.d.ts +79 -1
  288. package/dist/tax/index.js +52 -0
  289. package/dist/tenant/email-style.d.ts +102 -0
  290. package/dist/tenant/email-style.js +72 -0
  291. package/dist/tenant/email-templates.d.ts +287 -0
  292. package/dist/tenant/email-templates.js +271 -0
  293. package/dist/tenant/notifications.d.ts +23 -17
  294. package/dist/tenant/settings-sources.d.ts +1 -1
  295. package/dist/tenant/time-zone.d.ts +17 -0
  296. package/dist/tenant/time-zone.js +11 -0
  297. package/dist/types.d.ts +50 -3
  298. package/dist/users/index.d.ts +3 -0
  299. package/dist/users/index.js +2 -0
  300. package/dist/users/warehouses.d.ts +50 -0
  301. package/dist/users/warehouses.js +51 -0
  302. package/package.json +2 -2
@@ -1,9 +1,13 @@
1
1
  import { createAssetsModule } from './assets.js';
2
2
  import { createCollectionsModule } from './collections.js';
3
+ import { createProductDocumentsModule } from './documents.js';
3
4
  import { createFacetsModule } from './facets.js';
5
+ import { createProductMetadataModule } from './metadata.js';
4
6
  import { createOptionsModule } from './options.js';
5
7
  import { createProductsModule } from './products.js';
6
- import { createPropertiesModule, syncContentFacets, } from './properties.js';
8
+ import { createPropertiesModule, syncContentFacets, syncContentItemFacets, } from './properties.js';
9
+ import { createRecommendationsModule } from './recommendations.js';
10
+ import { createReviewsModule } from './reviews.js';
7
11
  import { createVariantsModule } from './variants.js';
8
12
  import { createVisibilityModule } from './visibility.js';
9
13
  export function createCatalogModule(transport) {
@@ -15,7 +19,12 @@ export function createCatalogModule(transport) {
15
19
  collections: createCollectionsModule(transport),
16
20
  assets: createAssetsModule(transport),
17
21
  visibility: createVisibilityModule(transport),
22
+ documents: createProductDocumentsModule(transport),
23
+ metadata: createProductMetadataModule(transport),
18
24
  properties: createPropertiesModule(transport),
25
+ recommendations: createRecommendationsModule(transport),
26
+ reviews: createReviewsModule(transport),
19
27
  syncContentFacets: (propertyId, options) => syncContentFacets(transport, propertyId, options),
28
+ syncContentItemFacets: (contentItemIds, options) => syncContentItemFacets(transport, contentItemIds, options),
20
29
  };
21
30
  }
@@ -0,0 +1,244 @@
1
+ /**
2
+ * A product's SEO, social and AI-agent metadata (headless spec §9.2 `catalog`: "metadata";
3
+ * W10-10): what the storefront puts in a product page's `<head>`, its share cards and its
4
+ * structured data. One record per product and store; its texts are per language.
5
+ *
6
+ * - **Partial writes.** `upsert` changes only the fields it names. A translation is written per
7
+ * language: a language not sent keeps its values, and a field not sent in a sent language keeps
8
+ * its value (send `null` to clear one).
9
+ * - **Coverage.** `coverage` lists the store's products with their own meta title and description
10
+ * in one language, optionally only those missing one, filtered by the engine BEFORE the page is
11
+ * cut: every page is full and `totalItems` counts the filter.
12
+ * - **Bulk.** `bulkUpsert` writes many products, each through `upsert`'s rules in a transaction
13
+ * of its own and reported on its own (ruling G117).
14
+ *
15
+ * Every record is the store's own: a product of another store is never answered and never
16
+ * written.
17
+ *
18
+ * Permissions: `get` and `coverage` need `ReadCatalog`, the writes `UpdateCatalog`.
19
+ *
20
+ * Refusals, GraphQL errors mapped by `core`:
21
+ * - `PRODUCT_METADATA_INPUT_INVALID` → `ValidationError` (400): an input that breaks a rule (a
22
+ * length, an allowed value, an `https://` URL, an instant without an offset, a language sent
23
+ * twice). `details` names each failure (`field` an RFC 6901 pointer such as
24
+ * `/input/translations/0/seoTitle`, `code` the rule such as `INVALID_FIELD_LENGTH`). Nothing is
25
+ * written.
26
+ * - `ENTITY_NOT_FOUND` → `NotFoundError` (404): `upsert` of a product the store does not hold.
27
+ * - `TRANSLATION_LANGUAGE_NOT_OFFERED` → `ValidationError` (400): a new translation in a language
28
+ * the store does not offer.
29
+ * - `BULK_ACTION_INPUT_INVALID` → `ValidationError` (400): a `bulkUpsert` of no product, more than
30
+ * {@link MAX_BULK_METADATA_EDITS} or one product twice, refused before anything is sent.
31
+ * - `INVALID_DATETIME` → `ValidationError` (400): `metaRobotsUnavailableAfter` without an offset,
32
+ * refused before anything is sent.
33
+ * - `FORBIDDEN` → `PermissionDeniedError` (403) without the permission.
34
+ *
35
+ * Release: `coverage` and `bulkUpsert` need a platform release with `productMetadataCoverage` and
36
+ * `bulkUpsertProductMetadata` (deploy vendure-server first: an older one refuses the document as
37
+ * `GRAPHQL_VALIDATION_FAILED`); on such a release `upsert` also answers a validation failure as an
38
+ * uncoded `INTERNAL_SERVER_ERROR` and writes a row for a product the store does not hold. `get`
39
+ * and `delete` are served by every release this SDK supports. No method deduplicates on
40
+ * `idempotencyKey`; every write is naturally repeatable.
41
+ */
42
+ import { type Page, type Transport } from '@sixtyninecommerce/core';
43
+ import type { Call, ListOptions } from '../types.js';
44
+ import { type LanguageCode } from './types.js';
45
+ /** How many products one `bulkUpsert` call may name. */
46
+ export declare const MAX_BULK_METADATA_EDITS = 200;
47
+ /** An image the metadata names (the share image, the X/Twitter override). */
48
+ export interface ProductMetadataImage {
49
+ id: string;
50
+ name: string;
51
+ preview: string;
52
+ source: string;
53
+ /** A string on the wire: a newer engine may add a type. */
54
+ type: 'IMAGE' | 'VIDEO' | 'BINARY' | (string & {});
55
+ mimeType: string;
56
+ }
57
+ /**
58
+ * One language's texts. The four JSON fields are read as the engine stores them; every write
59
+ * checks them (`breadcrumbOverride` `{ label, url }[]` of at most 5, `focusKeywords` up to 10
60
+ * distinct strings, `qaPairs` `{ question, answer }[]` of at most 20), but read them defensively.
61
+ */
62
+ export interface ProductMetadataTranslation {
63
+ id: string;
64
+ languageCode: LanguageCode;
65
+ seoTitle: string | null;
66
+ seoDescription: string | null;
67
+ canonicalUrl: string | null;
68
+ breadcrumbOverride: unknown;
69
+ focusKeywords: unknown;
70
+ socialTitle: string | null;
71
+ socialDescription: string | null;
72
+ socialImageAlt: string | null;
73
+ twitterTitleOverride: string | null;
74
+ twitterDescriptionOverride: string | null;
75
+ aiSummary: string | null;
76
+ qaPairs: unknown;
77
+ }
78
+ export interface ProductMetadata {
79
+ id: string;
80
+ productId: string;
81
+ channelId: string;
82
+ /** `index` or `noindex`. */
83
+ metaRobotsIndex: string;
84
+ /** `follow` or `nofollow`. */
85
+ metaRobotsFollow: string;
86
+ metaRobotsNoarchive: boolean;
87
+ metaRobotsNosnippet: boolean;
88
+ metaRobotsNoimageindex: boolean;
89
+ /** `none`, `standard` or `large`. */
90
+ metaRobotsMaxImagePreview: string;
91
+ metaRobotsMaxSnippet: number | null;
92
+ metaRobotsMaxVideoPreview: number | null;
93
+ /** ISO-8601 UTC instant after which search engines should drop the page; null when none. */
94
+ metaRobotsUnavailableAfter: string | null;
95
+ socialImageId: string | null;
96
+ socialImage: ProductMetadataImage | null;
97
+ twitterImageOverrideId: string | null;
98
+ twitterImageOverride: ProductMetadataImage | null;
99
+ twitterCardType: string;
100
+ socialOgType: string;
101
+ seoIncludeReviewsInJsonld: boolean;
102
+ aiSummaryAudience: string;
103
+ /** The product's own `{ [botKey]: 'allow' | 'deny' }`; null when it has none. */
104
+ aiBotOverrides: Record<string, unknown> | null;
105
+ qaEmitFaqpageJsonld: boolean;
106
+ hreflangOverrides: unknown;
107
+ /** The product's overrides on top of the store's defaults, as the storefront applies them. */
108
+ resolvedBotDirectives: Record<string, unknown>;
109
+ translations: ProductMetadataTranslation[];
110
+ }
111
+ export interface ProductMetadataTranslationInput {
112
+ languageCode: LanguageCode;
113
+ seoTitle?: string | null;
114
+ seoDescription?: string | null;
115
+ /** An absolute `https://` URL. */
116
+ canonicalUrl?: string | null;
117
+ breadcrumbOverride?: Array<{
118
+ label: string;
119
+ url: string;
120
+ }> | null;
121
+ focusKeywords?: string[] | null;
122
+ socialTitle?: string | null;
123
+ socialDescription?: string | null;
124
+ socialImageAlt?: string | null;
125
+ twitterTitleOverride?: string | null;
126
+ twitterDescriptionOverride?: string | null;
127
+ aiSummary?: string | null;
128
+ qaPairs?: Array<{
129
+ question: string;
130
+ answer: string;
131
+ }> | null;
132
+ }
133
+ /** Only the fields named change. */
134
+ export interface ProductMetadataInput {
135
+ metaRobotsIndex?: 'index' | 'noindex';
136
+ metaRobotsFollow?: 'follow' | 'nofollow';
137
+ metaRobotsNoarchive?: boolean;
138
+ metaRobotsNosnippet?: boolean;
139
+ metaRobotsNoimageindex?: boolean;
140
+ metaRobotsMaxImagePreview?: 'none' | 'standard' | 'large';
141
+ metaRobotsMaxSnippet?: number | null;
142
+ metaRobotsMaxVideoPreview?: number | null;
143
+ /**
144
+ * An ISO-8601 instant WITH an explicit offset (`Z` or `±HH:mm`). One without is refused before
145
+ * the request leaves (`INVALID_DATETIME`).
146
+ */
147
+ metaRobotsUnavailableAfter?: string | null;
148
+ socialImageId?: string | null;
149
+ twitterImageOverrideId?: string | null;
150
+ twitterCardType?: 'summary' | 'summary_large_image';
151
+ socialOgType?: 'product' | 'website';
152
+ seoIncludeReviewsInJsonld?: boolean;
153
+ aiSummaryAudience?: 'customers' | 'developers' | 'both';
154
+ aiBotOverrides?: Record<string, 'allow' | 'deny'> | null;
155
+ qaEmitFaqpageJsonld?: boolean;
156
+ hreflangOverrides?: Array<{
157
+ language: string;
158
+ country?: string;
159
+ url: string;
160
+ }> | null;
161
+ translations?: ProductMetadataTranslationInput[];
162
+ }
163
+ export type ProductMetadataCoverageFilter = 'ALL' | 'MISSING_TITLE' | 'MISSING_DESCRIPTION';
164
+ export interface ProductMetadataCoverageQuery extends ListOptions {
165
+ /** The language whose meta each row shows, and which the filter judges. */
166
+ languageCode: LanguageCode;
167
+ /** Defaults to `ALL`. A value is missing when it is null or empty. */
168
+ filter?: ProductMetadataCoverageFilter;
169
+ /** Matches the product's name. */
170
+ term?: string;
171
+ }
172
+ /** One product of the coverage list. */
173
+ export interface ProductMetadataCoverageRow {
174
+ productId: string;
175
+ /** The product's name in the request's language. */
176
+ name: string;
177
+ slug: string;
178
+ enabled: boolean;
179
+ /** The product's own meta title in the query's language; null when it has none. */
180
+ seoTitle: string | null;
181
+ /** The product's own meta description in the query's language; null when it has none. */
182
+ seoDescription: string | null;
183
+ }
184
+ export interface ProductMetadataBulkEdit {
185
+ productId: string;
186
+ input: ProductMetadataInput;
187
+ }
188
+ /** One product's outcome in a `bulkUpsert` call. */
189
+ export interface ProductMetadataBulkOutcome {
190
+ productId: string;
191
+ /** `OK`, or `FAILED` (a string on the wire: read anything else as not done). */
192
+ status: 'OK' | 'FAILED' | (string & {});
193
+ /**
194
+ * When FAILED: the single door's own code (`ENTITY_NOT_FOUND`,
195
+ * `PRODUCT_METADATA_INPUT_INVALID`, …); `INTERNAL_SERVER_ERROR` for a fault the engine logged.
196
+ */
197
+ code: string | null;
198
+ /** When FAILED: the door's sentence. */
199
+ message: string | null;
200
+ }
201
+ export interface ProductMetadataModule {
202
+ /**
203
+ * The product's metadata; null when it has none yet. `NotFoundError` (`PRODUCT_NOT_FOUND`) for
204
+ * a product the store does not hold.
205
+ *
206
+ * @effect read
207
+ */
208
+ get(productId: string, options?: Call): Promise<ProductMetadata | null>;
209
+ /**
210
+ * Creates the product's metadata or changes the fields named, and answers the whole record.
211
+ *
212
+ * @effect write
213
+ */
214
+ upsert(productId: string, input: ProductMetadataInput, options?: Call): Promise<ProductMetadata>;
215
+ /**
216
+ * Removes the product's metadata in this store: the storefront falls back to the product's own
217
+ * name and description. Resolves the same way whether or not there was any.
218
+ *
219
+ * @effect destructive
220
+ */
221
+ delete(productId: string, options?: Call): Promise<void>;
222
+ /**
223
+ * The store's products, name first, each with its own meta in `languageCode`; with a `filter`,
224
+ * only those missing a title (or a description) there. Filtered before paging: pages are full
225
+ * and `totalItems` counts the filter and the term. Never answers a deleted product.
226
+ *
227
+ * @effect read
228
+ */
229
+ coverage(query: ProductMetadataCoverageQuery): Promise<Page<ProductMetadataCoverageRow>>;
230
+ /**
231
+ * Upserts 1 to {@link MAX_BULK_METADATA_EDITS} products, each through `upsert`'s rules in a
232
+ * transaction of its own, and answers one outcome per product in the order named: a refused
233
+ * product changes nothing and the others still go through. Not keyed: a repeat writes again.
234
+ *
235
+ * @effect write
236
+ */
237
+ bulkUpsert(edits: readonly ProductMetadataBulkEdit[], options?: Call): Promise<ProductMetadataBulkOutcome[]>;
238
+ }
239
+ export declare const PRODUCT_METADATA_QUERY = "\n query CatalogProductMetadata($productId: ID!) {\n product(id: $productId) {\n id\n metadata { \n id\n productId\n channelId\n metaRobotsIndex\n metaRobotsFollow\n metaRobotsNoarchive\n metaRobotsNosnippet\n metaRobotsNoimageindex\n metaRobotsMaxImagePreview\n metaRobotsMaxSnippet\n metaRobotsMaxVideoPreview\n metaRobotsUnavailableAfter\n socialImageId\n socialImage { \n id\n name\n preview\n source\n type\n mimeType\n }\n twitterImageOverrideId\n twitterImageOverride { \n id\n name\n preview\n source\n type\n mimeType\n }\n twitterCardType\n socialOgType\n seoIncludeReviewsInJsonld\n aiSummaryAudience\n aiBotOverrides\n qaEmitFaqpageJsonld\n hreflangOverrides\n resolvedBotDirectives\n translations {\n id\n languageCode\n seoTitle\n seoDescription\n canonicalUrl\n breadcrumbOverride\n focusKeywords\n socialTitle\n socialDescription\n socialImageAlt\n twitterTitleOverride\n twitterDescriptionOverride\n aiSummary\n qaPairs\n }\n }\n }\n }\n";
240
+ export declare const UPSERT_PRODUCT_METADATA_MUTATION = "\n mutation CatalogUpsertProductMetadata($productId: ID!, $input: ProductMetadataInput!) {\n upsertProductMetadata(productId: $productId, input: $input) { \n id\n productId\n channelId\n metaRobotsIndex\n metaRobotsFollow\n metaRobotsNoarchive\n metaRobotsNosnippet\n metaRobotsNoimageindex\n metaRobotsMaxImagePreview\n metaRobotsMaxSnippet\n metaRobotsMaxVideoPreview\n metaRobotsUnavailableAfter\n socialImageId\n socialImage { \n id\n name\n preview\n source\n type\n mimeType\n }\n twitterImageOverrideId\n twitterImageOverride { \n id\n name\n preview\n source\n type\n mimeType\n }\n twitterCardType\n socialOgType\n seoIncludeReviewsInJsonld\n aiSummaryAudience\n aiBotOverrides\n qaEmitFaqpageJsonld\n hreflangOverrides\n resolvedBotDirectives\n translations {\n id\n languageCode\n seoTitle\n seoDescription\n canonicalUrl\n breadcrumbOverride\n focusKeywords\n socialTitle\n socialDescription\n socialImageAlt\n twitterTitleOverride\n twitterDescriptionOverride\n aiSummary\n qaPairs\n }\n }\n }\n";
241
+ export declare const DELETE_PRODUCT_METADATA_MUTATION = "\n mutation CatalogDeleteProductMetadata($productId: ID!) {\n deleteProductMetadata(productId: $productId) { \n result\n message\n }\n }\n";
242
+ export declare const PRODUCT_METADATA_COVERAGE_QUERY = "\n query CatalogProductMetadataCoverage(\n $languageCode: LanguageCode!\n $filter: ProductMetadataCoverageFilter\n $term: String\n $options: ProductMetadataCoverageListOptions\n ) {\n productMetadataCoverage(\n languageCode: $languageCode\n filter: $filter\n term: $term\n options: $options\n ) {\n items {\n productId\n name\n slug\n enabled\n seoTitle\n seoDescription\n }\n totalItems\n }\n }\n";
243
+ export declare const BULK_UPSERT_PRODUCT_METADATA_MUTATION = "\n mutation CatalogBulkUpsertProductMetadata($edits: [ProductMetadataBulkEdit!]!) {\n bulkUpsertProductMetadata(edits: $edits) {\n items {\n productId\n status\n code\n message\n }\n }\n }\n";
244
+ export declare function createProductMetadataModule(transport: Transport): ProductMetadataModule;
@@ -0,0 +1,220 @@
1
+ /**
2
+ * A product's SEO, social and AI-agent metadata (headless spec §9.2 `catalog`: "metadata";
3
+ * W10-10): what the storefront puts in a product page's `<head>`, its share cards and its
4
+ * structured data. One record per product and store; its texts are per language.
5
+ *
6
+ * - **Partial writes.** `upsert` changes only the fields it names. A translation is written per
7
+ * language: a language not sent keeps its values, and a field not sent in a sent language keeps
8
+ * its value (send `null` to clear one).
9
+ * - **Coverage.** `coverage` lists the store's products with their own meta title and description
10
+ * in one language, optionally only those missing one, filtered by the engine BEFORE the page is
11
+ * cut: every page is full and `totalItems` counts the filter.
12
+ * - **Bulk.** `bulkUpsert` writes many products, each through `upsert`'s rules in a transaction
13
+ * of its own and reported on its own (ruling G117).
14
+ *
15
+ * Every record is the store's own: a product of another store is never answered and never
16
+ * written.
17
+ *
18
+ * Permissions: `get` and `coverage` need `ReadCatalog`, the writes `UpdateCatalog`.
19
+ *
20
+ * Refusals, GraphQL errors mapped by `core`:
21
+ * - `PRODUCT_METADATA_INPUT_INVALID` → `ValidationError` (400): an input that breaks a rule (a
22
+ * length, an allowed value, an `https://` URL, an instant without an offset, a language sent
23
+ * twice). `details` names each failure (`field` an RFC 6901 pointer such as
24
+ * `/input/translations/0/seoTitle`, `code` the rule such as `INVALID_FIELD_LENGTH`). Nothing is
25
+ * written.
26
+ * - `ENTITY_NOT_FOUND` → `NotFoundError` (404): `upsert` of a product the store does not hold.
27
+ * - `TRANSLATION_LANGUAGE_NOT_OFFERED` → `ValidationError` (400): a new translation in a language
28
+ * the store does not offer.
29
+ * - `BULK_ACTION_INPUT_INVALID` → `ValidationError` (400): a `bulkUpsert` of no product, more than
30
+ * {@link MAX_BULK_METADATA_EDITS} or one product twice, refused before anything is sent.
31
+ * - `INVALID_DATETIME` → `ValidationError` (400): `metaRobotsUnavailableAfter` without an offset,
32
+ * refused before anything is sent.
33
+ * - `FORBIDDEN` → `PermissionDeniedError` (403) without the permission.
34
+ *
35
+ * Release: `coverage` and `bulkUpsert` need a platform release with `productMetadataCoverage` and
36
+ * `bulkUpsertProductMetadata` (deploy vendure-server first: an older one refuses the document as
37
+ * `GRAPHQL_VALIDATION_FAILED`); on such a release `upsert` also answers a validation failure as an
38
+ * uncoded `INTERNAL_SERVER_ERROR` and writes a row for a product the store does not hold. `get`
39
+ * and `delete` are served by every release this SDK supports. No method deduplicates on
40
+ * `idempotencyKey`; every write is naturally repeatable.
41
+ */
42
+ import { NotFoundError, UpstreamError, ValidationError, } from '@sixtyninecommerce/core';
43
+ import { assertDeleted, assertInstant, DELETION_FIELDS, fieldOf, vendureListPage, } from './types.js';
44
+ /** How many products one `bulkUpsert` call may name. */
45
+ export const MAX_BULK_METADATA_EDITS = 200;
46
+ const IMAGE_FIELDS = /* GraphQL */ `
47
+ id
48
+ name
49
+ preview
50
+ source
51
+ type
52
+ mimeType
53
+ `;
54
+ const METADATA_FIELDS = /* GraphQL */ `
55
+ id
56
+ productId
57
+ channelId
58
+ metaRobotsIndex
59
+ metaRobotsFollow
60
+ metaRobotsNoarchive
61
+ metaRobotsNosnippet
62
+ metaRobotsNoimageindex
63
+ metaRobotsMaxImagePreview
64
+ metaRobotsMaxSnippet
65
+ metaRobotsMaxVideoPreview
66
+ metaRobotsUnavailableAfter
67
+ socialImageId
68
+ socialImage { ${IMAGE_FIELDS} }
69
+ twitterImageOverrideId
70
+ twitterImageOverride { ${IMAGE_FIELDS} }
71
+ twitterCardType
72
+ socialOgType
73
+ seoIncludeReviewsInJsonld
74
+ aiSummaryAudience
75
+ aiBotOverrides
76
+ qaEmitFaqpageJsonld
77
+ hreflangOverrides
78
+ resolvedBotDirectives
79
+ translations {
80
+ id
81
+ languageCode
82
+ seoTitle
83
+ seoDescription
84
+ canonicalUrl
85
+ breadcrumbOverride
86
+ focusKeywords
87
+ socialTitle
88
+ socialDescription
89
+ socialImageAlt
90
+ twitterTitleOverride
91
+ twitterDescriptionOverride
92
+ aiSummary
93
+ qaPairs
94
+ }
95
+ `;
96
+ export const PRODUCT_METADATA_QUERY = /* GraphQL */ `
97
+ query CatalogProductMetadata($productId: ID!) {
98
+ product(id: $productId) {
99
+ id
100
+ metadata { ${METADATA_FIELDS} }
101
+ }
102
+ }
103
+ `;
104
+ export const UPSERT_PRODUCT_METADATA_MUTATION = /* GraphQL */ `
105
+ mutation CatalogUpsertProductMetadata($productId: ID!, $input: ProductMetadataInput!) {
106
+ upsertProductMetadata(productId: $productId, input: $input) { ${METADATA_FIELDS} }
107
+ }
108
+ `;
109
+ export const DELETE_PRODUCT_METADATA_MUTATION = /* GraphQL */ `
110
+ mutation CatalogDeleteProductMetadata($productId: ID!) {
111
+ deleteProductMetadata(productId: $productId) { ${DELETION_FIELDS} }
112
+ }
113
+ `;
114
+ export const PRODUCT_METADATA_COVERAGE_QUERY = /* GraphQL */ `
115
+ query CatalogProductMetadataCoverage(
116
+ $languageCode: LanguageCode!
117
+ $filter: ProductMetadataCoverageFilter
118
+ $term: String
119
+ $options: ProductMetadataCoverageListOptions
120
+ ) {
121
+ productMetadataCoverage(
122
+ languageCode: $languageCode
123
+ filter: $filter
124
+ term: $term
125
+ options: $options
126
+ ) {
127
+ items {
128
+ productId
129
+ name
130
+ slug
131
+ enabled
132
+ seoTitle
133
+ seoDescription
134
+ }
135
+ totalItems
136
+ }
137
+ }
138
+ `;
139
+ export const BULK_UPSERT_PRODUCT_METADATA_MUTATION = /* GraphQL */ `
140
+ mutation CatalogBulkUpsertProductMetadata($edits: [ProductMetadataBulkEdit!]!) {
141
+ bulkUpsertProductMetadata(edits: $edits) {
142
+ items {
143
+ productId
144
+ status
145
+ code
146
+ message
147
+ }
148
+ }
149
+ }
150
+ `;
151
+ function bulkInvalid(message) {
152
+ return new ValidationError({
153
+ code: 'BULK_ACTION_INPUT_INVALID',
154
+ message,
155
+ status: 400,
156
+ service: 'vendure',
157
+ });
158
+ }
159
+ /** The edits as sent, refused before the request leaves when the engine would refuse them. */
160
+ function bulkEdits(edits) {
161
+ if (!Array.isArray(edits))
162
+ throw bulkInvalid('edits must be a list.');
163
+ if (edits.length === 0)
164
+ throw bulkInvalid('Name at least one product.');
165
+ if (edits.length > MAX_BULK_METADATA_EDITS) {
166
+ throw bulkInvalid(`A bulk upsert takes at most ${String(MAX_BULK_METADATA_EDITS)} products; this one names ${String(edits.length)}.`);
167
+ }
168
+ const ids = edits.map((edit) => String(edit.productId).toLowerCase());
169
+ if (new Set(ids).size !== ids.length)
170
+ throw bulkInvalid('Name each product once.');
171
+ for (const edit of edits) {
172
+ assertInstant('metaRobotsUnavailableAfter', edit.input.metaRobotsUnavailableAfter);
173
+ }
174
+ return edits.map((edit) => ({ productId: edit.productId, input: edit.input }));
175
+ }
176
+ export function createProductMetadataModule(transport) {
177
+ return {
178
+ async get(productId, options) {
179
+ const product = await fieldOf(transport, PRODUCT_METADATA_QUERY, 'product', { productId }, options);
180
+ if (!product) {
181
+ throw new NotFoundError({
182
+ code: 'PRODUCT_NOT_FOUND',
183
+ message: `This store has no product ${productId}.`,
184
+ status: 404,
185
+ service: 'vendure',
186
+ });
187
+ }
188
+ return product.metadata;
189
+ },
190
+ async upsert(productId, input, options) {
191
+ assertInstant('metaRobotsUnavailableAfter', input.metaRobotsUnavailableAfter);
192
+ return fieldOf(transport, UPSERT_PRODUCT_METADATA_MUTATION, 'upsertProductMetadata', { productId, input }, options);
193
+ },
194
+ async delete(productId, options) {
195
+ const response = await fieldOf(transport, DELETE_PRODUCT_METADATA_MUTATION, 'deleteProductMetadata', { productId }, options);
196
+ assertDeleted('PRODUCT_METADATA_NOT_DELETED', [response]);
197
+ },
198
+ async coverage(query) {
199
+ const { languageCode, filter, term, ...options } = query;
200
+ return vendureListPage(transport, PRODUCT_METADATA_COVERAGE_QUERY, 'productMetadataCoverage', (skip, take) => ({
201
+ languageCode,
202
+ filter: filter ?? 'ALL',
203
+ ...(term === undefined || term.trim() === '' ? {} : { term }),
204
+ options: { skip, take },
205
+ }), options);
206
+ },
207
+ async bulkUpsert(edits, options) {
208
+ const data = await fieldOf(transport, BULK_UPSERT_PRODUCT_METADATA_MUTATION, 'bulkUpsertProductMetadata', { edits: bulkEdits(edits) }, options);
209
+ if (data === null) {
210
+ throw new UpstreamError({
211
+ code: 'UNEXPECTED_RESPONSE',
212
+ message: 'The engine answered bulkUpsertProductMetadata with nothing.',
213
+ status: 502,
214
+ service: 'vendure',
215
+ });
216
+ }
217
+ return data.items;
218
+ },
219
+ };
220
+ }