@shop-on/theme-server 0.5.0 → 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/dist/account.js +7 -4
- package/dist/analytics-proxy.d.ts +2 -1
- package/dist/analytics-proxy.js +2 -1
- package/dist/blog.d.ts +26 -0
- package/dist/blog.js +65 -1
- package/dist/cart.d.ts +7 -7
- package/dist/checkout.js +1 -1
- package/dist/collection.d.ts +41 -4
- package/dist/collection.js +66 -5
- package/dist/content-version.d.ts +12 -6
- package/dist/content-version.js +25 -4
- package/dist/graphql-env.d.ts +525 -29
- package/dist/graphql.js +8 -2
- package/dist/marketing-cookies.d.ts +6 -4
- package/dist/marketing-cookies.js +35 -14
- package/dist/metafields.d.ts +7 -0
- package/dist/metafields.js +33 -0
- package/dist/page.d.ts +15 -0
- package/dist/page.js +35 -1
- package/dist/product.d.ts +18 -1
- package/dist/product.js +48 -34
- package/dist/recommendations.d.ts +3 -0
- package/dist/recommendations.js +8 -3
- package/dist/schema.storefront.graphql +499 -20
- package/dist/search.d.ts +3 -0
- package/dist/search.js +5 -0
- package/dist/structured-data.d.ts +47 -0
- package/dist/structured-data.js +110 -0
- package/package.json +4 -3
package/dist/search.d.ts
CHANGED
|
@@ -37,6 +37,9 @@ type FetchProductSearchPageInput = {
|
|
|
37
37
|
includeOptions?: boolean;
|
|
38
38
|
includeVariants?: boolean;
|
|
39
39
|
locale?: string;
|
|
40
|
+
/** Storefront-visible metafields (`"namespace.key"`) each card carries, for
|
|
41
|
+
* the `closest_product_metafield` sources bound on it. Omitted ⇒ none. */
|
|
42
|
+
metafieldKeys?: readonly string[];
|
|
40
43
|
};
|
|
41
44
|
/** Server-driven /search page. Returns `null` for blank query (caller
|
|
42
45
|
* route renders the empty-prompt UI) or on GraphQL error. Uncached because
|
package/dist/search.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { graphql, request } from "./graphql.js";
|
|
2
2
|
import { toFileInfo } from "./files.js";
|
|
3
|
+
import { metafieldKeysVariables } from "./product.js";
|
|
3
4
|
import { CollectionImageFragment, CollectionProductNodeFragment, toCollectionProduct, toPriceBounds, variantsVariable } from "./collection.js";
|
|
4
5
|
//#region src/search.ts
|
|
5
6
|
const SEARCH_PAGE_SIZE = 24;
|
|
@@ -10,6 +11,9 @@ const FetchProductSearchPageQuery = graphql(`
|
|
|
10
11
|
$mediaFirst: Int!
|
|
11
12
|
$includeOptions: Boolean!
|
|
12
13
|
$includeVariants: Boolean = false
|
|
14
|
+
$withMetafields: Boolean = false
|
|
15
|
+
$metafieldFirst: Int = 0
|
|
16
|
+
$metafieldKeys: [MetafieldKeyInput!] = []
|
|
13
17
|
$after: String
|
|
14
18
|
$filter: CollectionProductFilterInput
|
|
15
19
|
$sort: ProductSearchSortEnum
|
|
@@ -81,6 +85,7 @@ const fetchProductSearchPage = async (data) => {
|
|
|
81
85
|
mediaFirst,
|
|
82
86
|
includeOptions,
|
|
83
87
|
...variantsVariable(data.includeVariants),
|
|
88
|
+
...metafieldKeysVariables(data.metafieldKeys),
|
|
84
89
|
after: after ?? null,
|
|
85
90
|
filter: filter ?? null,
|
|
86
91
|
sort: sort ?? null
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { ResultOf } from "./graphql.js";
|
|
2
|
+
import { OrganizationSchemaSource, ReturnPolicySchemaSource } from "@shop-on/theme-runtime/template-types";
|
|
3
|
+
//#region src/structured-data.d.ts
|
|
4
|
+
declare const ShopStructuredDataQuery: import("gql.tada").TadaDocumentNode<{
|
|
5
|
+
shop: {
|
|
6
|
+
branding: {
|
|
7
|
+
logo: {
|
|
8
|
+
url: string | null;
|
|
9
|
+
} | null;
|
|
10
|
+
} | null;
|
|
11
|
+
countryCode: string | null;
|
|
12
|
+
refundPolicy: {
|
|
13
|
+
url: string;
|
|
14
|
+
} | null;
|
|
15
|
+
returnPolicy: {
|
|
16
|
+
configured: boolean;
|
|
17
|
+
flatRateAmount: {
|
|
18
|
+
amount: string;
|
|
19
|
+
currencyCode: "CNY" | "EUR" | "GBP" | "JPY" | "KRW" | "MNT" | "RUB" | "USD";
|
|
20
|
+
} | null;
|
|
21
|
+
restockingFeePercentage: number | null;
|
|
22
|
+
returnShippingCost: "CUSTOMER_PROVIDED" | "FLAT_RATE" | "FREE";
|
|
23
|
+
returnWindowDays: number | null;
|
|
24
|
+
};
|
|
25
|
+
} | null;
|
|
26
|
+
}, {}, void>;
|
|
27
|
+
type ShopRecord = NonNullable<ResultOf<typeof ShopStructuredDataQuery>["shop"]>;
|
|
28
|
+
/**
|
|
29
|
+
* The shop's return policy as schema.org reads it, for one product or (with
|
|
30
|
+
* `finalSale` false) the shop as a whole. Undefined until the merchant saves a
|
|
31
|
+
* policy: the defaults an unconfigured shop reads back ("unlimited window,
|
|
32
|
+
* free returns") are no promise the merchant made, and publishing them would
|
|
33
|
+
* tell Google and every agent exactly that. Also undefined without a country,
|
|
34
|
+
* which Google requires.
|
|
35
|
+
*/
|
|
36
|
+
export declare function returnPolicySource(shop: ShopRecord, { finalSale, base }: {
|
|
37
|
+
finalSale: boolean;
|
|
38
|
+
base: string;
|
|
39
|
+
}): ReturnPolicySchemaSource | undefined;
|
|
40
|
+
/** A product's return policy: the shop's, or "not permitted" when the
|
|
41
|
+
* product is final sale (`ShopProduct.finalSale`). */
|
|
42
|
+
export declare function fetchProductReturnPolicy(finalSale: boolean, base: string): Promise<ReturnPolicySchemaSource | undefined>;
|
|
43
|
+
/** The homepage's Organization node. Undefined without a name or origin —
|
|
44
|
+
* schema.org needs both and the homepage then emits no node at all. */
|
|
45
|
+
export declare function fetchOrganizationSchema(name: string, base: string): Promise<OrganizationSchemaSource | undefined>;
|
|
46
|
+
//#endregion
|
|
47
|
+
export {};
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { memoTTL } from "./cache.js";
|
|
2
|
+
import { graphql, request } from "./graphql.js";
|
|
3
|
+
import { getContentVersion } from "./content-version.js";
|
|
4
|
+
//#region src/structured-data.ts
|
|
5
|
+
const ShopStructuredDataQuery = graphql(`
|
|
6
|
+
query StorefrontShopStructuredData {
|
|
7
|
+
shop {
|
|
8
|
+
countryCode
|
|
9
|
+
branding {
|
|
10
|
+
logo {
|
|
11
|
+
url
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
refundPolicy {
|
|
15
|
+
url
|
|
16
|
+
}
|
|
17
|
+
returnPolicy {
|
|
18
|
+
configured
|
|
19
|
+
returnWindowDays
|
|
20
|
+
restockingFeePercentage
|
|
21
|
+
returnShippingCost
|
|
22
|
+
flatRateAmount {
|
|
23
|
+
amount
|
|
24
|
+
currencyCode
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
`);
|
|
30
|
+
const fetchShopRecord = memoTTL(async () => (await request(ShopStructuredDataQuery)).shop ?? null, {
|
|
31
|
+
key: () => "",
|
|
32
|
+
version: getContentVersion
|
|
33
|
+
});
|
|
34
|
+
async function safeShopRecord() {
|
|
35
|
+
try {
|
|
36
|
+
return await fetchShopRecord();
|
|
37
|
+
} catch (err) {
|
|
38
|
+
console.warn("[structured-data] shop fetch failed:", err);
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
const FEES = {
|
|
43
|
+
FREE: "FreeReturn",
|
|
44
|
+
CUSTOMER_PROVIDED: "ReturnFeesCustomerResponsibility",
|
|
45
|
+
FLAT_RATE: "ReturnShippingFees"
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* The shop's return policy as schema.org reads it, for one product or (with
|
|
49
|
+
* `finalSale` false) the shop as a whole. Undefined until the merchant saves a
|
|
50
|
+
* policy: the defaults an unconfigured shop reads back ("unlimited window,
|
|
51
|
+
* free returns") are no promise the merchant made, and publishing them would
|
|
52
|
+
* tell Google and every agent exactly that. Also undefined without a country,
|
|
53
|
+
* which Google requires.
|
|
54
|
+
*/
|
|
55
|
+
function returnPolicySource(shop, { finalSale, base }) {
|
|
56
|
+
const policy = shop.returnPolicy;
|
|
57
|
+
if (!policy.configured || !shop.countryCode) return void 0;
|
|
58
|
+
const url = shop.refundPolicy && base ? `${base}${shop.refundPolicy.url}` : void 0;
|
|
59
|
+
const country = shop.countryCode;
|
|
60
|
+
if (finalSale) return {
|
|
61
|
+
country,
|
|
62
|
+
category: "NotPermitted",
|
|
63
|
+
...url ? { url } : {}
|
|
64
|
+
};
|
|
65
|
+
const days = policy.returnWindowDays;
|
|
66
|
+
const fee = policy.flatRateAmount;
|
|
67
|
+
const restocking = policy.restockingFeePercentage;
|
|
68
|
+
return {
|
|
69
|
+
country,
|
|
70
|
+
...days ? {
|
|
71
|
+
category: "FiniteReturnWindow",
|
|
72
|
+
days
|
|
73
|
+
} : { category: "UnlimitedWindow" },
|
|
74
|
+
fees: FEES[policy.returnShippingCost],
|
|
75
|
+
...policy.returnShippingCost === "FLAT_RATE" && fee ? { shippingFee: {
|
|
76
|
+
amount: Number(fee.amount),
|
|
77
|
+
currency: fee.currencyCode
|
|
78
|
+
} } : {},
|
|
79
|
+
...restocking ? { restockingFeePercent: restocking } : {},
|
|
80
|
+
...url ? { url } : {}
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
/** A product's return policy: the shop's, or "not permitted" when the
|
|
84
|
+
* product is final sale (`ShopProduct.finalSale`). */
|
|
85
|
+
async function fetchProductReturnPolicy(finalSale, base) {
|
|
86
|
+
const shop = await safeShopRecord();
|
|
87
|
+
return shop ? returnPolicySource(shop, {
|
|
88
|
+
finalSale,
|
|
89
|
+
base
|
|
90
|
+
}) : void 0;
|
|
91
|
+
}
|
|
92
|
+
/** The homepage's Organization node. Undefined without a name or origin —
|
|
93
|
+
* schema.org needs both and the homepage then emits no node at all. */
|
|
94
|
+
async function fetchOrganizationSchema(name, base) {
|
|
95
|
+
if (!name || !base) return void 0;
|
|
96
|
+
const shop = await safeShopRecord();
|
|
97
|
+
const logo = shop?.branding?.logo?.url;
|
|
98
|
+
const returnPolicy = shop ? returnPolicySource(shop, {
|
|
99
|
+
finalSale: false,
|
|
100
|
+
base
|
|
101
|
+
}) : void 0;
|
|
102
|
+
return {
|
|
103
|
+
name,
|
|
104
|
+
url: `${base}/`,
|
|
105
|
+
...logo ? { logo } : {},
|
|
106
|
+
...returnPolicy ? { returnPolicy } : {}
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
//#endregion
|
|
110
|
+
export { fetchOrganizationSchema, fetchProductReturnPolicy, returnPolicySource };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shop-on/theme-server",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"license": "SEE LICENSE IN dist/LICENSE.md",
|
|
5
5
|
"files": [
|
|
6
6
|
"dist"
|
|
@@ -51,6 +51,7 @@
|
|
|
51
51
|
"./shop": "./dist/shop.js",
|
|
52
52
|
"./shop-seo": "./dist/shop-seo.js",
|
|
53
53
|
"./store-availability": "./dist/store-availability.js",
|
|
54
|
+
"./structured-data": "./dist/structured-data.js",
|
|
54
55
|
"./translations": "./dist/translations.js",
|
|
55
56
|
"./package.json": "./package.json"
|
|
56
57
|
},
|
|
@@ -59,8 +60,8 @@
|
|
|
59
60
|
"graphql": "17.0.2",
|
|
60
61
|
"graphql-request": "7.4.0",
|
|
61
62
|
"liquidjs": "10.30.0",
|
|
62
|
-
"@shop-on/
|
|
63
|
-
"@shop-on/
|
|
63
|
+
"@shop-on/theme-runtime": "0.7.0",
|
|
64
|
+
"@shop-on/customer-runtime": "0.7.0"
|
|
64
65
|
},
|
|
65
66
|
"devDependencies": {
|
|
66
67
|
"@tanstack/react-start": "1.169.3",
|