@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,3 +1,5 @@
1
+ import { createAccountCreditModule } from './account-credit.js';
2
+ import { createPaymentMethodsModule } from './methods.js';
1
3
  import { createProvidersModule } from './providers.js';
2
4
  const PAID_ORDER_FIELDS = /* GraphQL */ `
3
5
  id
@@ -64,9 +66,66 @@ export const REFUND_ORDER_PAYMENT_MUTATION = /* GraphQL */ `
64
66
  }
65
67
  }
66
68
  `;
69
+ export const UNRECONCILED_REFUNDS_QUERY = /* GraphQL */ `
70
+ query UnreconciledRefunds($orderId: ID!) {
71
+ orderRefundPosition(orderId: $orderId) {
72
+ unreconciled {
73
+ refundId
74
+ paymentId
75
+ amount
76
+ createdAt
77
+ }
78
+ }
79
+ }
80
+ `;
81
+ export const RECONCILE_REFUND_OUTCOME_MUTATION = /* GraphQL */ `
82
+ mutation ReconcileRefundOutcome($input: ReconcileRefundOutcomeInput!, $idempotencyKey: String) {
83
+ reconcileRefundOutcome(input: $input, idempotencyKey: $idempotencyKey) {
84
+ id
85
+ total
86
+ state
87
+ method
88
+ transactionId
89
+ reason
90
+ }
91
+ }
92
+ `;
93
+ export const REPORT_APP_PAYMENT_MUTATION = /* GraphQL */ `
94
+ mutation ReportAppPayment(
95
+ $paymentId: ID!
96
+ $input: ReportAppPaymentInput!
97
+ $idempotencyKey: String!
98
+ ) {
99
+ reportAppPayment(paymentId: $paymentId, input: $input, idempotencyKey: $idempotencyKey) {
100
+ id
101
+ state
102
+ amount
103
+ method
104
+ transactionId
105
+ }
106
+ }
107
+ `;
108
+ export const APP_PAYMENTS_QUERY = /* GraphQL */ `
109
+ query AppPayments($orderId: ID!) {
110
+ appPayments(orderId: $orderId) {
111
+ id
112
+ orderId
113
+ orderCode
114
+ state
115
+ amount
116
+ currencyCode
117
+ providerReference
118
+ methodCode
119
+ outcomeUnknown
120
+ createdAt
121
+ }
122
+ }
123
+ `;
67
124
  export function createPaymentsModule(transport) {
68
125
  return {
69
126
  providers: createProvidersModule(transport),
127
+ accountCredit: createAccountCreditModule(transport),
128
+ methods: createPaymentMethodsModule(transport),
70
129
  async record(orderId, input, options) {
71
130
  const data = await transport.graphql({
72
131
  api: 'admin',
@@ -99,5 +158,38 @@ export function createPaymentsModule(transport) {
99
158
  const data = await transport.graphql({ api: 'admin', query: ORDER_REFUND_POSITION_QUERY, variables: { orderId } }, options);
100
159
  return data.orderRefundPosition;
101
160
  },
161
+ // Its own query rather than a field on `refundPosition`'s: an SDK released
162
+ // ahead of a server without `unreconciled` must not break that read.
163
+ async unreconciledRefunds(orderId, options) {
164
+ const data = await transport.graphql({ api: 'admin', query: UNRECONCILED_REFUNDS_QUERY, variables: { orderId } }, options);
165
+ return data.orderRefundPosition.unreconciled;
166
+ },
167
+ async reconcileRefund(input, options) {
168
+ const data = await transport.graphql({
169
+ api: 'admin',
170
+ query: RECONCILE_REFUND_OUTCOME_MUTATION,
171
+ // A variable, not a header: the Admin API takes the key as an argument.
172
+ variables: { input, idempotencyKey: options?.idempotencyKey },
173
+ }, options);
174
+ return data.reconcileRefundOutcome;
175
+ },
176
+ async reportAppPayment(paymentId, input, options) {
177
+ // Refused here rather than sent: the argument is non-null on the wire, and
178
+ // a report without a key could never be safely repeated.
179
+ if (typeof options?.idempotencyKey !== 'string' || options.idempotencyKey.trim() === '') {
180
+ throw new TypeError('reportAppPayment needs options.idempotencyKey: one key per provider event.');
181
+ }
182
+ const data = await transport.graphql({
183
+ api: 'admin',
184
+ query: REPORT_APP_PAYMENT_MUTATION,
185
+ // A variable, not a header: the Admin API takes the key as an argument.
186
+ variables: { paymentId, input, idempotencyKey: options.idempotencyKey },
187
+ }, options);
188
+ return data.reportAppPayment;
189
+ },
190
+ async appPayments(orderId, options) {
191
+ const data = await transport.graphql({ api: 'admin', query: APP_PAYMENTS_QUERY, variables: { orderId } }, options);
192
+ return data.appPayments;
193
+ },
102
194
  };
103
195
  }
@@ -0,0 +1,37 @@
1
+ import type { Page, Transport } from '@sixtyninecommerce/core';
2
+ import type { ListOptions } from '../types.js';
3
+ /** One payment method of the store, as staff choose one. */
4
+ export interface PaymentMethod {
5
+ id: string;
6
+ /**
7
+ * The method's code: what `payments.record` takes as `method`, and what a
8
+ * deposit policy names in `storefront.methods`.
9
+ */
10
+ code: string;
11
+ name: string;
12
+ /** A disabled method takes no new payments; it still names the ones it took. */
13
+ enabled: boolean;
14
+ /**
15
+ * The code of the handler that takes the money (`manual-payment`, `mollie`, a
16
+ * payment app's handler, …): what kind of method this is.
17
+ */
18
+ handlerCode: string;
19
+ }
20
+ /**
21
+ * The store's payment methods (`admin.payments.methods`): what staff record a
22
+ * payment with, and what a deposit policy names.
23
+ *
24
+ * Needs `ReadSettings` or `ReadPaymentMethod`. Scoped to the channel the client
25
+ * is on: another store's methods are not listed.
26
+ */
27
+ export interface PaymentMethodsModule {
28
+ /**
29
+ * Every method of the store, enabled or not, in the engine's order. Paged
30
+ * (`options.limit`, default 100, at most 1,000 or `INVALID_PAGE_SIZE`; and
31
+ * `options.after` from a previous page); `for await (const method of page)`
32
+ * walks them all.
33
+ */
34
+ list(options?: ListOptions): Promise<Page<PaymentMethod>>;
35
+ }
36
+ export declare const PAYMENT_METHODS_QUERY = "\n query StorePaymentMethods($options: PaymentMethodListOptions) {\n paymentMethods(options: $options) {\n items {\n id\n code\n name\n enabled\n handler {\n code\n }\n }\n totalItems\n }\n }\n";
37
+ export declare function createPaymentMethodsModule(transport: Transport): PaymentMethodsModule;
@@ -0,0 +1,33 @@
1
+ import { vendureListPage } from '../catalog/types.js';
2
+ /** A store has a handful; one page holds them. */
3
+ const DEFAULT_PAGE_SIZE = 100;
4
+ export const PAYMENT_METHODS_QUERY = /* GraphQL */ `
5
+ query StorePaymentMethods($options: PaymentMethodListOptions) {
6
+ paymentMethods(options: $options) {
7
+ items {
8
+ id
9
+ code
10
+ name
11
+ enabled
12
+ handler {
13
+ code
14
+ }
15
+ }
16
+ totalItems
17
+ }
18
+ }
19
+ `;
20
+ const toMethod = (method) => ({
21
+ id: method.id,
22
+ code: method.code,
23
+ name: method.name,
24
+ enabled: method.enabled,
25
+ handlerCode: method.handler.code,
26
+ });
27
+ export function createPaymentMethodsModule(transport) {
28
+ return {
29
+ list(options) {
30
+ return vendureListPage(transport, PAYMENT_METHODS_QUERY, 'paymentMethods', (skip, take) => ({ options: { skip, take } }), { ...options, limit: options?.limit ?? DEFAULT_PAGE_SIZE }, toMethod);
31
+ },
32
+ };
33
+ }
@@ -0,0 +1,148 @@
1
+ import type { Transport } from '@sixtyninecommerce/core';
2
+ import type { AdminClientConfig } from '../client.js';
3
+ import type { Call } from '../types.js';
4
+ /** Which kind of catalogue entity a repair row is about. */
5
+ export type TranslationRepairEntityKind = 'PRODUCT' | 'VARIANT';
6
+ /**
7
+ * Why the repair left a candidate alone. A string on the wire, not an enum, so
8
+ * a reason added later never breaks a caller:
9
+ *
10
+ * - `SHARED_CHANNEL_LANGUAGES_DIFFER`: another store holds the entity and offers
11
+ * other languages, so a row foreign here may be that store's own.
12
+ * - `CARRIER_PRODUCT`: the platform's hidden external-line carrier.
13
+ * - `DEFAULT_ROW_GONE`: its default-language row disappeared before it could be
14
+ * repaired.
15
+ */
16
+ export type TranslationRepairSkipReason = 'SHARED_CHANNEL_LANGUAGES_DIFFER' | 'CARRIER_PRODUCT' | 'DEFAULT_ROW_GONE' | (string & {});
17
+ export interface RepairTranslationLanguagesInput {
18
+ /** The store to repair. The root channel and organization hubs are refused. */
19
+ channelId: string;
20
+ /**
21
+ * `true` reports what would be removed and changes nothing; `false` removes
22
+ * it. Both take the same path on the server, so a dry run is an exact preview
23
+ * of the apply that follows it (as long as the catalogue does not change in
24
+ * between).
25
+ */
26
+ dryRun: boolean;
27
+ /**
28
+ * Products and variants examined per page. The server defaults to 100 and
29
+ * treats anything above 500 as 500; below 1 is refused
30
+ * (`TRANSLATION_REPAIR_LIMIT_INVALID`).
31
+ */
32
+ limit?: number;
33
+ /**
34
+ * The `nextCursor` of the previous page of the SAME run: same store, same
35
+ * `dryRun`. Omitted or null starts from the beginning. A cursor issued for
36
+ * another store or the other mode is refused (`TRANSLATION_REPAIR_CURSOR_INVALID`),
37
+ * so a dry run's cursor can never make an apply skip the pages before it.
38
+ */
39
+ cursor?: string | null;
40
+ /**
41
+ * On the first page (no cursor) only: also list the store's single-variant
42
+ * products whose variant name differs from the product name.
43
+ */
44
+ includeNameReport?: boolean;
45
+ }
46
+ /**
47
+ * A translation row the repair removes, or in a dry run would remove. The whole
48
+ * row, so a dry run's answer is the undo record of the apply.
49
+ */
50
+ export interface TranslationRepairRemovedRow {
51
+ translationId: string;
52
+ languageCode: string;
53
+ name: string;
54
+ /** Products only; null for a variant. */
55
+ slug: string | null;
56
+ /** Products only; null for a variant. */
57
+ description: string | null;
58
+ }
59
+ /**
60
+ * A product or variant with a row in the store's default language and one or
61
+ * more rows in languages the store does not offer.
62
+ */
63
+ export interface TranslationRepairEntity {
64
+ kind: TranslationRepairEntityKind;
65
+ id: string;
66
+ /** The product itself for a `PRODUCT`, the variant's product for a `VARIANT`. */
67
+ productId: string;
68
+ /** Variants only; null for a product. */
69
+ sku: string | null;
70
+ /** The language of the row the merchant sees, which stays. */
71
+ keptLanguageCode: string;
72
+ /** The name the merchant sees, which stays. */
73
+ keptName: string;
74
+ removed: TranslationRepairRemovedRow[];
75
+ }
76
+ /** A candidate the repair leaves alone, and why. */
77
+ export interface TranslationRepairSkipped {
78
+ kind: TranslationRepairEntityKind;
79
+ id: string;
80
+ productId: string;
81
+ sku: string | null;
82
+ /** The name the merchant sees. */
83
+ name: string;
84
+ reason: TranslationRepairSkipReason;
85
+ }
86
+ /**
87
+ * A single-variant product whose variant shows another name than the product.
88
+ * Report only: the repair never renames.
89
+ */
90
+ export interface TranslationRepairNameMismatch {
91
+ productId: string;
92
+ variantId: string;
93
+ sku: string;
94
+ productName: string;
95
+ variantName: string;
96
+ }
97
+ /** One page of a repair run, exactly as the engine answers it. */
98
+ export interface TranslationRepairPage {
99
+ /** Fresh for every call; the audit rows of an apply page carry it. */
100
+ runId: string;
101
+ dryRun: boolean;
102
+ channelId: string;
103
+ channelCode: string;
104
+ defaultLanguageCode: string;
105
+ /** The default language first, then the store's other available languages. */
106
+ offeredLanguageCodes: string[];
107
+ repaired: TranslationRepairEntity[];
108
+ skipped: TranslationRepairSkipped[];
109
+ /**
110
+ * Products and variants whose rows are ALL in languages the store does not
111
+ * offer. Never touched (removing them would remove their only name). Counted
112
+ * on the first page only; 0 on later pages.
113
+ */
114
+ foreignOnlyCount: number;
115
+ /** First page with `includeNameReport` only, for the whole store; empty otherwise. */
116
+ nameMismatches: TranslationRepairNameMismatch[];
117
+ /** Pass back as `cursor` to continue this run; null when every candidate was examined. */
118
+ nextCursor: string | null;
119
+ }
120
+ /**
121
+ * `admin.platform.catalog` — SuperAdmin catalogue maintenance across stores.
122
+ * An API token or an app credential is refused BEFORE anything is sent
123
+ * (`PLATFORM_REQUIRES_SESSION`); the server refuses one anyway.
124
+ */
125
+ export interface PlatformCatalogModule {
126
+ /**
127
+ * One page of the translation-language repair for one store: removes the
128
+ * store's product and variant translation rows in languages it does not
129
+ * offer, ONLY from entities that also have a row in the store's default
130
+ * language. It never writes a name and never touches an entity whose rows
131
+ * are all foreign (`foreignOnlyCount`).
132
+ *
133
+ * Run it with `dryRun: true` first and keep the answer: its removed rows are
134
+ * the undo record. Follow `nextCursor` until it is null to cover the store;
135
+ * each page is its own transaction, so an apply that stops half-way leaves
136
+ * the pages before it applied and is safe to run again from the start.
137
+ *
138
+ * Refusals: `TRANSLATION_REPAIR_CHANNEL_NOT_FOUND` (404 `NotFoundError`),
139
+ * `TRANSLATION_REPAIR_NOT_A_STORE` (422 `BusinessRuleError`: the root channel
140
+ * or an organization hub), `TRANSLATION_REPAIR_CURSOR_INVALID` and
141
+ * `TRANSLATION_REPAIR_LIMIT_INVALID` (400 `ValidationError`), and
142
+ * `CHANNEL_ACCESS_DENIED` / `FORBIDDEN` (403 `PermissionDeniedError`) for a
143
+ * caller who is not a SuperAdmin.
144
+ */
145
+ repairTranslationLanguages(input: RepairTranslationLanguagesInput, call?: Call): Promise<TranslationRepairPage>;
146
+ }
147
+ export declare const REPAIR_TRANSLATION_LANGUAGES_MUTATION = "\n mutation PlatformRepairProductTranslationLanguages($input: ProductTranslationRepairInput!) {\n repairProductTranslationLanguages(input: $input) {\n runId\n dryRun\n channelId\n channelCode\n defaultLanguageCode\n offeredLanguageCodes\n repaired {\n kind\n id\n productId\n sku\n keptLanguageCode\n keptName\n removed {\n translationId\n languageCode\n name\n slug\n description\n }\n }\n skipped {\n kind\n id\n productId\n sku\n name\n reason\n }\n foreignOnlyCount\n nameMismatches {\n productId\n variantId\n sku\n productName\n variantName\n }\n nextCursor\n }\n }\n";
148
+ export declare function createPlatformCatalogModule(transport: Transport, config: AdminClientConfig): PlatformCatalogModule;
@@ -0,0 +1,74 @@
1
+ import { requireHumanSession } from './guard.js';
2
+ export const REPAIR_TRANSLATION_LANGUAGES_MUTATION = /* GraphQL */ `
3
+ mutation PlatformRepairProductTranslationLanguages($input: ProductTranslationRepairInput!) {
4
+ repairProductTranslationLanguages(input: $input) {
5
+ runId
6
+ dryRun
7
+ channelId
8
+ channelCode
9
+ defaultLanguageCode
10
+ offeredLanguageCodes
11
+ repaired {
12
+ kind
13
+ id
14
+ productId
15
+ sku
16
+ keptLanguageCode
17
+ keptName
18
+ removed {
19
+ translationId
20
+ languageCode
21
+ name
22
+ slug
23
+ description
24
+ }
25
+ }
26
+ skipped {
27
+ kind
28
+ id
29
+ productId
30
+ sku
31
+ name
32
+ reason
33
+ }
34
+ foreignOnlyCount
35
+ nameMismatches {
36
+ productId
37
+ variantId
38
+ sku
39
+ productName
40
+ variantName
41
+ }
42
+ nextCursor
43
+ }
44
+ }
45
+ `;
46
+ export function createPlatformCatalogModule(transport, config) {
47
+ return {
48
+ async repairTranslationLanguages(input, call) {
49
+ requireHumanSession(config);
50
+ // Only what the caller named: an omitted `limit` must reach the server as
51
+ // absent, so the SERVER's default applies rather than one copied here.
52
+ const variables = {
53
+ channelId: input.channelId,
54
+ dryRun: input.dryRun,
55
+ };
56
+ if (input.limit !== undefined)
57
+ variables.limit = input.limit;
58
+ if (input.cursor !== undefined && input.cursor !== null)
59
+ variables.cursor = input.cursor;
60
+ if (input.includeNameReport !== undefined) {
61
+ variables.includeNameReport = input.includeNameReport;
62
+ }
63
+ const data = await transport.graphql({
64
+ api: 'admin',
65
+ query: REPAIR_TRANSLATION_LANGUAGES_MUTATION,
66
+ variables: { input: variables },
67
+ },
68
+ // The store is named in the input, not by the tenant: a SuperAdmin
69
+ // repairs any store from wherever the client happens to be scoped.
70
+ { ...call, allowUnscoped: true });
71
+ return data.repairProductTranslationLanguages;
72
+ },
73
+ };
74
+ }
@@ -0,0 +1,111 @@
1
+ import type { Transport } from '@sixtyninecommerce/core';
2
+ import type { AdminClientConfig } from '../client.js';
3
+ import type { Call } from '../types.js';
4
+ /** Where a store's plugin grant comes from. */
5
+ export type PlatformEntitlementSource = 'channel' | 'organization';
6
+ /**
7
+ * One plugin grant of a named store (or of an organization's workspace), as the
8
+ * platform's control plane reads it.
9
+ */
10
+ export interface PlatformPluginEntitlement {
11
+ /**
12
+ * The grant row's id. An `organization` row carries the ORGANIZATION grant's
13
+ * id, shared by every channel that grant reaches, so pair it with
14
+ * `channelId` when you need a key that is unique across a `listAll`.
15
+ */
16
+ id: string;
17
+ /** The store, or the organization's workspace channel, the grant applies to. */
18
+ channelId: string;
19
+ /** The plugin's catalog key (`pos`, `afas`, `mollie`, …), exactly as the engine sends it. */
20
+ pluginKey: string;
21
+ /** ISO-8601 UTC. Connecting a plugin that is already connected refreshes it. */
22
+ connectedAt: string;
23
+ connectedByUserId: string | null;
24
+ notes: string | null;
25
+ /** ISO-8601 UTC. */
26
+ createdAt: string;
27
+ updatedAt: string;
28
+ /**
29
+ * `channel` for the store's own grant, `organization` for one its
30
+ * organization holds for every member store and its workspace. When both
31
+ * exist the store's own row wins, so a store lists a key once. An
32
+ * `organization` grant is changed on the organization
33
+ * (`platform.organizations.entitlements.set`), never through
34
+ * {@link PlatformEntitlementsModule.set}. A string on the wire: a newer
35
+ * engine may answer a source this SDK does not name yet.
36
+ */
37
+ source: PlatformEntitlementSource | (string & {});
38
+ }
39
+ export interface SetPlatformEntitlementOptions {
40
+ /** `true` connects the plugin for the store; `false` removes the store's own grant. */
41
+ connected: boolean;
42
+ /**
43
+ * Operator notes, kept with the store's own grant. Omitted keeps the notes
44
+ * already stored; `''` clears them. Ignored on a disconnect, which deletes
45
+ * the row. At most 512 characters: the engine stores no more, and a longer
46
+ * value fails the write.
47
+ */
48
+ notes?: string;
49
+ }
50
+ /**
51
+ * `admin.platform.entitlements` — which external plugins each store may use,
52
+ * read and switched by the platform's operators (spec §7, §9.2 `platform`).
53
+ *
54
+ * SuperAdmin with a HUMAN session only: an API token or an app credential is
55
+ * refused with `PLATFORM_REQUIRES_SESSION` (403) before anything is sent, and a
56
+ * session without SuperAdmin is refused `FORBIDDEN` (403) by the engine. The
57
+ * calls are platform-wide, so the client needs no tenant.
58
+ *
59
+ * Core plugins (search, email) are on for every store and never have a row.
60
+ * The engine reads these rows on every operation; the plugins service's own
61
+ * gates follow a change within its 60-second entitlement cache.
62
+ */
63
+ export interface PlatformEntitlementsModule {
64
+ /**
65
+ * The plugins one store may use: its own grants and the ones its
66
+ * organization holds (`source`), by plugin key. The store's own row wins
67
+ * where both exist. An organization's workspace channel lists its
68
+ * organization's grants the same way. The engine does not check that the
69
+ * channel exists: an unknown id is not refused, it answers the rows stored
70
+ * under it (normally none).
71
+ */
72
+ list(channelId: string, call?: Call): Promise<PlatformPluginEntitlement[]>;
73
+ /**
74
+ * Every grant across the platform. An organization grant appears once per
75
+ * member store and once for the organization's workspace
76
+ * (`source: 'organization'`, each under that channel's id), except where the
77
+ * channel holds the plugin itself.
78
+ */
79
+ listAll(call?: Call): Promise<PlatformPluginEntitlement[]>;
80
+ /**
81
+ * Connect (`connected: true`) or disconnect (`connected: false`) an external
82
+ * plugin for one store.
83
+ *
84
+ * - Connecting answers the store's own row; connecting again refreshes
85
+ * `connectedAt` and `connectedByUserId` (and the notes, when given).
86
+ * - Disconnecting deletes the store's OWN row and answers `null`. A plugin
87
+ * the store's organization also grants stays connected through it: read
88
+ * {@link list} again to see it as `source: 'organization'`.
89
+ *
90
+ * Refusals: `MANAGED_BY_ORGANIZATION` (422 `BusinessRuleError`: the store
91
+ * holds the plugin only through its organization; revoke it there),
92
+ * `ORGANIZATION_WORKSPACE_NOT_A_STORE` (422: an organization's workspace
93
+ * cannot be connected on its own; grant the plugin to the organization),
94
+ * `USER_INPUT_ERROR` (400 `ValidationError`: a key that is not an external
95
+ * plugin of the catalog), `FORBIDDEN` (403). The engine does not check
96
+ * that `channelId` names a store, so pass an id read from the platform's
97
+ * channel list.
98
+ *
99
+ * Not deduplicated by an idempotency key (an Admin API mutation). A second
100
+ * connect refreshes the same row. A second disconnect answers `null` again,
101
+ * unless the organization also grants the plugin: the first removed the
102
+ * store's own grant, so the second is refused `MANAGED_BY_ORGANIZATION`.
103
+ * After a disconnect whose answer was lost, read {@link list} instead of
104
+ * repeating it.
105
+ */
106
+ set(channelId: string, pluginKey: string, options: SetPlatformEntitlementOptions, call?: Call): Promise<PlatformPluginEntitlement | null>;
107
+ }
108
+ export declare const TENANT_PLUGIN_ENTITLEMENTS_QUERY = "\n query PlatformTenantPluginEntitlements($channelId: ID!) {\n tenantPluginEntitlements(channelId: $channelId) { \n id\n channelId\n pluginKey\n connectedAt\n connectedByUserId\n notes\n createdAt\n updatedAt\n source\n }\n }\n";
109
+ export declare const ALL_PLUGIN_ENTITLEMENTS_QUERY = "\n query PlatformAllPluginEntitlements {\n allPluginEntitlements { \n id\n channelId\n pluginKey\n connectedAt\n connectedByUserId\n notes\n createdAt\n updatedAt\n source\n }\n }\n";
110
+ export declare const SET_TENANT_PLUGIN_ENTITLEMENT_MUTATION = "\n mutation PlatformSetTenantPluginEntitlement(\n $channelId: ID!\n $pluginKey: String!\n $connected: Boolean!\n $notes: String\n ) {\n setTenantPluginEntitlement(\n channelId: $channelId\n pluginKey: $pluginKey\n connected: $connected\n notes: $notes\n ) { \n id\n channelId\n pluginKey\n connectedAt\n connectedByUserId\n notes\n createdAt\n updatedAt\n source\n }\n }\n";
111
+ export declare function createPlatformEntitlementsModule(transport: Transport, config: AdminClientConfig): PlatformEntitlementsModule;
@@ -0,0 +1,86 @@
1
+ import { requireHumanSession } from './guard.js';
2
+ const ENTITLEMENT_FIELDS = /* GraphQL */ `
3
+ id
4
+ channelId
5
+ pluginKey
6
+ connectedAt
7
+ connectedByUserId
8
+ notes
9
+ createdAt
10
+ updatedAt
11
+ source
12
+ `;
13
+ export const TENANT_PLUGIN_ENTITLEMENTS_QUERY = /* GraphQL */ `
14
+ query PlatformTenantPluginEntitlements($channelId: ID!) {
15
+ tenantPluginEntitlements(channelId: $channelId) { ${ENTITLEMENT_FIELDS} }
16
+ }
17
+ `;
18
+ export const ALL_PLUGIN_ENTITLEMENTS_QUERY = /* GraphQL */ `
19
+ query PlatformAllPluginEntitlements {
20
+ allPluginEntitlements { ${ENTITLEMENT_FIELDS} }
21
+ }
22
+ `;
23
+ export const SET_TENANT_PLUGIN_ENTITLEMENT_MUTATION = /* GraphQL */ `
24
+ mutation PlatformSetTenantPluginEntitlement(
25
+ $channelId: ID!
26
+ $pluginKey: String!
27
+ $connected: Boolean!
28
+ $notes: String
29
+ ) {
30
+ setTenantPluginEntitlement(
31
+ channelId: $channelId
32
+ pluginKey: $pluginKey
33
+ connected: $connected
34
+ notes: $notes
35
+ ) { ${ENTITLEMENT_FIELDS} }
36
+ }
37
+ `;
38
+ /** Only the fields this type promises, whatever else a newer engine adds to a row. */
39
+ function toEntitlement(row) {
40
+ return {
41
+ id: row.id,
42
+ channelId: row.channelId,
43
+ pluginKey: row.pluginKey,
44
+ connectedAt: row.connectedAt,
45
+ connectedByUserId: row.connectedByUserId,
46
+ notes: row.notes,
47
+ createdAt: row.createdAt,
48
+ updatedAt: row.updatedAt,
49
+ source: row.source,
50
+ };
51
+ }
52
+ export function createPlatformEntitlementsModule(transport, config) {
53
+ // Platform-wide: a SuperAdmin names the store by id, so no tenant is needed.
54
+ // Scoped to a store instead, the transport would first resolve that store
55
+ // through the CMS channel mirror and fail on one missing there.
56
+ const unscoped = (call) => ({ ...call, allowUnscoped: true });
57
+ return {
58
+ async list(channelId, call) {
59
+ requireHumanSession(config);
60
+ const data = await transport.graphql({ api: 'admin', query: TENANT_PLUGIN_ENTITLEMENTS_QUERY, variables: { channelId } }, unscoped(call));
61
+ return data.tenantPluginEntitlements.map(toEntitlement);
62
+ },
63
+ async listAll(call) {
64
+ requireHumanSession(config);
65
+ const data = await transport.graphql({ api: 'admin', query: ALL_PLUGIN_ENTITLEMENTS_QUERY }, unscoped(call));
66
+ return data.allPluginEntitlements.map(toEntitlement);
67
+ },
68
+ async set(channelId, pluginKey, options, call) {
69
+ requireHumanSession(config);
70
+ const data = await transport.graphql({
71
+ api: 'admin',
72
+ query: SET_TENANT_PLUGIN_ENTITLEMENT_MUTATION,
73
+ variables: {
74
+ channelId,
75
+ pluginKey,
76
+ connected: options.connected,
77
+ // The engine reads null as "keep the stored notes"; '' is a value
78
+ // that clears them, so it must not collapse into null here.
79
+ notes: options.notes ?? null,
80
+ },
81
+ }, unscoped(call));
82
+ const row = data.setTenantPluginEntitlement;
83
+ return row === null ? null : toEntitlement(row);
84
+ },
85
+ };
86
+ }