@sixtyninecommerce/admin 0.9.0 → 0.11.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.
- package/README.md +4598 -128
- package/dist/catalog/assets.d.ts +120 -8
- package/dist/catalog/assets.js +98 -12
- package/dist/catalog/collections.d.ts +279 -14
- package/dist/catalog/collections.js +246 -15
- package/dist/catalog/index.d.ts +9 -2
- package/dist/catalog/index.js +4 -0
- package/dist/catalog/products.d.ts +318 -17
- package/dist/catalog/products.js +182 -14
- package/dist/catalog/properties.d.ts +102 -14
- package/dist/catalog/properties.js +13 -0
- package/dist/catalog/recommendations.d.ts +261 -0
- package/dist/catalog/recommendations.js +145 -0
- package/dist/catalog/reviews.d.ts +368 -0
- package/dist/catalog/reviews.js +318 -0
- package/dist/catalog/types.d.ts +17 -1
- package/dist/catalog/types.js +10 -0
- package/dist/catalog/variant-find.d.ts +136 -0
- package/dist/catalog/variant-find.js +239 -0
- package/dist/catalog/variants.d.ts +282 -25
- package/dist/catalog/variants.js +173 -25
- package/dist/catalog/visibility.d.ts +20 -5
- package/dist/catalog/visibility.js +19 -1
- package/dist/client.d.ts +116 -9
- package/dist/client.js +41 -7
- package/dist/content/assets.d.ts +286 -0
- package/dist/content/assets.js +181 -0
- package/dist/content/items.d.ts +269 -0
- package/dist/content/items.js +170 -0
- package/dist/content/types.d.ts +282 -0
- package/dist/content/types.js +100 -0
- package/dist/customers/age-verification.d.ts +66 -0
- package/dist/customers/age-verification.js +79 -0
- package/dist/customers/credit.d.ts +263 -0
- package/dist/customers/credit.js +244 -0
- package/dist/customers/delivery-preferences.d.ts +120 -0
- package/dist/customers/delivery-preferences.js +97 -0
- package/dist/customers/groups.d.ts +207 -0
- package/dist/customers/groups.js +438 -0
- package/dist/customers/index.d.ts +447 -37
- package/dist/customers/index.js +313 -40
- package/dist/customers/invitation-status.d.ts +57 -0
- package/dist/customers/invitation-status.js +21 -0
- package/dist/customers/notes.d.ts +67 -0
- package/dist/customers/notes.js +51 -0
- package/dist/customers/notification-recipients.d.ts +97 -0
- package/dist/customers/notification-recipients.js +84 -0
- package/dist/customers/saved-lists.d.ts +42 -0
- package/dist/customers/saved-lists.js +61 -0
- package/dist/customers/vat.d.ts +83 -0
- package/dist/customers/vat.js +46 -0
- package/dist/customers/wire.d.ts +20 -0
- package/dist/customers/wire.js +35 -0
- package/dist/documents/ar.d.ts +307 -0
- package/dist/documents/ar.js +254 -0
- package/dist/documents/index.d.ts +731 -21
- package/dist/documents/index.js +436 -24
- package/dist/documents/pdf.d.ts +2 -0
- package/dist/documents/pdf.js +11 -0
- package/dist/fulfillments/create.d.ts +167 -0
- package/dist/fulfillments/create.js +216 -0
- package/dist/fulfillments/index.d.ts +121 -10
- package/dist/fulfillments/index.js +120 -2
- package/dist/fulfillments/payment-policy.d.ts +64 -0
- package/dist/fulfillments/payment-policy.js +140 -0
- package/dist/fulfillments/warehouse.d.ts +724 -0
- package/dist/fulfillments/warehouse.js +914 -0
- package/dist/geo/index.d.ts +119 -0
- package/dist/geo/index.js +107 -0
- package/dist/index.d.ts +129 -30
- package/dist/index.js +47 -9
- package/dist/integrations/afas-actions.d.ts +234 -0
- package/dist/integrations/afas-actions.js +310 -0
- package/dist/integrations/afas-types.d.ts +483 -0
- package/dist/integrations/afas-types.js +5 -0
- package/dist/integrations/afas.d.ts +234 -0
- package/dist/integrations/afas.js +290 -0
- package/dist/integrations/connections.d.ts +103 -0
- package/dist/integrations/connections.js +55 -0
- package/dist/integrations/esp.d.ts +313 -0
- package/dist/integrations/esp.js +169 -0
- package/dist/integrations/exact-online-types.d.ts +423 -0
- package/dist/integrations/exact-online-types.js +6 -0
- package/dist/integrations/exact-online.d.ts +248 -0
- package/dist/integrations/exact-online.js +376 -0
- package/dist/integrations/index.d.ts +40 -0
- package/dist/integrations/index.js +24 -0
- package/dist/integrations/pipeline-keys.d.ts +13 -0
- package/dist/integrations/pipeline-keys.js +22 -0
- package/dist/integrations/pipelines.d.ts +114 -0
- package/dist/integrations/pipelines.js +46 -0
- package/dist/integrations/types.d.ts +322 -0
- package/dist/integrations/types.js +8 -0
- package/dist/jobs/bulk-action-run.d.ts +11 -2
- package/dist/jobs/bulk-action-run.js +4 -5
- package/dist/locations/index.d.ts +66 -0
- package/dist/locations/index.js +31 -0
- package/dist/meta/index.d.ts +162 -0
- package/dist/orders/bulk.d.ts +75 -0
- package/dist/orders/bulk.js +150 -0
- package/dist/orders/cancel-with-money.d.ts +227 -0
- package/dist/orders/cancel-with-money.js +153 -0
- package/dist/orders/commit-edit.d.ts +7 -1
- package/dist/orders/commit-edit.js +5 -1
- package/dist/orders/deposit-policy.d.ts +103 -0
- package/dist/orders/deposit-policy.js +202 -0
- package/dist/orders/drafts.d.ts +800 -0
- package/dist/orders/drafts.js +771 -0
- package/dist/orders/fields.d.ts +245 -0
- package/dist/orders/fields.js +188 -0
- package/dist/orders/get.d.ts +29 -19
- package/dist/orders/get.js +17 -8
- package/dist/orders/index.d.ts +125 -5
- package/dist/orders/index.js +35 -2
- package/dist/orders/lifecycle.d.ts +128 -0
- package/dist/orders/lifecycle.js +158 -0
- package/dist/orders/line-price-preview.d.ts +7 -2
- package/dist/orders/list.d.ts +222 -0
- package/dist/orders/list.js +377 -0
- package/dist/orders/manual-discounts.d.ts +11 -2
- package/dist/orders/manual-discounts.js +2 -0
- package/dist/orders/manual-pricing.d.ts +5 -0
- package/dist/orders/order-entry.d.ts +20 -7
- package/dist/orders/plan-edit.d.ts +1 -1
- package/dist/orders/plan-edit.js +4 -1
- package/dist/orders/settings.d.ts +201 -0
- package/dist/orders/settings.js +325 -0
- package/dist/orders/status.d.ts +159 -13
- package/dist/orders/status.js +21 -0
- package/dist/orders/surcharges.d.ts +8 -1
- package/dist/orders/surcharges.js +3 -2
- package/dist/payments/account-credit.d.ts +30 -0
- package/dist/payments/account-credit.js +19 -0
- package/dist/payments/ar.d.ts +292 -0
- package/dist/payments/ar.js +223 -0
- package/dist/payments/connected-account.d.ts +47 -0
- package/dist/payments/connected-account.js +61 -0
- package/dist/payments/credit-noted-fields.d.ts +12 -0
- package/dist/payments/credit-noted-fields.js +37 -0
- package/dist/payments/index.d.ts +263 -10
- package/dist/payments/index.js +133 -1
- package/dist/payments/methods.d.ts +332 -0
- package/dist/payments/methods.js +363 -0
- package/dist/payments/mollie-methods.d.ts +159 -0
- package/dist/payments/mollie-methods.js +141 -0
- package/dist/payments/payment-links.d.ts +180 -0
- package/dist/payments/payment-links.js +97 -0
- package/dist/payments/provider-keys.d.ts +150 -0
- package/dist/payments/provider-keys.js +118 -0
- package/dist/payments/providers.d.ts +86 -11
- package/dist/payments/providers.js +29 -0
- package/dist/payments/requests.d.ts +144 -0
- package/dist/payments/requests.js +92 -0
- package/dist/payments/store-credit-fields.d.ts +15 -0
- package/dist/payments/store-credit-fields.js +51 -0
- package/dist/pickups/index.d.ts +104 -0
- package/dist/pickups/index.js +105 -0
- package/dist/platform/channels.d.ts +132 -0
- package/dist/platform/channels.js +128 -0
- package/dist/platform/geo.d.ts +123 -0
- package/dist/platform/geo.js +144 -0
- package/dist/platform/tax.d.ts +154 -0
- package/dist/platform/tax.js +139 -0
- package/dist/pos/anomalies.d.ts +158 -0
- package/dist/pos/anomalies.js +64 -0
- package/dist/pos/assisted-selling.d.ts +79 -0
- package/dist/pos/assisted-selling.js +38 -0
- package/dist/pos/exchanges.d.ts +103 -0
- package/dist/pos/exchanges.js +22 -0
- package/dist/pos/gift-cards.d.ts +135 -0
- package/dist/pos/gift-cards.js +47 -0
- package/dist/pos/index.d.ts +2125 -32
- package/dist/pos/index.js +742 -4
- package/dist/pos/no-receipt.d.ts +94 -0
- package/dist/pos/no-receipt.js +25 -0
- package/dist/pos/offline-sales.d.ts +187 -0
- package/dist/pos/offline-sales.js +116 -0
- package/dist/pos/pay-by-link.d.ts +143 -0
- package/dist/pos/pay-by-link.js +57 -0
- package/dist/pos/quick-keys.d.ts +103 -0
- package/dist/pos/quick-keys.js +45 -0
- package/dist/pos/reports.d.ts +360 -0
- package/dist/pos/reports.js +62 -0
- package/dist/pos/returns.d.ts +217 -0
- package/dist/pos/returns.js +53 -0
- package/dist/pricing/bulk-coupons.d.ts +139 -0
- package/dist/pricing/bulk-coupons.js +112 -0
- package/dist/pricing/index.d.ts +177 -4
- package/dist/pricing/index.js +176 -1
- package/dist/pricing/price-lists.d.ts +604 -0
- package/dist/pricing/price-lists.js +600 -0
- package/dist/pricing/vouchers.d.ts +103 -0
- package/dist/pricing/vouchers.js +69 -0
- package/dist/purchasing/alerts.d.ts +89 -0
- package/dist/purchasing/alerts.js +62 -0
- package/dist/purchasing/cost.d.ts +110 -0
- package/dist/purchasing/cost.js +86 -0
- package/dist/purchasing/index.d.ts +36 -0
- package/dist/purchasing/index.js +16 -0
- package/dist/purchasing/purchase-orders.d.ts +548 -0
- package/dist/purchasing/purchase-orders.js +491 -0
- package/dist/purchasing/replenishment.d.ts +281 -0
- package/dist/purchasing/replenishment.js +240 -0
- package/dist/purchasing/settings.d.ts +152 -0
- package/dist/purchasing/settings.js +181 -0
- package/dist/purchasing/suppliers.d.ts +476 -0
- package/dist/purchasing/suppliers.js +438 -0
- package/dist/quotes/index.d.ts +501 -0
- package/dist/quotes/index.js +439 -0
- package/dist/reports/index.d.ts +71 -8
- package/dist/reports/index.js +74 -11
- package/dist/returns/index.d.ts +818 -0
- package/dist/returns/index.js +844 -0
- package/dist/search/index.d.ts +174 -0
- package/dist/search/index.js +125 -0
- package/dist/shipping/definitions.d.ts +29 -0
- package/dist/shipping/definitions.js +47 -0
- package/dist/shipping/fulfillment-profiles.d.ts +174 -0
- package/dist/shipping/fulfillment-profiles.js +104 -0
- package/dist/shipping/hendriks.d.ts +97 -0
- package/dist/shipping/hendriks.js +50 -0
- package/dist/shipping/index.d.ts +48 -0
- package/dist/shipping/index.js +25 -0
- package/dist/shipping/methods.d.ts +198 -0
- package/dist/shipping/methods.js +240 -0
- package/dist/shipping/myparcel.d.ts +254 -0
- package/dist/shipping/myparcel.js +178 -0
- package/dist/shipping/parcel-count-suggestion.d.ts +2 -0
- package/dist/shipping/parcel-rule.d.ts +6 -5
- package/dist/shipping/parcel-rule.js +21 -23
- package/dist/shipping/rate-tables.d.ts +179 -0
- package/dist/shipping/rate-tables.js +184 -0
- package/dist/stock/index.d.ts +10 -3
- package/dist/stock/index.js +3 -0
- package/dist/stock/locations.d.ts +144 -0
- package/dist/stock/locations.js +168 -0
- package/dist/stock/transfers.d.ts +216 -43
- package/dist/stock/transfers.js +140 -13
- package/dist/stored-value/index.d.ts +214 -4
- package/dist/stored-value/index.js +152 -0
- package/dist/subscriptions/index.d.ts +682 -0
- package/dist/subscriptions/index.js +815 -0
- package/dist/tax/index.d.ts +76 -1
- package/dist/tax/index.js +50 -0
- package/dist/tenant/email-style.d.ts +102 -0
- package/dist/tenant/email-style.js +72 -0
- package/dist/tenant/email-templates.d.ts +66 -0
- package/dist/tenant/email-templates.js +54 -0
- package/dist/tenant/notifications.d.ts +23 -17
- package/dist/tenant/settings-sources.d.ts +1 -1
- package/dist/tenant/time-zone.d.ts +17 -0
- package/dist/tenant/time-zone.js +11 -0
- package/dist/types.d.ts +51 -4
- package/dist/users/index.d.ts +3 -0
- package/dist/users/index.js +2 -0
- package/dist/users/warehouses.d.ts +50 -0
- package/dist/users/warehouses.js +51 -0
- package/package.json +2 -2
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Product reviews (headless spec §9.2 `catalog`: "reviews moderation"; W10-117): the reviews the
|
|
3
|
+
* store's customers wrote, moderating them (what the storefront shows under the store's name),
|
|
4
|
+
* the store's reply to one, the review statistics, and the review attributes (the questions a
|
|
5
|
+
* review form asks: "Quality", "Fit", …).
|
|
6
|
+
*
|
|
7
|
+
* - **What the storefront shows**: only `APPROVED` reviews, with the reviewer's display name,
|
|
8
|
+
* rating, title, text, attribute answers and the store's reply. `PENDING` (waiting), `FLAGGED`
|
|
9
|
+
* (the automatic checks or a shopper's report asked for a look) and `REJECTED` reviews are never
|
|
10
|
+
* shown. Approving publishes the review; rejecting, flagging or re-opening it (`PENDING`) takes it
|
|
11
|
+
* off. Each moderation is recorded in the review's moderation log with the staff member and the
|
|
12
|
+
* reason given.
|
|
13
|
+
* - **The reply** is the store's own text, shown under an approved review on the storefront.
|
|
14
|
+
*
|
|
15
|
+
* Customer data (headless spec §6.3): an app installation below `customerData: full` reads every
|
|
16
|
+
* review with what the customer typed blanked: `title`, `body`, `attributeValues` (ratings and
|
|
17
|
+
* self-declared details such as an age band) and `ipAddressHash`, the edit history's previous
|
|
18
|
+
* texts and the moderation log's reasons and AI analysis answer `null`, and so does
|
|
19
|
+
* `customerDisplayName` below `identity`. Every record of such an answer carries `redacted: true`,
|
|
20
|
+
* so a blanked field never reads as "the customer wrote none". A `search` below `full` is refused
|
|
21
|
+
* `CUSTOMER_DATA_NOT_GRANTED` (`PermissionDeniedError`, 403). The answers of the writes are
|
|
22
|
+
* blanked the same way.
|
|
23
|
+
*
|
|
24
|
+
* Permissions: reads need `ReadCatalog`, writes `UpdateCatalog`. Every row is the store's own: a
|
|
25
|
+
* review or attribute of another store is never answered and never changed.
|
|
26
|
+
*
|
|
27
|
+
* Refusals, GraphQL errors mapped by `core`. An unknown id and another store's get the same
|
|
28
|
+
* answer, so none tells whether another store holds the row:
|
|
29
|
+
* - `ENTITY_NOT_FOUND` → `NotFoundError` (404): a review or attribute the store does not have. An
|
|
30
|
+
* id that is not a uuid gets it too, before anything is read.
|
|
31
|
+
* - `REVIEW_ATTRIBUTE_SLUG_TAKEN` → `ConflictError` (409): another of the store's attributes holds
|
|
32
|
+
* the slug, the one derived from the label included, also when two writes race for it.
|
|
33
|
+
* - `REVIEW_ATTRIBUTE_COLLECTION_NOT_FOUND` → `NotFoundError` (404): `attributes.setCollections`
|
|
34
|
+
* named a collection the store does not hold, each in `details` (`field` `/collectionIds/<i>`).
|
|
35
|
+
* - `BULK_ACTION_INPUT_INVALID` → `ValidationError` (400): a `bulkModerate` selection of no
|
|
36
|
+
* review, more than {@link MAX_BULK_REVIEWS} or one twice, refused before anything is sent.
|
|
37
|
+
* - `USER_INPUT_ERROR` → `ValidationError` (400), in the engine's words: a reply over 2,000
|
|
38
|
+
* characters, an attribute type other than `rating` / `selection`, a selection with fewer than
|
|
39
|
+
* two options.
|
|
40
|
+
* - `FORBIDDEN` → `PermissionDeniedError` (403) without the permission.
|
|
41
|
+
*
|
|
42
|
+
* Release: `bulkModerate` needs a platform release with `moderateReviews` (deploy vendure-server
|
|
43
|
+
* first: an older one refuses the document). Every other method is served by every release this
|
|
44
|
+
* SDK supports; an older release answers each not-found and a taken slug as `USER_INPUT_ERROR`
|
|
45
|
+
* (400), a taken slug as an uncoded database error, and takes another store's collection in
|
|
46
|
+
* `setCollections`. No method deduplicates on `idempotencyKey`.
|
|
47
|
+
*/
|
|
48
|
+
import { pageFromVendureList, readOffsetCursor, UpstreamError, ValidationError, } from '@sixtyninecommerce/core';
|
|
49
|
+
import { redactionWatcher } from '../customer-data-redaction.js';
|
|
50
|
+
import { assertDeleted, DELETION_FIELDS, fieldOf } from './types.js';
|
|
51
|
+
export const REVIEW_STATUSES = [
|
|
52
|
+
'PENDING',
|
|
53
|
+
'APPROVED',
|
|
54
|
+
'REJECTED',
|
|
55
|
+
'FLAGGED',
|
|
56
|
+
];
|
|
57
|
+
/** How many reviews one list page holds at most: the engine answers no more. */
|
|
58
|
+
export const MAX_REVIEW_PAGE_SIZE = 50;
|
|
59
|
+
/** How many reviews one `bulkModerate` call may name. */
|
|
60
|
+
export const MAX_BULK_REVIEWS = 100;
|
|
61
|
+
const REVIEW_FIELDS = /* GraphQL */ `
|
|
62
|
+
id
|
|
63
|
+
channelId
|
|
64
|
+
customerId
|
|
65
|
+
productId
|
|
66
|
+
productName
|
|
67
|
+
variantId
|
|
68
|
+
rating
|
|
69
|
+
title
|
|
70
|
+
body
|
|
71
|
+
status
|
|
72
|
+
isVerifiedPurchase
|
|
73
|
+
purchaseDate
|
|
74
|
+
deliveryDate
|
|
75
|
+
isEdited
|
|
76
|
+
editCount
|
|
77
|
+
customerDisplayName
|
|
78
|
+
imageAssetIds
|
|
79
|
+
videoAssetIds
|
|
80
|
+
attributeValues
|
|
81
|
+
helpfulCount
|
|
82
|
+
notHelpfulCount
|
|
83
|
+
merchantReply
|
|
84
|
+
merchantReplyAt
|
|
85
|
+
ipAddressHash
|
|
86
|
+
createdAt
|
|
87
|
+
updatedAt
|
|
88
|
+
`;
|
|
89
|
+
const REVIEW_DETAIL_FIELDS = /* GraphQL */ `
|
|
90
|
+
${REVIEW_FIELDS}
|
|
91
|
+
editHistory {
|
|
92
|
+
id
|
|
93
|
+
previousRating
|
|
94
|
+
previousTitle
|
|
95
|
+
previousBody
|
|
96
|
+
previousAttributeValues
|
|
97
|
+
editedAt
|
|
98
|
+
reEnteredModeration
|
|
99
|
+
createdAt
|
|
100
|
+
}
|
|
101
|
+
moderationLogs {
|
|
102
|
+
id
|
|
103
|
+
step
|
|
104
|
+
action
|
|
105
|
+
reason
|
|
106
|
+
confidence
|
|
107
|
+
aiAnalysis
|
|
108
|
+
performedBy
|
|
109
|
+
tokenUsage
|
|
110
|
+
createdAt
|
|
111
|
+
}
|
|
112
|
+
`;
|
|
113
|
+
const ATTRIBUTE_FIELDS = /* GraphQL */ `
|
|
114
|
+
id
|
|
115
|
+
slug
|
|
116
|
+
label
|
|
117
|
+
inputType
|
|
118
|
+
options
|
|
119
|
+
isRequired
|
|
120
|
+
enabled
|
|
121
|
+
section
|
|
122
|
+
position
|
|
123
|
+
collectionIds
|
|
124
|
+
createdAt
|
|
125
|
+
updatedAt
|
|
126
|
+
`;
|
|
127
|
+
export const PRODUCT_REVIEWS_QUERY = /* GraphQL */ `
|
|
128
|
+
query CatalogProductReviews($options: AdminReviewListOptions) {
|
|
129
|
+
adminProductReviews(options: $options) {
|
|
130
|
+
items { ${REVIEW_FIELDS} }
|
|
131
|
+
totalItems
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
`;
|
|
135
|
+
export const PRODUCT_REVIEW_QUERY = /* GraphQL */ `
|
|
136
|
+
query CatalogProductReview($id: ID!) {
|
|
137
|
+
adminProductReview(id: $id) { ${REVIEW_DETAIL_FIELDS} }
|
|
138
|
+
}
|
|
139
|
+
`;
|
|
140
|
+
export const REVIEW_STATS_QUERY = /* GraphQL */ `
|
|
141
|
+
query CatalogReviewStats {
|
|
142
|
+
reviewStats {
|
|
143
|
+
totalReviews
|
|
144
|
+
pendingCount
|
|
145
|
+
approvedCount
|
|
146
|
+
rejectedCount
|
|
147
|
+
flaggedCount
|
|
148
|
+
averageRating
|
|
149
|
+
ratingsThisWeek
|
|
150
|
+
ratingsThisMonth
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
`;
|
|
154
|
+
export const MODERATE_REVIEW_MUTATION = /* GraphQL */ `
|
|
155
|
+
mutation CatalogModerateReview($id: ID!, $action: ReviewStatus!, $reason: String) {
|
|
156
|
+
moderateReview(id: $id, action: $action, reason: $reason) { ${REVIEW_FIELDS} }
|
|
157
|
+
}
|
|
158
|
+
`;
|
|
159
|
+
export const MODERATE_REVIEWS_MUTATION = /* GraphQL */ `
|
|
160
|
+
mutation CatalogModerateReviews($input: ModerateReviewsInput!) {
|
|
161
|
+
moderateReviews(input: $input) {
|
|
162
|
+
items { id status code message }
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
`;
|
|
166
|
+
export const SET_MERCHANT_REPLY_MUTATION = /* GraphQL */ `
|
|
167
|
+
mutation CatalogSetMerchantReply($reviewId: ID!, $reply: String!) {
|
|
168
|
+
setMerchantReply(reviewId: $reviewId, reply: $reply) { ${REVIEW_FIELDS} }
|
|
169
|
+
}
|
|
170
|
+
`;
|
|
171
|
+
export const REMOVE_MERCHANT_REPLY_MUTATION = /* GraphQL */ `
|
|
172
|
+
mutation CatalogRemoveMerchantReply($reviewId: ID!) {
|
|
173
|
+
removeMerchantReply(reviewId: $reviewId) { ${REVIEW_FIELDS} }
|
|
174
|
+
}
|
|
175
|
+
`;
|
|
176
|
+
export const REVIEW_ATTRIBUTES_QUERY = /* GraphQL */ `
|
|
177
|
+
query CatalogReviewAttributes {
|
|
178
|
+
adminReviewAttributes { ${ATTRIBUTE_FIELDS} }
|
|
179
|
+
}
|
|
180
|
+
`;
|
|
181
|
+
export const CREATE_REVIEW_ATTRIBUTE_MUTATION = /* GraphQL */ `
|
|
182
|
+
mutation CatalogCreateReviewAttribute($input: AdminReviewAttributeInput!) {
|
|
183
|
+
createReviewAttribute(input: $input) { ${ATTRIBUTE_FIELDS} }
|
|
184
|
+
}
|
|
185
|
+
`;
|
|
186
|
+
export const UPDATE_REVIEW_ATTRIBUTE_MUTATION = /* GraphQL */ `
|
|
187
|
+
mutation CatalogUpdateReviewAttribute($id: ID!, $input: UpdateReviewAttributeInput!) {
|
|
188
|
+
updateReviewAttribute(id: $id, input: $input) { ${ATTRIBUTE_FIELDS} }
|
|
189
|
+
}
|
|
190
|
+
`;
|
|
191
|
+
export const DELETE_REVIEW_ATTRIBUTE_MUTATION = /* GraphQL */ `
|
|
192
|
+
mutation CatalogDeleteReviewAttribute($id: ID!) {
|
|
193
|
+
deleteReviewAttribute(id: $id) { ${DELETION_FIELDS} }
|
|
194
|
+
}
|
|
195
|
+
`;
|
|
196
|
+
export const REORDER_REVIEW_ATTRIBUTES_MUTATION = /* GraphQL */ `
|
|
197
|
+
mutation CatalogReorderReviewAttributes($ids: [ID!]!) {
|
|
198
|
+
reorderReviewAttributes(ids: $ids) { ${ATTRIBUTE_FIELDS} }
|
|
199
|
+
}
|
|
200
|
+
`;
|
|
201
|
+
export const SET_REVIEW_ATTRIBUTE_COLLECTIONS_MUTATION = /* GraphQL */ `
|
|
202
|
+
mutation CatalogSetReviewAttributeCollections($attributeId: ID!, $collectionIds: [ID!]!) {
|
|
203
|
+
setReviewAttributeCollections(attributeId: $attributeId, collectionIds: $collectionIds) {
|
|
204
|
+
${ATTRIBUTE_FIELDS}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
`;
|
|
208
|
+
function unexpected(field) {
|
|
209
|
+
return new UpstreamError({
|
|
210
|
+
code: 'UNEXPECTED_RESPONSE',
|
|
211
|
+
message: `The engine answered without "${field}".`,
|
|
212
|
+
status: 502,
|
|
213
|
+
service: 'vendure',
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
function invalid(code, message) {
|
|
217
|
+
return new ValidationError({ code, message, status: 400, service: 'vendure' });
|
|
218
|
+
}
|
|
219
|
+
/** Attributes in `position` order, the older first on a tie (the engine orders by position only). */
|
|
220
|
+
function byPosition(attributes) {
|
|
221
|
+
return [...attributes].sort((a, b) => a.position - b.position || a.createdAt.localeCompare(b.createdAt));
|
|
222
|
+
}
|
|
223
|
+
/** The selection of a bulk call, checked as the engine checks it, before anything is sent. */
|
|
224
|
+
function bulkVariables(input) {
|
|
225
|
+
const refused = (message) => invalid('BULK_ACTION_INPUT_INVALID', message);
|
|
226
|
+
if (typeof input !== 'object' || input === null) {
|
|
227
|
+
throw refused('A bulk moderation needs ids and an action.');
|
|
228
|
+
}
|
|
229
|
+
if (!REVIEW_STATUSES.includes(input.action)) {
|
|
230
|
+
throw refused(`action must be one of ${REVIEW_STATUSES.join(', ')}.`);
|
|
231
|
+
}
|
|
232
|
+
const ids = input.ids;
|
|
233
|
+
if (!Array.isArray(ids) || !ids.every((id) => typeof id === 'string')) {
|
|
234
|
+
throw refused('ids must be a list of strings.');
|
|
235
|
+
}
|
|
236
|
+
if (ids.length === 0)
|
|
237
|
+
throw refused('Name at least one review.');
|
|
238
|
+
if (ids.length > MAX_BULK_REVIEWS) {
|
|
239
|
+
throw refused(`A bulk moderation takes at most ${String(MAX_BULK_REVIEWS)} reviews.`);
|
|
240
|
+
}
|
|
241
|
+
if (new Set(ids.map((id) => id.toLowerCase())).size !== ids.length) {
|
|
242
|
+
throw refused('Name each review once.');
|
|
243
|
+
}
|
|
244
|
+
return {
|
|
245
|
+
input: {
|
|
246
|
+
ids: [...ids],
|
|
247
|
+
action: input.action,
|
|
248
|
+
...(input.reason === undefined ? {} : { reason: input.reason }),
|
|
249
|
+
},
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
export function createReviewsModule(transport) {
|
|
253
|
+
async function reviewCall(document, field, variables, options, nullable = false) {
|
|
254
|
+
const redaction = redactionWatcher();
|
|
255
|
+
const data = await transport.graphql({ api: 'admin', query: document, variables, onExtensions: redaction.onExtensions }, options);
|
|
256
|
+
if (data === null || typeof data !== 'object' || !Object.hasOwn(data, field)) {
|
|
257
|
+
throw unexpected(field);
|
|
258
|
+
}
|
|
259
|
+
const review = data[field] ?? null;
|
|
260
|
+
if (review === null && !nullable)
|
|
261
|
+
throw unexpected(field);
|
|
262
|
+
return review === null ? null : redaction.mark(review);
|
|
263
|
+
}
|
|
264
|
+
async function reviewsPage(filters, skip, take, call) {
|
|
265
|
+
const redaction = redactionWatcher();
|
|
266
|
+
const data = await transport.graphql({
|
|
267
|
+
api: 'admin',
|
|
268
|
+
query: PRODUCT_REVIEWS_QUERY,
|
|
269
|
+
variables: { options: { ...filters, skip, take } },
|
|
270
|
+
onExtensions: redaction.onExtensions,
|
|
271
|
+
}, call);
|
|
272
|
+
const list = data?.adminProductReviews;
|
|
273
|
+
if (!list)
|
|
274
|
+
throw unexpected('adminProductReviews');
|
|
275
|
+
return pageFromVendureList({ items: list.items.map((review) => redaction.mark(review)), totalItems: list.totalItems }, skip, take, (cursor) => reviewsPage(filters, readOffsetCursor(cursor), take, call));
|
|
276
|
+
}
|
|
277
|
+
return {
|
|
278
|
+
// `async`: a refused option rejects the promise, as an engine refusal does.
|
|
279
|
+
async list(options) {
|
|
280
|
+
const { status, productId, customerId, search, rating, sort, limit, after, ...call } = options ?? {};
|
|
281
|
+
const take = limit ?? 25;
|
|
282
|
+
if (!Number.isInteger(take) || take < 1 || take > MAX_REVIEW_PAGE_SIZE) {
|
|
283
|
+
throw invalid('INVALID_PAGE_SIZE', `limit must be a whole number from 1 to ${String(MAX_REVIEW_PAGE_SIZE)}; got ${String(take)}.`);
|
|
284
|
+
}
|
|
285
|
+
const filters = Object.fromEntries(Object.entries({ status, productId, customerId, search, rating, sort }).filter(([, value]) => value !== undefined && value !== ''));
|
|
286
|
+
return reviewsPage(filters, after === undefined ? 0 : readOffsetCursor(after), take, call);
|
|
287
|
+
},
|
|
288
|
+
get: (id, options) => reviewCall(PRODUCT_REVIEW_QUERY, 'adminProductReview', { id }, options, true),
|
|
289
|
+
async stats(options) {
|
|
290
|
+
const stats = await fieldOf(transport, REVIEW_STATS_QUERY, 'reviewStats', {}, options);
|
|
291
|
+
if (stats === null)
|
|
292
|
+
throw unexpected('reviewStats');
|
|
293
|
+
return stats;
|
|
294
|
+
},
|
|
295
|
+
moderate: (id, input, options) => reviewCall(MODERATE_REVIEW_MUTATION, 'moderateReview', { id, action: input.action, reason: input.reason ?? null }, options),
|
|
296
|
+
async bulkModerate(input, options) {
|
|
297
|
+
const data = await fieldOf(transport, MODERATE_REVIEWS_MUTATION, 'moderateReviews', bulkVariables(input), options);
|
|
298
|
+
if (data === null)
|
|
299
|
+
throw unexpected('moderateReviews');
|
|
300
|
+
return data.items;
|
|
301
|
+
},
|
|
302
|
+
reply: {
|
|
303
|
+
set: (reviewId, reply, options) => reviewCall(SET_MERCHANT_REPLY_MUTATION, 'setMerchantReply', { reviewId, reply }, options),
|
|
304
|
+
remove: (reviewId, options) => reviewCall(REMOVE_MERCHANT_REPLY_MUTATION, 'removeMerchantReply', { reviewId }, options),
|
|
305
|
+
},
|
|
306
|
+
attributes: {
|
|
307
|
+
list: async (options) => byPosition(await fieldOf(transport, REVIEW_ATTRIBUTES_QUERY, 'adminReviewAttributes', {}, options)),
|
|
308
|
+
create: (input, options) => fieldOf(transport, CREATE_REVIEW_ATTRIBUTE_MUTATION, 'createReviewAttribute', { input }, options),
|
|
309
|
+
update: (id, input, options) => fieldOf(transport, UPDATE_REVIEW_ATTRIBUTE_MUTATION, 'updateReviewAttribute', { id, input }, options),
|
|
310
|
+
async delete(id, options) {
|
|
311
|
+
const response = await fieldOf(transport, DELETE_REVIEW_ATTRIBUTE_MUTATION, 'deleteReviewAttribute', { id }, options);
|
|
312
|
+
assertDeleted('REVIEW_ATTRIBUTE_NOT_DELETED', [response]);
|
|
313
|
+
},
|
|
314
|
+
reorder: async (ids, options) => byPosition(await fieldOf(transport, REORDER_REVIEW_ATTRIBUTES_MUTATION, 'reorderReviewAttributes', { ids }, options)),
|
|
315
|
+
setCollections: (attributeId, collectionIds, options) => fieldOf(transport, SET_REVIEW_ATTRIBUTE_COLLECTIONS_MUTATION, 'setReviewAttributeCollections', { attributeId, collectionIds }, options),
|
|
316
|
+
},
|
|
317
|
+
};
|
|
318
|
+
}
|
package/dist/catalog/types.d.ts
CHANGED
|
@@ -9,7 +9,23 @@ export interface AssetRef {
|
|
|
9
9
|
/** A preview URL the asset server scales; absolute or relative to the engine. */
|
|
10
10
|
preview: string;
|
|
11
11
|
source: string;
|
|
12
|
+
/** A string on the wire: a newer engine may add a type. */
|
|
13
|
+
type: 'IMAGE' | 'VIDEO' | 'BINARY' | (string & {});
|
|
14
|
+
mimeType: string;
|
|
15
|
+
/**
|
|
16
|
+
* The image's alt text: the store's default-language description
|
|
17
|
+
* (`catalog.assets.descriptions`); null when it has none.
|
|
18
|
+
*/
|
|
19
|
+
altText: string | null;
|
|
20
|
+
}
|
|
21
|
+
/** An {@link AssetRef} as the Admin API answers it, before {@link toAssetRef}. */
|
|
22
|
+
export interface WireAssetRef extends Omit<AssetRef, 'altText'> {
|
|
23
|
+
customFields: {
|
|
24
|
+
altText?: string | null;
|
|
25
|
+
} | null;
|
|
12
26
|
}
|
|
27
|
+
/** Lift the alt text out of the asset's custom fields. */
|
|
28
|
+
export declare function toAssetRef(wire: WireAssetRef): AssetRef;
|
|
13
29
|
export interface FacetValueRef {
|
|
14
30
|
id: string;
|
|
15
31
|
code: string;
|
|
@@ -121,7 +137,7 @@ export interface CatalogueOwnership {
|
|
|
121
137
|
}
|
|
122
138
|
/** `catalogueOwnership` on the top-level row of each catalogue read (never on nested refs). */
|
|
123
139
|
export declare const CATALOGUE_OWNERSHIP_FIELDS = "\n catalogueOwnership {\n state\n organizationName\n editableHere\n }\n";
|
|
124
|
-
export declare const ASSET_REF_FIELDS = "\n id\n name\n preview\n source\n";
|
|
140
|
+
export declare const ASSET_REF_FIELDS = "\n id\n name\n preview\n source\n type\n mimeType\n customFields {\n altText\n }\n";
|
|
125
141
|
export declare const FACET_VALUE_REF_FIELDS = "\n id\n code\n name\n facet {\n id\n code\n name\n }\n";
|
|
126
142
|
/**
|
|
127
143
|
* Refuse a datetime with no offset, or one that is not a date at all (spec
|
package/dist/catalog/types.js
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
import { ConflictError, errorFromErrorResult, isErrorResult, pageFromVendureList, readOffsetCursor, UpstreamError, ValidationError, } from '@sixtyninecommerce/core';
|
|
2
|
+
/** Lift the alt text out of the asset's custom fields. */
|
|
3
|
+
export function toAssetRef(wire) {
|
|
4
|
+
const { customFields, ...rest } = wire;
|
|
5
|
+
return { ...rest, altText: customFields?.altText ?? null };
|
|
6
|
+
}
|
|
2
7
|
/** `catalogueOwnership` on the top-level row of each catalogue read (never on nested refs). */
|
|
3
8
|
export const CATALOGUE_OWNERSHIP_FIELDS = /* GraphQL */ `
|
|
4
9
|
catalogueOwnership {
|
|
@@ -12,6 +17,11 @@ export const ASSET_REF_FIELDS = /* GraphQL */ `
|
|
|
12
17
|
name
|
|
13
18
|
preview
|
|
14
19
|
source
|
|
20
|
+
type
|
|
21
|
+
mimeType
|
|
22
|
+
customFields {
|
|
23
|
+
altText
|
|
24
|
+
}
|
|
15
25
|
`;
|
|
16
26
|
export const FACET_VALUE_REF_FIELDS = /* GraphQL */ `
|
|
17
27
|
id
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { type Transport } from '@sixtyninecommerce/core';
|
|
2
|
+
import type { Call } from '../types.js';
|
|
3
|
+
import type { InventoryFlag, VariantStorePrice } from './variants.js';
|
|
4
|
+
/** The most variants one `variants.find` answers. */
|
|
5
|
+
export declare const MAX_VARIANT_FIND_TAKE = 500;
|
|
6
|
+
/** The most products one `variants.find` search reads (`take / perProduct`, rounded up). */
|
|
7
|
+
export declare const MAX_VARIANT_FIND_PRODUCTS = 100;
|
|
8
|
+
/** Which variants `variants.find` answers: exactly one of `ids`, `term` and `skuPrefix`. */
|
|
9
|
+
export interface FindVariantsInput {
|
|
10
|
+
/**
|
|
11
|
+
* Exactly these variants, in the order asked. An id the store does not hold (another store's,
|
|
12
|
+
* a deleted variant, a hidden carrier variant) is left out, never an error; a repeated id is
|
|
13
|
+
* answered once. More than `take` distinct ids is refused.
|
|
14
|
+
*/
|
|
15
|
+
ids?: readonly string[];
|
|
16
|
+
/**
|
|
17
|
+
* Full text over the store's product index (a product's name, SKUs, slug, variant names and
|
|
18
|
+
* description): the variants of the best-matching products, best product first.
|
|
19
|
+
*/
|
|
20
|
+
term?: string;
|
|
21
|
+
/**
|
|
22
|
+
* The products that have a SKU starting with this text, by name, and of each the variants
|
|
23
|
+
* whose own SKU starts with it. Capitals do not matter.
|
|
24
|
+
*/
|
|
25
|
+
skuPrefix?: string;
|
|
26
|
+
/** The most variants the answer holds, 1 to {@link MAX_VARIANT_FIND_TAKE}. */
|
|
27
|
+
take: number;
|
|
28
|
+
/**
|
|
29
|
+
* A search only: the most variants of ONE product, so a product line of thousands cannot fill
|
|
30
|
+
* the answer. From 1 to `take`; a tenth of `take`, rounded up, when omitted. The search reads
|
|
31
|
+
* the best `take / perProduct` products (rounded up, at most
|
|
32
|
+
* {@link MAX_VARIANT_FIND_PRODUCTS}).
|
|
33
|
+
*/
|
|
34
|
+
perProduct?: number;
|
|
35
|
+
/**
|
|
36
|
+
* Price the variants in this currency. The store's default when omitted. The engine refuses a
|
|
37
|
+
* currency the store does not sell.
|
|
38
|
+
*/
|
|
39
|
+
currencyCode?: string;
|
|
40
|
+
}
|
|
41
|
+
/** A found variant's stock at one of the store's locations. */
|
|
42
|
+
export interface FoundVariantStockLevel {
|
|
43
|
+
stockLocationId: string;
|
|
44
|
+
stockLocationName: string;
|
|
45
|
+
stockOnHand: number;
|
|
46
|
+
/** Units open orders have reserved there. */
|
|
47
|
+
stockAllocated: number;
|
|
48
|
+
}
|
|
49
|
+
/** The product a found variant belongs to. */
|
|
50
|
+
export interface FoundVariantProduct {
|
|
51
|
+
id: string;
|
|
52
|
+
name: string;
|
|
53
|
+
slug: string;
|
|
54
|
+
featuredAsset: {
|
|
55
|
+
id: string;
|
|
56
|
+
preview: string;
|
|
57
|
+
} | null;
|
|
58
|
+
}
|
|
59
|
+
/** A variant as a picker shows it: what it is, what it costs here and what is in stock. */
|
|
60
|
+
export interface FoundVariant {
|
|
61
|
+
id: string;
|
|
62
|
+
name: string;
|
|
63
|
+
sku: string;
|
|
64
|
+
/** A disabled variant is listed too; the engine refuses to add it to an order. */
|
|
65
|
+
enabled: boolean;
|
|
66
|
+
/**
|
|
67
|
+
* The variant's price in the store, in the currency asked for, or null when the store has no
|
|
68
|
+
* price for it there. Never 0 for a missing price.
|
|
69
|
+
*/
|
|
70
|
+
storePrice: VariantStorePrice | null;
|
|
71
|
+
trackInventory: InventoryFlag;
|
|
72
|
+
/** The variant's own threshold; the platform's applies instead when the flag below is true. */
|
|
73
|
+
outOfStockThreshold: number;
|
|
74
|
+
useGlobalOutOfStockThreshold: boolean;
|
|
75
|
+
/**
|
|
76
|
+
* Units on hand and reserved across the store's locations, as the engine's stock rule reads
|
|
77
|
+
* them. What an order can still take is `stockOnHand − stockAllocated − threshold`.
|
|
78
|
+
*/
|
|
79
|
+
stockOnHand: number;
|
|
80
|
+
stockAllocated: number;
|
|
81
|
+
/** The same per location; the store's own locations only. */
|
|
82
|
+
stockLevels: FoundVariantStockLevel[];
|
|
83
|
+
product: FoundVariantProduct;
|
|
84
|
+
}
|
|
85
|
+
/** One product of a search's answer. */
|
|
86
|
+
export interface FoundVariantsProduct {
|
|
87
|
+
id: string;
|
|
88
|
+
/** How many of the product's variants `items` holds. */
|
|
89
|
+
shown: number;
|
|
90
|
+
/** How many of its variants match: all of them for `term`, the prefix matches for `skuPrefix`. */
|
|
91
|
+
total: number;
|
|
92
|
+
}
|
|
93
|
+
export interface FoundVariants {
|
|
94
|
+
/**
|
|
95
|
+
* `ids`: in the order asked. A search: best product first, each product's variants together,
|
|
96
|
+
* in a stable order (by id), which is not the order a merchant arranged them in.
|
|
97
|
+
*/
|
|
98
|
+
items: FoundVariant[];
|
|
99
|
+
/**
|
|
100
|
+
* A search: one entry per product in `items`, in their order. `total` above `shown` means the
|
|
101
|
+
* product has more matching variants than the answer holds. Empty for `ids`.
|
|
102
|
+
*/
|
|
103
|
+
products: FoundVariantsProduct[];
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* What a picker reads of a variant. `storePrice`, never core's `price` / `priceWithTax`: those
|
|
107
|
+
* throw for a variant the store has no price for, and one such variant would empty the answer.
|
|
108
|
+
*/
|
|
109
|
+
export declare const FOUND_VARIANT_FIELDS = "\n id\n name\n sku\n enabled\n storePrice {\n price\n priceWithTax\n currencyCode\n }\n trackInventory\n outOfStockThreshold\n useGlobalOutOfStockThreshold\n stockOnHand\n stockAllocated\n stockLevels {\n stockLocationId\n stockOnHand\n stockAllocated\n stockLocation {\n id\n name\n }\n }\n";
|
|
110
|
+
/** A found variant with its product: the selection of every list that answers variants flat. */
|
|
111
|
+
export declare const FOUND_VARIANT_WITH_PRODUCT_FIELDS = "\n \n id\n name\n sku\n enabled\n storePrice {\n price\n priceWithTax\n currencyCode\n }\n trackInventory\n outOfStockThreshold\n useGlobalOutOfStockThreshold\n stockOnHand\n stockAllocated\n stockLevels {\n stockLocationId\n stockOnHand\n stockAllocated\n stockLocation {\n id\n name\n }\n }\n\n product {\n \n id\n name\n slug\n featuredAsset {\n id\n preview\n }\n\n }\n";
|
|
112
|
+
export declare const FIND_VARIANTS_BY_IDS_QUERY = "\n query CatalogFindVariantsByIds($options: ProductVariantListOptions) {\n productVariants(options: $options) {\n items {\n \n \n id\n name\n sku\n enabled\n storePrice {\n price\n priceWithTax\n currencyCode\n }\n trackInventory\n outOfStockThreshold\n useGlobalOutOfStockThreshold\n stockOnHand\n stockAllocated\n stockLevels {\n stockLocationId\n stockOnHand\n stockAllocated\n stockLocation {\n id\n name\n }\n }\n\n product {\n \n id\n name\n slug\n featuredAsset {\n id\n preview\n }\n\n }\n\n }\n totalItems\n }\n }\n";
|
|
113
|
+
/**
|
|
114
|
+
* The variants of some products, bounded PER PRODUCT (`variantList`, never `variants`, which the
|
|
115
|
+
* engine answers whole and then cuts at 1,000 without saying so).
|
|
116
|
+
*/
|
|
117
|
+
export declare const FIND_VARIANTS_OF_PRODUCTS_QUERY = "\n query CatalogFindVariantsOfProducts(\n $options: ProductListOptions\n $variantOptions: ProductVariantListOptions\n ) {\n products(options: $options) {\n items {\n \n id\n name\n slug\n featuredAsset {\n id\n preview\n }\n\n variantList(options: $variantOptions) {\n items {\n \n id\n name\n sku\n enabled\n storePrice {\n price\n priceWithTax\n currencyCode\n }\n trackInventory\n outOfStockThreshold\n useGlobalOutOfStockThreshold\n stockOnHand\n stockAllocated\n stockLevels {\n stockLocationId\n stockOnHand\n stockAllocated\n stockLocation {\n id\n name\n }\n }\n\n }\n totalItems\n }\n }\n totalItems\n }\n }\n";
|
|
118
|
+
interface WireFoundVariant extends Omit<FoundVariant, 'stockLevels' | 'product'> {
|
|
119
|
+
stockLevels: Array<{
|
|
120
|
+
stockLocationId: string;
|
|
121
|
+
stockOnHand: number;
|
|
122
|
+
stockAllocated: number;
|
|
123
|
+
stockLocation: {
|
|
124
|
+
id: string;
|
|
125
|
+
name: string;
|
|
126
|
+
} | null;
|
|
127
|
+
}>;
|
|
128
|
+
}
|
|
129
|
+
/** A found variant as a flat list answers it, its product on the row. */
|
|
130
|
+
export type WireFoundVariantWithProduct = WireFoundVariant & {
|
|
131
|
+
product: FoundVariantProduct;
|
|
132
|
+
};
|
|
133
|
+
export declare function toFoundVariant(wire: WireFoundVariantWithProduct): FoundVariant;
|
|
134
|
+
/** `catalog.variants.find`; its contract is on `VariantsModule.find`. */
|
|
135
|
+
export declare function findVariants(transport: Transport, input: FindVariantsInput, options?: Call): Promise<FoundVariants>;
|
|
136
|
+
export {};
|