@sixtyninecommerce/admin 0.10.0 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (302) hide show
  1. package/README.md +5772 -118
  2. package/dist/auth/index.d.ts +7 -1
  3. package/dist/auth/index.js +8 -1
  4. package/dist/catalog/assets.d.ts +120 -8
  5. package/dist/catalog/assets.js +98 -12
  6. package/dist/catalog/collections.d.ts +279 -14
  7. package/dist/catalog/collections.js +246 -15
  8. package/dist/catalog/documents.d.ts +142 -0
  9. package/dist/catalog/documents.js +114 -0
  10. package/dist/catalog/index.d.ts +54 -6
  11. package/dist/catalog/index.js +10 -1
  12. package/dist/catalog/metadata.d.ts +244 -0
  13. package/dist/catalog/metadata.js +220 -0
  14. package/dist/catalog/products.d.ts +159 -19
  15. package/dist/catalog/products.js +88 -13
  16. package/dist/catalog/properties.d.ts +140 -14
  17. package/dist/catalog/properties.js +65 -1
  18. package/dist/catalog/recommendations.d.ts +288 -0
  19. package/dist/catalog/recommendations.js +237 -0
  20. package/dist/catalog/reviews.d.ts +381 -0
  21. package/dist/catalog/reviews.js +325 -0
  22. package/dist/catalog/types.d.ts +17 -1
  23. package/dist/catalog/types.js +10 -0
  24. package/dist/catalog/variant-find.d.ts +136 -0
  25. package/dist/catalog/variant-find.js +239 -0
  26. package/dist/catalog/variants.d.ts +293 -26
  27. package/dist/catalog/variants.js +164 -7
  28. package/dist/catalog/visibility.d.ts +38 -7
  29. package/dist/catalog/visibility.js +19 -1
  30. package/dist/client.d.ts +121 -9
  31. package/dist/client.js +53 -14
  32. package/dist/content/assets.d.ts +285 -0
  33. package/dist/content/assets.js +181 -0
  34. package/dist/content/forms.d.ts +332 -1
  35. package/dist/content/forms.js +140 -4
  36. package/dist/content/items.d.ts +292 -0
  37. package/dist/content/items.js +170 -0
  38. package/dist/content/menus.d.ts +254 -0
  39. package/dist/content/menus.js +70 -0
  40. package/dist/content/strings.d.ts +137 -0
  41. package/dist/content/strings.js +82 -0
  42. package/dist/content/types.d.ts +282 -0
  43. package/dist/content/types.js +100 -0
  44. package/dist/customers/age-verification.d.ts +66 -0
  45. package/dist/customers/age-verification.js +79 -0
  46. package/dist/customers/credit.d.ts +263 -0
  47. package/dist/customers/credit.js +244 -0
  48. package/dist/customers/delivery-preferences.d.ts +120 -0
  49. package/dist/customers/delivery-preferences.js +97 -0
  50. package/dist/customers/delivery-terms.d.ts +44 -0
  51. package/dist/customers/delivery-terms.js +32 -0
  52. package/dist/customers/groups.d.ts +207 -0
  53. package/dist/customers/groups.js +438 -0
  54. package/dist/customers/index.d.ts +462 -37
  55. package/dist/customers/index.js +317 -40
  56. package/dist/customers/invitation-status.d.ts +57 -0
  57. package/dist/customers/invitation-status.js +21 -0
  58. package/dist/customers/notes.d.ts +67 -0
  59. package/dist/customers/notes.js +51 -0
  60. package/dist/customers/notification-recipients.d.ts +97 -0
  61. package/dist/customers/notification-recipients.js +84 -0
  62. package/dist/customers/saved-lists.d.ts +42 -0
  63. package/dist/customers/saved-lists.js +61 -0
  64. package/dist/customers/vat.d.ts +83 -0
  65. package/dist/customers/vat.js +46 -0
  66. package/dist/customers/wire.d.ts +20 -0
  67. package/dist/customers/wire.js +35 -0
  68. package/dist/documents/ar.d.ts +307 -0
  69. package/dist/documents/ar.js +254 -0
  70. package/dist/documents/emails.d.ts +150 -0
  71. package/dist/documents/emails.js +220 -0
  72. package/dist/documents/index.d.ts +744 -21
  73. package/dist/documents/index.js +438 -24
  74. package/dist/documents/pdf.d.ts +2 -0
  75. package/dist/documents/pdf.js +11 -0
  76. package/dist/fulfillments/create.d.ts +167 -0
  77. package/dist/fulfillments/create.js +216 -0
  78. package/dist/fulfillments/index.d.ts +163 -11
  79. package/dist/fulfillments/index.js +135 -6
  80. package/dist/fulfillments/payment-policy.d.ts +64 -0
  81. package/dist/fulfillments/payment-policy.js +140 -0
  82. package/dist/fulfillments/warehouse.d.ts +724 -0
  83. package/dist/fulfillments/warehouse.js +914 -0
  84. package/dist/geo/index.d.ts +119 -0
  85. package/dist/geo/index.js +107 -0
  86. package/dist/index.d.ts +147 -39
  87. package/dist/index.js +51 -12
  88. package/dist/integrations/afas-actions.d.ts +234 -0
  89. package/dist/integrations/afas-actions.js +310 -0
  90. package/dist/integrations/afas-types.d.ts +483 -0
  91. package/dist/integrations/afas-types.js +5 -0
  92. package/dist/integrations/afas.d.ts +234 -0
  93. package/dist/integrations/afas.js +290 -0
  94. package/dist/integrations/connections.d.ts +103 -0
  95. package/dist/integrations/connections.js +55 -0
  96. package/dist/integrations/esp.d.ts +313 -0
  97. package/dist/integrations/esp.js +169 -0
  98. package/dist/integrations/exact-online-types.d.ts +965 -0
  99. package/dist/integrations/exact-online-types.js +6 -0
  100. package/dist/integrations/exact-online.d.ts +784 -0
  101. package/dist/integrations/exact-online.js +1089 -0
  102. package/dist/integrations/index.d.ts +44 -3
  103. package/dist/integrations/index.js +23 -3
  104. package/dist/integrations/pipeline-discoveries.d.ts +65 -0
  105. package/dist/integrations/pipeline-discoveries.js +65 -0
  106. package/dist/integrations/pipeline-keys.d.ts +13 -0
  107. package/dist/integrations/pipeline-keys.js +22 -0
  108. package/dist/integrations/pipeline-runs.d.ts +119 -0
  109. package/dist/integrations/pipeline-runs.js +87 -0
  110. package/dist/integrations/pipelines.d.ts +87 -15
  111. package/dist/integrations/pipelines.js +41 -22
  112. package/dist/integrations/price-import.d.ts +223 -0
  113. package/dist/integrations/price-import.js +124 -0
  114. package/dist/integrations/types.d.ts +367 -3
  115. package/dist/integrations/types.js +4 -3
  116. package/dist/locations/index.d.ts +66 -0
  117. package/dist/locations/index.js +31 -0
  118. package/dist/meta/index.d.ts +172 -0
  119. package/dist/metafields/index.d.ts +8 -1
  120. package/dist/metafields/index.js +10 -0
  121. package/dist/orders/cancel-with-money.d.ts +237 -0
  122. package/dist/orders/cancel-with-money.js +155 -0
  123. package/dist/orders/commit-edit.d.ts +23 -1
  124. package/dist/orders/commit-edit.js +19 -2
  125. package/dist/orders/deposit-policy.d.ts +3 -1
  126. package/dist/orders/drafts.d.ts +800 -0
  127. package/dist/orders/drafts.js +771 -0
  128. package/dist/orders/fields.d.ts +245 -0
  129. package/dist/orders/fields.js +188 -0
  130. package/dist/orders/get.d.ts +29 -19
  131. package/dist/orders/get.js +17 -8
  132. package/dist/orders/index.d.ts +66 -15
  133. package/dist/orders/index.js +25 -2
  134. package/dist/orders/lifecycle.d.ts +128 -0
  135. package/dist/orders/lifecycle.js +158 -0
  136. package/dist/orders/line-price-preview.d.ts +7 -2
  137. package/dist/orders/list.d.ts +243 -0
  138. package/dist/orders/list.js +487 -0
  139. package/dist/orders/manual-discounts.d.ts +13 -5
  140. package/dist/orders/manual-pricing.d.ts +5 -0
  141. package/dist/orders/money-view.d.ts +149 -0
  142. package/dist/orders/money-view.js +99 -0
  143. package/dist/orders/order-entry.d.ts +20 -7
  144. package/dist/orders/plan-edit.d.ts +34 -2
  145. package/dist/orders/plan-edit.js +112 -11
  146. package/dist/orders/settings.d.ts +201 -0
  147. package/dist/orders/settings.js +325 -0
  148. package/dist/orders/status.d.ts +159 -13
  149. package/dist/orders/status.js +21 -0
  150. package/dist/orders/surcharges.d.ts +20 -1
  151. package/dist/orders/surcharges.js +37 -11
  152. package/dist/payments/account-credit.d.ts +46 -0
  153. package/dist/payments/account-credit.js +42 -0
  154. package/dist/payments/ar.d.ts +292 -0
  155. package/dist/payments/ar.js +223 -0
  156. package/dist/payments/connected-account.d.ts +47 -0
  157. package/dist/payments/connected-account.js +61 -0
  158. package/dist/payments/credit-noted-fields.d.ts +19 -0
  159. package/dist/payments/credit-noted-fields.js +69 -0
  160. package/dist/payments/customer-consent.d.ts +30 -0
  161. package/dist/payments/customer-consent.js +49 -0
  162. package/dist/payments/index.d.ts +340 -19
  163. package/dist/payments/index.js +151 -1
  164. package/dist/payments/methods.d.ts +303 -8
  165. package/dist/payments/methods.js +337 -7
  166. package/dist/payments/mollie-methods.d.ts +159 -0
  167. package/dist/payments/mollie-methods.js +141 -0
  168. package/dist/payments/payment-links.d.ts +180 -0
  169. package/dist/payments/payment-links.js +97 -0
  170. package/dist/payments/provider-keys.d.ts +150 -0
  171. package/dist/payments/provider-keys.js +118 -0
  172. package/dist/payments/providers.d.ts +86 -11
  173. package/dist/payments/providers.js +29 -0
  174. package/dist/payments/requests.d.ts +147 -0
  175. package/dist/payments/requests.js +93 -0
  176. package/dist/payments/store-credit-fields.d.ts +15 -0
  177. package/dist/payments/store-credit-fields.js +51 -0
  178. package/dist/pickups/index.d.ts +106 -0
  179. package/dist/pickups/index.js +108 -0
  180. package/dist/platform/channels.d.ts +132 -0
  181. package/dist/platform/channels.js +128 -0
  182. package/dist/platform/email-gateways.d.ts +165 -0
  183. package/dist/platform/email-gateways.js +78 -0
  184. package/dist/platform/geo.d.ts +123 -0
  185. package/dist/platform/geo.js +144 -0
  186. package/dist/platform/tax.d.ts +162 -0
  187. package/dist/platform/tax.js +169 -0
  188. package/dist/pos/anomalies.d.ts +158 -0
  189. package/dist/pos/anomalies.js +64 -0
  190. package/dist/pos/assisted-selling.d.ts +79 -0
  191. package/dist/pos/assisted-selling.js +38 -0
  192. package/dist/pos/exchanges.d.ts +108 -0
  193. package/dist/pos/exchanges.js +22 -0
  194. package/dist/pos/gift-cards.d.ts +135 -0
  195. package/dist/pos/gift-cards.js +47 -0
  196. package/dist/pos/index.d.ts +2370 -58
  197. package/dist/pos/index.js +854 -7
  198. package/dist/pos/no-receipt.d.ts +94 -0
  199. package/dist/pos/no-receipt.js +25 -0
  200. package/dist/pos/offline-sales.d.ts +228 -0
  201. package/dist/pos/offline-sales.js +170 -0
  202. package/dist/pos/pay-by-link.d.ts +143 -0
  203. package/dist/pos/pay-by-link.js +57 -0
  204. package/dist/pos/quick-keys.d.ts +103 -0
  205. package/dist/pos/quick-keys.js +45 -0
  206. package/dist/pos/reports.d.ts +360 -0
  207. package/dist/pos/reports.js +62 -0
  208. package/dist/pos/returns.d.ts +244 -0
  209. package/dist/pos/returns.js +83 -0
  210. package/dist/pricing/bulk-coupons.d.ts +139 -0
  211. package/dist/pricing/bulk-coupons.js +112 -0
  212. package/dist/pricing/index.d.ts +174 -4
  213. package/dist/pricing/index.js +174 -1
  214. package/dist/pricing/price-lists.d.ts +606 -0
  215. package/dist/pricing/price-lists.js +600 -0
  216. package/dist/purchasing/alerts.d.ts +104 -0
  217. package/dist/purchasing/alerts.js +88 -0
  218. package/dist/purchasing/backorders.d.ts +61 -0
  219. package/dist/purchasing/backorders.js +92 -0
  220. package/dist/purchasing/cost.d.ts +110 -0
  221. package/dist/purchasing/cost.js +86 -0
  222. package/dist/purchasing/index.d.ts +54 -0
  223. package/dist/purchasing/index.js +24 -0
  224. package/dist/purchasing/links.d.ts +263 -0
  225. package/dist/purchasing/links.js +192 -0
  226. package/dist/purchasing/purchase-orders.d.ts +551 -0
  227. package/dist/purchasing/purchase-orders.js +491 -0
  228. package/dist/purchasing/receipts.d.ts +396 -0
  229. package/dist/purchasing/receipts.js +287 -0
  230. package/dist/purchasing/replenishment.d.ts +281 -0
  231. package/dist/purchasing/replenishment.js +240 -0
  232. package/dist/purchasing/returns.d.ts +273 -0
  233. package/dist/purchasing/returns.js +229 -0
  234. package/dist/purchasing/settings.d.ts +152 -0
  235. package/dist/purchasing/settings.js +181 -0
  236. package/dist/purchasing/suppliers.d.ts +476 -0
  237. package/dist/purchasing/suppliers.js +438 -0
  238. package/dist/quotes/index.d.ts +518 -0
  239. package/dist/quotes/index.js +439 -0
  240. package/dist/reports/index.d.ts +71 -8
  241. package/dist/reports/index.js +74 -11
  242. package/dist/returns/index.d.ts +675 -21
  243. package/dist/returns/index.js +756 -11
  244. package/dist/search/index.d.ts +76 -1
  245. package/dist/search/index.js +63 -0
  246. package/dist/seo/config.d.ts +128 -0
  247. package/dist/seo/config.js +95 -0
  248. package/dist/seo/redirects.d.ts +125 -0
  249. package/dist/seo/redirects.js +60 -1
  250. package/dist/seo/settings.d.ts +147 -0
  251. package/dist/seo/settings.js +209 -0
  252. package/dist/shipping/definitions.d.ts +29 -0
  253. package/dist/shipping/definitions.js +47 -0
  254. package/dist/shipping/fulfillment-profiles.d.ts +174 -0
  255. package/dist/shipping/fulfillment-profiles.js +104 -0
  256. package/dist/shipping/hendriks.d.ts +97 -0
  257. package/dist/shipping/hendriks.js +50 -0
  258. package/dist/shipping/index.d.ts +54 -0
  259. package/dist/shipping/index.js +27 -0
  260. package/dist/shipping/methods.d.ts +205 -0
  261. package/dist/shipping/methods.js +240 -0
  262. package/dist/shipping/myparcel.d.ts +668 -0
  263. package/dist/shipping/myparcel.js +466 -0
  264. package/dist/shipping/parcel-count-suggestion.d.ts +2 -0
  265. package/dist/shipping/parcel-rule.d.ts +6 -5
  266. package/dist/shipping/parcel-rule.js +21 -23
  267. package/dist/shipping/rate-tables.d.ts +324 -0
  268. package/dist/shipping/rate-tables.js +390 -0
  269. package/dist/shipping/ship-to-countries.d.ts +75 -0
  270. package/dist/shipping/ship-to-countries.js +47 -0
  271. package/dist/stock/by-location.d.ts +86 -0
  272. package/dist/stock/by-location.js +94 -0
  273. package/dist/stock/index.d.ts +18 -4
  274. package/dist/stock/index.js +7 -0
  275. package/dist/stock/locations.d.ts +144 -0
  276. package/dist/stock/locations.js +168 -0
  277. package/dist/stock/settings.d.ts +106 -0
  278. package/dist/stock/settings.js +103 -0
  279. package/dist/stock/transfers.d.ts +386 -52
  280. package/dist/stock/transfers.js +222 -17
  281. package/dist/stored-value/index.d.ts +312 -11
  282. package/dist/stored-value/index.js +223 -4
  283. package/dist/subscriptions/index.d.ts +682 -0
  284. package/dist/subscriptions/index.js +815 -0
  285. package/dist/tax/display-policy.d.ts +105 -0
  286. package/dist/tax/display-policy.js +84 -0
  287. package/dist/tax/index.d.ts +79 -1
  288. package/dist/tax/index.js +52 -0
  289. package/dist/tenant/email-style.d.ts +102 -0
  290. package/dist/tenant/email-style.js +72 -0
  291. package/dist/tenant/email-templates.d.ts +287 -0
  292. package/dist/tenant/email-templates.js +271 -0
  293. package/dist/tenant/notifications.d.ts +23 -17
  294. package/dist/tenant/settings-sources.d.ts +1 -1
  295. package/dist/tenant/time-zone.d.ts +17 -0
  296. package/dist/tenant/time-zone.js +11 -0
  297. package/dist/types.d.ts +50 -3
  298. package/dist/users/index.d.ts +3 -0
  299. package/dist/users/index.js +2 -0
  300. package/dist/users/warehouses.d.ts +50 -0
  301. package/dist/users/warehouses.js +51 -0
  302. package/package.json +2 -2
