orionplace-svelte 0.6.1 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -480,7 +480,9 @@ The corresponding `.cfg` is read on every request. Backend URLs must use HTTPS,
480
480
  end in `/api`, and belong to `*.orionplace.app`. API methods, query strings, bodies,
481
481
  authorization and cookies are forwarded. Backend statuses are retained and cookie
482
482
  Domain attributes are removed to bind cookies to the storefront. Gateway responses
483
- use `Cache-Control: no-store`. Network failures return `502`.
483
+ use `Cache-Control: no-store`, except public blog images and videos: they keep the
484
+ backend's `private, max-age` rule, so the browser does not download them again on
485
+ every page. Network failures return `502`.
484
486
 
485
487
  A changing request (POST, PUT, PATCH, DELETE) from another site is rejected with
486
488
  `403`. A request from the page's own host or the storefront domain passes, and the
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "orionplace-svelte",
3
- "version": "0.6.1",
3
+ "version": "0.7.0",
4
4
  "description": "SvelteKit 2 storefront for your Orionplace marketplace: setup CLI, server API gateway, typed store and ready-made catalog, cart, checkout and account components in English and Russian, plus an Android app build.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -37,6 +37,11 @@ export declare function marketplacePublicOrigin(
37
37
  tenantDomain?: string,
38
38
  ): string;
39
39
 
40
+ export declare function marketplaceCacheControl(
41
+ media: boolean,
42
+ upstream: Response,
43
+ ): string;
44
+
40
45
  export declare function noindexPathPrefixes(value?: string): string[];
41
46
 
42
47
  export declare function marketplaceNoindexPath(
@@ -41,6 +41,18 @@ const hostnamePattern =
41
41
  const blogMediaPattern =
42
42
  /^\/ps3\/blog\/posts\/(?:images\/\d+|videos\/\d+(?:\/preview)?)$/;
43
43
 
44
+ /**
45
+ * API answers are never stored. A media file keeps the backend's own browser
46
+ * cache rule when the backend allows it (a published public blog image:
47
+ * "private, max-age=…"); "private" keeps it out of shared caches.
48
+ */
49
+ export function marketplaceCacheControl(media, upstream) {
50
+ const value = upstream.headers.get('cache-control') ?? '';
51
+ return media && upstream.ok && /\bprivate\b/i.test(value) && /\bmax-age=\d+/i.test(value) && !/no-store|no-cache/i.test(value)
52
+ ? value
53
+ : 'no-store';
54
+ }
55
+
44
56
  /** @param {string} pathname */
45
57
  export function isMarketplaceMediaPath(pathname) {
46
58
  return blogMediaPattern.test(pathname);
@@ -307,7 +319,7 @@ export async function forwardMarketplace(request, configuration = {}) {
307
319
  : destination.href,
308
320
  );
309
321
  }
310
- responseHeaders.set('Cache-Control', 'no-store');
322
+ responseHeaders.set('Cache-Control', marketplaceCacheControl(media, upstream));
311
323
  responseHeaders.set('Vary', 'Host, X-Forwarded-Host, X-Front-Domain');
312
324
  // fetch decodes compressed bodies: do not reuse upstream Content-Length/Encoding.
313
325
  return new Response(upstream.body, {
@@ -403,6 +403,14 @@ for example \`ORIONPLACE_NOINDEX_PATHS="/catalog /category /product"\`. Those
403
403
  pages answer with \`X-Robots-Tag: noindex, follow\` and the sitemap leaves them
404
404
  out; the journal and other pages stay indexable. Leave it empty on a live shop.
405
405
 
406
+ Every product page carries schema.org product data for Google and Yandex: the
407
+ price, the crossed-out old price, stock, photos, brand, seller, rating and the
408
+ return terms the page shows. To include the return terms, set
409
+ \`ORIONPLACE_SHIPPING_COUNTRIES\` in \`.env\` to the countries you ship to, as
410
+ ISO codes separated by spaces, for example
411
+ \`ORIONPLACE_SHIPPING_COUNTRIES="RU BY"\`; without it the return terms are left
412
+ out. The delivery cost is not included: it depends on the buyer's address.
413
+
406
414
  This setup does not add analytics.
407
415
 
408
416
  ## Android app
@@ -12,10 +12,12 @@ function escapeAttribute(value: string) {
12
12
 
13
13
  /**
14
14
  * Personalized pages are never shared between visitors or storefront domains.
15
- * The API gateway, robots.txt and the sitemap set their own headers, so those
16
- * requests are left alone.
15
+ * The API gateway (/api, /ps3 media), robots.txt and the sitemap set their own
16
+ * headers, so those requests are left alone: public blog images keep the
17
+ * backend's browser cache rule.
17
18
  */
18
19
  const ownHeaders = ['/robots.txt', '/sitemap.xml'];
20
+ const gatewayPaths = ['/api/', '/ps3/'];
19
21
 
20
22
  export const handle: Handle = async ({ event, resolve }) => {
21
23
  const response = await resolve(event, {
@@ -26,7 +28,7 @@ export const handle: Handle = async ({ event, resolve }) => {
26
28
  ),
27
29
  });
28
30
  if (
29
- !event.url.pathname.startsWith('/api/') &&
31
+ !gatewayPaths.some((path) => event.url.pathname.startsWith(path)) &&
30
32
  !ownHeaders.includes(event.url.pathname)
31
33
  ) {
32
34
  response.headers.set('Cache-Control', 'private, no-store');
@@ -8,6 +8,8 @@ Article videos may come as the backend's own paths (`/ps3/...`). In an Android a
8
8
 
9
9
  The cover and the images of the text open larger: a click, Enter or Space on one opens `ProductReviewImageViewer` at that image, with the article's other images a step away and each image's caption (its `figcaption`, else its `title`, else its `alt`) under it. The images get `role="button"`, a keyboard stop and an «Open image» name; a zoom-in cursor marks them. Images inside a link, a button or a product placement keep their own action. The helper is `src/lib/utils/orionplace/blogImageViewer.ts`.
10
10
 
11
+ Products the author places in the text appear at those positions: an empty block with `data-product-id="12"` shows one product card, `data-product-ids="12,15,9"` a row of up to 24 cards. The block keeps the editor's float or centred style, and `data-product-hidden-badges` (`new`, `popular`, `good-deal`) turns card badges off. Products missing from `related_products` are loaded with `products_search` and `filters.id_in`, up to 35 IDs per request, and stored with `store.setProduct()`; a block whose products are not found stays hidden. Article text styles do not reach the cards, and in a narrow article a floated card stands alone in the middle. The helper is `src/lib/utils/orionplace/blogProductPlacements.ts`.
12
+
11
13
  Source: `src/lib/components/orionplace/blog/BlogArticle.svelte`.
12
14
 
13
15
  ## Inputs