@sixtyninecommerce/admin 0.8.0 → 0.10.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 (179) hide show
  1. package/README.md +2739 -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 +650 -0
  28. package/dist/catalog/products.js +458 -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 +324 -0
  34. package/dist/catalog/variants.js +288 -0
  35. package/dist/catalog/visibility.d.ts +57 -0
  36. package/dist/catalog/visibility.js +59 -0
  37. package/dist/client.d.ts +192 -3
  38. package/dist/client.js +87 -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 +437 -0
  66. package/dist/fulfillments/index.js +534 -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 +95 -9
  74. package/dist/index.js +21 -1
  75. package/dist/integrations/index.d.ts +14 -0
  76. package/dist/integrations/index.js +9 -0
  77. package/dist/integrations/pipelines.d.ts +113 -0
  78. package/dist/integrations/pipelines.js +62 -0
  79. package/dist/integrations/types.d.ts +176 -0
  80. package/dist/integrations/types.js +8 -0
  81. package/dist/jobs/bulk-action-run.d.ts +74 -0
  82. package/dist/jobs/bulk-action-run.js +69 -0
  83. package/dist/locations/index.d.ts +6 -2
  84. package/dist/meta/index.d.ts +32 -0
  85. package/dist/orders/bulk.d.ts +75 -0
  86. package/dist/orders/bulk.js +150 -0
  87. package/dist/orders/commit-edit.d.ts +123 -0
  88. package/dist/orders/commit-edit.js +142 -0
  89. package/dist/orders/deposit-policy.d.ts +103 -0
  90. package/dist/orders/deposit-policy.js +202 -0
  91. package/dist/orders/get.d.ts +722 -0
  92. package/dist/orders/get.js +530 -0
  93. package/dist/orders/index.d.ts +186 -21
  94. package/dist/orders/index.js +57 -0
  95. package/dist/orders/line-price-preview.d.ts +297 -0
  96. package/dist/orders/line-price-preview.js +50 -0
  97. package/dist/orders/manual-discounts.d.ts +141 -0
  98. package/dist/orders/manual-discounts.js +97 -0
  99. package/dist/orders/manual-pricing.d.ts +192 -0
  100. package/dist/orders/manual-pricing.js +72 -0
  101. package/dist/orders/order-entry.d.ts +566 -0
  102. package/dist/orders/order-entry.js +129 -0
  103. package/dist/orders/parcel-charge.d.ts +45 -0
  104. package/dist/orders/parcel-charge.js +55 -0
  105. package/dist/orders/plan-edit.d.ts +489 -0
  106. package/dist/orders/plan-edit.js +93 -0
  107. package/dist/orders/status.d.ts +235 -0
  108. package/dist/orders/status.js +96 -0
  109. package/dist/orders/surcharges.d.ts +75 -2
  110. package/dist/orders/surcharges.js +110 -4
  111. package/dist/payments/account-credit.d.ts +171 -0
  112. package/dist/payments/account-credit.js +107 -0
  113. package/dist/payments/index.d.ts +241 -9
  114. package/dist/payments/index.js +92 -0
  115. package/dist/payments/methods.d.ts +37 -0
  116. package/dist/payments/methods.js +33 -0
  117. package/dist/platform/catalog.d.ts +148 -0
  118. package/dist/platform/catalog.js +74 -0
  119. package/dist/platform/entitlements.d.ts +111 -0
  120. package/dist/platform/entitlements.js +86 -0
  121. package/dist/platform/legal-entities.d.ts +139 -0
  122. package/dist/platform/legal-entities.js +126 -0
  123. package/dist/platform/legal-entity-numbering.d.ts +103 -0
  124. package/dist/platform/legal-entity-numbering.js +101 -0
  125. package/dist/platform/organization-membership.d.ts +80 -0
  126. package/dist/platform/organization-membership.js +71 -0
  127. package/dist/platform/organization-settings.d.ts +33 -0
  128. package/dist/platform/organization-settings.js +42 -0
  129. package/dist/platform/organization-sharing.d.ts +172 -0
  130. package/dist/platform/organization-sharing.js +120 -0
  131. package/dist/platform/organizations.d.ts +205 -0
  132. package/dist/platform/organizations.js +180 -0
  133. package/dist/platform/provision-store.d.ts +88 -8
  134. package/dist/platform/provision-store.js +12 -0
  135. package/dist/platform/reports.d.ts +46 -0
  136. package/dist/platform/reports.js +31 -0
  137. package/dist/platform/server-metrics.d.ts +58 -0
  138. package/dist/platform/server-metrics.js +11 -0
  139. package/dist/pos/index.d.ts +567 -0
  140. package/dist/pos/index.js +205 -0
  141. package/dist/pricing/index.d.ts +408 -0
  142. package/dist/pricing/index.js +353 -0
  143. package/dist/pricing/vouchers.d.ts +103 -0
  144. package/dist/pricing/vouchers.js +69 -0
  145. package/dist/provenance/index.d.ts +93 -0
  146. package/dist/provenance/index.js +53 -0
  147. package/dist/reports/index.d.ts +199 -0
  148. package/dist/reports/index.js +154 -0
  149. package/dist/returns/index.d.ts +225 -0
  150. package/dist/returns/index.js +160 -0
  151. package/dist/search/index.d.ts +99 -0
  152. package/dist/search/index.js +62 -0
  153. package/dist/shipping/parcel-count-suggestion.d.ts +52 -0
  154. package/dist/shipping/parcel-count-suggestion.js +73 -0
  155. package/dist/shipping/parcel-rule.d.ts +48 -0
  156. package/dist/shipping/parcel-rule.js +91 -0
  157. package/dist/stock/index.d.ts +166 -0
  158. package/dist/stock/index.js +115 -0
  159. package/dist/stock/transfers.d.ts +520 -0
  160. package/dist/stock/transfers.js +437 -0
  161. package/dist/stored-value/index.d.ts +154 -0
  162. package/dist/stored-value/index.js +130 -0
  163. package/dist/tenant/capabilities.d.ts +10 -0
  164. package/dist/tenant/entitlements.d.ts +35 -0
  165. package/dist/tenant/entitlements.js +15 -0
  166. package/dist/tenant/field-groups.d.ts +64 -0
  167. package/dist/tenant/field-groups.js +34 -0
  168. package/dist/tenant/legal-entity.d.ts +32 -0
  169. package/dist/tenant/legal-entity.js +41 -0
  170. package/dist/tenant/notifications.d.ts +148 -0
  171. package/dist/tenant/notifications.js +79 -0
  172. package/dist/tenant/retention.d.ts +10 -4
  173. package/dist/tenant/settings-sources.d.ts +103 -0
  174. package/dist/tenant/settings-sources.js +92 -0
  175. package/dist/types.d.ts +86 -11
  176. package/dist/uploads/index.d.ts +11 -7
  177. package/dist/users/index.d.ts +104 -0
  178. package/dist/users/index.js +80 -0
  179. package/package.json +3 -2
