@shop-on/theme-server 0.3.0 → 0.5.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.
@@ -0,0 +1,10 @@
1
+ //#region src/availability.d.ts
2
+ /**
3
+ * Request middleware: every theme registers it in `src/start.ts` after
4
+ * `storefrontActivityMiddleware`, which answers `/health` before it. A closed
5
+ * or suspended store (see docs/shop-lifecycle.md) serves one uncacheable,
6
+ * unindexed 503 page in place of every route; the shop record is the
7
+ * memoised one the root loader reads, so this costs no extra request.
8
+ */
9
+ export declare const storefrontAvailabilityMiddleware: import("@tanstack/react-start").RequestMiddlewareAfterServer<{}, undefined, undefined>;
10
+ //#endregion
@@ -0,0 +1,59 @@
1
+ import { fetchShopInfo } from "./shop.js";
2
+ import { escapeHtmlAttr } from "./html.js";
3
+ import { createMiddleware } from "@tanstack/react-start";
4
+ //#region src/availability.ts
5
+ /**
6
+ * Request middleware: every theme registers it in `src/start.ts` after
7
+ * `storefrontActivityMiddleware`, which answers `/health` before it. A closed
8
+ * or suspended store (see docs/shop-lifecycle.md) serves one uncacheable,
9
+ * unindexed 503 page in place of every route; the shop record is the
10
+ * memoised one the root loader reads, so this costs no extra request.
11
+ */
12
+ const storefrontAvailabilityMiddleware = createMiddleware().server(async ({ next }) => {
13
+ let shop;
14
+ try {
15
+ shop = await fetchShopInfo();
16
+ } catch {
17
+ return next();
18
+ }
19
+ if (!shop || shop.status === "active") return next();
20
+ return unavailableResponse(shop.name);
21
+ });
22
+ function unavailableResponse(shopName) {
23
+ const name = escapeHtmlAttr(shopName);
24
+ const html = `<!doctype html>
25
+ <html lang="mn">
26
+ <head>
27
+ <meta charset="utf-8">
28
+ <meta name="viewport" content="width=device-width, initial-scale=1">
29
+ <meta name="robots" content="noindex">
30
+ <title>${name}</title>
31
+ <style>
32
+ :root{color-scheme:light dark;--bg:#fafafa;--fg:#171717;--muted:#737373}
33
+ @media (prefers-color-scheme:dark){:root{--bg:#0a0a0a;--fg:#fafafa;--muted:#a3a3a3}}
34
+ body{margin:0;min-height:100svh;display:grid;place-items:center;padding:24px;box-sizing:border-box;background:var(--bg);color:var(--fg);font:16px/1.5 system-ui,-apple-system,"Segoe UI",Roboto,sans-serif;text-align:center}
35
+ main{max-width:28rem}
36
+ h1{margin:0 0 12px;font-size:1.25rem;font-weight:600}
37
+ p{margin:4px 0;color:var(--muted)}
38
+ </style>
39
+ </head>
40
+ <body>
41
+ <main>
42
+ <h1>${name}</h1>
43
+ <p>Энэ дэлгүүр одоогоор түр хаалттай байна.</p>
44
+ <p lang="en">This store is currently unavailable.</p>
45
+ </main>
46
+ </body>
47
+ </html>
48
+ `;
49
+ return new Response(html, {
50
+ status: 503,
51
+ headers: {
52
+ "Content-Type": "text/html; charset=utf-8",
53
+ "Cache-Control": "no-store",
54
+ "X-Robots-Tag": "noindex"
55
+ }
56
+ });
57
+ }
58
+ //#endregion
59
+ export { storefrontAvailabilityMiddleware };
@@ -184,6 +184,7 @@ export declare const CollectionImageFragment: import("gql.tada").TadaDocumentNod
184
184
  thumbUrl: string | null;
185
185
  thumbhash: string | null;
186
186
  url: string | null;
187
+ url1_5x: string | null;
187
188
  url2x: string | null;
188
189
  url3x: string | null;
189
190
  width: number | null;
