react-shopwave-connect 0.4.3 → 0.4.5

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/dist/index.d.cts CHANGED
@@ -676,6 +676,9 @@ declare function fetchEntityById<K extends EntityKind>(kind: K, id: EntityId, pa
676
676
  * read) so an edited price is sent as a new instance. As a safeguard, a new
677
677
  * product (no `id`, e.g. a duplicate) never sends instance ids: an id copied from
678
678
  * another product would otherwise point the save at that product's instances.
679
+ *
680
+ * `images` is sent as file names ({@link toStoredProductImages}), so the URLs a
681
+ * read returns can be passed straight back; leaving `images` out keeps them.
679
682
  */
680
683
  declare const saveProduct: (product: ProductSaveInput, options?: RequestOptions) => Promise<Saved<Product>>;
681
684
  declare const deleteProduct: (id: EntityId, options?: RequestOptions) => Promise<void>;
@@ -844,20 +847,40 @@ declare const SHOPWAVE_RESOURCES: Readonly<Record<ResourceKind, ResourceDefiniti
844
847
  type UploadKind = "merchant" | "merchantFeature" | "product" | "user" | "applicationLogo" | "applicationImages";
845
848
  /** `UploadKind` → the API's `contentType` value. */
846
849
  declare const UPLOAD_KINDS: Readonly<Record<UploadKind, string>>;
850
+ /**
851
+ * The role an image plays, sent to Shopwave as the uploader's `prefix` header.
852
+ * The uploader names the file `<prefix>_<hash>.<ext>`, so the role travels with
853
+ * the file wherever a record stores it (a product's `images`). Live, merchant
854
+ * 5644, 4 Nov 2026: all four values answered 201.
855
+ * - `product`: a product gallery image;
856
+ * - `product_thumbnail`: the product's lead image, shown in lists;
857
+ * - `variant` / `variant_thumbnail`: a variant's own image and its lead image
858
+ * (used by the Connect integrations).
859
+ *
860
+ * Values are the ones Connect's `uploadFileToShopwave` sends.
861
+ */
862
+ type UploadPrefix = "product" | "product_thumbnail" | "variant" | "variant_thumbnail";
863
+ /** Every `UploadPrefix`, longest first so a name check never takes `product` for `product_thumbnail`. */
864
+ declare const UPLOAD_PREFIXES: ReadonlyArray<UploadPrefix>;
847
865
  /** How uploads are addressed (app route and Shopwave uploader). */
848
866
  declare const SHOPWAVE_UPLOAD: Readonly<{
849
- /** App route: `POST /api/<route>` (multipart `file` + `kind`). */
867
+ /** App route: `POST /api/<route>` (multipart `file` + `kind`, optional `prefix`). */
850
868
  route: "upload";
851
869
  /** Shopwave path: `PUT {apiUrl}/uploader` (multipart `file`). */
852
870
  upstream: "uploader";
853
871
  /** Header that carries the upload kind upstream. */
854
872
  kindHeader: "contentType";
873
+ /** Header that carries the image role upstream (see `UploadPrefix`). */
874
+ prefixHeader: "prefix";
855
875
  /** Multipart field name for the file, on both sides. */
856
876
  fileField: "file";
857
877
  /** Multipart field name for the kind in the app request. */
858
878
  kindField: "kind";
879
+ /** Multipart field name for the image role in the app request. */
880
+ prefixField: "prefix";
859
881
  }>;
860
882
  declare function isUploadKind(value: unknown): value is UploadKind;
883
+ declare function isUploadPrefix(value: unknown): value is UploadPrefix;
861
884
  /**
862
885
  * Read-only lists that aren't records: the merchant's distinct tags.
863
886
  *
@@ -890,11 +913,12 @@ declare const SHOPWAVE_LOOKUPS: Readonly<Record<LookupKind, LookupDefinition>>;
890
913
  //#endregion
891
914
  //#region src/core/imageFit.d.ts
892
915
  /**
893
- * Browser helpers for getting an image to an exact pixel size before upload.
916
+ * Browser helpers for getting an image to the right size before upload.
894
917
  *
895
- * Shopwave doesn't resize uploads, and the merchant image slots have fixed
896
- * sizes (adminV1 refuses any other size), so the SDK scales and centre-crops
897
- * the image in the browser instead.
918
+ * Shopwave doesn't resize uploads, so images are scaled and centre-cropped in
919
+ * the browser: to an exact size for the merchant image slots
920
+ * ({@link fitImageToSize}), or to a rule (ratio, minimum, maximum) with
921
+ * {@link prepareImage}, which apps use to give each kind of image its own rule.
898
922
  */
899
923
  /** Source rectangle to draw so the image covers `dstWidth × dstHeight` (centre crop, no stretching). */