@@ -1,4 +1,4 @@
1
- import { createPage, ValidationError } from '@sixtyninecommerce/core';
1
+ import { createPage, idempotencyKey as mintIdempotencyKey, pageFromVendureList, readOffsetCursor, ValidationError, } from '@sixtyninecommerce/core';
2
2
  const API_DOCUMENT_TYPE = {
3
3
  invoice: 'INVOICE',
4
4
  'credit-note': 'CREDIT_NOTE',
@@ -10,6 +10,18 @@ const API_DOCUMENT_TYPE = {
10
10
  * dropped the overflow — a document is a page, not a feed.
11
11
  */
12
12
  export const MAX_DOCUMENT_EXTENSIONS = 10;
13
+ const DOCUMENT_LINE_FIELDS = /* GraphQL */ `
14
+ description
15
+ sku
16
+ orderLineId
17
+ quantity
18
+ unitPriceNet
19
+ unitPriceWithTax
20
+ taxRate
21
+ lineTotalNet
22
+ lineTotalWithTax
23
+ unitPriceBasis
24
+ `;
13
25
  const INVOICE_FIELDS = /* GraphQL */ `
14
26
  id
15
27
  invoiceNumber
@@ -21,7 +33,13 @@ const INVOICE_FIELDS = /* GraphQL */ `
21
33
  currencyCode
22
34
  note
23
35
  voidedOn
36
+ reversedByCreditNoteId
37
+ supersededByInvoiceId
38
+ reissueOfInvoiceId
24
39
  kind
40
+ supplementsInvoiceId
41
+ lines { ${DOCUMENT_LINE_FIELDS} }
42
+ numberingProvider
25
43
  createdAt
26
44
  `;
27
45
  const CREDIT_NOTE_FIELDS = /* GraphQL */ `
@@ -33,6 +51,11 @@ const CREDIT_NOTE_FIELDS = /* GraphQL */ `
33
51
  reason
34
52
  invoiceId
35
53
  kind
54
+ reversalOfInvoiceId
55
+ returnId
56
+ lines { ${DOCUMENT_LINE_FIELDS} }
57
+ refundId
58
+ numberingProvider
36
59
  storageKey
37
60
  createdAt
38
61
  `;
@@ -88,6 +111,15 @@ export const DOCUMENT_DATA_QUERY = /* GraphQL */ `
88
111
  documentData(documentId: $documentId, type: $type)
89
112
  }
90
113
  `;
114
+ export const CREATE_DOCUMENT_DOWNLOAD_URL_MUTATION = /* GraphQL */ `
115
+ mutation CreateDocumentDownloadUrl($documentId: ID!, $type: DocumentType!) {
116
+ createDocumentDownloadUrl(documentId: $documentId, type: $type) {
117
+ url
118
+ expiresAt
119
+ filename
120
+ }
121
+ }
122
+ `;
91
123
  export const ORDER_INVOICES_QUERY = /* GraphQL */ `
92
124
  query OrderInvoices($orderId: ID!) {
93
125
  orderInvoices(orderId: $orderId) { ${INVOICE_FIELDS} }
@@ -98,6 +130,20 @@ export const ORDER_CREDIT_NOTES_QUERY = /* GraphQL */ `
98
130
  orderCreditNotes(orderId: $orderId) { ${CREDIT_NOTE_FIELDS} }
99
131
  }
100
132
  `;
133
+ export const CUSTOMER_DOCUMENTS_QUERY = /* GraphQL */ `
134
+ query CustomerDocuments($customerId: ID!, $options: CustomerDocumentListOptions) {
135
+ customerDocuments(customerId: $customerId, options: $options) {
136
+ totalItems
137
+ items {
138
+ type
139
+ orderId
140
+ orderCode
141
+ invoice { ${INVOICE_FIELDS} }
142
+ creditNote { ${CREDIT_NOTE_FIELDS} }
143
+ }
144
+ }
145
+ }
146
+ `;
101
147
  export const PREVIEW_INVOICE_MUTATION = /* GraphQL */ `
102
148
  mutation PreviewInvoice($orderId: ID!, $invoicedOn: String!, $languageCode: String, $note: String) {
103
149
  previewInvoice(orderId: $orderId, invoicedOn: $invoicedOn, languageCode: $languageCode, note: $note)
@@ -108,6 +154,23 @@ export const SEND_INVOICE_EMAIL_MUTATION = /* GraphQL */ `
108
154
  sendInvoiceEmail(input: $input)
109
155
  }
110
156
  `;
157
+ export const SEND_CREDIT_NOTE_EMAIL_MUTATION = /* GraphQL */ `
158
+ mutation SendCreditNoteEmail($input: SendCreditNoteEmailInput!) {
159
+ sendCreditNoteEmail(input: $input)
160
+ }
161
+ `;
162
+ export const ORDER_CREDIT_NOTE_EMAIL_SENDS_QUERY = /* GraphQL */ `
163
+ query OrderCreditNoteEmailSends($orderId: ID!) {
164
+ orderCreditNoteEmailSends(orderId: $orderId) {
165
+ id
166
+ orderId
167
+ creditNoteId
168
+ recipientsTo
169
+ recipientsCc
170
+ createdAt
171
+ }
172
+ }
173
+ `;
111
174
  export const ISSUE_PARTIAL_CREDIT_NOTE_MUTATION = /* GraphQL */ `
112
175
  mutation IssuePartialCreditNote($input: IssuePartialCreditNoteInput!) {
113
176
  issuePartialCreditNote(input: $input) { ${CREDIT_NOTE_FIELDS} }
@@ -118,6 +181,98 @@ export const ISSUE_SUPPLEMENTARY_INVOICE_MUTATION = /* GraphQL */ `
118
181
  issueSupplementaryInvoice(input: $input) { ${INVOICE_FIELDS} }
119
182
  }
120
183
  `;
184
+ const TOTALS_FIELDS = /* GraphQL */ `
185
+ net
186
+ tax
187
+ gross
188
+ taxSummary { taxRate net tax gross }
189
+ `;
190
+ const INVOICE_CORRECTION_PLAN_FIELDS = /* GraphQL */ `
191
+ planHash
192
+ ledger
193
+ invoice { id invoiceNumber kind totalWithTax creditable currencyCode orderIds voidedOn }
194
+ outcomes {
195
+ outcome
196
+ available
197
+ unavailableCode
198
+ unavailableMessage
199
+ document
200
+ needsLines
201
+ prefillsLines
202
+ needsAddresses
203
+ allowsRefund
204
+ allowsNote
205
+ }
206
+ prefill {
207
+ source
208
+ basis
209
+ lines { description sku orderLineId quantity unitPrice unitPriceBasis taxRate }
210
+ seededGross
211
+ billedGross
212
+ differs
213
+ }
214
+ outcome
215
+ operation
216
+ lineTotals { ${TOTALS_FIELDS} }
217
+ documents {
218
+ type
219
+ kind
220
+ documentDate
221
+ currencyCode
222
+ lines { ${DOCUMENT_LINE_FIELDS} }
223
+ totals { ${TOTALS_FIELDS} }
224
+ issuer
225
+ numbering { provider providerLabel external ready legalEntityId testStore }
226
+ }
227
+ refund {
228
+ amount
229
+ available
230
+ unavailableCode
231
+ unavailableMessage
232
+ options { paymentId method amount refundable }
233
+ recommendedPaymentId
234
+ }
235
+ accountCredit { available unavailableCode message }
236
+ settlement
237
+ blockers { code message path }
238
+ `;
239
+ export const PLAN_INVOICE_CORRECTION_QUERY = /* GraphQL */ `
240
+ query PlanInvoiceCorrection($input: InvoiceCorrectionInput!) {
241
+ planInvoiceCorrection(input: $input) { ${INVOICE_CORRECTION_PLAN_FIELDS} }
242
+ }
243
+ `;
244
+ /**
245
+ * Its own document on purpose: `commitInvoiceCorrection` exists only on an engine with the
246
+ * correction commit, so on an older one this call alone fails validation.
247
+ */
248
+ export const COMMIT_INVOICE_CORRECTION_MUTATION = /* GraphQL */ `
249
+ mutation CommitInvoiceCorrection(
250
+ $input: InvoiceCorrectionInput!
251
+ $expectedPlanHash: String!
252
+ $idempotencyKey: String!
253
+ ) {
254
+ commitInvoiceCorrection(
255
+ input: $input
256
+ expectedPlanHash: $expectedPlanHash
257
+ idempotencyKey: $idempotencyKey
258
+ ) {
259
+ plan { ${INVOICE_CORRECTION_PLAN_FIELDS} }
260
+ documents {
261
+ ledger
262
+ type
263
+ id
264
+ number
265
+ kind
266
+ totalWithTax
267
+ currencyCode
268
+ issuedOn
269
+ numberingProvider
270
+ }
271
+ refund { id state total paymentId }
272
+ accountCredit { amount currencyCode unappliedCashIds }
273
+ }
274
+ }
275
+ `;
121
276
  export const DOCUMENT_EXTENSIONS_QUERY = /* GraphQL */ `
122
277
  query DocumentExtensions {
123
278
  documentExtensions { ${DOCUMENT_EXTENSION_FIELDS} }
@@ -157,9 +312,12 @@ function toApiType(type) {
157
312
  * sees that the block is registered for something — which is why the decoded
158
313
  * field is typed `string[]`.
159
314
  */
315
+ const DOCUMENT_TYPE_BY_API = new Map(Object.entries(API_DOCUMENT_TYPE).map(([type, api]) => [api, type]));
316
+ function fromApiType(value) {
317
+ return DOCUMENT_TYPE_BY_API.get(value) ?? value;
318
+ }
160
319
  function fromApiTypes(values) {
161
- const byApi = new Map(Object.entries(API_DOCUMENT_TYPE).map(([type, api]) => [api, type]));
162
- return values.map((value) => byApi.get(value) ?? value);
320
+ return values.map(fromApiType);
163
321
  }
164
322
  function toSchema(raw) {
165
323
  return { ...raw, documentTypes: fromApiTypes(raw.documentTypes) };
@@ -175,7 +333,19 @@ function decodePdf(base64) {
175
333
  }
176
334
  return bytes;
177
335
  }
336
+ /** The server's default page; the SDK states it so the cursor arithmetic knows the size. */
337
+ const CUSTOMER_DOCUMENTS_PAGE_SIZE = 25;
178
338
  export function createDocumentsModule(transport) {
339
+ const customerDocumentsPage = async (customerId, skip, take, options) => {
340
+ const data = await transport.graphql({
341
+ api: 'admin',
342
+ query: CUSTOMER_DOCUMENTS_QUERY,
343
+ variables: { customerId, options: { skip, take } },
344
+ }, options);
345
+ const list = data.customerDocuments;
346
+ const items = list.items.map((item) => ({ ...item, type: fromApiType(item.type) }));
347
+ return pageFromVendureList({ items, totalItems: list.totalItems }, skip, take, (cursor) => customerDocumentsPage(customerId, readOffsetCursor(cursor), take, options));
348
+ };
179
349
  return {
180
350
  // One implementation behind the two overloads the interface declares; the
181
351
  // cast is the standard TypeScript shape for that, since an implementation
@@ -232,6 +402,14 @@ export function createDocumentsModule(transport) {
232
402
  }, options);
233
403
  return data.documentData;
234
404
  },
405
+ async downloadUrl(documentId, type, options) {
406
+ const data = await transport.graphql({
407
+ api: 'admin',
408
+ query: CREATE_DOCUMENT_DOWNLOAD_URL_MUTATION,
409
+ variables: { documentId, type: toApiType(type) },
410
+ }, options);
411
+ return data.createDocumentDownloadUrl;
412
+ },
235
413
  async invoices(orderId, options) {
236
414
  const data = await transport.graphql({ api: 'admin', query: ORDER_INVOICES_QUERY, variables: { orderId } }, options);
237
415
  return wholeList(data.orderInvoices, 'orderInvoices');
@@ -240,6 +418,12 @@ export function createDocumentsModule(transport) {
240
418
  const data = await transport.graphql({ api: 'admin', query: ORDER_CREDIT_NOTES_QUERY, variables: { orderId } }, options);
241
419
  return wholeList(data.orderCreditNotes, 'orderCreditNotes');
242
420
  },
421
+ forCustomer(customerId, options) {
422
+ // The Admin API is offset-paged; the offset hides inside the cursor (spec §9.3).
423
+ const take = options?.limit ?? CUSTOMER_DOCUMENTS_PAGE_SIZE;
424
+ const skip = options?.after === undefined ? 0 : readOffsetCursor(options.after);
425
+ return customerDocumentsPage(customerId, skip, take, options);
426
+ },
243
427
  async previewInvoice(orderId, options) {
244
428
  const data = await transport.graphql({
245
429
  api: 'admin',
@@ -257,10 +441,52 @@ export function createDocumentsModule(transport) {
257
441
  const data = await transport.graphql({ api: 'admin', query: SEND_INVOICE_EMAIL_MUTATION, variables: { input } }, options);
258
442
  return data.sendInvoiceEmail;
259
443
  },
444
+ async sendCreditNoteEmail(input, options) {
445
+ const data = await transport.graphql({
446
+ api: 'admin',
447
+ query: SEND_CREDIT_NOTE_EMAIL_MUTATION,
448
+ variables: {
449
+ input: {
450
+ creditNoteId: input.creditNoteId,
451
+ recipientsTo: input.recipientsTo,
452
+ recipientsCc: input.recipientsCc ?? null,
453
+ },
454
+ },
455
+ }, options);
456
+ return data.sendCreditNoteEmail;
457
+ },
458
+ async creditNoteEmailSends(orderId, options) {
459
+ const data = await transport.graphql({ api: 'admin', query: ORDER_CREDIT_NOTE_EMAIL_SENDS_QUERY, variables: { orderId } }, options);
460
+ return wholeList(data.orderCreditNoteEmailSends, 'orderCreditNoteEmailSends');
461
+ },
260
462
  async issuePartialCreditNote(input, options) {
261
463
  const data = await transport.graphql({ api: 'admin', query: ISSUE_PARTIAL_CREDIT_NOTE_MUTATION, variables: { input } }, options);
262
464
  return data.issuePartialCreditNote;
263
465
  },
466
+ async planCorrection(input, options) {
467
+ const data = await transport.graphql({ api: 'admin', query: PLAN_INVOICE_CORRECTION_QUERY, variables: { input } }, options);
468
+ return data.planInvoiceCorrection;
469
+ },
470
+ async commitCorrection(input, options) {
471
+ const key = options.idempotencyKey ?? mintIdempotencyKey();
472
+ try {
473
+ const data = await transport.graphql({
474
+ api: 'admin',
475
+ query: COMMIT_INVOICE_CORRECTION_MUTATION,
476
+ // The input goes out as given, never normalised: it must be the request the plan was
477
+ // made for, and a retry under the same key must send the same one.
478
+ variables: { input, expectedPlanHash: options.expectedPlanHash, idempotencyKey: key },
479
+ }, { ...options, idempotencyKey: key });
480
+ return { ...data.commitInvoiceCorrection, idempotencyKey: key };
481
+ }
482
+ catch (error) {
483
+ // The key the caller retries under, when it did not choose one.
484
+ if (error !== null && typeof error === 'object' && !('idempotencyKey' in error)) {
485
+ Object.defineProperty(error, 'idempotencyKey', { value: key, enumerable: true });
486
+ }
487
+ throw error;
488
+ }
489
+ },
264
490
  async issueSupplementaryInvoice(input, options) {
265
491
  const data = await transport.graphql({ api: 'admin', query: ISSUE_SUPPLEMENTARY_INVOICE_MUTATION, variables: { input } }, options);
266
492
  return data.issueSupplementaryInvoice;
@@ -0,0 +1,259 @@
1
+ import { createEventHandler, type EventsClient, type Page, type Transport } from '@sixtyninecommerce/core';
2
+ import type { Call, ListOptions } from '../types.js';
3
+ /**
4
+ * A webhook subscription: where the platform POSTs this store's events, which ones, and
5
+ * how the destination is doing. The index signature keeps fields a newer platform adds.
6
+ */
7
+ export interface EventSubscription {
8
+ id: string;
9
+ name: string;
10
+ /** The endpoint. Must resolve to a public address; the platform re-checks it per delivery. */
11
+ url: string;
12
+ /** Exact types, family wildcards (`order.*`) or `*`, as given. */
13
+ eventFilters: string[];
14
+ enabled: boolean;
15
+ /** Failures in a row that count toward auto-disable (a transient failure does not). */
16
+ consecutiveFailures: number;
17
+ /** ISO-8601 UTC, or null while enabled. */
18
+ disabledAt: string | null;
19
+ disabledReason: string | null;
20
+ /** Set when retention destroyed events this subscription had not yet received. */
21
+ gapDetectedAt: string | null;
22
+ lastDeliveredAt: string | null;
23
+ createdAt: string;
24
+ /** Whether this destination receives customer identity stripped. */
25
+ redactCustomerIdentity: boolean;
26
+ /**
27
+ * The app installation this subscription belongs to, or null for the store's
28
+ * own. An app's subscription follows the app's declarations: `update`,
29
+ * `setEnabled`, `rotateSecret` and `remove` refuse it (`ConflictError`,
30
+ * `SUBSCRIPTION_OWNED_BY_APP`) — uninstall the app or approve its declarations
31
+ * instead. Absent from a platform older than app subscriptions.
32
+ */
33
+ ownerInstallationId?: string | null;
34
+ /** `filters`, or `compliance_only` for an uninstalled app awaiting its redact request. */
35
+ deliveryScope?: string;
36
+ /** How the most recent attempt ended (`delivered`, `transient`, …), null before the first. */
37
+ lastOutcome?: string | null;
38
+ lastAttemptAt?: string | null;
39
+ [extra: string]: unknown;
40
+ }
41
+ /** What `create` answers: the subscription plus its signing secret, shown this ONCE. */
42
+ export interface CreatedEventSubscription extends EventSubscription {
43
+ /**
44
+ * The signing secret (hex). Store it now — no read returns it again. Pass it to
45
+ * `createEventHandler` / `verifyEventDelivery`; its Standard Webhooks spelling for other
46
+ * libraries is `whsec_` + base64 of this string's UTF-8 bytes.
47
+ */
48
+ secret: string;
49
+ }
50
+ /** What `rotateSecret` answers: the NEW secret, shown this once. */
51
+ export interface RotatedEventSubscriptionSecret {
52
+ /** The new secret (hex), the spelling `create` answers. Store it now. */
53
+ secret: string;
54
+ /** The same key material as a Standard Webhooks `whsec_` secret, for stock libraries. */
55
+ secretStandard: string;
56
+ /**
57
+ * ISO-8601 UTC. Until then every delivery is signed with BOTH secrets
58
+ * (`webhook-signature: v1,<new> v1,<old>`) and the legacy `x-webhook-signature` with the
59
+ * OLD one, so verify with `[newSecret, oldSecret]` meanwhile and drop the old one after.
60
+ */
61
+ previousExpiresAt: string;
62
+ [extra: string]: unknown;
63
+ }
64
+ export interface EventSubscriptionInput {
65
+ name: string;
66
+ url: string;
67
+ eventFilters: string[];
68
+ /** Strip customer identity before signing. Default false. */
69
+ redactCustomerIdentity?: boolean;
70
+ }
71
+ export type EventSubscriptionUpdate = Partial<EventSubscriptionInput>;
72
+ /** One delivery attempt, newest first. */
73
+ export interface EventDelivery {
74
+ id: string;
75
+ eventId: string;
76
+ eventType: string;
77
+ logSeq: string;
78
+ attempt: number;
79
+ /** `delivered` | `transient` | `fault` | `dead_lettered` | … */
80
+ outcome: string;
81
+ /** The subscriber's HTTP status, or null when nothing came back. */
82
+ responseStatus: number | null;
83
+ durationMs: number | null;
84
+ error: string | null;
85
+ /** Per-attempt evidence the dispatcher recorded; keys as stored. */
86
+ evidence: Record<string, unknown> | null;
87
+ createdAt: string;
88
+ [extra: string]: unknown;
89
+ }
90
+ /**
91
+ * What `sendTest` answered: how the endpoint took ONE synthetic delivery. An endpoint that
92
+ * refused it is still an answer (`delivered: false`), not a thrown error.
93
+ */
94
+ export interface EventSendTestResult {
95
+ /** True when the endpoint answered 2xx. */
96
+ delivered: boolean;
97
+ /** `delivered` | `transient` | `subscription_fault` | `dead_lettered` — as a real delivery's would. */
98
+ outcome: string;
99
+ /** The endpoint's HTTP status, or null when nothing came back. */
100
+ status: number | null;
101
+ durationMs: number;
102
+ /** The synthetic envelope's `id` (its `webhook-id`). It is not in the event log. */
103
+ webhookId: string;
104
+ /** Why nothing came back — a timeout, a refused connection, an endpoint the platform refused. */
105
+ error: string | null;
106
+ [extra: string]: unknown;
107
+ }
108
+ export interface EventSendTestOptions extends Call {
109
+ /**
110
+ * The envelope `data` to send, verbatim (a JSON object, at most 64 KiB). Omit it for the
111
+ * platform's example payload of the type.
112
+ */
113
+ data?: Record<string, unknown>;
114
+ }
115
+ /** Most event ids one `redrive` call may name. */
116
+ export declare const MAX_REDRIVE_EVENT_IDS = 100;
117
+ /**
118
+ * What `redrive` answered for one id. `queued`: it will be delivered again;
119
+ * `already_queued`: a request for it is still waiting; `filtered_out`: the subscription does
120
+ * not take its type; `expired`: it has left the event log; `not_found`: no such event in this
121
+ * store's log.
122
+ */
123
+ export type RedriveRequestState = 'queued' | 'already_queued' | 'filtered_out' | 'expired' | 'not_found' | (string & {});
124
+ export interface RedriveAnswer {
125
+ items: Array<{
126
+ eventId: string;
127
+ state: RedriveRequestState;
128
+ }>;
129
+ }
130
+ /** One redrive request, as the platform tracks it. */
131
+ export interface EventRedrive {
132
+ id: string;
133
+ eventId: string;
134
+ logSeq: string;
135
+ occurredAt: string;
136
+ requestedAt: string;
137
+ /** `<kind>:<id>` of the principal that asked (`user:`, `api_token:`, `app_installation:`). */
138
+ requestedBy: string | null;
139
+ /** `queued` until the platform settles it, then `delivered` or `failed`. */
140
+ state: 'queued' | 'delivered' | 'failed' | (string & {});
141
+ attempts: number;
142
+ /** The last delivery outcome, or why it settled without one (`expired`, `filtered_out`). */
143
+ lastOutcome: string | null;
144
+ completedAt: string | null;
145
+ [extra: string]: unknown;
146
+ }
147
+ export interface EventSubscriptionsModule {
148
+ /** Needs `ReadSettings` or `ManageEventSubscriptions`. Newest first. */
149
+ list(options?: ListOptions): Promise<Page<EventSubscription>>;
150
+ /**
151
+ * Needs `ManageEventSubscriptions` (or `UpdateSettings`). Answers the secret ONCE. Takes no idempotency key: the route is
152
+ * deliberately not idempotent, because a stored replay would keep the secret for 24 hours
153
+ * — so a retry after a lost response creates a second subscription; `list` and `remove`
154
+ * the spare.
155
+ */
156
+ create(input: EventSubscriptionInput, options?: Omit<Call, 'idempotencyKey'>): Promise<CreatedEventSubscription>;
157
+ /** Needs `ManageEventSubscriptions` (or `UpdateSettings`). Name, endpoint, filters and the redaction flag. */
158
+ update(id: string, input: EventSubscriptionUpdate, options?: Call): Promise<EventSubscription>;
159
+ /** Needs `ManageEventSubscriptions` (or `UpdateSettings`). Turning a subscription on clears its failure count. */
160
+ setEnabled(id: string, enabled: boolean, options?: Call): Promise<EventSubscription>;
161
+ /** Needs `ManageEventSubscriptions` (or `UpdateSettings`). */
162
+ remove(id: string, options?: Call): Promise<void>;
163
+ /**
164
+ * Needs `ManageEventSubscriptions` (or `UpdateSettings`). Issues a new signing secret and answers it ONCE. The secret it
165
+ * replaces keeps co-signing for 24 hours (`previousExpiresAt`), so a receiver switches
166
+ * over without dropping a delivery:
167
+ *
168
+ * ```ts
169
+ * const { secret } = await admin.events.subscriptions.rotateSecret(id);
170
+ * // deploy the receiver with both, newest first …
171
+ * createEventHandler({ secret: [secret, oldSecret], on: { … } });
172
+ * // … and drop oldSecret after previousExpiresAt.
173
+ * ```
174
+ *
175
+ * A second rotation inside that window is refused (`ConflictError`, 409,
176
+ * `ROTATION_IN_PROGRESS`). `{ force: true }` rotates anyway and the OLDER secret stops
177
+ * signing at once — receivers still holding only it stop verifying. The secret being
178
+ * replaced always keeps co-signing, forced or not, so to revoke a LEAKED current secret
179
+ * immediately call `rotateSecret(id, { force: true })` TWICE: the second call drops the
180
+ * leaked secret. (Both forced: a plain call is refused while an overlap is running.) Takes no idempotency key, like `create`: the answer carries a secret. A retry
181
+ * after a lost answer is refused `ROTATION_IN_PROGRESS`; recover with `force`.
182
+ */
183
+ rotateSecret(id: string, options?: {
184
+ force?: boolean;
185
+ } & Omit<Call, 'idempotencyKey'>): Promise<RotatedEventSubscriptionSecret>;
186
+ /**
187
+ * Needs `ReadSettings` or `ManageEventSubscriptions`. Recent attempts, newest first. A `sendTest` attempt is listed
188
+ * too, marked `evidence.test === true` (its `logSeq` is `"0"`: it was never in the log).
189
+ */
190
+ deliveries(id: string, options?: ListOptions): Promise<Page<EventDelivery>>;
191
+ /**
192
+ * Needs `ManageEventSubscriptions` (or `UpdateSettings`). Sign and POST ONE synthetic event of `type` to this
193
+ * subscription's endpoint — to prove a receiver verifies and parses a delivery without
194
+ * waiting for the store to produce one:
195
+ *
196
+ * ```ts
197
+ * const result = await admin.events.subscriptions.sendTest(id, 'order.created');
198
+ * if (!result.delivered) console.error(result.status ?? result.error);
199
+ * ```
200
+ *
201
+ * The envelope is signed, redacted and SSRF-checked like a real delivery, with a fresh
202
+ * `id`, `actor: { type: 'system', id: 'system:send-test' }` and `test: true` — acknowledge
203
+ * it and do not act on it. It is never written to the event log (not in `poll` or
204
+ * `stream`), never moves the subscription's position and never counts toward auto-disable.
205
+ * `data` defaults to the platform's example payload for the type.
206
+ *
207
+ * Works on a disabled subscription (check a fixed endpoint before `setEnabled(id, true)`).
208
+ * Refused: an uncatalogued type (`ValidationError`, `UNKNOWN_EVENT_TYPE`); a type outside
209
+ * the subscription's filters (`BusinessRuleError`, 422, `EVENT_TYPE_NOT_SUBSCRIBED`); an
210
+ * app's subscription (`ConflictError`, `SUBSCRIPTION_OWNED_BY_APP`); a closed store
211
+ * (`PermissionDeniedError`, 403, `CHANNEL_SOFT_DELETED`, as everywhere else). Takes an idempotency key: a retry under the same key
212
+ * replays the first answer instead of sending a second test.
213
+ */
214
+ sendTest(id: string, type: string, options?: EventSendTestOptions): Promise<EventSendTestResult>;
215
+ }
216
+ /**
217
+ * Platform events (headless spec §6.9): pull the store's event log, answer webhooks, and
218
+ * manage where they are pushed.
219
+ *
220
+ * `poll` / `pollAll` / `replay` / `head` read `GET /events`, and `stream` / `streamTicket`
221
+ * push the same events over Server-Sent Events (`GET /events/stream`); all of them need a
222
+ * MACHINE credential holding `ReadPlatformEvents` and scoped to one channel, and a stream
223
+ * cursor and a feed cursor are interchangeable. A credential's live streams are limited
224
+ * (one by default): opening another closes the oldest (`EventStreamClosedError`,
225
+ * `superseded`). Events reach the log
226
+ * through relays that tick once a minute, so a stream delivers them up to about a minute
227
+ * after the change.
228
+ *
229
+ * `subscriptions.*`, `redrive` and `redrives` need `ManageEventSubscriptions` — the `events`
230
+ * API-token template grants it beside `ReadPlatformEvents`. Reads also accept `ReadSettings`;
231
+ * writes still accept `UpdateSettings` for credentials minted before the permission existed,
232
+ * a transition that a later release ends (announced in the changelog first).
233
+ */
234
+ export interface EventsModule extends EventsClient {
235
+ /**
236
+ * `events.subscribe` — build a webhook endpoint `(Request) => Promise<Response>` with typed
237
+ * per-event handlers. Needs no credential: it verifies with the subscription secret.
238
+ */
239
+ subscribe: typeof createEventHandler;
240
+ subscriptions: EventSubscriptionsModule;
241
+ /**
242
+ * Needs `ManageEventSubscriptions` (or `UpdateSettings`). Queue chosen events to be delivered to ONE subscription again — a
243
+ * dead letter, or any event still in the log that passes the subscription's filters (a
244
+ * replay to one destination). 1-100 ids. Answers a state per id; only `queued` ids will be
245
+ * delivered.
246
+ *
247
+ * A queued id is delivered again with the same envelope `id`, so `webhook-id` dedupe holds;
248
+ * only `delivered_at` / `webhook-timestamp` move. `redrives` shows it `delivered`, or
249
+ * `failed` when the endpoint dead-letters it again or keeps refusing it (8 attempts). Events
250
+ * that have left the log (`expired`) are recovered from the pull feed (`events.replay`).
251
+ * A disabled subscription is refused
252
+ * (`BusinessRuleError`, 422, `SUBSCRIPTION_DISABLED`). Takes an idempotency key: a retry
253
+ * under the same key replays the first answer.
254
+ */
255
+ redrive(subscriptionId: string, eventIds: readonly string[], options?: Call): Promise<RedriveAnswer>;
256
+ /** Needs `ReadSettings` or `ManageEventSubscriptions`. One subscription's redrive requests, newest first. */
257
+ redrives(subscriptionId: string, options?: ListOptions): Promise<Page<EventRedrive>>;
258
+ }
259
+ export declare function createEventsModule(transport: Transport): EventsModule;