react-shopwave-connect 0.4.5 → 0.4.7

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.
@@ -185,12 +185,15 @@ async function apiSend(path, method, payload, options = {}) {
185
185
  /**
186
186
  * DELETE helper. Shopwave answers deletes with 205 and an empty body; if a body
187
187
  * does come back and carries API errors, they are thrown. Returns the parsed
188
- * body (`null` when empty).
188
+ * body (`null` when empty). `extras`, when given, travel in the `extras` header
189
+ * (the server forwards only keys its delete route allows).
189
190
  */
190
- async function apiDelete(path, options = {}) {
191
+ async function apiDelete(path, options = {}, extras) {
192
+ const headers = baseHeaders(options);
193
+ if (extras) headers.extras = JSON.stringify(buildExtras(extras));
191
194
  const { status, body } = await send(path, {
192
195
  method: "DELETE",
193
- headers: baseHeaders(options)
196
+ headers
194
197
  }, options);
195
198
  if (getApiErrorMap(body)) throw toShopwaveApiError(status, body);
196
199
  return body;
@@ -461,133 +464,6 @@ function fetchProductInstanceTags(options = {}) {
461
464
  return fetchLookup("productInstanceTags", options);
462
465
  }
463
466
 
464
- //#endregion
465
- //#region src/core/promotion.ts
466
- /** Promotion rule identifiers as stored by the Shopwave API. */
467
- let PromotionRuleSet = /* @__PURE__ */ function(PromotionRuleSet$1) {
468
- PromotionRuleSet$1["Percentage"] = "|%^/";
469
- PromotionRuleSet$1["Combo"] = "&%</";
470
- PromotionRuleSet$1["MealDeal"] = "&=^/";
471
- return PromotionRuleSet$1;
472
- }({});
473
- /**
474
- * Fetches promotions. Returns the flattened `Promotion[]`.
475
- */
476
- async function fetchPromotions(params = {}, options = {}) {
477
- const extras = { deleted: params.deleted ?? false };
478
- if (params.promotionIds) extras.promotionIds = params.promotionIds;
479
- if (params.imminent !== void 0) extras.imminent = params.imminent;
480
- if (params.active !== void 0) extras.active = params.active;
481
- if (params.expired !== void 0) extras.expired = params.expired;
482
- const json = await apiGet("/api/promotions", extras, withToken(options, params.token));
483
- assertNoApiErrors(json, 200);
484
- return Object.values(json?.promotions ?? {});
485
- }
486
-
487
- //#endregion
488
- //#region src/core/store.ts
489
- /**
490
- * Fetches stores. Returns the flattened `Store[]`.
491
- */
492
- async function fetchStores(params = {}, options = {}) {
493
- const extras = { deleted: params.deleted ?? false };
494
- if (params.storeIds) extras.storeIds = params.storeIds;
495
- const json = await apiGet("/api/stores", extras, withToken(options, params.token));
496
- assertNoApiErrors(json, 200);
497
- return Object.values(json?.stores ?? {});
498
- }
499
-
500
- //#endregion
501
- //#region src/core/report.ts
502
- /**
503
- * Runs one or more report queries and returns the `reports` map keyed by query
504
- * name. The query is JSON-encoded into the `extras.query` header, matching the
505
- * original endpoint contract.
506
- */
507
- async function fetchReport(query, options = {}) {
508
- const json = await apiGet("/api/report", { query: JSON.stringify(query) }, options);
509
- assertNoApiErrors(json, 200);
510
- return json?.reports ?? {};
511
- }
512
-
513
- //#endregion
514
- //#region src/core/session.ts
515
- /**
516
- * Reads the current login status from `GET /api/session`.
517
- *
518
- * Works with the new `react-shopwave-connect/next` session route
519
- * (`{ loggedIn }`) and with older routes that returned the raw session
520
- * (`{ token: {...} }`) — in that case only a boolean is surfaced.
521
- */
522
- async function fetchSession(options = {}) {
523
- const doFetch = options.fetch ?? (typeof fetch !== "undefined" ? fetch : void 0);
524
- if (!doFetch) throw new Error("No fetch implementation available. Pass `options.fetch`.");
525
- const response = await doFetch(`${options.baseUrl ?? ""}/api/session`, {
526
- method: "GET",
527
- headers: { Accept: "application/json" },
528
- cache: "no-store",
529
- credentials: "same-origin",
530
- signal: options.signal
531
- });
532
- if (response.status === 401) return { loggedIn: false };
533
- if (!response.ok) throw new Error(`Session request failed (${response.status})`);
534
- const json = await response.json();
535
- if (json && typeof json.loggedIn === "boolean") return {
536
- loggedIn: json.loggedIn,
537
- expiresAt: typeof json.expiresAt === "number" ? json.expiresAt : void 0
538
- };
539
- return { loggedIn: Boolean(json && json.token) };
540
- }
541
- /**
542
- * Path that starts a login and returns to `returnTo` afterwards.
543
- * Navigate the browser to it (`window.location.href = loginPath(...)`).
544
- */
545
- function loginPath(returnTo, authPath = "/auth") {
546
- return returnTo ? `${authPath}?returnTo=${encodeURIComponent(returnTo)}` : authPath;
547
- }
548
- /** Path that logs out of the app and the Shopwave auth server. */
549
- function logoutPath(authPath = "/auth") {
550
- return `${authPath}/logout`;
551
- }
552
- /**
553
- * Ends the current server session (no redirect).
554
- *
555
- * Prefer navigating to {@link logoutPath} when using
556
- * `react-shopwave-connect/next`: it also signs the user out of the auth server.
557
- */
558
- async function logout(options = {}) {
559
- await apiDelete("/api/session?action=logout", options);
560
- }
561
-
562
- //#endregion
563
- //#region src/core/entity.ts
564
- /**
565
- * Low-level, untyped mutations: the caller supplies the route and payload.
566
- *
567
- * Prefer the typed functions in `./entities` (`saveProduct`, `deleteCategory`,
568
- * …): they know each route, build the request envelope, check the response and
569
- * return the saved entity with its id. Keep these for routes that have no typed
570
- * function yet (e.g. `/api/merchant`).
571
- */
572
- /**
573
- * Deletes `/api/{resourcePath}/{entityId}`.
574
- */
575
- async function deleteEntity(resourcePath, entityId, options = {}) {
576
- if (entityId == null || entityId === "") throw new Error("entityId is missing");
577
- await apiDelete(`/api/${resourcePath}/${entityId}`, options);
578
- }
579
- /**
580
- * Sends a create/update mutation. Returns the response's `result` field when it
581
- * has one (older app routes wrapped their answer that way), otherwise the whole
582
- * parsed response body. Throws `ShopwaveApiError` on HTTP or API errors.
583
- */
584
- async function submitEntity({ endpoint, method = "POST", payload }, options = {}) {
585
- const json = await apiSend(endpoint, method, payload, options);
586
- assertNoApiErrors(json, 200);
587
- if (json && typeof json === "object" && "result" in json) return json.result;
588
- return json ?? void 0;
589
- }
590
-
591
467
  //#endregion
592
468
  //#region src/core/imageFit.ts
593
469
  /**
@@ -874,7 +750,7 @@ const STATIC_HOST = /^https?:\/\/static\.merchantstack\.com\/images\//i;
874
750
  /**
875
751
  * Turns an image path from the API into a URL a page can load over HTTPS:
876
752
  * `http://static.merchantstack.com/images/x.png` → `<base>/x.png`, and a
877
- * relative path (`merchant/5644/logo/x.png`) → `<base>/merchant/5644/logo/x.png`.
753
+ * relative path (`merchant/123/logo/x.png`) → `<base>/merchant/123/logo/x.png`.
878
754
  * Other absolute URLs are returned unchanged; empty input gives `""`.
879
755
  */
880
756
  function getImageUrl(path, base = SHOPWAVE_IMAGE_BASE_URL) {
@@ -926,7 +802,7 @@ const MERCHANT_IMAGE_SLOT_KEYS = [
926
802
  "featured"
927
803
  ];
928
804
  /**
929
- * Where each slot lives, its upload kind and its exact size (from adminV1's
805
+ * Where each slot lives, its upload kind and its exact size (from the existing admin's
930
806
  * merchant settings page).
931
807
  */
932
808
  const MERCHANT_IMAGE_SLOTS = Object.freeze({
@@ -1017,7 +893,7 @@ function toStored(value) {
1017
893
  }
1018
894
  /**
1019
895
  * The `imageIds` to send on a save: every URL is turned back into its file
1020
- * name (what adminV1 does with images you didn't change) and empty values
896
+ * name (what the existing admin does with images you didn't change) and empty values
1021
897
  * are dropped. No images gives `{}`.
1022
898
  */
1023
899
  function toStoredImageIds(imageIds) {
@@ -1038,7 +914,7 @@ async function uploadMerchantImage(file, slot, options = {}) {
1038
914
  //#endregion
1039
915
  //#region src/core/productImages.ts
1040
916
  /**
1041
- * Product images, as the live API stores them (merchant 5644, 4 Nov 2026):
917
+ * Product images, as the live API stores them (4 Nov 2026):
1042
918
  * - A product's `images` is a list. **Reads return full URLs**
1043
919
  * (`http://static.merchantstack.com/images/merchant/<id>/product/<file>`);
1044
920
  * **writes take the uploader's file names** ({@link toStoredProductImages};
@@ -1083,6 +959,27 @@ function toStoredProductImages(images) {
1083
959
  * - employee: POST updates `roleId`, `joinedDate`, `exitDate` (names can't be
1084
960
  * changed); no DELETE (live API: "Cannot DELETE /employee") → retired via `exitDate`.
1085
961
  * - consumer: read-only (`GET` with `ids`, comma-separated); live POST → 404.
962
+ * - productComponent: modifier groups, `/product/component` (live, staging, 11 Nov 2026). The
963
+ * API reference lists no read filters; these are what the live API does:
964
+ * - Reads filter by `componentIds` (group ids) or `productIds` (the parent product; option
965
+ * products don't match). `productComponentIds` is ignored on reads: every group comes back.
966
+ * `deleted: false` gives live groups only, `true` deleted groups only, no header both.
967
+ * No match → 204 with an empty body. No order, sort, priority or position field anywhere.
968
+ * - Groups are keyed by id; `products` (option products) by product id; `productInstances`
969
+ * (option rows) by row id. Reads reformat values: price `"100"` → `"100.0"`,
970
+ * vatPercentage `"0.2000"` → `"0.200"`. `tags` and `name` are stored as sent (no trimming;
971
+ * a `tags` array is joined with commas; `tags` null or left out → null).
972
+ * - POST upserts (201, echoed under the ref). For each option product in `products`, its rows
973
+ * are replaced: every row sent is created afresh with a new id (a row `id` is ignored, also
974
+ * on a price change) and rows left out are removed. Option products left out of the save
975
+ * are untouched, and an empty `productInstances` removes nothing. A save without `products`
976
+ * updates only the group's own fields. The echo isn't proof of what was stored: renames of
977
+ * groups created moments earlier by a test run echoed the new name but never stuck (re-read
978
+ * for 90 s), while older groups, and groups created in Shopwave's own admin, renamed at
979
+ * once. Cause not yet known.
980
+ * - DELETE with `productComponentIds` (plural, comma list) → 205, soft delete. Adding
981
+ * `productComponentInstanceIds` deletes just those rows (205); they stay in live reads with
982
+ * `deleteDate` set. The singular `productComponentId` hangs (504 after 60 s).
1086
983
  */
1087
984
  const SHOPWAVE_ENTITIES = Object.freeze({
1088
985
  product: {
@@ -1142,6 +1039,16 @@ const SHOPWAVE_ENTITIES = Object.freeze({
1142
1039
  idHeader: "consumerId",
1143
1040
  writable: false,
1144
1041
  deleteMode: "none"
1042
+ },
1043
+ productComponent: {
1044
+ route: "product-components",
1045
+ collection: "productComponents",
1046
+ upstream: "product/component",
1047
+ idsHeader: "componentIds",
1048
+ idHeader: "productComponentIds",
1049
+ writable: true,
1050
+ deleteMode: "delete",
1051
+ childIdsHeader: "productComponentInstanceIds"
1145
1052
  }
1146
1053
  });
1147
1054
  function definition(kind) {
@@ -1267,6 +1174,237 @@ const deleteEmployee = (id, options) => deleteEntityById("employee", id, options
1267
1174
  const fetchEmployee = (id, params, options) => fetchEntityById("employee", id, params, options);
1268
1175
  const fetchConsumer = (id, params, options) => fetchEntityById("consumer", id, params, options);
1269
1176
 
1177
+ //#endregion
1178
+ //#region src/core/productComponent.ts
1179
+ /** Row fields the server sets; never sent back. */
1180
+ const SERVER_ROW_FIELDS = [
1181
+ "id",
1182
+ "activeDate",
1183
+ "deleteDate",
1184
+ "timestamp"
1185
+ ];
1186
+ const isObject = (v) => !!v && typeof v === "object" && !Array.isArray(v);
1187
+ function listOf(value) {
1188
+ if (!value) return [];
1189
+ if (Array.isArray(value)) return value.map((v) => [null, v]);
1190
+ return Object.entries(value);
1191
+ }
1192
+ /**
1193
+ * Builds the save body for a product component from a group as read (or as a form holds it):
1194
+ * - `products` keyed by product id (a product without `id` takes its key);
1195
+ * - each product's rows keyed `"0"`, `"1"`, …, like every other ref in a save;
1196
+ * - rows that have a `deleteDate` are left out: the API recreates every row it's sent, so
1197
+ * sending one back would bring a deleted option back as a new row;
1198
+ * - server row fields (`id`, `activeDate`, `deleteDate`, `timestamp`) are removed, since the
1199
+ * API gives every row it's sent a new id anyway;
1200
+ * - a `tags` list becomes a comma string.
1201
+ *
1202
+ * The API replaces the rows of every option product in the save and leaves the others alone,
1203
+ * so send all the rows to keep for each product included. To remove an option product
1204
+ * entirely, delete its rows with {@link deleteProductComponentInstances}.
1205
+ */
1206
+ function toProductComponentForSave(input) {
1207
+ const { products,...group } = input;
1208
+ if (products == null) return { ...group };
1209
+ const out = {};
1210
+ for (const [key, product] of listOf(products)) {
1211
+ if (!isObject(product)) continue;
1212
+ const id = product.id ?? key;
1213
+ if (id == null || String(id).trim() === "") continue;
1214
+ const { productInstances,...rest } = product;
1215
+ const rows = {};
1216
+ let index = 0;
1217
+ for (const [, row] of listOf(productInstances)) {
1218
+ if (!isObject(row) || row.deleteDate) continue;
1219
+ const copy = { ...row };
1220
+ for (const field of SERVER_ROW_FIELDS) delete copy[field];
1221
+ if (Array.isArray(copy.tags)) copy.tags = copy.tags.map(String).join(",");
1222
+ rows[String(index++)] = copy;
1223
+ }
1224
+ out[String(id)] = {
1225
+ ...rest,
1226
+ id,
1227
+ productInstances: rows
1228
+ };
1229
+ }
1230
+ return {
1231
+ ...group,
1232
+ products: out
1233
+ };
1234
+ }
1235
+ /**
1236
+ * Reads product components, optionally filtered by parent product and/or group id.
1237
+ * With no filter it returns every group on the merchant. Returns `[]` when nothing matches
1238
+ * (the API answers 204), and when a filter list is empty (no request is made).
1239
+ *
1240
+ * Rows deleted with {@link deleteProductComponentInstances} are still returned, with
1241
+ * `deleteDate` set; skip them when listing options.
1242
+ */
1243
+ async function fetchProductComponents(params = {}, options = {}) {
1244
+ const def = SHOPWAVE_ENTITIES.productComponent;
1245
+ if (params.productIds?.length === 0 || params.productComponentIds?.length === 0) return [];
1246
+ const extras = { deleted: params.deleted ?? false };
1247
+ if (params.productIds) extras.productIds = params.productIds;
1248
+ if (params.productComponentIds) extras[def.idsHeader] = params.productComponentIds;
1249
+ const json = await apiGet(`/api/${def.route}`, extras, options);
1250
+ assertNoApiErrors(json, 200);
1251
+ const map = json?.[def.collection];
1252
+ return isObject(map) ? Object.values(map) : [];
1253
+ }
1254
+ /**
1255
+ * Reads one product component by id, or `null` when there's no such live group.
1256
+ * Note that `{ deleted: true }` returns the group only if it *has* been deleted.
1257
+ */
1258
+ const fetchProductComponent = (id, params, options) => fetchEntityById("productComponent", id, params, options);
1259
+ /**
1260
+ * Creates (no `id`) or updates (with `id`) a product component and returns it with its id.
1261
+ * The body is built with {@link toProductComponentForSave}. Every option row sent is stored
1262
+ * under a new id, so re-read with {@link fetchProductComponent} for the stored rows.
1263
+ */
1264
+ const saveProductComponent = (input, options) => saveEntity("productComponent", toProductComponentForSave(input), options);
1265
+ /** Deletes a product component (soft delete: `deleted: true` reads still return it). */
1266
+ const deleteProductComponent = (id, options) => deleteEntityById("productComponent", id, options);
1267
+ /**
1268
+ * Deletes some option rows of a product component, leaving the group and its other rows.
1269
+ * The rows stay in reads with `deleteDate` set. Throws without calling the API when
1270
+ * `instanceIds` is empty, because a delete without them would delete the whole group.
1271
+ */
1272
+ async function deleteProductComponentInstances(id, instanceIds, options = {}) {
1273
+ const def = SHOPWAVE_ENTITIES.productComponent;
1274
+ const groupId = id == null ? "" : String(id).trim();
1275
+ if (!groupId) throw new Error("productComponent id is missing");
1276
+ const ids = (instanceIds ?? []).map((v) => String(v).trim()).filter(Boolean);
1277
+ if (ids.length === 0) throw new Error("productComponent instance ids are missing");
1278
+ await apiDelete(`/api/${def.route}/${encodeURIComponent(groupId)}`, options, { [def.childIdsHeader]: ids.join(",") });
1279
+ }
1280
+
1281
+ //#endregion
1282
+ //#region src/core/promotion.ts
1283
+ /** Promotion rule identifiers as stored by the Shopwave API. */
1284
+ let PromotionRuleSet = /* @__PURE__ */ function(PromotionRuleSet$1) {
1285
+ PromotionRuleSet$1["Percentage"] = "|%^/";
1286
+ PromotionRuleSet$1["Combo"] = "&%</";
1287
+ PromotionRuleSet$1["MealDeal"] = "&=^/";
1288
+ return PromotionRuleSet$1;
1289
+ }({});
1290
+ /**
1291
+ * Fetches promotions. Returns the flattened `Promotion[]`.
1292
+ */
1293
+ async function fetchPromotions(params = {}, options = {}) {
1294
+ const extras = { deleted: params.deleted ?? false };
1295
+ if (params.promotionIds) extras.promotionIds = params.promotionIds;
1296
+ if (params.imminent !== void 0) extras.imminent = params.imminent;
1297
+ if (params.active !== void 0) extras.active = params.active;
1298
+ if (params.expired !== void 0) extras.expired = params.expired;
1299
+ const json = await apiGet("/api/promotions", extras, withToken(options, params.token));
1300
+ assertNoApiErrors(json, 200);
1301
+ return Object.values(json?.promotions ?? {});
1302
+ }
1303
+
1304
+ //#endregion
1305
+ //#region src/core/store.ts
1306
+ /**
1307
+ * Fetches stores. Returns the flattened `Store[]`.
1308
+ */
1309
+ async function fetchStores(params = {}, options = {}) {
1310
+ const extras = { deleted: params.deleted ?? false };
1311
+ if (params.storeIds) extras.storeIds = params.storeIds;
1312
+ const json = await apiGet("/api/stores", extras, withToken(options, params.token));
1313
+ assertNoApiErrors(json, 200);
1314
+ return Object.values(json?.stores ?? {});
1315
+ }
1316
+
1317
+ //#endregion
1318
+ //#region src/core/report.ts
1319
+ /**
1320
+ * Runs one or more report queries and returns the `reports` map keyed by query
1321
+ * name. The query is JSON-encoded into the `extras.query` header, matching the
1322
+ * original endpoint contract.
1323
+ */
1324
+ async function fetchReport(query, options = {}) {
1325
+ const json = await apiGet("/api/report", { query: JSON.stringify(query) }, options);
1326
+ assertNoApiErrors(json, 200);
1327
+ return json?.reports ?? {};
1328
+ }
1329
+
1330
+ //#endregion
1331
+ //#region src/core/session.ts
1332
+ /**
1333
+ * Reads the current login status from `GET /api/session`.
1334
+ *
1335
+ * Works with the new `react-shopwave-connect/next` session route
1336
+ * (`{ loggedIn }`) and with older routes that returned the raw session
1337
+ * (`{ token: {...} }`) — in that case only a boolean is surfaced.
1338
+ */
1339
+ async function fetchSession(options = {}) {
1340
+ const doFetch = options.fetch ?? (typeof fetch !== "undefined" ? fetch : void 0);
1341
+ if (!doFetch) throw new Error("No fetch implementation available. Pass `options.fetch`.");
1342
+ const response = await doFetch(`${options.baseUrl ?? ""}/api/session`, {
1343
+ method: "GET",
1344
+ headers: { Accept: "application/json" },
1345
+ cache: "no-store",
1346
+ credentials: "same-origin",
1347
+ signal: options.signal
1348
+ });
1349
+ if (response.status === 401) return { loggedIn: false };
1350
+ if (!response.ok) throw new Error(`Session request failed (${response.status})`);
1351
+ const json = await response.json();
1352
+ if (json && typeof json.loggedIn === "boolean") return {
1353
+ loggedIn: json.loggedIn,
1354
+ expiresAt: typeof json.expiresAt === "number" ? json.expiresAt : void 0
1355
+ };
1356
+ return { loggedIn: Boolean(json && json.token) };
1357
+ }
1358
+ /**
1359
+ * Path that starts a login and returns to `returnTo` afterwards.
1360
+ * Navigate the browser to it (`window.location.href = loginPath(...)`).
1361
+ */
1362
+ function loginPath(returnTo, authPath = "/auth") {
1363
+ return returnTo ? `${authPath}?returnTo=${encodeURIComponent(returnTo)}` : authPath;
1364
+ }
1365
+ /** Path that logs out of the app and the Shopwave auth server. */
1366
+ function logoutPath(authPath = "/auth") {
1367
+ return `${authPath}/logout`;
1368
+ }
1369
+ /**
1370
+ * Ends the current server session (no redirect).
1371
+ *
1372
+ * Prefer navigating to {@link logoutPath} when using
1373
+ * `react-shopwave-connect/next`: it also signs the user out of the auth server.
1374
+ */
1375
+ async function logout(options = {}) {
1376
+ await apiDelete("/api/session?action=logout", options);
1377
+ }
1378
+
1379
+ //#endregion
1380
+ //#region src/core/entity.ts
1381
+ /**
1382
+ * Low-level, untyped mutations: the caller supplies the route and payload.
1383
+ *
1384
+ * Prefer the typed functions in `./entities` (`saveProduct`, `deleteCategory`,
1385
+ * …): they know each route, build the request envelope, check the response and
1386
+ * return the saved entity with its id. Keep these for routes that have no typed
1387
+ * function yet (e.g. `/api/merchant`).
1388
+ */
1389
+ /**
1390
+ * Deletes `/api/{resourcePath}/{entityId}`.
1391
+ */
1392
+ async function deleteEntity(resourcePath, entityId, options = {}) {
1393
+ if (entityId == null || entityId === "") throw new Error("entityId is missing");
1394
+ await apiDelete(`/api/${resourcePath}/${entityId}`, options);
1395
+ }
1396
+ /**
1397
+ * Sends a create/update mutation. Returns the response's `result` field when it
1398
+ * has one (older app routes wrapped their answer that way), otherwise the whole
1399
+ * parsed response body. Throws `ShopwaveApiError` on HTTP or API errors.
1400
+ */
1401
+ async function submitEntity({ endpoint, method = "POST", payload }, options = {}) {
1402
+ const json = await apiSend(endpoint, method, payload, options);
1403
+ assertNoApiErrors(json, 200);
1404
+ if (json && typeof json === "object" && "result" in json) return json.result;
1405
+ return json ?? void 0;
1406
+ }
1407
+
1270
1408
  //#endregion
1271
1409
  //#region src/core/basket.ts
1272
1410
  const DEFAULT_BASKET_FILTERS = {
@@ -1440,9 +1578,9 @@ function trimmed(value) {
1440
1578
  /**
1441
1579
  * Reads `merchant.links` as flat fields (`{ website, twitter, facebook,
1442
1580
  * instagram }`), whatever shape it was saved in:
1443
- * - the nested shape adminV1 saves (`{ website: { home }, social: { … } }`);
1581
+ * - the nested shape the existing admin saves (`{ website: { home }, social: { … } }`);
1444
1582
  * - flat keys (`{ website: "…" }`, what SDK 0.4.0 pre-releases sent);
1445
- * - an array in {@link MERCHANT_LINK_KEYS} order (older AdminUI versions).
1583
+ * - an array in {@link MERCHANT_LINK_KEYS} order (older app versions).
1446
1584
  * Empty values are dropped.
1447
1585
  */
1448
1586
  function merchantLinksToForm(links) {
@@ -1479,7 +1617,7 @@ function merchantLinksFromForm(form) {
1479
1617
  }
1480
1618
  return out;
1481
1619
  }
1482
- /** Reads any stored or older `links` value into the nested shape adminV1 saves. */
1620
+ /** Reads any stored or older `links` value into the nested shape the existing admin saves. */
1483
1621
  function normalizeMerchantLinks(links) {
1484
1622
  return merchantLinksFromForm(merchantLinksToForm(links));
1485
1623
  }
@@ -1587,5 +1725,5 @@ async function fetchUser(options = {}) {
1587
1725
  }
1588
1726
 
1589
1727
  //#endregion
1590
- export { DEFAULT_BASKET_FILTERS, DEFAULT_PAGINATION, ImageRuleError, ImageTooSmallError, 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, PromotionRuleSet, SHOPWAVE_ENTITIES, SHOPWAVE_IMAGE_BASE_URL, SHOPWAVE_LOOKUPS, SHOPWAVE_RESOURCES, SHOPWAVE_UPLOAD, ShopwaveApiError, UPLOAD_KINDS, UPLOAD_PREFIXES, apiDelete, apiGet, apiRequest, 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, validateMerchant, withToken };
1728
+ export { DEFAULT_BASKET_FILTERS, DEFAULT_PAGINATION, ImageRuleError, ImageTooSmallError, 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, PromotionRuleSet, SHOPWAVE_ENTITIES, SHOPWAVE_IMAGE_BASE_URL, SHOPWAVE_LOOKUPS, SHOPWAVE_RESOURCES, SHOPWAVE_UPLOAD, ShopwaveApiError, UPLOAD_KINDS, UPLOAD_PREFIXES, apiDelete, apiGet, apiRequest, apiSend, assertNoApiErrors, authorizationFor, buildBasketReportQuery, buildConsumerMap, checkImageRule, collectConsumerIds, collectProductIds, combineBasketRows, computeBasketSummary, coverCrop, deleteCategory, deleteEmployee, deleteEntity, deleteEntityById, deleteProduct, deleteProductComponent, deleteProductComponentInstances, deletePromotion, deleteStore, fetchCategories, fetchCategory, fetchConsumer, fetchConsumers, fetchEmployee, fetchEmployees, fetchEntityById, fetchMerchant, fetchProduct, fetchProductComponent, fetchProductComponents, 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, saveProductComponent, savePromotion, saveStore, setMerchantImage, submitEntity, toProductComponentForSave, toProductInstancesForSave, toShopwaveApiError, toStoredImageIds, toStoredProductImages, updateMerchant, uploadImage, uploadMerchantImage, validateMerchant, withToken };
1591
1729
  //# sourceMappingURL=index.js.map