@@ -0,0 +1,150 @@
1
+ /**
2
+ * `admin.documents.emails` — the store's email delivery log, its counts, a retry of one delivery,
3
+ * and a test send of a template (headless spec §9.2 `documents`, "email sends"; W10-103).
4
+ *
5
+ * Every call needs `UpdateSettings` on the active store. A delivery's recipients, subject, error
6
+ * and metadata are customer data: below `customerData: full` (an app installation) they read
7
+ * `''`/`null` and the row carries `redacted: true`.
8
+ */
9
+ import { type Page, type Transport } from '@sixtyninecommerce/core';
10
+ import type { Call, ListOptions } from '../types.js';
11
+ /**
12
+ * Where a delivery stands. `skipped`: the store's `sender: none` for the event (the platform sent
13
+ * nothing, on purpose). `suppressed_sandbox`: a sandbox store withheld the customer's copy. The
14
+ * engine may add others; a reader keeps a default branch.
15
+ */
16
+ export type EmailDeliveryStatus = 'sent' | 'failed' | 'blocked' | 'pending' | 'skipped' | 'suppressed_sandbox' | (string & {});
17
+ /** One email the store sent, or tried to. */
18
+ export interface EmailDelivery {
19
+ id: string;
20
+ /** The template it was rendered from (`order_confirmation`, …). */
21
+ templateType: string;
22
+ /** The notification event that sent it (`order_placed`, …). */
23
+ notificationEvent: string;
24
+ /** Comma-separated addresses. */
25
+ recipientTo: string;
26
+ recipientCc: string | null;
27
+ recipientBcc: string | null;
28
+ subject: string;
29
+ gatewayId: string | null;
30
+ status: EmailDeliveryStatus;
31
+ /** 1 for the first send; each retry adds one. */
32
+ attempt: number;
33
+ /** The mail provider's message id, when it took the email. */
34
+ messageId: string | null;
35
+ /** Why the last attempt failed, as the provider or the mail server said it. */
36
+ error: string | null;
37
+ durationMs: number | null;
38
+ /** The ids the email was rendered from (`orderId`, `invoiceId`, …) and other send facts. */
39
+ metadata: Record<string, unknown> | null;
40
+ /** ISO-8601 UTC. */
41
+ createdAt: string;
42
+ /**
43
+ * Set when the caller's customer-data level withheld part of the row (an app installation
44
+ * below `customerData: full`): the recipients, subject, error and metadata then read `''` or
45
+ * `null`, meaning "not granted", never "none on record".
46
+ */
47
+ redacted?: true;
48
+ }
49
+ /**
50
+ * Which deliveries a list or a count covers. The two instants need an explicit offset (`Z` or
51
+ * `±HH:mm`) and are refused before the request leaves without one (`INVALID_DATETIME`): a store's
52
+ * day is not the caller's. To cover the store's days, send the first instant of the first day as
53
+ * `createdFrom` and the first instant of the day AFTER the last one as `createdBefore`
54
+ * ({@link EmailDeliveriesModule.list}).
55
+ */
56
+ export interface EmailDeliveryFilter {
57
+ status?: EmailDeliveryStatus;
58
+ templateType?: string;
59
+ /** Created at or after this instant. */
60
+ createdFrom?: string;
61
+ /** Created strictly before this instant. */
62
+ createdBefore?: string;
63
+ }
64
+ /** How many of the store's deliveries are in each state, over a filter. */
65
+ export interface EmailDeliveryStats {
66
+ sent: number;
67
+ failed: number;
68
+ /** Never sent: every recipient is on the mail provider's suppression list. */
69
+ blocked: number;
70
+ pending: number;
71
+ }
72
+ export interface SendTestEmailInput {
73
+ /** A template type the platform renders (`admin.tenant.emailTemplates.catalog()`). */
74
+ templateType: string;
75
+ /** One email address. */
76
+ recipientEmail: string;
77
+ }
78
+ export interface EmailDeliveriesModule {
79
+ /**
80
+ * The store's deliveries, newest first, one page at a time (`limit` 1–100, 50 when omitted).
81
+ *
82
+ * **Store days.** The page that shows "from 24 to 25 October" in Europe/Amsterdam sends
83
+ * `createdFrom: '2026-10-24T00:00:00+02:00'` and `createdBefore: '2026-10-26T00:00:00+01:00'`:
84
+ * 25 October 2026 is 25 hours long there (the clocks go back), and the end is exclusive, so
85
+ * the whole last day is in and nothing of the next.
86
+ *
87
+ * **Coded refusals:** `INVALID_DATETIME` → `ValidationError` (400), an instant without an
88
+ * offset, before the request leaves; `EMAIL_DELIVERY_FILTER_INVALID` → `ValidationError`
89
+ * (400), a `limit` outside 1–100 or a range that ends before it starts.
90
+ *
91
+ * Rows carry `redacted: true` below `customerData: full` (see {@link EmailDelivery}).
92
+ *
93
+ * @effect read
94
+ * @permission UpdateSettings
95
+ */
96
+ list(filter?: EmailDeliveryFilter, options?: ListOptions): Promise<Page<EmailDelivery>>;
97
+ /**
98
+ * How many deliveries are `sent`, `failed`, `blocked` and `pending`, over the same filter as
99
+ * {@link list} without its `status` (a count by status). Without a filter: the store's whole
100
+ * log. On an engine from before this release (no filter argument) a filtered count is made
101
+ * from the list's totals, one read per status.
102
+ *
103
+ * @effect read
104
+ * @permission UpdateSettings
105
+ */
106
+ stats(filter?: Omit<EmailDeliveryFilter, 'status'>, options?: Call): Promise<EmailDeliveryStats>;
107
+ /**
108
+ * Send a `failed` or `blocked` delivery again, now, and answer the row with the new outcome:
109
+ * `status` is `sent`, `failed` again (with its `error`), `blocked` (the address is still on the
110
+ * provider's suppression list) or `suppressed_sandbox`. It resolves either way; read `status`.
111
+ * Synchronous: the email has been handed to the provider (or not) when it resolves.
112
+ *
113
+ * This sends the email to the CUSTOMER (or whoever it was addressed to) once more. Never call
114
+ * it in a loop or on a timer without a person deciding to.
115
+ *
116
+ * **Coded refusals** (nothing is sent): `EMAIL_DELIVERY_NOT_FOUND` → `NotFoundError` (404), no
117
+ * such delivery in this store; `EMAIL_DELIVERY_NOT_RETRYABLE` → `BusinessRuleError` (422), the
118
+ * delivery is not `failed` or `blocked`, it is a gift card's email (its code is not kept), or
119
+ * another retry of it started first.
120
+ *
121
+ * @effect irreversible
122
+ * @permission UpdateSettings
123
+ */
124
+ retry(id: string, options?: Call): Promise<EmailDelivery>;
125
+ }
126
+ export interface DocumentEmailsModule {
127
+ deliveries: EmailDeliveriesModule;
128
+ /**
129
+ * Render `templateType` with sample data and send it, now, through the store's own email
130
+ * gateway, to `recipientEmail`. It is real mail. The send writes no delivery row.
131
+ *
132
+ * **Coded refusals:** `INVALID_TEST_EMAIL_RECIPIENT` and `INVALID_TEST_EMAIL_TEMPLATE` →
133
+ * `ValidationError` (400), before anything is sent; `TEST_EMAIL_SEND_FAILED` →
134
+ * `BusinessRuleError` (422), the gateway did not take it (the message names the SMTP step or
135
+ * the provider's code, never the server's own words).
136
+ *
137
+ * @effect irreversible
138
+ * @permission UpdateSettings
139
+ */
140
+ sendTest(input: SendTestEmailInput, options?: Call): Promise<void>;
141
+ }
142
+ export declare const EMAIL_DELIVERIES_QUERY = "\n query AdminEmailDeliveries($options: EmailDeliveryLogListOptions) {\n emailDeliveryLogs(options: $options) {\n items { \n id\n templateType\n notificationEvent\n recipientTo\n recipientCc\n recipientBcc\n subject\n gatewayId\n status\n attempt\n messageId\n error\n durationMs\n metadata\n createdAt\n }\n totalItems\n }\n }\n";
143
+ export declare const EMAIL_DELIVERY_STATS_QUERY = "\n query AdminEmailDeliveryStats($options: EmailDeliveryStatsOptions) {\n emailDeliveryStats(options: $options) {\n sent\n failed\n blocked\n pending\n }\n }\n";
144
+ /** The store's whole log, without the filter argument an older engine does not know. */
145
+ export declare const EMAIL_DELIVERY_STATS_ALL_QUERY = "\n query AdminEmailDeliveryStatsAll {\n emailDeliveryStats {\n sent\n failed\n blocked\n pending\n }\n }\n";
146
+ /** How many deliveries match: the older engine's way to count over a filter. */
147
+ export declare const EMAIL_DELIVERY_COUNT_QUERY = "\n query AdminEmailDeliveryCount($options: EmailDeliveryLogListOptions) {\n emailDeliveryLogs(options: $options) {\n totalItems\n }\n }\n";
148
+ export declare const RETRY_EMAIL_DELIVERY_MUTATION = "\n mutation AdminRetryEmailDelivery($id: ID!) {\n retryEmailDelivery(id: $id) { \n id\n templateType\n notificationEvent\n recipientTo\n recipientCc\n recipientBcc\n subject\n gatewayId\n status\n attempt\n messageId\n error\n durationMs\n metadata\n createdAt\n }\n }\n";
149
+ export declare const SEND_TEST_EMAIL_MUTATION = "\n mutation AdminSendTestEmail($templateType: String!, $recipientEmail: String!) {\n sendTestEmail(templateType: $templateType, recipientEmail: $recipientEmail)\n }\n";
150
+ export declare function createDocumentEmailsModule(transport: Transport): DocumentEmailsModule;
@@ -0,0 +1,220 @@
1
+ /**
2
+ * `admin.documents.emails` — the store's email delivery log, its counts, a retry of one delivery,
3
+ * and a test send of a template (headless spec §9.2 `documents`, "email sends"; W10-103).
4
+ *
5
+ * Every call needs `UpdateSettings` on the active store. A delivery's recipients, subject, error
6
+ * and metadata are customer data: below `customerData: full` (an app installation) they read
7
+ * `''`/`null` and the row carries `redacted: true`.
8
+ */
9
+ import { pageFromVendureList, readOffsetCursor, ValidationError, } from '@sixtyninecommerce/core';
10
+ import { assertInstant } from '../catalog/types.js';
11
+ import { customerDataField } from '../customers/wire.js';
12
+ const DELIVERY_FIELDS = /* GraphQL */ `
13
+ id
14
+ templateType
15
+ notificationEvent
16
+ recipientTo
17
+ recipientCc
18
+ recipientBcc
19
+ subject
20
+ gatewayId
21
+ status
22
+ attempt
23
+ messageId
24
+ error
25
+ durationMs
26
+ metadata
27
+ createdAt
28
+ `;
29
+ export const EMAIL_DELIVERIES_QUERY = /* GraphQL */ `
30
+ query AdminEmailDeliveries($options: EmailDeliveryLogListOptions) {
31
+ emailDeliveryLogs(options: $options) {
32
+ items { ${DELIVERY_FIELDS} }
33
+ totalItems
34
+ }
35
+ }
36
+ `;
37
+ export const EMAIL_DELIVERY_STATS_QUERY = /* GraphQL */ `
38
+ query AdminEmailDeliveryStats($options: EmailDeliveryStatsOptions) {
39
+ emailDeliveryStats(options: $options) {
40
+ sent
41
+ failed
42
+ blocked
43
+ pending
44
+ }
45
+ }
46
+ `;
47
+ /** The store's whole log, without the filter argument an older engine does not know. */
48
+ export const EMAIL_DELIVERY_STATS_ALL_QUERY = /* GraphQL */ `
49
+ query AdminEmailDeliveryStatsAll {
50
+ emailDeliveryStats {
51
+ sent
52
+ failed
53
+ blocked
54
+ pending
55
+ }
56
+ }
57
+ `;
58
+ /** How many deliveries match: the older engine's way to count over a filter. */
59
+ export const EMAIL_DELIVERY_COUNT_QUERY = /* GraphQL */ `
60
+ query AdminEmailDeliveryCount($options: EmailDeliveryLogListOptions) {
61
+ emailDeliveryLogs(options: $options) {
62
+ totalItems
63
+ }
64
+ }
65
+ `;
66
+ export const RETRY_EMAIL_DELIVERY_MUTATION = /* GraphQL */ `
67
+ mutation AdminRetryEmailDelivery($id: ID!) {
68
+ retryEmailDelivery(id: $id) { ${DELIVERY_FIELDS} }
69
+ }
70
+ `;
71
+ export const SEND_TEST_EMAIL_MUTATION = /* GraphQL */ `
72
+ mutation AdminSendTestEmail($templateType: String!, $recipientEmail: String!) {
73
+ sendTestEmail(templateType: $templateType, recipientEmail: $recipientEmail)
74
+ }
75
+ `;
76
+ /** The engine's default page; the SDK states it so the cursor arithmetic knows the size. */
77
+ const DELIVERIES_PAGE_SIZE = 50;
78
+ function toDelivery(row) {
79
+ return {
80
+ id: String(row.id),
81
+ templateType: row.templateType,
82
+ notificationEvent: row.notificationEvent,
83
+ recipientTo: row.recipientTo,
84
+ recipientCc: row.recipientCc ?? null,
85
+ recipientBcc: row.recipientBcc ?? null,
86
+ subject: row.subject,
87
+ gatewayId: row.gatewayId ?? null,
88
+ status: row.status,
89
+ attempt: row.attempt,
90
+ messageId: row.messageId ?? null,
91
+ error: row.error ?? null,
92
+ durationMs: row.durationMs ?? null,
93
+ metadata: row.metadata ?? null,
94
+ createdAt: row.createdAt,
95
+ };
96
+ }
97
+ /** The engine's filter input: only the keys the caller set, so an older engine still answers. */
98
+ function wireFilter(filter) {
99
+ if (filter === undefined)
100
+ return {};
101
+ if (filter === null || typeof filter !== 'object' || Array.isArray(filter)) {
102
+ throw new ValidationError({
103
+ code: 'EMAIL_DELIVERY_FILTER_INVALID',
104
+ message: 'The delivery filter must be an object.',
105
+ status: 400,
106
+ service: 'vendure',
107
+ });
108
+ }
109
+ assertInstant('createdFrom', filter.createdFrom);
110
+ assertInstant('createdBefore', filter.createdBefore);
111
+ return {
112
+ ...(filter.status ? { status: filter.status } : {}),
113
+ ...(filter.templateType ? { templateType: filter.templateType } : {}),
114
+ ...(filter.createdFrom ? { dateFrom: filter.createdFrom } : {}),
115
+ ...(filter.createdBefore ? { dateBefore: filter.createdBefore } : {}),
116
+ };
117
+ }
118
+ /**
119
+ * The engine refused the document over a field or argument it does not know: an engine from
120
+ * before this release (no `dateBefore`, no `emailDeliveryStats(options:)`). The dashboard can be
121
+ * live before vendure-server (`.claude/rules/backend.md`, additive rule), so the module falls
122
+ * back to the older spelling instead of failing. Matched on the names this module added, so a
123
+ * real refusal is never retried.
124
+ */
125
+ function isOlderEngineRejection(error) {
126
+ if (!(error instanceof ValidationError) || error.service !== 'vendure')
127
+ return false;
128
+ // Apollo answers a document that fails validation (an unknown type or argument: the stats
129
+ // filter) with HTTP 400, which the transport reports as BAD_REQUEST without the engine's
130
+ // words; an unknown input field (dateBefore) is a variable coercion error at HTTP 200 and
131
+ // keeps them. Only ever asked about the documents this module added the names to.
132
+ return (error.code === 'BAD_REQUEST' ||
133
+ error.code === 'GRAPHQL_VALIDATION_FAILED' ||
134
+ /dateBefore|EmailDeliveryStatsOptions|Unknown argument "options"/.test(error.message));
135
+ }
136
+ /** The old inclusive spelling of an exclusive end: one millisecond before it. */
137
+ function legacyFilter(filter) {
138
+ const { dateBefore, ...rest } = filter;
139
+ if (typeof dateBefore !== 'string')
140
+ return rest;
141
+ return { ...rest, dateTo: new Date(Date.parse(dateBefore) - 1).toISOString() };
142
+ }
143
+ const COUNTED_STATUSES = ['sent', 'failed', 'blocked', 'pending'];
144
+ export function createDocumentEmailsModule(transport) {
145
+ /** Latched once the engine proved it predates `dateBefore` (one refused request per client). */
146
+ let olderEngine = false;
147
+ const listPage = async (skip, take, filter, options) => {
148
+ const read = (wire) => customerDataField(transport, EMAIL_DELIVERIES_QUERY, 'emailDeliveryLogs', { options: { ...wire, take, skip } }, options);
149
+ let answer;
150
+ if (olderEngine || !('dateBefore' in filter)) {
151
+ answer = await read(olderEngine ? legacyFilter(filter) : filter);
152
+ }
153
+ else {
154
+ try {
155
+ answer = await read(filter);
156
+ }
157
+ catch (error) {
158
+ if (!isOlderEngineRejection(error))
159
+ throw error;
160
+ olderEngine = true;
161
+ answer = await read(legacyFilter(filter));
162
+ }
163
+ }
164
+ const { value, mark } = answer;
165
+ const items = value.items.map((row) => mark(toDelivery(row)));
166
+ // Each following page re-sends the same filter and per-call options.
167
+ return pageFromVendureList({ items, totalItems: value.totalItems }, skip, take, (cursor) => listPage(readOffsetCursor(cursor), take, filter, options));
168
+ };
169
+ return {
170
+ deliveries: {
171
+ async list(filter, options) {
172
+ const wire = wireFilter(filter);
173
+ const take = options?.limit ?? DELIVERIES_PAGE_SIZE;
174
+ const skip = options?.after === undefined ? 0 : readOffsetCursor(options.after);
175
+ return listPage(skip, take, wire, options);
176
+ },
177
+ async stats(filter, options) {
178
+ const { status: _status, ...counted } = wireFilter(filter);
179
+ if (Object.keys(counted).length === 0) {
180
+ const data = await transport.graphql({ api: 'admin', query: EMAIL_DELIVERY_STATS_ALL_QUERY }, options);
181
+ return data.emailDeliveryStats;
182
+ }
183
+ if (!olderEngine) {
184
+ try {
185
+ const data = await transport.graphql({ api: 'admin', query: EMAIL_DELIVERY_STATS_QUERY, variables: { options: counted } }, options);
186
+ return data.emailDeliveryStats;
187
+ }
188
+ catch (error) {
189
+ if (!isOlderEngineRejection(error))
190
+ throw error;
191
+ olderEngine = true;
192
+ }
193
+ }
194
+ // An older engine counts only the whole log: count each status over the filter through
195
+ // the list's totals instead (one row read per status).
196
+ const totals = await Promise.all(COUNTED_STATUSES.map(async (status) => {
197
+ const data = await transport.graphql({
198
+ api: 'admin',
199
+ query: EMAIL_DELIVERY_COUNT_QUERY,
200
+ variables: { options: { ...legacyFilter(counted), status, take: 1, skip: 0 } },
201
+ }, options);
202
+ return data.emailDeliveryLogs.totalItems;
203
+ }));
204
+ const [sent = 0, failed = 0, blocked = 0, pending = 0] = totals;
205
+ return { sent, failed, blocked, pending };
206
+ },
207
+ async retry(id, options) {
208
+ const { value, mark } = await customerDataField(transport, RETRY_EMAIL_DELIVERY_MUTATION, 'retryEmailDelivery', { id }, options);
209
+ return mark(toDelivery(value));
210
+ },
211
+ },
212
+ async sendTest(input, options) {
213
+ await transport.graphql({
214
+ api: 'admin',
215
+ query: SEND_TEST_EMAIL_MUTATION,
216
+ variables: { templateType: input.templateType, recipientEmail: input.recipientEmail },
217
+ }, options);
218
+ },
219
+ };
220
+ }