@@ -134,6 +134,7 @@ const CollectionImageFragment = graphql(`
134
134
  thumbhash
135
135
  ... on ShopMediaImage {
136
136
  url(transform: { maxWidth: 640 })
137
+ url1_5x: url(transform: { maxWidth: 960 })
137
138
  url2x: url(transform: { maxWidth: 640, scale: 2 })
138
139
  url3x: url(transform: { maxWidth: 640, scale: 3 })
139
140
  thumbUrl: url(transform: { maxWidth: 80 })
package/dist/files.d.ts CHANGED
@@ -36,6 +36,7 @@ type FileLikeNode = {
36
36
  preview?: {
37
37
  url?: string | null;
38
38
  } | null;
39
+ url1_5x?: string | null;
39
40
  url2x?: string | null;
40
41
  url3x?: string | null;
41
42
  thumbUrl?: string | null;
package/dist/files.js CHANGED
@@ -6,6 +6,7 @@ const FileByIdQuery = graphql(`
6
6
  query FileById(
7
7
  $id: ID!
8
8
  $transform: ImageTransformInput
9
+ $transform1_5x: ImageTransformInput
9
10
  $transform2x: ImageTransformInput
10
11
  $transform3x: ImageTransformInput
11
12
  ) {
@@ -18,6 +19,7 @@ const FileByIdQuery = graphql(`
18
19
  width
19
20
  height
20
21
  url(transform: $transform)
22
+ url1_5x: url(transform: $transform1_5x)
21
23
  url2x: url(transform: $transform2x)
22
24
  url3x: url(transform: $transform3x)
23
25
  focalPoint {
@@ -50,23 +52,30 @@ function buildSrcset(entries) {
50
52
  }
51
53
  return parts.length > 1 ? parts.join(", ") : null;
52
54
  }
55
+ function ladderSrcset(node, baseWidth) {
56
+ return buildSrcset([
57
+ {
58
+ url: node.url,
59
+ w: baseWidth
60
+ },
61
+ {
62
+ url: node.url1_5x,
63
+ w: Math.round(baseWidth * 1.5)
64
+ },
65
+ {
66
+ url: node.url2x,
67
+ w: baseWidth * 2
68
+ },
69
+ {
70
+ url: node.url3x,
71
+ w: baseWidth * 3
72
+ }
73
+ ]);
74
+ }
53
75
  function toFileInfo(node, extra = {}) {
54
76
  if (!node) return null;
55
77
  if (node.__typename === "ShopMediaImage") {
56
- const computedSrcset = extra.baseWidth != null ? buildSrcset([
57
- {
58
- url: node.url,
59
- w: extra.baseWidth
60
- },
61
- {
62
- url: node.url2x,
63
- w: extra.baseWidth * 2
64
- },
65
- {
66
- url: node.url3x,
67
- w: extra.baseWidth * 3
68
- }
69
- ]) : null;
78
+ const computedSrcset = extra.baseWidth != null ? ladderSrcset(node, extra.baseWidth) : null;
70
79
  return {
71
80
  kind: "image",
72
81
  id: node.id ?? "",
@@ -106,29 +115,31 @@ function scaled(t, scale) {
106
115
  scale
107
116
  };
108
117
  }
118
+ function widened(t) {
119
+ if (!t?.maxWidth) return null;
120
+ const { scale: _, ...rest } = t;
121
+ return {
122
+ ...rest,
123
+ maxWidth: Math.round(t.maxWidth * 1.5),
124
+ ...t.maxHeight ? { maxHeight: Math.round(t.maxHeight * 1.5) } : {}
125
+ };
126
+ }
109
127
  const fetchFileById = memoTTL(async ({ id, transform }) => {
110
128
  const node = (await request(FileByIdQuery, {
111
129
  id,
112
130
  transform: transform ?? null,
131
+ transform1_5x: widened(transform),
113
132
  transform2x: scaled(transform, 2),
114
133
  transform3x: scaled(transform, 3)
115
134
  })).node;
116
135
  if (!node) return null;
117
136
  const baseWidth = transform?.maxWidth;
118
- return toFileInfo(node, { srcset: node.__typename === "ShopMediaImage" && baseWidth != null ? buildSrcset([
119
- {
120
- url: node.url,
121
- w: baseWidth
122
- },
123
- {
124
- url: node.url2x,
125
- w: baseWidth * 2
126
- },
127
- {
128
- url: node.url3x,
129
- w: baseWidth * 3
130
- }
131
- ]) : null });
137
+ return toFileInfo(node, { srcset: node.__typename === "ShopMediaImage" && baseWidth != null ? ladderSrcset({
138
+ url: node.url,
139
+ url1_5x: node.url1_5x,
140
+ url2x: node.url2x,
141
+ url3x: node.url3x
142
+ }, baseWidth) : null });
132
143
  }, {
133
144
  key: ({ id, transform }) => `${id}|${transform ? JSON.stringify(transform) : ""}`,
134
145
  version: getContentVersion
package/dist/shop.d.ts CHANGED
@@ -27,6 +27,7 @@ declare const ShopQuery: import("gql.tada").TadaDocumentNode<{
27
27
  turnstileSitekey: string | null;
28
28
  } | null;
29
29
  primaryLocale: string;
30
+ status: string;
30
31
  taxesIncluded: boolean;
31
32
  timezone: string;
32
33
  turnstileSitekey: string | null;
package/dist/shop.js CHANGED
@@ -8,6 +8,7 @@ const ShopQuery = graphql(`
8
8
  id
9
9
  name
10
10
  handle
11
+ status
11
12
  currency
12
13
  timezone
13
14
  primaryLocale
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shop-on/theme-server",
3
- "version": "0.3.0",
3
+ "version": "0.5.0",
4
4
  "license": "SEE LICENSE IN dist/LICENSE.md",
5
5
  "files": [
6
6
  "dist"
@@ -11,6 +11,7 @@
11
11
  "./activity": "./dist/activity.js",
12
12
  "./analytics-proxy": "./dist/analytics-proxy.js",
13
13
  "./analytics-script-proxy": "./dist/analytics-script-proxy.js",
14
+ "./availability": "./dist/availability.js",
14
15
  "./blog": "./dist/blog.js",
15
16
  "./cache": "./dist/cache.js",
16
17
  "./canonical-host": "./dist/canonical-host.js",
@@ -58,8 +59,8 @@
58
59
  "graphql": "17.0.2",
59
60
  "graphql-request": "7.4.0",
60
61
  "liquidjs": "10.30.0",
61
- "@shop-on/customer-runtime": "0.3.0",
62
- "@shop-on/theme-runtime": "0.3.0"
62
+ "@shop-on/customer-runtime": "0.5.0",
63
+ "@shop-on/theme-runtime": "0.5.0"
63
64
  },
64
65
  "devDependencies": {
65
66
  "@tanstack/react-start": "1.169.3",