@shop-on/theme-server 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/availability.d.ts +10 -0
- package/dist/availability.js +59 -0
- package/dist/collection.d.ts +1 -0
- package/dist/collection.js +1 -0
- package/dist/files.d.ts +1 -0
- package/dist/files.js +39 -28
- package/dist/shop.d.ts +1 -0
- package/dist/shop.js +1 -0
- package/package.json +4 -3
|
@@ -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 };
|
package/dist/collection.d.ts
CHANGED
|
@@ -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;
|
package/dist/collection.js
CHANGED
|
@@ -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
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 ?
|
|
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 ?
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
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
package/dist/shop.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shop-on/theme-server",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.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.
|
|
62
|
-
"@shop-on/theme-runtime": "0.
|
|
62
|
+
"@shop-on/customer-runtime": "0.4.0",
|
|
63
|
+
"@shop-on/theme-runtime": "0.4.0"
|
|
63
64
|
},
|
|
64
65
|
"devDependencies": {
|
|
65
66
|
"@tanstack/react-start": "1.169.3",
|