900
924
  interface CropRect {
@@ -928,11 +952,113 @@ interface FitImageOptions {
928
952
  * input, with the output type's extension.
929
953
  */
930
954
  declare function fitImageToSize(file: Blob, width: number, height: number, options?: FitImageOptions): Promise<File>;
955
+ /**
956
+ * How an image must end up before upload. Every field is optional; give an
957
+ * exact size (`width` + `height`) or an `aspectRatio`, not both.
958
+ *
959
+ * Images are always scaled down first and then centre-cropped (never
960
+ * stretched): the kept area is the largest one with the right shape.
961
+ *
962
+ * @example
963
+ * // "Cropped to 100:77, at least 600 × 462", at most 2000 px on the long side
964
+ * { aspectRatio: "100:77", minWidth: 600, minHeight: 462, maxLongSide: 2000 }
965
+ * // Exactly 600 × 462
966
+ * { width: 600, height: 462 }
967
+ * // Any shape, scaled down to fit 2000 px
968
+ * { maxLongSide: 2000 }
969
+ */
970
+ interface ImageRule {
971
+ /** Exact output width (needs `height`). */
972
+ width?: number;
973
+ /** Exact output height (needs `width`). */
974
+ height?: number;
975
+ /** Shape to crop to: `1.3`, `"100:77"`, `"100/77"` or `[100, 77]`. */
976
+ aspectRatio?: number | string | readonly [number, number];
977
+ /** Smallest acceptable output; a smaller image is refused (see `allowUpscale`). */
978
+ minWidth?: number;
979
+ minHeight?: number;
980
+ /** Largest output; a bigger image is scaled down to fit. */
981
+ maxWidth?: number;
982
+ maxHeight?: number;
983
+ /** Largest output on the longer side, whichever it is. */
984
+ maxLongSide?: number;
985
+ /** Enlarge a too-small image instead of refusing it. Default false. */
986
+ allowUpscale?: boolean;
987
+ }
988
+ /** What {@link prepareImage} will do to an image, worked out without touching it. */
989
+ interface ImageFitPlan {
990
+ /** The area of the source that's kept. */
991
+ crop: CropRect;
992
+ /** Output size. */
993
+ width: number;
994
+ height: number;
995
+ /** True when the source already fits the rule, so it can be uploaded as it is. */
996
+ unchanged: boolean;
997
+ }
998
+ /** A rule that can't be met by any image (a mistake in the rule, not the image). */
999
+ declare class ImageRuleError extends Error {
1000
+ constructor(message: string);
1001
+ }
1002
+ /** The image is smaller than the rule's minimum (and `allowUpscale` is off). */
1003
+ declare class ImageTooSmallError extends Error {
1004
+ /** The smallest size the rule accepts. */
1005
+ readonly required: ImageSize;
1006
+ /** The largest area of the right shape the image could give. */
1007
+ readonly actual: ImageSize;
1008
+ constructor(required: ImageSize, actual: ImageSize);
1009
+ }
1010
+ /**
1011
+ * An aspect ratio as width ÷ height: `"100:77"`, `"100/77"`, `[100, 77]` →
1012
+ * 1.2987…; `1.3` → 1.3. Throws {@link ImageRuleError} for anything else.
1013
+ */
1014
+ declare function parseAspectRatio(value: number | string | readonly [number, number]): number;
1015
+ /** Throws {@link ImageRuleError} when the rule contradicts itself. */
1016
+ declare function checkImageRule(rule: ImageRule): void;
1017
+ /**
1018
+ * Works out what to do to a `source`-sized image to meet `rule`: the centred
1019
+ * crop of the right shape (the largest one), then the output size (scaled
1020
+ * down to the maximum, never below the minimum). Pure: no browser needed.
1021
+ *
1022
+ * Throws {@link ImageTooSmallError} when the crop is below the minimum (or
1023
+ * the exact size) and `allowUpscale` is off, and {@link ImageRuleError} for a
1024
+ * contradictory rule.
1025
+ *
1026
+ * @example
1027
+ * planImageFit({ width: 6000, height: 4000 }, { aspectRatio: "100:77", minWidth: 600, minHeight: 462, maxLongSide: 2000 })
1028
+ * // → crop { sx: 403, sy: 0, sw: 5194, sh: 4000 }, 2000 × 1540
1029
+ */
1030
+ declare function planImageFit(source: ImageSize, rule: ImageRule): ImageFitPlan;
1031
+ interface PrepareImageOptions {
1032
+ /** Output type. Default: the source's type when it's PNG, JPEG or WebP, else PNG. */
1033
+ type?: "image/png" | "image/jpeg" | "image/webp";
1034
+ /** 0–1, for JPEG and WebP. Default 0.9. */
1035
+ quality?: number;
1036
+ }
1037
+ /**
1038
+ * Returns the image meeting `rule` ({@link planImageFit}): centre-cropped to
1039
+ * the right shape and scaled in a single draw. An image that already fits is
1040
+ * returned untouched (same bytes). Browser only.
1041
+ *
1042
+ * Throws {@link ImageTooSmallError} (show `required` and `actual` to the
1043
+ * person) or {@link ImageRuleError}; an unreadable file rejects with the
1044
+ * browser's decoding error.
1045
+ *
1046
+ * @example
1047
+ * const ready = await prepareImage(file, { aspectRatio: "100:77", minWidth: 600, minHeight: 462, maxLongSide: 2000 });
1048
+ * await uploadImage(ready, { kind: "product", prefix: "product" });
1049
+ */
1050
+ declare function prepareImage(file: Blob, rule: ImageRule, options?: PrepareImageOptions): Promise<File>;
931
1051
  //#endregion
932
1052
  //#region src/core/upload.d.ts
933
1053
  interface UploadImageParams {
934
1054
  /** What the image is for (sent to Shopwave as the uploader's `contentType`). */
935
1055
  kind: UploadKind;
1056
+ /**
1057
+ * The image's role (sent to Shopwave as the uploader's `prefix` header), e.g.
1058
+ * `product_thumbnail` for a product's lead image. Leave it out for images
1059
+ * without a role (merchant logos).
1060
+ */
1061
+ prefix?: UploadPrefix;
936
1062
  /** File name to send when `file` is a Blob without one. Defaults to `File.name`, else `"upload"`. */
937
1063
  fileName?: string;
938
1064
  }
@@ -1224,6 +1350,31 @@ interface UserResponse {
1224
1350
  /** Reads the logged-in user, or `null` when the API returns none. */
1225
1351
  declare function fetchUser(options?: RequestOptions): Promise<User | null>;
1226
1352
  //#endregion
1353
+ //#region src/core/productImages.d.ts
1354
+ /**
1355
+ * Product images, as the live API stores them (merchant 5644, 4 Nov 2026):
1356
+ * - A product's `images` is a list. **Reads return full URLs**
1357
+ * (`http://static.merchantstack.com/images/merchant/<id>/product/<file>`);
1358
+ * **writes take the uploader's file names** ({@link toStoredProductImages};
1359
+ * `saveProduct` does this for you).
1360
+ * - The uploader takes one file per request. With a `prefix` (see
1361
+ * `UploadPrefix`) it names the file `<prefix>_<hash>.<ext>`
1362
+ * (`product_thumbnail_1bdd…41090b.png`); without one, `<hash>.<ext>`. So an
1363
+ * image's role can be read back from its name ({@link productImageRole}).
1364
+ */
1365
+ /**
1366
+ * The role in an image's file name or URL, from the uploader's `prefix`:
1367
+ * `"…/product_thumbnail_1bdd.png"` → `"product_thumbnail"`. `null` for a name
1368
+ * without one (images uploaded without a prefix, e.g. before prefixes existed).
1369
+ */
1370
+ declare function productImageRole(value: unknown): UploadPrefix | null;
1371
+ /**
1372
+ * A product's `images` as the API takes them on a save: file names, not the
1373
+ * URLs a read returns. Non-strings, blanks and duplicates are dropped; the
1374
+ * order is kept.
1375
+ */
1376
+ declare function toStoredProductImages(images: unknown): string[];
1377
+ //#endregion
1227
1378
  //#region src/hooks/shared.d.ts
1228
1379
  /**
1229
1380
  * Consistent return shape for every auto-fetching ("query") hook.
@@ -1457,5 +1608,5 @@ interface UseBasketReportReturn {
1457
1608
  */
1458
1609
  declare function useBasketReport(options?: RequestOptions): UseBasketReportReturn;
1459
1610
  //#endregion
1460
- export { Basket, BasketFilters, BasketItem, BasketPagination, BasketReportRow, BasketSummary, Category, CategoryResponse, CombineBasketRowsParams, Consumer, ConsumerResponse, CropRect, DEFAULT_BASKET_FILTERS, DEFAULT_PAGINATION, DeletableEntityKind, Employee, EmployeeResponse, EntityDefinition, EntityId, EntityInput, EntityKind, EntityTypes, ErrorMessage, Extras, FetchByIdParams, FetchCategoriesParams, FetchEmployeesParams, FetchProductsParams, FetchPromotionsParams, FetchStoresParams, FitImageOptions, ImageSize, LookupDefinition, LookupKind, MERCHANT_COLOUR_KEYS, MERCHANT_FIELD_MAX_LENGTH, MERCHANT_IMAGE_SLOTS, MERCHANT_IMAGE_SLOT_KEYS, MERCHANT_LINK_KEYS, MERCHANT_LINK_PATHS, MERCHANT_OBJECT_FIELDS, MERCHANT_READ_ONLY_FIELDS, Merchant, MerchantColourKey, MerchantColourSet, MerchantColours, MerchantImageIds, MerchantImageSlot, MerchantImageSlotDefinition, MerchantLinkKey, MerchantLinks, MerchantPatch, MerchantResponse, MessageInfo, MutationState, ParsedBasket, Product, ProductInstance, ProductInstanceInput, ProductInstances, ProductResponse, ProductSaveInput, Promotion, PromotionResponse, PromotionRuleSet, QueryState, ReportData, ReportKey, ReportQuery, ReportQueryMap, ReportResponse, RepricedInstance, RequestOptions, ResourceDefinition, ResourceKind, SHOPWAVE_ENTITIES, SHOPWAVE_IMAGE_BASE_URL, SHOPWAVE_LOOKUPS, SHOPWAVE_RESOURCES, SHOPWAVE_UPLOAD, Saved, SessionStatus, ShopwaveApiError, ShopwaveErrorMap, Store, StoreResponse, SubmitEntityArgs, SubmitEntityResponse, SuccessMessage, UPLOAD_KINDS, UpdateMerchantOptions, UploadImageParams, UploadKind, UploadMerchantImageOptions, UploadedImage, UseBasketReportReturn, User, UserEmployee, UserResponse, WritableEntityKind, apiDelete, apiGet, apiRequest, apiResponse, apiSend, assertNoApiErrors, authorizationFor, buildBasketReportQuery, buildConsumerMap, collectConsumerIds, collectProductIds, combineBasketRows, computeBasketSummary, coverCrop, deleteCategory, deleteEmployee, deleteEntity, deleteEntityById, deleteProduct, deletePromotion, deleteStore, fetchCategories, fetchCategory, fetchConsumer, fetchConsumers, fetchEmployee, fetchEmployees, fetchEntityById, fetchMerchant, fetchProduct, fetchProductInstanceTags, fetchProductTags, fetchProducts, fetchProductsMap, fetchPromotion, fetchPromotions, fetchReport, fetchSession, fetchStore, fetchStores, fetchUser, findRepricedInstances, fitImageToSize, formatDateForQuery, getApiErrorMap, getApiErrors, getImageUrl, getMerchantImage, getMerchantImageUrl, imageFileName, isSameInstancePrice, isUploadKind, loginPath, logout, logoutPath, merchantLinksFromForm, merchantLinksToForm, normalizeMerchantLinks, normalizeTagList, parseBasketReportData, readImageSize, rolesById, saveCategory, saveEmployee, saveEntities, saveEntity, saveProduct, savePromotion, saveStore, setMerchantImage, submitEntity, toProductInstancesForSave, toShopwaveApiError, toStoredImageIds, updateMerchant, uploadImage, uploadMerchantImage, useBasketReport, useCategory, useConsumer, useDelete, useEmployee, useLogout, useMerchant, useProduct, useProductInstanceTags, useProductTags, usePromotion, useReport, useSession, useStore, useSubmit, useUpdateMerchant, useUploadImage, useUploadMerchantImage, useUser, validateMerchant, withToken };
1611
+ export { Basket, BasketFilters, BasketItem, BasketPagination, BasketReportRow, BasketSummary, Category, CategoryResponse, CombineBasketRowsParams, Consumer, ConsumerResponse, CropRect, DEFAULT_BASKET_FILTERS, DEFAULT_PAGINATION, DeletableEntityKind, Employee, EmployeeResponse, EntityDefinition, EntityId, EntityInput, EntityKind, EntityTypes, ErrorMessage, Extras, FetchByIdParams, FetchCategoriesParams, FetchEmployeesParams, FetchProductsParams, FetchPromotionsParams, FetchStoresParams, FitImageOptions, ImageFitPlan, ImageRule, ImageRuleError, ImageSize, ImageTooSmallError, LookupDefinition, LookupKind, MERCHANT_COLOUR_KEYS, MERCHANT_FIELD_MAX_LENGTH, MERCHANT_IMAGE_SLOTS, MERCHANT_IMAGE_SLOT_KEYS, MERCHANT_LINK_KEYS, MERCHANT_LINK_PATHS, MERCHANT_OBJECT_FIELDS, MERCHANT_READ_ONLY_FIELDS, Merchant, MerchantColourKey, MerchantColourSet, MerchantColours, MerchantImageIds, MerchantImageSlot, MerchantImageSlotDefinition, MerchantLinkKey, MerchantLinks, MerchantPatch, MerchantResponse, MessageInfo, MutationState, ParsedBasket, PrepareImageOptions, Product, ProductInstance, ProductInstanceInput, ProductInstances, ProductResponse, ProductSaveInput, Promotion, PromotionResponse, PromotionRuleSet, QueryState, ReportData, ReportKey, ReportQuery, ReportQueryMap, ReportResponse, RepricedInstance, RequestOptions, ResourceDefinition, ResourceKind, SHOPWAVE_ENTITIES, SHOPWAVE_IMAGE_BASE_URL, SHOPWAVE_LOOKUPS, SHOPWAVE_RESOURCES, SHOPWAVE_UPLOAD, Saved, SessionStatus, ShopwaveApiError, ShopwaveErrorMap, Store, StoreResponse, SubmitEntityArgs, SubmitEntityResponse, SuccessMessage, UPLOAD_KINDS, UPLOAD_PREFIXES, UpdateMerchantOptions, UploadImageParams, UploadKind, UploadMerchantImageOptions, UploadPrefix, UploadedImage, UseBasketReportReturn, User, UserEmployee, UserResponse, WritableEntityKind, apiDelete, apiGet, apiRequest, apiResponse, apiSend, assertNoApiErrors, authorizationFor, buildBasketReportQuery, buildConsumerMap, checkImageRule, collectConsumerIds, collectProductIds, combineBasketRows, computeBasketSummary, coverCrop, deleteCategory, deleteEmployee, deleteEntity, deleteEntityById, deleteProduct, deletePromotion, deleteStore, fetchCategories, fetchCategory, fetchConsumer, fetchConsumers, fetchEmployee, fetchEmployees, fetchEntityById, fetchMerchant, fetchProduct, fetchProductInstanceTags, fetchProductTags, fetchProducts, fetchProductsMap, fetchPromotion, fetchPromotions, fetchReport, fetchSession, fetchStore, fetchStores, fetchUser, findRepricedInstances, fitImageToSize, formatDateForQuery, getApiErrorMap, getApiErrors, getImageUrl, getMerchantImage, getMerchantImageUrl, imageFileName, isSameInstancePrice, isUploadKind, isUploadPrefix, loginPath, logout, logoutPath, merchantLinksFromForm, merchantLinksToForm, normalizeMerchantLinks, normalizeTagList, parseAspectRatio, parseBasketReportData, planImageFit, prepareImage, productImageRole, readImageSize, rolesById, saveCategory, saveEmployee, saveEntities, saveEntity, saveProduct, savePromotion, saveStore, setMerchantImage, submitEntity, toProductInstancesForSave, toShopwaveApiError, toStoredImageIds, toStoredProductImages, updateMerchant, uploadImage, uploadMerchantImage, useBasketReport, useCategory, useConsumer, useDelete, useEmployee, useLogout, useMerchant, useProduct, useProductInstanceTags, useProductTags, usePromotion, useReport, useSession, useStore, useSubmit, useUpdateMerchant, useUploadImage, useUploadMerchantImage, useUser, validateMerchant, withToken };
1461
1612
  //# sourceMappingURL=index.d.cts.map
package/dist/index.d.ts CHANGED
@@ -676,6 +676,9 @@ declare function fetchEntityById<K extends EntityKind>(kind: K, id: EntityId, pa
676
676
  * read) so an edited price is sent as a new instance. As a safeguard, a new
677
677
  * product (no `id`, e.g. a duplicate) never sends instance ids: an id copied from
678
678
  * another product would otherwise point the save at that product's instances.
679
+ *
680
+ * `images` is sent as file names ({@link toStoredProductImages}), so the URLs a
681
+ * read returns can be passed straight back; leaving `images` out keeps them.
679
682
  */
680
683
  declare const saveProduct: (product: ProductSaveInput, options?: RequestOptions) => Promise<Saved<Product>>;
681
684
  declare const deleteProduct: (id: EntityId, options?: RequestOptions) => Promise<void>;
@@ -844,20 +847,40 @@ declare const SHOPWAVE_RESOURCES: Readonly<Record<ResourceKind, ResourceDefiniti
844
847
  type UploadKind = "merchant" | "merchantFeature" | "product" | "user" | "applicationLogo" | "applicationImages";
845
848
  /** `UploadKind` → the API's `contentType` value. */
846
849
  declare const UPLOAD_KINDS: Readonly<Record<UploadKind, string>>;
850
+ /**
851
+ * The role an image plays, sent to Shopwave as the uploader's `prefix` header.
852
+ * The uploader names the file `<prefix>_<hash>.<ext>`, so the role travels with
853
+ * the file wherever a record stores it (a product's `images`). Live, merchant
854
+ * 5644, 4 Nov 2026: all four values answered 201.
855
+ * - `product`: a product gallery image;
856
+ * - `product_thumbnail`: the product's lead image, shown in lists;
857
+ * - `variant` / `variant_thumbnail`: a variant's own image and its lead image
858
+ * (used by the Connect integrations).
859
+ *
860
+ * Values are the ones Connect's `uploadFileToShopwave` sends.
861
+ */
862
+ type UploadPrefix = "product" | "product_thumbnail" | "variant" | "variant_thumbnail";
863
+ /** Every `UploadPrefix`, longest first so a name check never takes `product` for `product_thumbnail`. */
864
+ declare const UPLOAD_PREFIXES: ReadonlyArray<UploadPrefix>;
847
865
  /** How uploads are addressed (app route and Shopwave uploader). */
848
866
  declare const SHOPWAVE_UPLOAD: Readonly<{
849
- /** App route: `POST /api/<route>` (multipart `file` + `kind`). */
867
+ /** App route: `POST /api/<route>` (multipart `file` + `kind`, optional `prefix`). */
850
868
  route: "upload";
851
869
  /** Shopwave path: `PUT {apiUrl}/uploader` (multipart `file`). */
852
870
  upstream: "uploader";
853
871
  /** Header that carries the upload kind upstream. */
854
872
  kindHeader: "contentType";
873
+ /** Header that carries the image role upstream (see `UploadPrefix`). */
874
+ prefixHeader: "prefix";
855
875
  /** Multipart field name for the file, on both sides. */
856
876
  fileField: "file";
857
877
  /** Multipart field name for the kind in the app request. */
858
878
  kindField: "kind";
879
+ /** Multipart field name for the image role in the app request. */
880
+ prefixField: "prefix";
859
881
  }>;
860
882
  declare function isUploadKind(value: unknown): value is UploadKind;
883
+ declare function isUploadPrefix(value: unknown): value is UploadPrefix;
861
884
  /**
862
885
  * Read-only lists that aren't records: the merchant's distinct tags.
863
886
  *
@@ -890,11 +913,12 @@ declare const SHOPWAVE_LOOKUPS: Readonly<Record<LookupKind, LookupDefinition>>;
890
913
  //#endregion
891
914
  //#region src/core/imageFit.d.ts
892
915
  /**
893
- * Browser helpers for getting an image to an exact pixel size before upload.
916
+ * Browser helpers for getting an image to the right size before upload.
894
917
  *
895
- * Shopwave doesn't resize uploads, and the merchant image slots have fixed
896
- * sizes (adminV1 refuses any other size), so the SDK scales and centre-crops
897
- * the image in the browser instead.
918
+ * Shopwave doesn't resize uploads, so images are scaled and centre-cropped in
919
+ * the browser: to an exact size for the merchant image slots
920
+ * ({@link fitImageToSize}), or to a rule (ratio, minimum, maximum) with
921
+ * {@link prepareImage}, which apps use to give each kind of image its own rule.
898
922
  */
899
923
  /** Source rectangle to draw so the image covers `dstWidth × dstHeight` (centre crop, no stretching). */
900
924
  interface CropRect {
@@ -928,11 +952,113 @@ interface FitImageOptions {
928
952
  * input, with the output type's extension.
929
953
  */
930
954
  declare function fitImageToSize(file: Blob, width: number, height: number, options?: FitImageOptions): Promise<File>;
955
+ /**
956
+ * How an image must end up before upload. Every field is optional; give an
957
+ * exact size (`width` + `height`) or an `aspectRatio`, not both.
958
+ *
959
+ * Images are always scaled down first and then centre-cropped (never
960
+ * stretched): the kept area is the largest one with the right shape.
961
+ *
962
+ * @example
963
+ * // "Cropped to 100:77, at least 600 × 462", at most 2000 px on the long side
964
+ * { aspectRatio: "100:77", minWidth: 600, minHeight: 462, maxLongSide: 2000 }
965
+ * // Exactly 600 × 462
966
+ * { width: 600, height: 462 }
967
+ * // Any shape, scaled down to fit 2000 px
968
+ * { maxLongSide: 2000 }
969
+ */
970
+ interface ImageRule {
971
+ /** Exact output width (needs `height`). */
972
+ width?: number;
973
+ /** Exact output height (needs `width`). */
974
+ height?: number;
975
+ /** Shape to crop to: `1.3`, `"100:77"`, `"100/77"` or `[100, 77]`. */
976
+ aspectRatio?: number | string | readonly [number, number];
977
+ /** Smallest acceptable output; a smaller image is refused (see `allowUpscale`). */
978
+ minWidth?: number;
979
+ minHeight?: number;
980
+ /** Largest output; a bigger image is scaled down to fit. */
981
+ maxWidth?: number;
982
+ maxHeight?: number;
983
+ /** Largest output on the longer side, whichever it is. */
984
+ maxLongSide?: number;
985
+ /** Enlarge a too-small image instead of refusing it. Default false. */
986
+ allowUpscale?: boolean;
987
+ }
988
+ /** What {@link prepareImage} will do to an image, worked out without touching it. */
989
+ interface ImageFitPlan {
990
+ /** The area of the source that's kept. */
991
+ crop: CropRect;
992
+ /** Output size. */
993
+ width: number;
994
+ height: number;
995
+ /** True when the source already fits the rule, so it can be uploaded as it is. */
996
+ unchanged: boolean;
997
+ }
998
+ /** A rule that can't be met by any image (a mistake in the rule, not the image). */
999
+ declare class ImageRuleError extends Error {
1000
+ constructor(message: string);
1001
+ }
1002
+ /** The image is smaller than the rule's minimum (and `allowUpscale` is off). */
1003
+ declare class ImageTooSmallError extends Error {
1004
+ /** The smallest size the rule accepts. */
1005
+ readonly required: ImageSize;
1006
+ /** The largest area of the right shape the image could give. */
1007
+ readonly actual: ImageSize;
1008
+ constructor(required: ImageSize, actual: ImageSize);
1009
+ }
1010
+ /**
1011
+ * An aspect ratio as width ÷ height: `"100:77"`, `"100/77"`, `[100, 77]` →
1012
+ * 1.2987…; `1.3` → 1.3. Throws {@link ImageRuleError} for anything else.
1013
+ */
1014
+ declare function parseAspectRatio(value: number | string | readonly [number, number]): number;
1015
+ /** Throws {@link ImageRuleError} when the rule contradicts itself. */
1016
+ declare function checkImageRule(rule: ImageRule): void;
1017
+ /**
1018
+ * Works out what to do to a `source`-sized image to meet `rule`: the centred
1019
+ * crop of the right shape (the largest one), then the output size (scaled
1020
+ * down to the maximum, never below the minimum). Pure: no browser needed.
1021
+ *
1022
+ * Throws {@link ImageTooSmallError} when the crop is below the minimum (or
1023
+ * the exact size) and `allowUpscale` is off, and {@link ImageRuleError} for a
1024
+ * contradictory rule.
1025
+ *
1026
+ * @example
1027
+ * planImageFit({ width: 6000, height: 4000 }, { aspectRatio: "100:77", minWidth: 600, minHeight: 462, maxLongSide: 2000 })
1028
+ * // → crop { sx: 403, sy: 0, sw: 5194, sh: 4000 }, 2000 × 1540
1029
+ */
1030
+ declare function planImageFit(source: ImageSize, rule: ImageRule): ImageFitPlan;
1031
+ interface PrepareImageOptions {
1032
+ /** Output type. Default: the source's type when it's PNG, JPEG or WebP, else PNG. */
1033
+ type?: "image/png" | "image/jpeg" | "image/webp";
1034
+ /** 0–1, for JPEG and WebP. Default 0.9. */
1035
+ quality?: number;
1036
+ }
1037
+ /**
1038
+ * Returns the image meeting `rule` ({@link planImageFit}): centre-cropped to
1039
+ * the right shape and scaled in a single draw. An image that already fits is
1040
+ * returned untouched (same bytes). Browser only.
1041
+ *
1042
+ * Throws {@link ImageTooSmallError} (show `required` and `actual` to the
1043
+ * person) or {@link ImageRuleError}; an unreadable file rejects with the
1044
+ * browser's decoding error.
1045
+ *
1046
+ * @example
1047
+ * const ready = await prepareImage(file, { aspectRatio: "100:77", minWidth: 600, minHeight: 462, maxLongSide: 2000 });
1048
+ * await uploadImage(ready, { kind: "product", prefix: "product" });
1049
+ */
1050
+ declare function prepareImage(file: Blob, rule: ImageRule, options?: PrepareImageOptions): Promise<File>;
931
1051
  //#endregion
932
1052
  //#region src/core/upload.d.ts
933
1053
  interface UploadImageParams {
934
1054
  /** What the image is for (sent to Shopwave as the uploader's `contentType`). */
935
1055
  kind: UploadKind;
1056
+ /**
1057
+ * The image's role (sent to Shopwave as the uploader's `prefix` header), e.g.
1058
+ * `product_thumbnail` for a product's lead image. Leave it out for images
1059
+ * without a role (merchant logos).
1060
+ */
1061
+ prefix?: UploadPrefix;
936
1062
  /** File name to send when `file` is a Blob without one. Defaults to `File.name`, else `"upload"`. */
937
1063
  fileName?: string;
938
1064
  }
@@ -1224,6 +1350,31 @@ interface UserResponse {
1224
1350
  /** Reads the logged-in user, or `null` when the API returns none. */
1225
1351
  declare function fetchUser(options?: RequestOptions): Promise<User | null>;
1226
1352
  //#endregion
1353
+ //#region src/core/productImages.d.ts
1354
+ /**
1355
+ * Product images, as the live API stores them (merchant 5644, 4 Nov 2026):
1356
+ * - A product's `images` is a list. **Reads return full URLs**
1357
+ * (`http://static.merchantstack.com/images/merchant/<id>/product/<file>`);
1358
+ * **writes take the uploader's file names** ({@link toStoredProductImages};
1359
+ * `saveProduct` does this for you).
1360
+ * - The uploader takes one file per request. With a `prefix` (see
1361
+ * `UploadPrefix`) it names the file `<prefix>_<hash>.<ext>`
1362
+ * (`product_thumbnail_1bdd…41090b.png`); without one, `<hash>.<ext>`. So an
1363
+ * image's role can be read back from its name ({@link productImageRole}).
1364
+ */
1365
+ /**
1366
+ * The role in an image's file name or URL, from the uploader's `prefix`:
1367
+ * `"…/product_thumbnail_1bdd.png"` → `"product_thumbnail"`. `null` for a name
1368
+ * without one (images uploaded without a prefix, e.g. before prefixes existed).
1369
+ */
1370
+ declare function productImageRole(value: unknown): UploadPrefix | null;
1371
+ /**
1372
+ * A product's `images` as the API takes them on a save: file names, not the
1373
+ * URLs a read returns. Non-strings, blanks and duplicates are dropped; the
1374
+ * order is kept.
1375
+ */
1376
+ declare function toStoredProductImages(images: unknown): string[];
1377
+ //#endregion
1227
1378
  //#region src/hooks/shared.d.ts
1228
1379
  /**
1229
1380
  * Consistent return shape for every auto-fetching ("query") hook.
@@ -1457,5 +1608,5 @@ interface UseBasketReportReturn {
1457
1608
  */
1458
1609
  declare function useBasketReport(options?: RequestOptions): UseBasketReportReturn;
1459
1610
  //#endregion
1460
- export { Basket, BasketFilters, BasketItem, BasketPagination, BasketReportRow, BasketSummary, Category, CategoryResponse, CombineBasketRowsParams, Consumer, ConsumerResponse, CropRect, DEFAULT_BASKET_FILTERS, DEFAULT_PAGINATION, DeletableEntityKind, Employee, EmployeeResponse, EntityDefinition, EntityId, EntityInput, EntityKind, EntityTypes, ErrorMessage, Extras, FetchByIdParams, FetchCategoriesParams, FetchEmployeesParams, FetchProductsParams, FetchPromotionsParams, FetchStoresParams, FitImageOptions, ImageSize, LookupDefinition, LookupKind, MERCHANT_COLOUR_KEYS, MERCHANT_FIELD_MAX_LENGTH, MERCHANT_IMAGE_SLOTS, MERCHANT_IMAGE_SLOT_KEYS, MERCHANT_LINK_KEYS, MERCHANT_LINK_PATHS, MERCHANT_OBJECT_FIELDS, MERCHANT_READ_ONLY_FIELDS, Merchant, MerchantColourKey, MerchantColourSet, MerchantColours, MerchantImageIds, MerchantImageSlot, MerchantImageSlotDefinition, MerchantLinkKey, MerchantLinks, MerchantPatch, MerchantResponse, MessageInfo, MutationState, ParsedBasket, Product, ProductInstance, ProductInstanceInput, ProductInstances, ProductResponse, ProductSaveInput, Promotion, PromotionResponse, PromotionRuleSet, QueryState, ReportData, ReportKey, ReportQuery, ReportQueryMap, ReportResponse, RepricedInstance, RequestOptions, ResourceDefinition, ResourceKind, SHOPWAVE_ENTITIES, SHOPWAVE_IMAGE_BASE_URL, SHOPWAVE_LOOKUPS, SHOPWAVE_RESOURCES, SHOPWAVE_UPLOAD, Saved, SessionStatus, ShopwaveApiError, ShopwaveErrorMap, Store, StoreResponse, SubmitEntityArgs, SubmitEntityResponse, SuccessMessage, UPLOAD_KINDS, UpdateMerchantOptions, UploadImageParams, UploadKind, UploadMerchantImageOptions, UploadedImage, UseBasketReportReturn, User, UserEmployee, UserResponse, WritableEntityKind, apiDelete, apiGet, apiRequest, apiResponse, apiSend, assertNoApiErrors, authorizationFor, buildBasketReportQuery, buildConsumerMap, collectConsumerIds, collectProductIds, combineBasketRows, computeBasketSummary, coverCrop, deleteCategory, deleteEmployee, deleteEntity, deleteEntityById, deleteProduct, deletePromotion, deleteStore, fetchCategories, fetchCategory, fetchConsumer, fetchConsumers, fetchEmployee, fetchEmployees, fetchEntityById, fetchMerchant, fetchProduct, fetchProductInstanceTags, fetchProductTags, fetchProducts, fetchProductsMap, fetchPromotion, fetchPromotions, fetchReport, fetchSession, fetchStore, fetchStores, fetchUser, findRepricedInstances, fitImageToSize, formatDateForQuery, getApiErrorMap, getApiErrors, getImageUrl, getMerchantImage, getMerchantImageUrl, imageFileName, isSameInstancePrice, isUploadKind, loginPath, logout, logoutPath, merchantLinksFromForm, merchantLinksToForm, normalizeMerchantLinks, normalizeTagList, parseBasketReportData, readImageSize, rolesById, saveCategory, saveEmployee, saveEntities, saveEntity, saveProduct, savePromotion, saveStore, setMerchantImage, submitEntity, toProductInstancesForSave, toShopwaveApiError, toStoredImageIds, updateMerchant, uploadImage, uploadMerchantImage, useBasketReport, useCategory, useConsumer, useDelete, useEmployee, useLogout, useMerchant, useProduct, useProductInstanceTags, useProductTags, usePromotion, useReport, useSession, useStore, useSubmit, useUpdateMerchant, useUploadImage, useUploadMerchantImage, useUser, validateMerchant, withToken };
1611
+ export { Basket, BasketFilters, BasketItem, BasketPagination, BasketReportRow, BasketSummary, Category, CategoryResponse, CombineBasketRowsParams, Consumer, ConsumerResponse, CropRect, DEFAULT_BASKET_FILTERS, DEFAULT_PAGINATION, DeletableEntityKind, Employee, EmployeeResponse, EntityDefinition, EntityId, EntityInput, EntityKind, EntityTypes, ErrorMessage, Extras, FetchByIdParams, FetchCategoriesParams, FetchEmployeesParams, FetchProductsParams, FetchPromotionsParams, FetchStoresParams, FitImageOptions, ImageFitPlan, ImageRule, ImageRuleError, ImageSize, ImageTooSmallError, LookupDefinition, LookupKind, MERCHANT_COLOUR_KEYS, MERCHANT_FIELD_MAX_LENGTH, MERCHANT_IMAGE_SLOTS, MERCHANT_IMAGE_SLOT_KEYS, MERCHANT_LINK_KEYS, MERCHANT_LINK_PATHS, MERCHANT_OBJECT_FIELDS, MERCHANT_READ_ONLY_FIELDS, Merchant, MerchantColourKey, MerchantColourSet, MerchantColours, MerchantImageIds, MerchantImageSlot, MerchantImageSlotDefinition, MerchantLinkKey, MerchantLinks, MerchantPatch, MerchantResponse, MessageInfo, MutationState, ParsedBasket, PrepareImageOptions, Product, ProductInstance, ProductInstanceInput, ProductInstances, ProductResponse, ProductSaveInput, Promotion, PromotionResponse, PromotionRuleSet, QueryState, ReportData, ReportKey, ReportQuery, ReportQueryMap, ReportResponse, RepricedInstance, RequestOptions, ResourceDefinition, ResourceKind, SHOPWAVE_ENTITIES, SHOPWAVE_IMAGE_BASE_URL, SHOPWAVE_LOOKUPS, SHOPWAVE_RESOURCES, SHOPWAVE_UPLOAD, Saved, SessionStatus, ShopwaveApiError, ShopwaveErrorMap, Store, StoreResponse, SubmitEntityArgs, SubmitEntityResponse, SuccessMessage, UPLOAD_KINDS, UPLOAD_PREFIXES, UpdateMerchantOptions, UploadImageParams, UploadKind, UploadMerchantImageOptions, UploadPrefix, UploadedImage, UseBasketReportReturn, User, UserEmployee, UserResponse, WritableEntityKind, apiDelete, apiGet, apiRequest, apiResponse, apiSend, assertNoApiErrors, authorizationFor, buildBasketReportQuery, buildConsumerMap, checkImageRule, collectConsumerIds, collectProductIds, combineBasketRows, computeBasketSummary, coverCrop, deleteCategory, deleteEmployee, deleteEntity, deleteEntityById, deleteProduct, deletePromotion, deleteStore, fetchCategories, fetchCategory, fetchConsumer, fetchConsumers, fetchEmployee, fetchEmployees, fetchEntityById, fetchMerchant, fetchProduct, fetchProductInstanceTags, fetchProductTags, fetchProducts, fetchProductsMap, fetchPromotion, fetchPromotions, fetchReport, fetchSession, fetchStore, fetchStores, fetchUser, findRepricedInstances, fitImageToSize, formatDateForQuery, getApiErrorMap, getApiErrors, getImageUrl, getMerchantImage, getMerchantImageUrl, imageFileName, isSameInstancePrice, isUploadKind, isUploadPrefix, loginPath, logout, logoutPath, merchantLinksFromForm, merchantLinksToForm, normalizeMerchantLinks, normalizeTagList, parseAspectRatio, parseBasketReportData, planImageFit, prepareImage, productImageRole, readImageSize, rolesById, saveCategory, saveEmployee, saveEntities, saveEntity, saveProduct, savePromotion, saveStore, setMerchantImage, submitEntity, toProductInstancesForSave, toShopwaveApiError, toStoredImageIds, toStoredProductImages, updateMerchant, uploadImage, uploadMerchantImage, useBasketReport, useCategory, useConsumer, useDelete, useEmployee, useLogout, useMerchant, useProduct, useProductInstanceTags, useProductTags, usePromotion, useReport, useSession, useStore, useSubmit, useUpdateMerchant, useUploadImage, useUploadMerchantImage, useUser, validateMerchant, withToken };
1461
1612
  //# sourceMappingURL=index.d.ts.map