@standhigher/besttrack-page-extension 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,9 @@
1
+ import { PageBuilderExtension } from '@standhigher/puck-page-builder/runtime';
2
+
3
+ /**
4
+ * Server-safe contracts for validating persisted documents. They intentionally
5
+ * omit React fields and renderers so Route Handlers never import client code.
6
+ */
7
+ declare const bestTrackDocumentValidationExtensions: PageBuilderExtension[];
8
+
9
+ export { bestTrackDocumentValidationExtensions };
@@ -0,0 +1,122 @@
1
+ import {
2
+ isSafeTrackingPageUrl,
3
+ isShopifyResourceReference,
4
+ isValidTrackingNumber
5
+ } from "./chunk-KD7KFFFK.js";
6
+
7
+ // src/validation.ts
8
+ var textField = { field: "besttrack.validation.text", control: "text" };
9
+ var productsField = { field: "besttrack.validation.products", control: "products" };
10
+ var strictUrlField = {
11
+ field: "besttrack.validation.url",
12
+ control: "url",
13
+ validation: { allowRelativeUrl: false, allowedUrlProtocols: ["https:"], allowLocalhost: true }
14
+ };
15
+ function fields(keys, urlKeys = [], resourceKeys = [], productsKeys = []) {
16
+ return Object.fromEntries(keys.map((key) => [key, productsKeys.includes(key) ? productsField : resourceKeys.includes(key) ? { field: "besttrack.validation.resource" } : urlKeys.includes(key) ? strictUrlField : textField]));
17
+ }
18
+ function block(type, version, keys, variants, validate, urlKeys = [], resourceKeys = [], productsKeys = []) {
19
+ return {
20
+ type,
21
+ version,
22
+ label: type,
23
+ category: "BestTrack",
24
+ targets: ["web"],
25
+ defaultProps: {},
26
+ fields: fields(keys, urlKeys, resourceKeys, productsKeys),
27
+ defaultVariant: variants[0],
28
+ variants: variants.map((id) => ({ id, label: id })),
29
+ validate,
30
+ render: {}
31
+ };
32
+ }
33
+ function requiredString(props, key, issues, maxLength = 160) {
34
+ const value = props[key];
35
+ if (typeof value !== "string" || !value.trim()) issues.push({ path: `props.${key}`, message: "A non-empty value is required." });
36
+ else if (value.length > maxLength) issues.push({ path: `props.${key}`, message: `Must be at most ${maxLength} characters.` });
37
+ }
38
+ function textProps(keys) {
39
+ return (props) => {
40
+ const issues = [];
41
+ keys.forEach(([key, maxLength]) => requiredString(props, key, issues, maxLength));
42
+ return issues;
43
+ };
44
+ }
45
+ function validateSalesQuery(props) {
46
+ const issues = textProps([["heading", 120], ["submitLabel", 80]])(props);
47
+ if (typeof props.defaultTrackingNumber !== "string" || !isValidTrackingNumber(props.defaultTrackingNumber)) {
48
+ issues.push({ path: "props.defaultTrackingNumber", message: "Use 6-64 letters, numbers, hyphens, or underscores. Use a non-sensitive preview placeholder only." });
49
+ }
50
+ if (props.heroImageUrl !== void 0 && !isSafeTrackingPageUrl(props.heroImageUrl)) {
51
+ issues.push({ path: "props.heroImageUrl", message: "Use an HTTPS URL for the merchant-configured hero image." });
52
+ }
53
+ return issues;
54
+ }
55
+ function validateCategories(props) {
56
+ const issues = textProps([["heading", 120]])(props);
57
+ if (!isShopifyResourceReference(props.collection, "collection")) {
58
+ issues.push({ path: "props.collection", message: "Select a Shopify collection through an authorized resource integration." });
59
+ }
60
+ return issues;
61
+ }
62
+ function validateFeaturedProduct(props) {
63
+ const issues = textProps([["heading", 120]])(props);
64
+ if (!isShopifyResourceReference(props.product, "product")) {
65
+ issues.push({ path: "props.product", message: "Select a Shopify product through an authorized resource integration." });
66
+ }
67
+ return issues;
68
+ }
69
+ var readyToGoContracts = [
70
+ block("besttrack.ready-to-go.query", 1, ["heading", "submitLabel", "defaultTrackingNumber", "defaultOrderNumber", "defaultQueryMode", "trackingTabLabel", "orderTabLabel"], ["default"]),
71
+ block("besttrack.ready-to-go.progress", 1, [], ["default"]),
72
+ block("besttrack.ready-to-go.delivery", 1, ["heading", "contentsHeading", "carrierHeading"], ["default", "compact"]),
73
+ block("besttrack.ready-to-go.recommendations", 1, ["heading", "products"], ["default", "grid"], void 0, [], [], ["products"])
74
+ ];
75
+ var brandedContracts = [
76
+ block("besttrack.branded.announcement", 1, ["message", "href"], ["brand", "minimal"], void 0, ["href"]),
77
+ block("besttrack.branded.tracking-experience", 1, ["heading", "submitLabel", "trackAnotherLabel", "defaultTrackingNumber", "defaultOrderNumber", "defaultQueryMode", "trackingTabLabel", "orderTabLabel", "shipmentLabels", "heroImageUrl"], ["brand", "compact"], void 0, ["heroImageUrl"]),
78
+ block("besttrack.branded.query", 1, ["heading", "submitLabel", "defaultTrackingNumber", "defaultOrderNumber", "defaultQueryMode", "trackingTabLabel", "orderTabLabel", "shipmentLabels", "heroImageUrl"], ["brand", "compact"], void 0, ["heroImageUrl"]),
79
+ block("besttrack.branded.order-items", 1, ["heading"], ["brand", "minimal"]),
80
+ block("besttrack.branded.recommendations", 1, ["heading", "hideWhenEmpty"], ["brand", "editorial"]),
81
+ block("besttrack.branded.quick-links", 1, ["heading", "primaryLabel", "primaryHref", "secondaryLabel", "secondaryHref"], ["brand"], void 0, ["primaryHref", "secondaryHref"]),
82
+ block("besttrack.branded.blog", 1, ["heading", "articleTitle", "excerpt", "articleHref", "linkLabel"], ["brand"], void 0, ["articleHref"])
83
+ ];
84
+ var salesContracts = [
85
+ block("besttrack.sales.announcement", 1, ["message"], ["hero", "commerce", "minimal"], textProps([["message", 280]])),
86
+ block("besttrack.sales.query", 1, ["heading", "submitLabel", "defaultTrackingNumber", "defaultOrderNumber", "heroImageUrl"], ["hero", "commerce", "compact"], validateSalesQuery, ["heroImageUrl"]),
87
+ block("besttrack.sales.order-items", 1, ["heading"], ["hero", "commerce"], textProps([["heading", 120]])),
88
+ block("besttrack.sales.other-tracking", 1, ["heading", "emptyMessage"], ["hero", "commerce"], textProps([["heading", 120], ["emptyMessage", 280]])),
89
+ block("besttrack.sales.service-cards", 1, ["heading", "firstTitle", "firstDescription", "secondTitle", "secondDescription"], ["hero", "commerce"], textProps([["heading", 120], ["firstTitle", 120], ["firstDescription", 280], ["secondTitle", 120], ["secondDescription", 280]])),
90
+ block("besttrack.sales.product-categories", 3, ["heading", "collection"], ["hero", "commerce", "grid"], validateCategories, [], ["collection"]),
91
+ block("besttrack.sales.featured-product", 1, ["heading", "product"], ["hero", "commerce"], validateFeaturedProduct, [], ["product"]),
92
+ block("besttrack.sales.recommendations", 1, ["heading"], ["hero", "commerce", "grid"], textProps([["heading", 120]]))
93
+ ];
94
+ function template(id, version, requiredBlocks) {
95
+ return {
96
+ id,
97
+ version,
98
+ name: id,
99
+ target: "web",
100
+ source: "built-in",
101
+ requiredBlocks: requiredBlocks.map((contract) => contract.type),
102
+ create: () => {
103
+ throw new Error("The validation-only template cannot create a PageDocument.");
104
+ }
105
+ };
106
+ }
107
+ function extension(name, contracts, templateVersion, requiredBlocks = contracts) {
108
+ return Object.freeze({
109
+ name,
110
+ version: "0.8.0",
111
+ blocks: contracts,
112
+ templates: [template(name, templateVersion, requiredBlocks)]
113
+ });
114
+ }
115
+ var bestTrackDocumentValidationExtensions = [
116
+ extension("besttrack.ready-to-go", readyToGoContracts, 1),
117
+ extension("besttrack.branded", brandedContracts, 1, brandedContracts.filter((contract) => !["besttrack.branded.query", "besttrack.branded.order-items"].includes(contract.type))),
118
+ extension("besttrack.sales", salesContracts, 3)
119
+ ];
120
+ export {
121
+ bestTrackDocumentValidationExtensions
122
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@standhigher/besttrack-page-extension",
3
- "version": "0.3.0",
3
+ "version": "0.5.0",
4
4
  "description": "BestTrack business extension entry point for @standhigher/puck-page-builder.",
5
5
  "type": "module",
6
6
  "files": [
@@ -13,10 +13,16 @@
13
13
  ".": {
14
14
  "types": "./dist/index.d.ts",
15
15
  "import": "./dist/index.js"
16
+ },
17
+ "./validation": {
18
+ "types": "./dist/validation.d.ts",
19
+ "import": "./dist/validation.js"
16
20
  }
17
21
  },
18
22
  "dependencies": {
19
- "@standhigher/puck-page-builder": "0.9.0"
23
+ "embla-carousel-autoplay": "^8.6.0",
24
+ "embla-carousel-react": "^8.6.0",
25
+ "@standhigher/puck-page-builder": "0.12.0"
20
26
  },
21
27
  "devDependencies": {
22
28
  "tsup": "^8.5.1",
@@ -26,7 +32,7 @@
26
32
  "react": "^18.0.0"
27
33
  },
28
34
  "scripts": {
29
- "build": "tsup src/index.ts --format esm --dts --clean",
35
+ "build": "tsup src/index.ts src/validation.ts --format esm --dts --clean",
30
36
  "typecheck": "tsc --noEmit"
31
37
  }
32
38
  }