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