@misiki/gocommerce-connector 0.3.0 → 0.4.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/dist/index.cjs CHANGED
@@ -138,6 +138,337 @@ function forgetAll() {
138
138
  entries.clear();
139
139
  }
140
140
 
141
+ // src/browser.ts
142
+ var inBrowser = () => typeof window !== "undefined" && typeof document !== "undefined";
143
+ function readLocal(key) {
144
+ if (!inBrowser()) return null;
145
+ try {
146
+ return window.localStorage.getItem(key);
147
+ } catch {
148
+ return null;
149
+ }
150
+ }
151
+ function writeLocal(key, value) {
152
+ if (!inBrowser()) return;
153
+ try {
154
+ window.localStorage.setItem(key, value);
155
+ } catch {
156
+ }
157
+ }
158
+ function removeLocal(key) {
159
+ if (!inBrowser()) return;
160
+ try {
161
+ window.localStorage.removeItem(key);
162
+ } catch {
163
+ }
164
+ }
165
+ function readJSON(key) {
166
+ const raw = readLocal(key);
167
+ if (!raw) return null;
168
+ try {
169
+ return JSON.parse(raw);
170
+ } catch {
171
+ return null;
172
+ }
173
+ }
174
+ var YEAR = 60 * 60 * 24 * 365;
175
+ function setCookie(name, value, maxAge = YEAR) {
176
+ if (!inBrowser()) return;
177
+ document.cookie = `${name}=${value}; path=/; max-age=${maxAge}; samesite=lax`;
178
+ }
179
+ function clearCookie(name) {
180
+ if (!inBrowser()) return;
181
+ document.cookie = `${name}=; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT`;
182
+ }
183
+ function readCookie(name) {
184
+ if (!inBrowser()) return null;
185
+ for (const pair of document.cookie.split(";")) {
186
+ const at = pair.indexOf("=");
187
+ if (at < 0) continue;
188
+ if (pair.slice(0, at).trim() === name) return pair.slice(at + 1).trim();
189
+ }
190
+ return null;
191
+ }
192
+ var TOKEN = "gocommerce_token";
193
+ var sessionToken = () => readLocal(TOKEN);
194
+ function toMe(c) {
195
+ const [firstName, ...rest] = (c.name ?? "").trim().split(/\s+/);
196
+ return {
197
+ id: String(c.id),
198
+ userId: String(c.id),
199
+ email: c.email,
200
+ phone: c.phone || null,
201
+ firstName: firstName || "",
202
+ lastName: rest.join(" "),
203
+ name: c.name ?? "",
204
+ fullName: c.name ?? "",
205
+ avatar: null,
206
+ role: "USER",
207
+ emailVerified: c.email_verified,
208
+ storeId: null
209
+ };
210
+ }
211
+ function startSession(auth) {
212
+ writeLocal(TOKEN, auth.token);
213
+ const me = toMe(auth.record);
214
+ const expires = Math.max(60, Math.floor((Date.parse(auth.expires_at) - Date.now()) / 1e3)) || YEAR;
215
+ setCookie("me", encodeURIComponent(JSON.stringify(me)), expires);
216
+ setCookie("connect.sid", "gocommerce", expires);
217
+ return me;
218
+ }
219
+ function updateMe(c) {
220
+ const me = toMe(c);
221
+ setCookie("me", encodeURIComponent(JSON.stringify(me)));
222
+ return me;
223
+ }
224
+ function endSession() {
225
+ removeLocal(TOKEN);
226
+ clearCookie("me");
227
+ clearCookie("connect.sid");
228
+ }
229
+ function currentMe() {
230
+ const raw = readCookie("me");
231
+ if (!raw) return null;
232
+ try {
233
+ return JSON.parse(decodeURIComponent(raw));
234
+ } catch {
235
+ return null;
236
+ }
237
+ }
238
+ function storedCartId() {
239
+ const v = readLocal("cart_id");
240
+ if (!v || v === "undefined" || v === "null") return null;
241
+ return v;
242
+ }
243
+ var rememberCartId = (id2) => writeLocal("cart_id", id2);
244
+ var extrasKey = (cartId) => `gocommerce_cart_${cartId}`;
245
+ var cartExtras = (cartId) => readJSON(extrasKey(cartId)) ?? {};
246
+ function saveCartExtras(cartId, patch) {
247
+ const next = { ...cartExtras(cartId), ...patch };
248
+ writeLocal(extrasKey(cartId), JSON.stringify(next));
249
+ return next;
250
+ }
251
+ var forgetCartExtras = (cartId) => removeLocal(extrasKey(cartId));
252
+ var ORDERS = "gocommerce_orders";
253
+ var placedOrders = () => readJSON(ORDERS) ?? [];
254
+ function rememberOrder(o) {
255
+ const kept = placedOrders().filter((p) => p.number !== o.number);
256
+ writeLocal(ORDERS, JSON.stringify([o, ...kept].slice(0, 50)));
257
+ }
258
+ var placedOrder = (number) => placedOrders().find((p) => p.number === number);
259
+ var placedOrderForCart = (cartId) => placedOrders().find((p) => p.cartId === cartId);
260
+ var placedOrderByReference = (reference) => placedOrders().find((p) => p.reference === reference);
261
+ var WISHLIST = "gocommerce_wishlist";
262
+ var wishlistToken = () => readLocal(WISHLIST);
263
+ var rememberWishlistToken = (token) => writeLocal(WISHLIST, token);
264
+ var forgetWishlistToken = () => removeLocal(WISHLIST);
265
+
266
+ // src/services/base.service.ts
267
+ function mediaUrl(url) {
268
+ if (!url) return null;
269
+ if (!url.startsWith("/") || url.startsWith("//")) return url;
270
+ const { apiUrl, proxyPath } = BaseService.getCredentials();
271
+ return (proxyPath || apiUrl || "").trim().replace(/\/+$/, "") + url;
272
+ }
273
+ var BaseService = class _BaseService {
274
+ static _credentials = { apiUrl: "" };
275
+ _fetch;
276
+ constructor(fetchFn) {
277
+ this._fetch = fetchFn ?? ((input, init) => globalThis.fetch(input, init));
278
+ }
279
+ static setCredentials(creds) {
280
+ _BaseService._credentials = { ..._BaseService._credentials, ...creds };
281
+ }
282
+ static getCredentials() {
283
+ return _BaseService._credentials;
284
+ }
285
+ get creds() {
286
+ return _BaseService._credentials;
287
+ }
288
+ /**
289
+ * The store this call is for: the request host's subdomain under
290
+ * `baseDomain`, else the static `store`. Read per call, never stored on the
291
+ * instance, because the server shares one process (and one credentials
292
+ * object) across every concurrent request and each may be for a different host.
293
+ */
294
+ get store() {
295
+ const { baseDomain, store } = this.creds;
296
+ const host = typeof window !== "undefined" ? window.location?.hostname : requestHost?.();
297
+ return storeForHost(host, baseDomain) ?? store;
298
+ }
299
+ /**
300
+ * The engine's base URL from wherever this code runs. In the browser a
301
+ * configured proxy path wins, because the engine answers no CORS preflight.
302
+ */
303
+ engineBase() {
304
+ const { apiUrl, proxyPath } = this.creds;
305
+ if (proxyPath && typeof window !== "undefined" && window.location) {
306
+ return new URL(proxyPath, window.location.origin).href.replace(/\/+$/, "");
307
+ }
308
+ const url = (apiUrl ?? "").trim();
309
+ if (!url) {
310
+ throw new GoCommerceError(
311
+ "PUBLIC_GOCOMMERCE_API_URL is not set, so this storefront does not know where its commerce engine is. Point it at a running GoCommerce, e.g. http://127.0.0.1:8080.",
312
+ 0
313
+ );
314
+ }
315
+ return url.replace(/\/+$/, "");
316
+ }
317
+ /** An absolute engine URL for a path and query, without sending anything. */
318
+ engineURL(path, query) {
319
+ return this.engineBase() + path + qs(query);
320
+ }
321
+ /**
322
+ * One request to the engine, envelope and all.
323
+ *
324
+ * The body is read as text first, so a proxy's HTML error page is reported
325
+ * as what it is rather than as a JSON parse error three frames away.
326
+ */
327
+ async engine(path, req = {}) {
328
+ const lang = (req.method ?? "GET") === "GET" ? this.creds.language : void 0;
329
+ const url = this.engineURL(path, lang ? { lang, ...req.query } : req.query);
330
+ const headers = { Accept: "application/json", ...req.headers };
331
+ if (req.body !== void 0) headers["Content-Type"] = "application/json";
332
+ const store = this.store;
333
+ if (store) headers["X-Store"] = store;
334
+ if (req.auth) {
335
+ const token = sessionToken();
336
+ if (!token) throw new GoCommerceError("Please sign in to continue.", 401, "unauthorized");
337
+ headers.Authorization = `Bearer ${token}`;
338
+ }
339
+ const init = {
340
+ method: req.method ?? "GET",
341
+ headers,
342
+ body: req.body === void 0 ? void 0 : JSON.stringify(req.body)
343
+ };
344
+ let res;
345
+ try {
346
+ res = await this._fetch.call(globalThis, url, init);
347
+ } catch (cause) {
348
+ if (typeof window === "undefined" && /CORS/i.test(String(cause?.message))) {
349
+ try {
350
+ res = await globalThis.fetch(url, init);
351
+ } catch (again) {
352
+ throw unreachable(this.engineBase(), again);
353
+ }
354
+ } else {
355
+ throw unreachable(this.engineBase(), cause);
356
+ }
357
+ }
358
+ return this.unwrap(res);
359
+ }
360
+ /** The envelope opened: the payload, or the engine's error as a GoCommerceError. */
361
+ async unwrap(res) {
362
+ const text = await res.text();
363
+ let parsed = null;
364
+ if (text) {
365
+ try {
366
+ parsed = JSON.parse(text);
367
+ } catch {
368
+ throw new GoCommerceError(
369
+ `The commerce engine answered ${res.status} with something that is not JSON: ${text.slice(0, 120)}`,
370
+ res.status
371
+ );
372
+ }
373
+ }
374
+ if (!res.ok) {
375
+ const err = parsed?.error ?? {};
376
+ throw new GoCommerceError(
377
+ err.message ?? `The commerce engine answered ${res.status}.`,
378
+ res.status,
379
+ err.code,
380
+ err.details
381
+ );
382
+ }
383
+ return parsed ?? { data: null };
384
+ }
385
+ /** The bare payload, for the many callers that never look at `meta`. */
386
+ async data(path, req = {}) {
387
+ return (await this.engine(path, req)).data;
388
+ }
389
+ /**
390
+ * Prices a cart at the signed-in shopper's customer-group prices. The
391
+ * engine does this only for an account whose email is confirmed (403
392
+ * otherwise), and the cart stays the shopper's either way — so a refusal
393
+ * is not an error, and neither is a build without ext/identity.
394
+ */
395
+ async claimCartForAccount(cartId) {
396
+ try {
397
+ await this.engine("/x/identity/me/carts", { method: "POST", body: { cart_id: cartId }, auth: true });
398
+ } catch {
399
+ }
400
+ }
401
+ // ------------------------------------------------------------ Litekart verbs
402
+ /**
403
+ * The family's path-addressed verbs. An absolute URL is fetched as-is; a
404
+ * relative one is a Litekart REST path, answered from the storefront's own
405
+ * data where it has some and refused otherwise (rest-guard.ts).
406
+ */
407
+ get(path) {
408
+ return this.raw("get", path);
409
+ }
410
+ post(path, data) {
411
+ return this.raw("post", path, data);
412
+ }
413
+ put(path, data) {
414
+ return this.raw("put", path, data);
415
+ }
416
+ patch(path, data) {
417
+ return this.raw("patch", path, data);
418
+ }
419
+ delete(path) {
420
+ return this.raw("delete", path);
421
+ }
422
+ async raw(method, path, data) {
423
+ if (!/^https?:\/\//i.test(path)) return await resolveRestLocally(method, path);
424
+ const res = await this._fetch.call(globalThis, path, {
425
+ method: method.toUpperCase(),
426
+ headers: { Accept: "application/json", ...data === void 0 ? {} : { "Content-Type": "application/json" } },
427
+ body: data === void 0 ? void 0 : JSON.stringify(data)
428
+ });
429
+ const body = await res.json().catch(() => ({}));
430
+ if (!res.ok)
431
+ throw new GoCommerceError(body?.error?.message ?? `Request failed with status ${res.status}`, res.status);
432
+ return body;
433
+ }
434
+ };
435
+ var requestHost;
436
+ function setRequestHost(fn) {
437
+ requestHost = fn;
438
+ }
439
+ function storeForHost(host, baseDomain) {
440
+ if (!host || !baseDomain) return void 0;
441
+ const h = host.toLowerCase().replace(/\.$/, "").replace(/:\d+$/, "");
442
+ const suffix = "." + baseDomain.toLowerCase().replace(/^\.+/, "").replace(/\.$/, "");
443
+ if (!h.endsWith(suffix)) return void 0;
444
+ const slug = h.slice(0, -suffix.length);
445
+ return slug && !slug.includes(".") ? slug : void 0;
446
+ }
447
+ var unreachable = (base, cause) => new GoCommerceError(`Could not reach the commerce engine at ${base}: ${cause?.message ?? cause}`, 0);
448
+ function qs(query) {
449
+ if (!query) return "";
450
+ const sp = new URLSearchParams();
451
+ for (const [key, value] of Object.entries(query)) {
452
+ if (value === void 0 || value === null || value === "") continue;
453
+ if (Array.isArray(value)) for (const v of value) sp.append(key, String(v));
454
+ else sp.append(key, String(value));
455
+ }
456
+ const s = sp.toString();
457
+ return s ? `?${s}` : "";
458
+ }
459
+ function toPage(env, map) {
460
+ const rows = env.data ?? [];
461
+ const meta = env.meta;
462
+ return {
463
+ data: rows.map(map),
464
+ count: meta?.total ?? rows.length,
465
+ pageSize: meta?.limit ?? rows.length,
466
+ noOfPage: meta?.total_pages ?? 1,
467
+ page: meta?.page ?? 1
468
+ };
469
+ }
470
+ var emptyPage = () => ({ data: [], count: 0, pageSize: 0, noOfPage: 0, page: 1 });
471
+
141
472
  // src/map.ts
142
473
  var str = (v) => v === null || v === void 0 ? "" : String(v);
143
474
  var arr = (v) => v ?? [];
@@ -145,7 +476,8 @@ var liveVariants = (p) => arr(p.variants).filter((v) => v.active);
145
476
  function variantImages(v) {
146
477
  const out = [];
147
478
  for (const img of [v.image, ...arr(v.images)]) {
148
- if (img?.url && (img.kind ?? "image") === "image" && !out.includes(img.url)) out.push(img.url);
479
+ const url = mediaUrl(img?.url);
480
+ if (url && (img?.kind ?? "image") === "image" && !out.includes(url)) out.push(url);
149
481
  }
150
482
  return out;
151
483
  }
@@ -154,7 +486,7 @@ function productImages(p) {
154
486
  const push = (url) => {
155
487
  if (url && !out.includes(url)) out.push(url);
156
488
  };
157
- push(p.image_url);
489
+ push(mediaUrl(p.image_url));
158
490
  for (const v of arr(p.variants)) for (const url of variantImages(v)) push(url);
159
491
  return out;
160
492
  }
@@ -284,9 +616,9 @@ function fromSearchDoc(d) {
284
616
  mrp: price,
285
617
  minPrice: price,
286
618
  maxPrice: minorToMajor(d.price_max_minor, d.currency),
287
- thumbnail: d.image ?? null,
288
- image_url: d.image ?? null,
289
- images: d.image ?? "",
619
+ thumbnail: mediaUrl(d.image),
620
+ image_url: mediaUrl(d.image),
621
+ images: mediaUrl(d.image) ?? "",
290
622
  stock: d.in_stock ? 1 : 0,
291
623
  hasStock: d.in_stock,
292
624
  // The index cannot say how much is left, only whether any is; a card
@@ -315,7 +647,7 @@ function toCategory(c) {
315
647
  level: c.depth,
316
648
  rank: c.position,
317
649
  childCount: c.child_count,
318
- thumbnail: c.metadata?.image ?? null,
650
+ thumbnail: mediaUrl(c.metadata?.image),
319
651
  description: c.metadata?.description ?? null,
320
652
  isActive: true,
321
653
  isFeatured: false,
@@ -335,7 +667,7 @@ function toCollection(c) {
335
667
  description: c.description || null,
336
668
  subTitle: null,
337
669
  rank: str(c.position),
338
- thumbnail: c.metadata?.image ?? null,
670
+ thumbnail: mediaUrl(c.metadata?.image),
339
671
  isActive: true,
340
672
  metaTitle: c.title,
341
673
  metaDescription: c.description || null,
@@ -418,7 +750,7 @@ function toCartLine(l, extra = {}) {
418
750
  title: l.title,
419
751
  name: l.title,
420
752
  slug: extra.slug ?? "",
421
- thumbnail: extra.thumbnail ?? null,
753
+ thumbnail: mediaUrl(extra.thumbnail),
422
754
  variantTitle: l.variant_label ?? "",
423
755
  qty: l.quantity,
424
756
  price: toMajor(l.unit_price),
@@ -526,8 +858,8 @@ function toOrderLine(l, currency) {
526
858
  title: l.title,
527
859
  name: l.title,
528
860
  slug: "",
529
- thumbnail: l.image_url ?? null,
530
- img: l.image_url ?? null,
861
+ thumbnail: mediaUrl(l.image_url),
862
+ img: mediaUrl(l.image_url),
531
863
  variantTitle: l.variant_label ?? "",
532
864
  qty: l.quantity,
533
865
  shippedQty: l.shipped_quantity,
@@ -629,338 +961,37 @@ function toVendor(v) {
629
961
  name: v.name,
630
962
  businessName: v.name,
631
963
  about: v.about,
632
- description: v.about,
633
- website: v.website,
634
- address: v.address,
635
- banners: [],
636
- rating: null
637
- };
638
- }
639
- function toUser(c) {
640
- const [firstName, ...rest] = (c.name ?? "").trim().split(/\s+/);
641
- return {
642
- id: str(c.id),
643
- userId: str(c.id),
644
- email: c.email,
645
- emailVerified: c.email_verified,
646
- phone: c.phone || null,
647
- firstName: firstName || "",
648
- lastName: rest.join(" "),
649
- name: c.name,
650
- fullName: c.name,
651
- avatar: null,
652
- role: "USER",
653
- createdAt: c.created_at,
654
- updatedAt: c.updated_at
655
- };
656
- }
657
- var priceOf = (m) => m ? toMajor(m) : 0;
658
-
659
- // src/browser.ts
660
- var inBrowser = () => typeof window !== "undefined" && typeof document !== "undefined";
661
- function readLocal(key) {
662
- if (!inBrowser()) return null;
663
- try {
664
- return window.localStorage.getItem(key);
665
- } catch {
666
- return null;
667
- }
668
- }
669
- function writeLocal(key, value) {
670
- if (!inBrowser()) return;
671
- try {
672
- window.localStorage.setItem(key, value);
673
- } catch {
674
- }
675
- }
676
- function removeLocal(key) {
677
- if (!inBrowser()) return;
678
- try {
679
- window.localStorage.removeItem(key);
680
- } catch {
681
- }
682
- }
683
- function readJSON(key) {
684
- const raw = readLocal(key);
685
- if (!raw) return null;
686
- try {
687
- return JSON.parse(raw);
688
- } catch {
689
- return null;
690
- }
691
- }
692
- var YEAR = 60 * 60 * 24 * 365;
693
- function setCookie(name, value, maxAge = YEAR) {
694
- if (!inBrowser()) return;
695
- document.cookie = `${name}=${value}; path=/; max-age=${maxAge}; samesite=lax`;
696
- }
697
- function clearCookie(name) {
698
- if (!inBrowser()) return;
699
- document.cookie = `${name}=; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT`;
700
- }
701
- function readCookie(name) {
702
- if (!inBrowser()) return null;
703
- for (const pair of document.cookie.split(";")) {
704
- const at = pair.indexOf("=");
705
- if (at < 0) continue;
706
- if (pair.slice(0, at).trim() === name) return pair.slice(at + 1).trim();
707
- }
708
- return null;
964
+ description: v.about,
965
+ website: v.website,
966
+ address: v.address,
967
+ banners: [],
968
+ rating: null
969
+ };
709
970
  }
710
- var TOKEN = "gocommerce_token";
711
- var sessionToken = () => readLocal(TOKEN);
712
- function toMe(c) {
971
+ function toUser(c) {
713
972
  const [firstName, ...rest] = (c.name ?? "").trim().split(/\s+/);
714
973
  return {
715
- id: String(c.id),
716
- userId: String(c.id),
974
+ id: str(c.id),
975
+ userId: str(c.id),
717
976
  email: c.email,
977
+ emailVerified: c.email_verified,
718
978
  phone: c.phone || null,
719
979
  firstName: firstName || "",
720
980
  lastName: rest.join(" "),
721
- name: c.name ?? "",
722
- fullName: c.name ?? "",
981
+ name: c.name,
982
+ fullName: c.name,
723
983
  avatar: null,
724
984
  role: "USER",
725
- emailVerified: c.email_verified,
726
- storeId: null
727
- };
728
- }
729
- function startSession(auth) {
730
- writeLocal(TOKEN, auth.token);
731
- const me = toMe(auth.record);
732
- const expires = Math.max(60, Math.floor((Date.parse(auth.expires_at) - Date.now()) / 1e3)) || YEAR;
733
- setCookie("me", encodeURIComponent(JSON.stringify(me)), expires);
734
- setCookie("connect.sid", "gocommerce", expires);
735
- return me;
736
- }
737
- function updateMe(c) {
738
- const me = toMe(c);
739
- setCookie("me", encodeURIComponent(JSON.stringify(me)));
740
- return me;
741
- }
742
- function endSession() {
743
- removeLocal(TOKEN);
744
- clearCookie("me");
745
- clearCookie("connect.sid");
746
- }
747
- function currentMe() {
748
- const raw = readCookie("me");
749
- if (!raw) return null;
750
- try {
751
- return JSON.parse(decodeURIComponent(raw));
752
- } catch {
753
- return null;
754
- }
755
- }
756
- function storedCartId() {
757
- const v = readLocal("cart_id");
758
- if (!v || v === "undefined" || v === "null") return null;
759
- return v;
760
- }
761
- var rememberCartId = (id2) => writeLocal("cart_id", id2);
762
- var extrasKey = (cartId) => `gocommerce_cart_${cartId}`;
763
- var cartExtras = (cartId) => readJSON(extrasKey(cartId)) ?? {};
764
- function saveCartExtras(cartId, patch) {
765
- const next = { ...cartExtras(cartId), ...patch };
766
- writeLocal(extrasKey(cartId), JSON.stringify(next));
767
- return next;
768
- }
769
- var forgetCartExtras = (cartId) => removeLocal(extrasKey(cartId));
770
- var ORDERS = "gocommerce_orders";
771
- var placedOrders = () => readJSON(ORDERS) ?? [];
772
- function rememberOrder(o) {
773
- const kept = placedOrders().filter((p) => p.number !== o.number);
774
- writeLocal(ORDERS, JSON.stringify([o, ...kept].slice(0, 50)));
775
- }
776
- var placedOrder = (number) => placedOrders().find((p) => p.number === number);
777
- var placedOrderForCart = (cartId) => placedOrders().find((p) => p.cartId === cartId);
778
- var placedOrderByReference = (reference) => placedOrders().find((p) => p.reference === reference);
779
- var WISHLIST = "gocommerce_wishlist";
780
- var wishlistToken = () => readLocal(WISHLIST);
781
- var rememberWishlistToken = (token) => writeLocal(WISHLIST, token);
782
- var forgetWishlistToken = () => removeLocal(WISHLIST);
783
-
784
- // src/services/base.service.ts
785
- var BaseService = class _BaseService {
786
- static _credentials = { apiUrl: "" };
787
- _fetch;
788
- constructor(fetchFn) {
789
- this._fetch = fetchFn ?? ((input, init) => globalThis.fetch(input, init));
790
- }
791
- static setCredentials(creds) {
792
- _BaseService._credentials = { ..._BaseService._credentials, ...creds };
793
- }
794
- static getCredentials() {
795
- return _BaseService._credentials;
796
- }
797
- get creds() {
798
- return _BaseService._credentials;
799
- }
800
- /**
801
- * The engine's base URL from wherever this code runs. In the browser a
802
- * configured proxy path wins, because the engine answers no CORS preflight.
803
- */
804
- engineBase() {
805
- const { apiUrl, proxyPath } = this.creds;
806
- if (proxyPath && typeof window !== "undefined" && window.location) {
807
- return new URL(proxyPath, window.location.origin).href.replace(/\/+$/, "");
808
- }
809
- const url = (apiUrl ?? "").trim();
810
- if (!url) {
811
- throw new GoCommerceError(
812
- "PUBLIC_GOCOMMERCE_API_URL is not set, so this storefront does not know where its commerce engine is. Point it at a running GoCommerce, e.g. http://127.0.0.1:8080.",
813
- 0
814
- );
815
- }
816
- return url.replace(/\/+$/, "");
817
- }
818
- /** An absolute engine URL for a path and query, without sending anything. */
819
- engineURL(path, query) {
820
- return this.engineBase() + path + qs(query);
821
- }
822
- /**
823
- * One request to the engine, envelope and all.
824
- *
825
- * The body is read as text first, so a proxy's HTML error page is reported
826
- * as what it is rather than as a JSON parse error three frames away.
827
- */
828
- async engine(path, req = {}) {
829
- const lang = (req.method ?? "GET") === "GET" ? this.creds.language : void 0;
830
- const url = this.engineURL(path, lang ? { lang, ...req.query } : req.query);
831
- const headers = { Accept: "application/json", ...req.headers };
832
- if (req.body !== void 0) headers["Content-Type"] = "application/json";
833
- if (this.creds.store) headers["X-Store"] = this.creds.store;
834
- if (req.auth) {
835
- const token = sessionToken();
836
- if (!token) throw new GoCommerceError("Please sign in to continue.", 401, "unauthorized");
837
- headers.Authorization = `Bearer ${token}`;
838
- }
839
- const init = {
840
- method: req.method ?? "GET",
841
- headers,
842
- body: req.body === void 0 ? void 0 : JSON.stringify(req.body)
843
- };
844
- let res;
845
- try {
846
- res = await this._fetch.call(globalThis, url, init);
847
- } catch (cause) {
848
- if (typeof window === "undefined" && /CORS/i.test(String(cause?.message))) {
849
- try {
850
- res = await globalThis.fetch(url, init);
851
- } catch (again) {
852
- throw unreachable(this.engineBase(), again);
853
- }
854
- } else {
855
- throw unreachable(this.engineBase(), cause);
856
- }
857
- }
858
- return this.unwrap(res);
859
- }
860
- /** The envelope opened: the payload, or the engine's error as a GoCommerceError. */
861
- async unwrap(res) {
862
- const text = await res.text();
863
- let parsed = null;
864
- if (text) {
865
- try {
866
- parsed = JSON.parse(text);
867
- } catch {
868
- throw new GoCommerceError(
869
- `The commerce engine answered ${res.status} with something that is not JSON: ${text.slice(0, 120)}`,
870
- res.status
871
- );
872
- }
873
- }
874
- if (!res.ok) {
875
- const err = parsed?.error ?? {};
876
- throw new GoCommerceError(
877
- err.message ?? `The commerce engine answered ${res.status}.`,
878
- res.status,
879
- err.code,
880
- err.details
881
- );
882
- }
883
- return parsed ?? { data: null };
884
- }
885
- /** The bare payload, for the many callers that never look at `meta`. */
886
- async data(path, req = {}) {
887
- return (await this.engine(path, req)).data;
888
- }
889
- /**
890
- * Prices a cart at the signed-in shopper's customer-group prices. The
891
- * engine does this only for an account whose email is confirmed (403
892
- * otherwise), and the cart stays the shopper's either way — so a refusal
893
- * is not an error, and neither is a build without ext/identity.
894
- */
895
- async claimCartForAccount(cartId) {
896
- try {
897
- await this.engine("/x/identity/me/carts", { method: "POST", body: { cart_id: cartId }, auth: true });
898
- } catch {
899
- }
900
- }
901
- // ------------------------------------------------------------ Litekart verbs
902
- /**
903
- * The family's path-addressed verbs. An absolute URL is fetched as-is; a
904
- * relative one is a Litekart REST path, answered from the storefront's own
905
- * data where it has some and refused otherwise (rest-guard.ts).
906
- */
907
- get(path) {
908
- return this.raw("get", path);
909
- }
910
- post(path, data) {
911
- return this.raw("post", path, data);
912
- }
913
- put(path, data) {
914
- return this.raw("put", path, data);
915
- }
916
- patch(path, data) {
917
- return this.raw("patch", path, data);
918
- }
919
- delete(path) {
920
- return this.raw("delete", path);
921
- }
922
- async raw(method, path, data) {
923
- if (!/^https?:\/\//i.test(path)) return await resolveRestLocally(method, path);
924
- const res = await this._fetch.call(globalThis, path, {
925
- method: method.toUpperCase(),
926
- headers: { Accept: "application/json", ...data === void 0 ? {} : { "Content-Type": "application/json" } },
927
- body: data === void 0 ? void 0 : JSON.stringify(data)
928
- });
929
- const body = await res.json().catch(() => ({}));
930
- if (!res.ok)
931
- throw new GoCommerceError(body?.error?.message ?? `Request failed with status ${res.status}`, res.status);
932
- return body;
933
- }
934
- };
935
- var unreachable = (base, cause) => new GoCommerceError(`Could not reach the commerce engine at ${base}: ${cause?.message ?? cause}`, 0);
936
- function qs(query) {
937
- if (!query) return "";
938
- const sp = new URLSearchParams();
939
- for (const [key, value] of Object.entries(query)) {
940
- if (value === void 0 || value === null || value === "") continue;
941
- if (Array.isArray(value)) for (const v of value) sp.append(key, String(v));
942
- else sp.append(key, String(value));
943
- }
944
- const s = sp.toString();
945
- return s ? `?${s}` : "";
946
- }
947
- function toPage(env, map) {
948
- const rows = env.data ?? [];
949
- const meta = env.meta;
950
- return {
951
- data: rows.map(map),
952
- count: meta?.total ?? rows.length,
953
- pageSize: meta?.limit ?? rows.length,
954
- noOfPage: meta?.total_pages ?? 1,
955
- page: meta?.page ?? 1
985
+ createdAt: c.created_at,
986
+ updatedAt: c.updated_at
956
987
  };
957
988
  }
958
- var emptyPage = () => ({ data: [], count: 0, pageSize: 0, noOfPage: 0, page: 1 });
989
+ var priceOf = (m) => m ? toMajor(m) : 0;
959
990
 
960
991
  // src/services/facts.ts
961
992
  var StoreFacts = class extends BaseService {
962
993
  key(name) {
963
- return `${this.engineBase()}|${this.creds.store ?? ""}|${name}`;
994
+ return `${this.engineBase()}|${this.store ?? ""}|${name}`;
964
995
  }
965
996
  /** Enabled plugins by key. A module that is not installed has no key here. */
966
997
  plugins() {
@@ -1973,7 +2004,7 @@ var OrderService = class extends BaseService {
1973
2004
  productId: l.product_id,
1974
2005
  variantId: l.variant_id,
1975
2006
  title: l.title,
1976
- img: l.image_url ?? null,
2007
+ img: mediaUrl(l.image_url),
1977
2008
  price: toMajor(l.unit_price),
1978
2009
  qty: l.quantity
1979
2010
  });
@@ -2390,7 +2421,7 @@ var MenuService = class extends BaseService {
2390
2421
  /** The header and footer menus, or `null` when the module is not there. Cached: a menu is store-level. */
2391
2422
  async engineMenus() {
2392
2423
  if (!await this.facts.pluginOn("navigation")) return null;
2393
- return remember(`${this.engineBase()}|${this.creds.store ?? ""}|menus`, async () => {
2424
+ return remember(`${this.engineBase()}|${this.store ?? ""}|menus`, async () => {
2394
2425
  const out = [];
2395
2426
  for (const handle of ["header", "footer"]) {
2396
2427
  try {
@@ -2424,6 +2455,7 @@ var NewsletterService = class extends BaseService {
2424
2455
  var newsletterService = new NewsletterService();
2425
2456
 
2426
2457
  // src/services/store-service.ts
2458
+ var isStoreMissing = (err) => err instanceof GoCommerceError && err.status === 404 && (err.code === "store_not_found" || /store not found|no such store/i.test(err.message));
2427
2459
  function symbolFor(code) {
2428
2460
  try {
2429
2461
  const part = new Intl.NumberFormat("en", { style: "currency", currency: code }).formatToParts(0).find((p) => p.type === "currency");
@@ -2436,6 +2468,14 @@ var StoreService = class extends BaseService {
2436
2468
  facts = new StoreFacts(this._fetch);
2437
2469
  async getStoreByIdOrDomain(_args = {}) {
2438
2470
  const base = await readStaticStore();
2471
+ const store = this.store;
2472
+ if (store) {
2473
+ await this.facts.plugins().catch((err) => {
2474
+ if (isStoreMissing(err)) {
2475
+ throw new GoCommerceError(`store not found: no store "${store}" on this engine`, 404, "store_not_found");
2476
+ }
2477
+ });
2478
+ }
2439
2479
  const live = await this.live().catch(() => ({}));
2440
2480
  return { ...base, ...live, id: base.id || "gocommerce" };
2441
2481
  }
@@ -3159,6 +3199,7 @@ exports.galleryService = galleryService;
3159
3199
  exports.hasStaticStore = hasStaticStore;
3160
3200
  exports.homeService = homeService;
3161
3201
  exports.initService = initService;
3202
+ exports.mediaUrl = mediaUrl;
3162
3203
  exports.meilisearchService = meilisearchService;
3163
3204
  exports.menuService = menuService;
3164
3205
  exports.minorToMajor = minorToMajor;
@@ -3181,11 +3222,13 @@ exports.regionService = regionService;
3181
3222
  exports.reviewService = reviewService;
3182
3223
  exports.searchService = searchService;
3183
3224
  exports.serveRestLocally = serveRestLocally;
3225
+ exports.setRequestHost = setRequestHost;
3184
3226
  exports.setStaticStore = setStaticStore;
3185
3227
  exports.settingService = settingService;
3186
3228
  exports.settingsService = settingsService;
3187
3229
  exports.stateService = stateService;
3188
3230
  exports.staticStore = staticStore;
3231
+ exports.storeForHost = storeForHost;
3189
3232
  exports.storeService = storeService;
3190
3233
  exports.toCart = toCart;
3191
3234
  exports.toCartLine = toCartLine;