@zappie/sdk 0.1.0 → 0.1.2
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/CHANGELOG.md +25 -0
- package/README.md +55 -313
- package/dist/cart/errors.js +13 -0
- package/dist/cart/errors.js.map +7 -0
- package/dist/cart/index.js +768 -0
- package/dist/cart/index.js.map +7 -0
- package/dist/cart/item.js +12 -1
- package/dist/cart/item.js.map +7 -1
- package/dist/cart/preview.js +32 -0
- package/dist/cart/preview.js.map +7 -0
- package/dist/cart/price.js +25 -0
- package/dist/cart/price.js.map +7 -0
- package/dist/cart/quote-client.js +76 -0
- package/dist/cart/quote-client.js.map +7 -0
- package/dist/cart/quote-response.js +178 -0
- package/dist/cart/quote-response.js.map +7 -0
- package/dist/cart/reconcile.js +108 -0
- package/dist/cart/reconcile.js.map +7 -0
- package/dist/cart/source-menu.js +21 -0
- package/dist/cart/source-menu.js.map +7 -0
- package/dist/cart/storage.js +174 -0
- package/dist/cart/storage.js.map +7 -0
- package/dist/cart/types.js +1 -0
- package/dist/cart/types.js.map +7 -0
- package/dist/cart/validate.js +265 -0
- package/dist/cart/validate.js.map +7 -0
- package/dist/cart/wire.js +42 -0
- package/dist/cart/wire.js.map +7 -0
- package/dist/cart/working-set.js +80 -0
- package/dist/cart/working-set.js.map +7 -0
- package/dist/checkout/client.js +102 -0
- package/dist/checkout/client.js.map +7 -0
- package/dist/checkout/error.js +21 -0
- package/dist/checkout/error.js.map +7 -0
- package/dist/checkout/index.js +13 -1
- package/dist/checkout/index.js.map +7 -1
- package/dist/checkout/readiness.js +36 -0
- package/dist/checkout/readiness.js.map +7 -0
- package/dist/checkout/sdk.js +9 -0
- package/dist/checkout/sdk.js.map +7 -0
- package/dist/checkout/session.js +58 -0
- package/dist/checkout/session.js.map +7 -0
- package/dist/checkout/types.js +1 -0
- package/dist/checkout/types.js.map +7 -0
- package/dist/client.js +253 -0
- package/dist/client.js.map +7 -0
- package/dist/config/index.js +13 -1
- package/dist/config/index.js.map +7 -0
- package/dist/config.js +28 -0
- package/dist/config.js.map +7 -0
- package/dist/customer/client.js +149 -0
- package/dist/customer/client.js.map +7 -0
- package/dist/customer/error.js +20 -0
- package/dist/customer/error.js.map +7 -0
- package/dist/customer/index.js +8 -3
- package/dist/customer/index.js.map +7 -1
- package/dist/customer/navigation.js +45 -0
- package/dist/customer/navigation.js.map +7 -0
- package/dist/customer/react.js +67 -2
- package/dist/customer/react.js.map +7 -1
- package/dist/customer/request.js +37 -0
- package/dist/customer/request.js.map +7 -0
- package/dist/customer/sdk.js +6 -0
- package/dist/customer/sdk.js.map +7 -0
- package/dist/customer/types.js +1 -0
- package/dist/customer/types.js.map +7 -0
- package/dist/customer/watch-order.js +80 -0
- package/dist/customer/watch-order.js.map +7 -0
- package/dist/debug.js +78 -1
- package/dist/debug.js.map +7 -1
- package/dist/errors.js +69 -0
- package/dist/errors.js.map +7 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +29 -1
- package/dist/index.js.map +7 -1
- package/dist/internal/abort.js +29 -0
- package/dist/internal/abort.js.map +7 -0
- package/dist/internal/cache.js +142 -0
- package/dist/internal/cache.js.map +7 -0
- package/dist/internal/customer-http.js +74 -0
- package/dist/internal/customer-http.js.map +7 -0
- package/dist/internal/digest.js +15 -0
- package/dist/internal/digest.js.map +7 -0
- package/dist/internal/emitter.js +27 -0
- package/dist/internal/emitter.js.map +7 -0
- package/dist/internal/money-math.js +30 -0
- package/dist/internal/money-math.js.map +7 -0
- package/dist/internal.js +9 -1
- package/dist/internal.js.map +7 -1
- package/dist/models.js +1 -0
- package/dist/models.js.map +7 -0
- package/dist/money.js +58 -0
- package/dist/money.js.map +7 -0
- package/dist/persist/catalogue.js +206 -2
- package/dist/persist/catalogue.js.map +7 -1
- package/dist/places/client.js +97 -0
- package/dist/places/client.js.map +7 -0
- package/dist/places/error.js +20 -0
- package/dist/places/error.js.map +7 -0
- package/dist/places/index.js +8 -1
- package/dist/places/index.js.map +7 -1
- package/dist/places/request.js +40 -0
- package/dist/places/request.js.map +7 -0
- package/dist/places/sdk.js +10 -0
- package/dist/places/sdk.js.map +7 -0
- package/dist/places/search.js +80 -0
- package/dist/places/search.js.map +7 -0
- package/dist/places/types.js +1 -0
- package/dist/places/types.js.map +7 -0
- package/dist/react/cart.js +30 -0
- package/dist/react/cart.js.map +7 -0
- package/dist/react/hooks.js +91 -0
- package/dist/react/hooks.js.map +7 -0
- package/dist/react/index.js +18 -1
- package/dist/react/index.js.map +7 -1
- package/dist/react/provider.js +34 -0
- package/dist/react/provider.js.map +7 -0
- package/dist/server/index.js +32 -2
- package/dist/server/index.js.map +7 -1
- package/dist/server/storefront-url/build-v1.js +75 -0
- package/dist/server/storefront-url/build-v1.js.map +7 -0
- package/dist/server/storefront-url/build-v2.js +79 -0
- package/dist/server/storefront-url/build-v2.js.map +7 -0
- package/dist/server/storefront-url/build.js +19 -0
- package/dist/server/storefront-url/build.js.map +7 -0
- package/dist/server/storefront-url/contract.js +25 -0
- package/dist/server/storefront-url/contract.js.map +7 -0
- package/dist/server/storefront-url/index.js +16 -0
- package/dist/server/storefront-url/index.js.map +7 -0
- package/dist/server/storefront-url/installation.js +58 -0
- package/dist/server/storefront-url/installation.js.map +7 -0
- package/dist/server/storefront-url/params.js +72 -0
- package/dist/server/storefront-url/params.js.map +7 -0
- package/dist/server/storefront-url/parse-v1.js +135 -0
- package/dist/server/storefront-url/parse-v1.js.map +7 -0
- package/dist/server/storefront-url/parse-v2.js +139 -0
- package/dist/server/storefront-url/parse-v2.js.map +7 -0
- package/dist/server/storefront-url/parse.js +9 -0
- package/dist/server/storefront-url/parse.js.map +7 -0
- package/dist/server/storefront-url/redirect.js +30 -0
- package/dist/server/storefront-url/redirect.js.map +7 -0
- package/dist/server/storefront-url/sign-in.js +17 -0
- package/dist/server/storefront-url/sign-in.js.map +7 -0
- package/dist/server/storefront-url/types.js +55 -0
- package/dist/server/storefront-url/types.js.map +7 -0
- package/dist/server/storefront-url/url.js +61 -0
- package/dist/server/storefront-url/url.js.map +7 -0
- package/dist/server/webhook.js +96 -0
- package/dist/server/webhook.js.map +7 -0
- package/dist/site-config/helpers.js +59 -0
- package/dist/site-config/helpers.js.map +7 -0
- package/dist/site-config/sections.js +105 -0
- package/dist/site-config/sections.js.map +7 -0
- package/dist/site-config/types.js +31 -0
- package/dist/site-config/types.js.map +7 -0
- package/dist/site-config/validate.js +165 -0
- package/dist/site-config/validate.js.map +7 -0
- package/dist/template/index.js +46 -3
- package/dist/template/index.js.map +7 -1
- package/dist/template/react.js +81 -2
- package/dist/template/react.js.map +7 -1
- package/dist/testing/index.js +451 -2
- package/dist/testing/index.js.map +7 -1
- package/dist/transport/category-page.js +21 -2
- package/dist/transport/category-page.js.map +7 -1
- package/dist/transport/mappers.js +326 -0
- package/dist/transport/mappers.js.map +7 -0
- package/dist/transport/rest.js +313 -0
- package/dist/transport/rest.js.map +7 -0
- package/dist/transport/search.js +32 -2
- package/dist/transport/search.js.map +7 -1
- package/dist/transport/selected-menu.js +79 -2
- package/dist/transport/selected-menu.js.map +7 -1
- package/dist/transport/store.js +120 -2
- package/dist/transport/store.js.map +7 -1
- package/dist/transport/types.js +1 -0
- package/dist/transport/types.js.map +7 -0
- package/dist/url/index.js +16 -2
- package/dist/url/index.js.map +7 -1
- package/dist/visits/index.js +62 -1
- package/dist/visits/index.js.map +7 -1
- package/dist/visits/page-views.js +103 -0
- package/dist/visits/page-views.js.map +7 -0
- package/package.json +5 -4
- package/dist/cart/quote.js +0 -1
- package/dist/cart/quote.js.map +0 -1
- package/dist/chunk-I55A5QXJ.js +0 -2
- package/dist/chunk-I55A5QXJ.js.map +0 -1
- package/dist/chunk-VG7NKIKR.js +0 -2
- package/dist/chunk-VG7NKIKR.js.map +0 -1
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
// src/site-config/sections.ts
|
|
2
|
+
import { record, unknownKeys } from "./helpers.js";
|
|
3
|
+
import { DEFAULT_BUDGETS } from "./types.js";
|
|
4
|
+
function nested(value, path, keys, issue, validate) {
|
|
5
|
+
if (value === void 0) {
|
|
6
|
+
return;
|
|
7
|
+
}
|
|
8
|
+
if (!record(value)) {
|
|
9
|
+
issue({
|
|
10
|
+
path,
|
|
11
|
+
code: "invalid_type",
|
|
12
|
+
message: `Expected an object. Set ${path} to an object or remove it.`,
|
|
13
|
+
severity: "error"
|
|
14
|
+
});
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
unknownKeys(value, keys, path, issue);
|
|
18
|
+
validate(value);
|
|
19
|
+
}
|
|
20
|
+
function budgets(value, issue, error) {
|
|
21
|
+
nested(
|
|
22
|
+
value,
|
|
23
|
+
"budgets",
|
|
24
|
+
["jsKbGz", "cssKbGz", "lcpMs", "override"],
|
|
25
|
+
issue,
|
|
26
|
+
(budget) => {
|
|
27
|
+
nested(
|
|
28
|
+
budget.override,
|
|
29
|
+
"budgets.override",
|
|
30
|
+
["reason"],
|
|
31
|
+
issue,
|
|
32
|
+
(override) => {
|
|
33
|
+
if (typeof override.reason !== "string" || !override.reason.trim()) {
|
|
34
|
+
error(
|
|
35
|
+
"budgets.override.reason",
|
|
36
|
+
"invalid_format",
|
|
37
|
+
"Expected a non-blank reason. Explain why the default budget must be raised."
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
);
|
|
42
|
+
for (const key of ["jsKbGz", "cssKbGz", "lcpMs"]) {
|
|
43
|
+
const amount = budget[key];
|
|
44
|
+
if (amount === void 0) {
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
if (typeof amount !== "number" || !Number.isFinite(amount) || amount <= 0) {
|
|
48
|
+
error(
|
|
49
|
+
`budgets.${key}`,
|
|
50
|
+
"invalid_type",
|
|
51
|
+
`Expected a positive finite number. Set budgets.${key} to a number greater than zero.`
|
|
52
|
+
);
|
|
53
|
+
} else if (amount > DEFAULT_BUDGETS[key]) {
|
|
54
|
+
const message = `The ${key} budget exceeds the ${DEFAULT_BUDGETS[key]} default.`;
|
|
55
|
+
if (budget.override === void 0) {
|
|
56
|
+
error(
|
|
57
|
+
`budgets.${key}`,
|
|
58
|
+
"budget_override",
|
|
59
|
+
`${message} Add budgets.override.reason to explain the increase.`
|
|
60
|
+
);
|
|
61
|
+
} else {
|
|
62
|
+
issue({
|
|
63
|
+
path: `budgets.${key}`,
|
|
64
|
+
code: "budget_override",
|
|
65
|
+
message: `${message} Lower it to the default when the exception is no longer needed.`,
|
|
66
|
+
severity: "warning"
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
function analytics(value, issue, error) {
|
|
75
|
+
nested(value, "analytics", ["forward"], issue, (analyticsValue) => {
|
|
76
|
+
nested(
|
|
77
|
+
analyticsValue.forward,
|
|
78
|
+
"analytics.forward",
|
|
79
|
+
["ga4", "metaPixel"],
|
|
80
|
+
issue,
|
|
81
|
+
(forward) => {
|
|
82
|
+
if (forward.ga4 !== void 0 && (typeof forward.ga4 !== "string" || !/^G-[A-Z0-9]+$/.test(forward.ga4))) {
|
|
83
|
+
error(
|
|
84
|
+
"analytics.forward.ga4",
|
|
85
|
+
"invalid_format",
|
|
86
|
+
"Expected a GA4 measurement id beginning with G-. Use a value such as G-XXXXXXXXXX."
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
if (forward.metaPixel !== void 0 && (typeof forward.metaPixel !== "string" || !/^\d+$/.test(forward.metaPixel))) {
|
|
90
|
+
error(
|
|
91
|
+
"analytics.forward.metaPixel",
|
|
92
|
+
"invalid_format",
|
|
93
|
+
"Expected a numeric Meta Pixel id. Use digits only."
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
);
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
export {
|
|
101
|
+
analytics,
|
|
102
|
+
budgets,
|
|
103
|
+
nested
|
|
104
|
+
};
|
|
105
|
+
//# sourceMappingURL=sections.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/site-config/sections.ts"],
|
|
4
|
+
"sourcesContent": ["import { record, unknownKeys } from \"./helpers\";\nimport { DEFAULT_BUDGETS, type ConfigIssue } from \"./types\";\n\ntype AddIssue = (value: ConfigIssue) => void;\ntype AddError = (\n path: string,\n code: ConfigIssue[\"code\"],\n message: string,\n) => void;\n\nexport function nested(\n value: unknown,\n path: string,\n keys: readonly string[],\n issue: AddIssue,\n validate: (value: Record<string, unknown>) => void,\n) {\n if (value === undefined) {\n return;\n }\n if (!record(value)) {\n issue({\n path,\n code: \"invalid_type\",\n message: `Expected an object. Set ${path} to an object or remove it.`,\n severity: \"error\",\n });\n return;\n }\n unknownKeys(value, keys, path, issue);\n validate(value);\n}\n\nexport function budgets(value: unknown, issue: AddIssue, error: AddError) {\n nested(\n value,\n \"budgets\",\n [\"jsKbGz\", \"cssKbGz\", \"lcpMs\", \"override\"],\n issue,\n (budget) => {\n nested(\n budget.override,\n \"budgets.override\",\n [\"reason\"],\n issue,\n (override) => {\n if (typeof override.reason !== \"string\" || !override.reason.trim()) {\n error(\n \"budgets.override.reason\",\n \"invalid_format\",\n \"Expected a non-blank reason. Explain why the default budget must be raised.\",\n );\n }\n },\n );\n for (const key of [\"jsKbGz\", \"cssKbGz\", \"lcpMs\"] as const) {\n const amount = budget[key];\n if (amount === undefined) {\n continue;\n }\n if (\n typeof amount !== \"number\" ||\n !Number.isFinite(amount) ||\n amount <= 0\n ) {\n error(\n `budgets.${key}`,\n \"invalid_type\",\n `Expected a positive finite number. Set budgets.${key} to a number greater than zero.`,\n );\n } else if (amount > DEFAULT_BUDGETS[key]) {\n const message = `The ${key} budget exceeds the ${DEFAULT_BUDGETS[key]} default.`;\n if (budget.override === undefined) {\n error(\n `budgets.${key}`,\n \"budget_override\",\n `${message} Add budgets.override.reason to explain the increase.`,\n );\n } else {\n issue({\n path: `budgets.${key}`,\n code: \"budget_override\",\n message: `${message} Lower it to the default when the exception is no longer needed.`,\n severity: \"warning\",\n });\n }\n }\n }\n },\n );\n}\n\nexport function analytics(value: unknown, issue: AddIssue, error: AddError) {\n nested(value, \"analytics\", [\"forward\"], issue, (analyticsValue) => {\n nested(\n analyticsValue.forward,\n \"analytics.forward\",\n [\"ga4\", \"metaPixel\"],\n issue,\n (forward) => {\n if (\n forward.ga4 !== undefined &&\n (typeof forward.ga4 !== \"string\" ||\n !/^G-[A-Z0-9]+$/.test(forward.ga4))\n ) {\n error(\n \"analytics.forward.ga4\",\n \"invalid_format\",\n \"Expected a GA4 measurement id beginning with G-. Use a value such as G-XXXXXXXXXX.\",\n );\n }\n if (\n forward.metaPixel !== undefined &&\n (typeof forward.metaPixel !== \"string\" ||\n !/^\\d+$/.test(forward.metaPixel))\n ) {\n error(\n \"analytics.forward.metaPixel\",\n \"invalid_format\",\n \"Expected a numeric Meta Pixel id. Use digits only.\",\n );\n }\n },\n );\n });\n}\n"],
|
|
5
|
+
"mappings": ";AAAA,SAAS,QAAQ,mBAAmB;AACpC,SAAS,uBAAyC;AAS3C,SAAS,OACd,OACA,MACA,MACA,OACA,UACA;AACA,MAAI,UAAU,QAAW;AACvB;AAAA,EACF;AACA,MAAI,CAAC,OAAO,KAAK,GAAG;AAClB,UAAM;AAAA,MACJ;AAAA,MACA,MAAM;AAAA,MACN,SAAS,2BAA2B,IAAI;AAAA,MACxC,UAAU;AAAA,IACZ,CAAC;AACD;AAAA,EACF;AACA,cAAY,OAAO,MAAM,MAAM,KAAK;AACpC,WAAS,KAAK;AAChB;AAEO,SAAS,QAAQ,OAAgB,OAAiB,OAAiB;AACxE;AAAA,IACE;AAAA,IACA;AAAA,IACA,CAAC,UAAU,WAAW,SAAS,UAAU;AAAA,IACzC;AAAA,IACA,CAAC,WAAW;AACV;AAAA,QACE,OAAO;AAAA,QACP;AAAA,QACA,CAAC,QAAQ;AAAA,QACT;AAAA,QACA,CAAC,aAAa;AACZ,cAAI,OAAO,SAAS,WAAW,YAAY,CAAC,SAAS,OAAO,KAAK,GAAG;AAClE;AAAA,cACE;AAAA,cACA;AAAA,cACA;AAAA,YACF;AAAA,UACF;AAAA,QACF;AAAA,MACF;AACA,iBAAW,OAAO,CAAC,UAAU,WAAW,OAAO,GAAY;AACzD,cAAM,SAAS,OAAO,GAAG;AACzB,YAAI,WAAW,QAAW;AACxB;AAAA,QACF;AACA,YACE,OAAO,WAAW,YAClB,CAAC,OAAO,SAAS,MAAM,KACvB,UAAU,GACV;AACA;AAAA,YACE,WAAW,GAAG;AAAA,YACd;AAAA,YACA,kDAAkD,GAAG;AAAA,UACvD;AAAA,QACF,WAAW,SAAS,gBAAgB,GAAG,GAAG;AACxC,gBAAM,UAAU,OAAO,GAAG,uBAAuB,gBAAgB,GAAG,CAAC;AACrE,cAAI,OAAO,aAAa,QAAW;AACjC;AAAA,cACE,WAAW,GAAG;AAAA,cACd;AAAA,cACA,GAAG,OAAO;AAAA,YACZ;AAAA,UACF,OAAO;AACL,kBAAM;AAAA,cACJ,MAAM,WAAW,GAAG;AAAA,cACpB,MAAM;AAAA,cACN,SAAS,GAAG,OAAO;AAAA,cACnB,UAAU;AAAA,YACZ,CAAC;AAAA,UACH;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;AAEO,SAAS,UAAU,OAAgB,OAAiB,OAAiB;AAC1E,SAAO,OAAO,aAAa,CAAC,SAAS,GAAG,OAAO,CAAC,mBAAmB;AACjE;AAAA,MACE,eAAe;AAAA,MACf;AAAA,MACA,CAAC,OAAO,WAAW;AAAA,MACnB;AAAA,MACA,CAAC,YAAY;AACX,YACE,QAAQ,QAAQ,WACf,OAAO,QAAQ,QAAQ,YACtB,CAAC,gBAAgB,KAAK,QAAQ,GAAG,IACnC;AACA;AAAA,YACE;AAAA,YACA;AAAA,YACA;AAAA,UACF;AAAA,QACF;AACA,YACE,QAAQ,cAAc,WACrB,OAAO,QAAQ,cAAc,YAC5B,CAAC,QAAQ,KAAK,QAAQ,SAAS,IACjC;AACA;AAAA,YACE;AAAA,YACA;AAAA,YACA;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF,CAAC;AACH;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// src/site-config/types.ts
|
|
2
|
+
var DEFAULT_BUDGETS = {
|
|
3
|
+
jsKbGz: 200,
|
|
4
|
+
cssKbGz: 50,
|
|
5
|
+
lcpMs: 2500
|
|
6
|
+
};
|
|
7
|
+
var GLOBAL_PACKAGE_ALLOWLIST = [
|
|
8
|
+
"react",
|
|
9
|
+
"react-dom",
|
|
10
|
+
"react-router",
|
|
11
|
+
"@react-router/*",
|
|
12
|
+
"@zappie/*",
|
|
13
|
+
"tailwindcss",
|
|
14
|
+
"@tailwindcss/*",
|
|
15
|
+
"clsx",
|
|
16
|
+
"tailwind-merge",
|
|
17
|
+
"class-variance-authority",
|
|
18
|
+
"lucide-react",
|
|
19
|
+
"date-fns",
|
|
20
|
+
"embla-carousel-react",
|
|
21
|
+
"framer-motion/motion",
|
|
22
|
+
"zod",
|
|
23
|
+
"@radix-ui/*",
|
|
24
|
+
"sonner",
|
|
25
|
+
"vaul"
|
|
26
|
+
];
|
|
27
|
+
export {
|
|
28
|
+
DEFAULT_BUDGETS,
|
|
29
|
+
GLOBAL_PACKAGE_ALLOWLIST
|
|
30
|
+
};
|
|
31
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/site-config/types.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Default site budgets: 200 KB gzipped JavaScript, 50 KB gzipped CSS and 2500 ms LCP.\n */\nexport const DEFAULT_BUDGETS = {\n jsKbGz: 200,\n cssKbGz: 50,\n lcpMs: 2500,\n} as const;\n\n/**\n * Package names and patterns allowed globally without a site-specific allowlist entry.\n */\nexport const GLOBAL_PACKAGE_ALLOWLIST = [\n \"react\",\n \"react-dom\",\n \"react-router\",\n \"@react-router/*\",\n \"@zappie/*\",\n \"tailwindcss\",\n \"@tailwindcss/*\",\n \"clsx\",\n \"tailwind-merge\",\n \"class-variance-authority\",\n \"lucide-react\",\n \"date-fns\",\n \"embla-carousel-react\",\n \"framer-motion/motion\",\n \"zod\",\n \"@radix-ui/*\",\n \"sonner\",\n \"vaul\",\n] as const;\n\n/**\n * The store, theme and optional build policies you export from zappie.config.ts.\n */\nexport type ZappieSiteConfig = {\n store: string;\n branches?: string[];\n theme: string;\n prerender?: string[];\n menu?: { hiddenCategories?: string[] };\n packages?: { allow?: string[] };\n outbound?: { allow?: string[] };\n /** Each budget left out uses DEFAULT_BUDGETS. */\n budgets?: {\n jsKbGz?: number;\n cssKbGz?: number;\n lcpMs?: number;\n override?: { reason: string };\n };\n analytics?: { forward?: { ga4?: string; metaPixel?: string } };\n};\n\n/**\n * A configuration diagnostic with its field path, stable code, message and severity.\n */\nexport type ConfigIssue = {\n path: string;\n code:\n | \"invalid_type\"\n | \"missing_required\"\n | \"unknown_key\"\n | \"invalid_format\"\n | \"duplicate\"\n | \"redundant_allowlist\"\n | \"budget_override\";\n message: string;\n severity: \"error\" | \"warning\";\n};\n\n/**\n * Configuration validation results. Warnings alone do not make valid false.\n */\nexport type ConfigValidation = { valid: boolean; issues: ConfigIssue[] };\n"],
|
|
5
|
+
"mappings": ";AAGO,IAAM,kBAAkB;AAAA,EAC7B,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,OAAO;AACT;AAKO,IAAM,2BAA2B;AAAA,EACtC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
// src/site-config/validate.ts
|
|
2
|
+
import {
|
|
3
|
+
globallyAllowed,
|
|
4
|
+
hostname,
|
|
5
|
+
nonblank,
|
|
6
|
+
packageName,
|
|
7
|
+
record,
|
|
8
|
+
unknownKeys
|
|
9
|
+
} from "./helpers.js";
|
|
10
|
+
import { analytics, budgets, nested } from "./sections.js";
|
|
11
|
+
var rootKeys = [
|
|
12
|
+
"store",
|
|
13
|
+
"branches",
|
|
14
|
+
"theme",
|
|
15
|
+
"prerender",
|
|
16
|
+
"menu",
|
|
17
|
+
"packages",
|
|
18
|
+
"outbound",
|
|
19
|
+
"budgets",
|
|
20
|
+
"analytics"
|
|
21
|
+
];
|
|
22
|
+
function validateConfig(input) {
|
|
23
|
+
const issues = [];
|
|
24
|
+
const issue = (value) => issues.push(value);
|
|
25
|
+
const error = (path, code, message) => issue({ path, code, message, severity: "error" });
|
|
26
|
+
if (!record(input)) {
|
|
27
|
+
return {
|
|
28
|
+
valid: false,
|
|
29
|
+
issues: [
|
|
30
|
+
{
|
|
31
|
+
path: "$",
|
|
32
|
+
code: "invalid_type",
|
|
33
|
+
message: "Expected a configuration object. Export an object from zappie.config.ts.",
|
|
34
|
+
severity: "error"
|
|
35
|
+
}
|
|
36
|
+
]
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
unknownKeys(input, rootKeys, "", issue);
|
|
40
|
+
requiredSlug(input.store, "store", error);
|
|
41
|
+
theme(input.theme, error);
|
|
42
|
+
stringList(input.branches, "branches", error, {
|
|
43
|
+
nonempty: true,
|
|
44
|
+
unique: true,
|
|
45
|
+
slug: true
|
|
46
|
+
});
|
|
47
|
+
paths(input.prerender, error);
|
|
48
|
+
nested(input.menu, "menu", ["hiddenCategories"], issue, (value) => {
|
|
49
|
+
stringList(value.hiddenCategories, "menu.hiddenCategories", error);
|
|
50
|
+
});
|
|
51
|
+
nested(input.packages, "packages", ["allow"], issue, (value) => {
|
|
52
|
+
const allow = stringList(value.allow, "packages.allow", error);
|
|
53
|
+
allow?.forEach((name, index) => {
|
|
54
|
+
if (globallyAllowed(name)) {
|
|
55
|
+
issue({
|
|
56
|
+
path: `packages.allow[${index}]`,
|
|
57
|
+
code: "redundant_allowlist",
|
|
58
|
+
message: `${name} is already globally allowed. Remove it from packages.allow.`,
|
|
59
|
+
severity: "warning"
|
|
60
|
+
});
|
|
61
|
+
} else if (!packageName(name)) {
|
|
62
|
+
error(
|
|
63
|
+
`packages.allow[${index}]`,
|
|
64
|
+
"invalid_format",
|
|
65
|
+
"Expected a lowercase npm package name. Use a package name such as lucide-react or @scope/name."
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
nested(input.outbound, "outbound", ["allow"], issue, (value) => {
|
|
71
|
+
const allow = stringList(value.allow, "outbound.allow", error);
|
|
72
|
+
allow?.forEach((host, index) => {
|
|
73
|
+
if (!hostname(host)) {
|
|
74
|
+
error(
|
|
75
|
+
`outbound.allow[${index}]`,
|
|
76
|
+
"invalid_format",
|
|
77
|
+
"Expected a hostname or leading *. subdomain pattern without a scheme, path, port or IP. Use a value such as api.example.com."
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
budgets(input.budgets, issue, error);
|
|
83
|
+
analytics(input.analytics, issue, error);
|
|
84
|
+
return {
|
|
85
|
+
valid: !issues.some((entry) => entry.severity === "error"),
|
|
86
|
+
issues
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
function requiredSlug(value, path, error) {
|
|
90
|
+
if (!nonblank(value)) {
|
|
91
|
+
error(
|
|
92
|
+
path,
|
|
93
|
+
value === void 0 ? "missing_required" : "invalid_type",
|
|
94
|
+
`Expected a non-empty storefront slug. Set ${path} to the slug used by createZappie().`
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
function theme(value, error) {
|
|
99
|
+
if (typeof value !== "string") {
|
|
100
|
+
error(
|
|
101
|
+
"theme",
|
|
102
|
+
value === void 0 ? "missing_required" : "invalid_type",
|
|
103
|
+
"Expected a relative .json path. Set theme to a value such as ./theme.json."
|
|
104
|
+
);
|
|
105
|
+
} else if (!/\.json$/.test(value) || /^(?:[a-z][a-z\d+.-]*:|\/|\\)/i.test(value) || value.includes("\\")) {
|
|
106
|
+
error(
|
|
107
|
+
"theme",
|
|
108
|
+
"invalid_format",
|
|
109
|
+
"Expected a relative .json path. Use a value such as ./theme.json."
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
function stringList(value, path, error, options = {}) {
|
|
114
|
+
if (value === void 0) {
|
|
115
|
+
return void 0;
|
|
116
|
+
}
|
|
117
|
+
if (!Array.isArray(value) || value.some((entry) => typeof entry !== "string")) {
|
|
118
|
+
error(
|
|
119
|
+
path,
|
|
120
|
+
"invalid_type",
|
|
121
|
+
`Expected an array of strings. Set ${path} to string values.`
|
|
122
|
+
);
|
|
123
|
+
return void 0;
|
|
124
|
+
}
|
|
125
|
+
if (options.nonempty && value.length === 0) {
|
|
126
|
+
error(
|
|
127
|
+
path,
|
|
128
|
+
"invalid_format",
|
|
129
|
+
`Expected at least one value. Add a value to ${path} or remove the field.`
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
value.forEach((entry, index) => {
|
|
133
|
+
if (options.slug && !nonblank(entry)) {
|
|
134
|
+
error(
|
|
135
|
+
`${path}[${index}]`,
|
|
136
|
+
"invalid_format",
|
|
137
|
+
"Expected a non-empty branch slug. Remove the empty value or use a branch slug."
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
if (options.unique && new Set(value).size !== value.length) {
|
|
142
|
+
error(
|
|
143
|
+
path,
|
|
144
|
+
"duplicate",
|
|
145
|
+
`Expected unique values. Remove duplicates from ${path}.`
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
return value;
|
|
149
|
+
}
|
|
150
|
+
function paths(value, error) {
|
|
151
|
+
const values = stringList(value, "prerender", error, { unique: true });
|
|
152
|
+
values?.forEach((path, index) => {
|
|
153
|
+
if (!path.startsWith("/")) {
|
|
154
|
+
error(
|
|
155
|
+
`prerender[${index}]`,
|
|
156
|
+
"invalid_format",
|
|
157
|
+
"Expected a route starting with /. Prefix the route with /."
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
export {
|
|
163
|
+
validateConfig
|
|
164
|
+
};
|
|
165
|
+
//# sourceMappingURL=validate.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/site-config/validate.ts"],
|
|
4
|
+
"sourcesContent": ["import {\n globallyAllowed,\n hostname,\n nonblank,\n packageName,\n record,\n unknownKeys,\n} from \"./helpers\";\nimport { analytics, budgets, nested } from \"./sections\";\nimport type { ConfigIssue, ConfigValidation } from \"./types\";\n\nconst rootKeys = [\n \"store\",\n \"branches\",\n \"theme\",\n \"prerender\",\n \"menu\",\n \"packages\",\n \"outbound\",\n \"budgets\",\n \"analytics\",\n] as const;\n\n/**\n * Validate a site configuration and collect errors and warnings without changing it.\n * Checks required fields, unknown keys, allowlists, budgets and analytics settings.\n *\n * @param input - The configuration value to inspect.\n * @returns Diagnostics and a valid flag that is true when there are no errors.\n */\nexport function validateConfig(input: unknown): ConfigValidation {\n const issues: ConfigIssue[] = [];\n const issue = (value: ConfigIssue) => issues.push(value);\n const error = (path: string, code: ConfigIssue[\"code\"], message: string) =>\n issue({ path, code, message, severity: \"error\" });\n if (!record(input)) {\n return {\n valid: false,\n issues: [\n {\n path: \"$\",\n code: \"invalid_type\",\n message:\n \"Expected a configuration object. Export an object from zappie.config.ts.\",\n severity: \"error\",\n },\n ],\n };\n }\n unknownKeys(input, rootKeys, \"\", issue);\n requiredSlug(input.store, \"store\", error);\n theme(input.theme, error);\n stringList(input.branches, \"branches\", error, {\n nonempty: true,\n unique: true,\n slug: true,\n });\n paths(input.prerender, error);\n nested(input.menu, \"menu\", [\"hiddenCategories\"], issue, (value) => {\n stringList(value.hiddenCategories, \"menu.hiddenCategories\", error);\n });\n nested(input.packages, \"packages\", [\"allow\"], issue, (value) => {\n const allow = stringList(value.allow, \"packages.allow\", error);\n allow?.forEach((name, index) => {\n if (globallyAllowed(name)) {\n issue({\n path: `packages.allow[${index}]`,\n code: \"redundant_allowlist\",\n message: `${name} is already globally allowed. Remove it from packages.allow.`,\n severity: \"warning\",\n });\n } else if (!packageName(name)) {\n error(\n `packages.allow[${index}]`,\n \"invalid_format\",\n \"Expected a lowercase npm package name. Use a package name such as lucide-react or @scope/name.\",\n );\n }\n });\n });\n nested(input.outbound, \"outbound\", [\"allow\"], issue, (value) => {\n const allow = stringList(value.allow, \"outbound.allow\", error);\n allow?.forEach((host, index) => {\n if (!hostname(host)) {\n error(\n `outbound.allow[${index}]`,\n \"invalid_format\",\n \"Expected a hostname or leading *. subdomain pattern without a scheme, path, port or IP. Use a value such as api.example.com.\",\n );\n }\n });\n });\n budgets(input.budgets, issue, error);\n analytics(input.analytics, issue, error);\n return {\n valid: !issues.some((entry) => entry.severity === \"error\"),\n issues,\n };\n}\n\ntype AddError = (\n path: string,\n code: ConfigIssue[\"code\"],\n message: string,\n) => void;\n\nfunction requiredSlug(value: unknown, path: string, error: AddError) {\n if (!nonblank(value)) {\n error(\n path,\n value === undefined ? \"missing_required\" : \"invalid_type\",\n `Expected a non-empty storefront slug. Set ${path} to the slug used by createZappie().`,\n );\n }\n}\n\nfunction theme(value: unknown, error: AddError) {\n if (typeof value !== \"string\") {\n error(\n \"theme\",\n value === undefined ? \"missing_required\" : \"invalid_type\",\n \"Expected a relative .json path. Set theme to a value such as ./theme.json.\",\n );\n } else if (\n !/\\.json$/.test(value) ||\n /^(?:[a-z][a-z\\d+.-]*:|\\/|\\\\)/i.test(value) ||\n value.includes(\"\\\\\")\n ) {\n error(\n \"theme\",\n \"invalid_format\",\n \"Expected a relative .json path. Use a value such as ./theme.json.\",\n );\n }\n}\n\nfunction stringList(\n value: unknown,\n path: string,\n error: AddError,\n options: { nonempty?: boolean; unique?: boolean; slug?: boolean } = {},\n) {\n if (value === undefined) {\n return undefined;\n }\n if (\n !Array.isArray(value) ||\n value.some((entry) => typeof entry !== \"string\")\n ) {\n error(\n path,\n \"invalid_type\",\n `Expected an array of strings. Set ${path} to string values.`,\n );\n return undefined;\n }\n if (options.nonempty && value.length === 0) {\n error(\n path,\n \"invalid_format\",\n `Expected at least one value. Add a value to ${path} or remove the field.`,\n );\n }\n value.forEach((entry, index) => {\n if (options.slug && !nonblank(entry)) {\n error(\n `${path}[${index}]`,\n \"invalid_format\",\n \"Expected a non-empty branch slug. Remove the empty value or use a branch slug.\",\n );\n }\n });\n if (options.unique && new Set(value).size !== value.length) {\n error(\n path,\n \"duplicate\",\n `Expected unique values. Remove duplicates from ${path}.`,\n );\n }\n return value;\n}\n\nfunction paths(value: unknown, error: AddError) {\n const values = stringList(value, \"prerender\", error, { unique: true });\n values?.forEach((path, index) => {\n if (!path.startsWith(\"/\")) {\n error(\n `prerender[${index}]`,\n \"invalid_format\",\n \"Expected a route starting with /. Prefix the route with /.\",\n );\n }\n });\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
package/dist/template/index.js
CHANGED
|
@@ -1,3 +1,46 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
// src/template/index.ts
|
|
2
|
+
function templateSettings(defaults, saved, preview, mode) {
|
|
3
|
+
if (mode === "review") {
|
|
4
|
+
return { ...defaults, ...preview ?? {} };
|
|
5
|
+
}
|
|
6
|
+
if (mode === "draft") {
|
|
7
|
+
return { ...defaults, ...saved, ...preview ?? {} };
|
|
8
|
+
}
|
|
9
|
+
return { ...defaults, ...saved };
|
|
10
|
+
}
|
|
11
|
+
function settingsFromMessage(data) {
|
|
12
|
+
if (!data || typeof data !== "object") {
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
const message = data;
|
|
16
|
+
if (message.zappie !== "settings" || !message.values || typeof message.values !== "object" || Array.isArray(message.values)) {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
const values = {};
|
|
20
|
+
for (const [key, value] of Object.entries(message.values)) {
|
|
21
|
+
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
|
|
22
|
+
values[key] = value;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return values;
|
|
26
|
+
}
|
|
27
|
+
var IMAGE_WIDTHS = [320, 480, 640, 960, 1280, 1600];
|
|
28
|
+
function resized(src, width, quality = 80) {
|
|
29
|
+
if (!/^https?:\/\//.test(src)) {
|
|
30
|
+
return src;
|
|
31
|
+
}
|
|
32
|
+
return `/cdn-cgi/image/width=${width},quality=${quality},format=auto,fit=scale-down/${src}`;
|
|
33
|
+
}
|
|
34
|
+
function srcSet(src, quality) {
|
|
35
|
+
return IMAGE_WIDTHS.map(
|
|
36
|
+
(width) => `${resized(src, width, quality)} ${width}w`
|
|
37
|
+
).join(", ");
|
|
38
|
+
}
|
|
39
|
+
export {
|
|
40
|
+
IMAGE_WIDTHS,
|
|
41
|
+
resized,
|
|
42
|
+
settingsFromMessage,
|
|
43
|
+
srcSet,
|
|
44
|
+
templateSettings
|
|
45
|
+
};
|
|
46
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1,7 @@
|
|
|
1
|
-
{
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/template/index.ts"],
|
|
4
|
+
"sourcesContent": ["/** A value accepted by a template manifest and the live preview bridge. */\nexport type TemplateSettingValue = string | number | boolean;\n\n/** Where a template is running. Live sites never accept preview messages. */\nexport type TemplateMode = \"site\" | \"draft\" | \"review\";\n\n/** Merge defaults, saved settings, and preview values for the current mode. */\nexport function templateSettings<T extends Record<string, unknown>>(\n defaults: T,\n saved: Record<string, TemplateSettingValue> | undefined,\n preview: Record<string, TemplateSettingValue> | null,\n mode: TemplateMode,\n): T {\n if (mode === \"review\") {\n return { ...defaults, ...(preview ?? {}) };\n }\n if (mode === \"draft\") {\n return { ...defaults, ...saved, ...(preview ?? {}) };\n }\n return { ...defaults, ...saved };\n}\n\n/** Read primitive setting values from an editor message. */\nexport function settingsFromMessage(\n data: unknown,\n): Record<string, TemplateSettingValue> | null {\n if (!data || typeof data !== \"object\") {\n return null;\n }\n const message = data as { zappie?: unknown; values?: unknown };\n if (\n message.zappie !== \"settings\" ||\n !message.values ||\n typeof message.values !== \"object\" ||\n Array.isArray(message.values)\n ) {\n return null;\n }\n const values: Record<string, TemplateSettingValue> = {};\n for (const [key, value] of Object.entries(message.values)) {\n if (\n typeof value === \"string\" ||\n typeof value === \"number\" ||\n typeof value === \"boolean\"\n ) {\n values[key] = value;\n }\n }\n return values;\n}\n\n/** Cloudflare image widths shared by template sites. */\nexport const IMAGE_WIDTHS = [320, 480, 640, 960, 1280, 1600] as const;\n\n/** Build a same-origin Cloudflare resize URL for an external image. */\nexport function resized(src: string, width: number, quality = 80): string {\n if (!/^https?:\\/\\//.test(src)) {\n return src;\n }\n return `/cdn-cgi/image/width=${width},quality=${quality},format=auto,fit=scale-down/${src}`;\n}\n\n/** Build the responsive Cloudflare image candidates used by a template. */\nexport function srcSet(src: string, quality?: number): string {\n return IMAGE_WIDTHS.map(\n (width) => `${resized(src, width, quality)} ${width}w`,\n ).join(\", \");\n}\n"],
|
|
5
|
+
"mappings": ";AAOO,SAAS,iBACd,UACA,OACA,SACA,MACG;AACH,MAAI,SAAS,UAAU;AACrB,WAAO,EAAE,GAAG,UAAU,GAAI,WAAW,CAAC,EAAG;AAAA,EAC3C;AACA,MAAI,SAAS,SAAS;AACpB,WAAO,EAAE,GAAG,UAAU,GAAG,OAAO,GAAI,WAAW,CAAC,EAAG;AAAA,EACrD;AACA,SAAO,EAAE,GAAG,UAAU,GAAG,MAAM;AACjC;AAGO,SAAS,oBACd,MAC6C;AAC7C,MAAI,CAAC,QAAQ,OAAO,SAAS,UAAU;AACrC,WAAO;AAAA,EACT;AACA,QAAM,UAAU;AAChB,MACE,QAAQ,WAAW,cACnB,CAAC,QAAQ,UACT,OAAO,QAAQ,WAAW,YAC1B,MAAM,QAAQ,QAAQ,MAAM,GAC5B;AACA,WAAO;AAAA,EACT;AACA,QAAM,SAA+C,CAAC;AACtD,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,QAAQ,MAAM,GAAG;AACzD,QACE,OAAO,UAAU,YACjB,OAAO,UAAU,YACjB,OAAO,UAAU,WACjB;AACA,aAAO,GAAG,IAAI;AAAA,IAChB;AAAA,EACF;AACA,SAAO;AACT;AAGO,IAAM,eAAe,CAAC,KAAK,KAAK,KAAK,KAAK,MAAM,IAAI;AAGpD,SAAS,QAAQ,KAAa,OAAe,UAAU,IAAY;AACxE,MAAI,CAAC,eAAe,KAAK,GAAG,GAAG;AAC7B,WAAO;AAAA,EACT;AACA,SAAO,wBAAwB,KAAK,YAAY,OAAO,+BAA+B,GAAG;AAC3F;AAGO,SAAS,OAAO,KAAa,SAA0B;AAC5D,SAAO,aAAa;AAAA,IAClB,CAAC,UAAU,GAAG,QAAQ,KAAK,OAAO,OAAO,CAAC,IAAI,KAAK;AAAA,EACrD,EAAE,KAAK,IAAI;AACb;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
package/dist/template/react.js
CHANGED
|
@@ -1,3 +1,82 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
|
|
3
|
+
// src/template/react.tsx
|
|
4
|
+
import { useStore } from "@zappie/sdk/react";
|
|
5
|
+
import {
|
|
6
|
+
settingsFromMessage,
|
|
7
|
+
templateSettings
|
|
8
|
+
} from "@zappie/sdk/template";
|
|
9
|
+
import {
|
|
10
|
+
createContext,
|
|
11
|
+
useContext,
|
|
12
|
+
useEffect,
|
|
13
|
+
useMemo,
|
|
14
|
+
useState
|
|
15
|
+
} from "react";
|
|
16
|
+
import { jsx } from "react/jsx-runtime";
|
|
17
|
+
var Context = createContext({ mode: "site", preview: null });
|
|
18
|
+
function TemplateSettingsProvider({
|
|
19
|
+
mode,
|
|
20
|
+
children
|
|
21
|
+
}) {
|
|
22
|
+
const [preview, setPreview] = useState(null);
|
|
23
|
+
useEffect(() => {
|
|
24
|
+
setPreview(null);
|
|
25
|
+
if (mode === "site") {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
const send = (message2) => window.parent.postMessage(message2, "*");
|
|
29
|
+
const route = () => send({ zappie: "route", path: location.pathname + location.search });
|
|
30
|
+
const scroll = () => send({
|
|
31
|
+
zappie: "scroll",
|
|
32
|
+
y: window.scrollY,
|
|
33
|
+
height: document.documentElement.scrollHeight
|
|
34
|
+
});
|
|
35
|
+
const message = (event) => {
|
|
36
|
+
const values = settingsFromMessage(event.data);
|
|
37
|
+
if (values) {
|
|
38
|
+
setPreview(values);
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
const pushState = history.pushState;
|
|
42
|
+
const replaceState = history.replaceState;
|
|
43
|
+
history.pushState = function(...args) {
|
|
44
|
+
pushState.apply(this, args);
|
|
45
|
+
route();
|
|
46
|
+
};
|
|
47
|
+
history.replaceState = function(...args) {
|
|
48
|
+
replaceState.apply(this, args);
|
|
49
|
+
route();
|
|
50
|
+
};
|
|
51
|
+
window.addEventListener("message", message);
|
|
52
|
+
window.addEventListener("popstate", route);
|
|
53
|
+
window.addEventListener("scroll", scroll, { passive: true });
|
|
54
|
+
send({ zappie: "ready" });
|
|
55
|
+
route();
|
|
56
|
+
scroll();
|
|
57
|
+
return () => {
|
|
58
|
+
window.removeEventListener("message", message);
|
|
59
|
+
window.removeEventListener("popstate", route);
|
|
60
|
+
window.removeEventListener("scroll", scroll);
|
|
61
|
+
history.pushState = pushState;
|
|
62
|
+
history.replaceState = replaceState;
|
|
63
|
+
};
|
|
64
|
+
}, [mode]);
|
|
65
|
+
const value = useMemo(() => ({ mode, preview }), [mode, preview]);
|
|
66
|
+
return /* @__PURE__ */ jsx(Context.Provider, { value, children });
|
|
67
|
+
}
|
|
68
|
+
function useTemplateSettings(defaults) {
|
|
69
|
+
const store = useStore();
|
|
70
|
+
const { mode, preview } = useContext(Context);
|
|
71
|
+
return templateSettings(
|
|
72
|
+
defaults,
|
|
73
|
+
store.data?.templateSettings,
|
|
74
|
+
preview,
|
|
75
|
+
mode
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
export {
|
|
79
|
+
TemplateSettingsProvider,
|
|
80
|
+
useTemplateSettings
|
|
81
|
+
};
|
|
82
|
+
//# sourceMappingURL=react.js.map
|
|
@@ -1 +1,7 @@
|
|
|
1
|
-
{
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/template/react.tsx"],
|
|
4
|
+
"sourcesContent": ["\"use client\";\nimport { useStore } from \"@zappie/sdk/react\";\nimport {\n settingsFromMessage,\n templateSettings,\n type TemplateMode,\n type TemplateSettingValue,\n} from \"@zappie/sdk/template\";\nimport {\n createContext,\n useContext,\n useEffect,\n useMemo,\n useState,\n type ReactNode,\n} from \"react\";\n\nconst Context = createContext<{\n mode: TemplateMode;\n preview: Record<string, TemplateSettingValue> | null;\n}>({ mode: \"site\", preview: null });\n\n/** Supply the host-controlled template mode and editor bridge to a storefront. */\nexport function TemplateSettingsProvider({\n mode,\n children,\n}: {\n mode: TemplateMode;\n children: ReactNode;\n}) {\n const [preview, setPreview] = useState<Record<\n string,\n TemplateSettingValue\n > | null>(null);\n useEffect(() => {\n setPreview(null);\n if (mode === \"site\") {\n return;\n }\n const send = (message: object) => window.parent.postMessage(message, \"*\");\n const route = () =>\n send({ zappie: \"route\", path: location.pathname + location.search });\n const scroll = () =>\n send({\n zappie: \"scroll\",\n y: window.scrollY,\n height: document.documentElement.scrollHeight,\n });\n const message = (event: MessageEvent) => {\n const values = settingsFromMessage(event.data);\n if (values) {\n setPreview(values);\n }\n };\n const pushState = history.pushState;\n const replaceState = history.replaceState;\n history.pushState = function (...args) {\n pushState.apply(this, args);\n route();\n };\n history.replaceState = function (...args) {\n replaceState.apply(this, args);\n route();\n };\n window.addEventListener(\"message\", message);\n window.addEventListener(\"popstate\", route);\n window.addEventListener(\"scroll\", scroll, { passive: true });\n send({ zappie: \"ready\" });\n route();\n scroll();\n return () => {\n window.removeEventListener(\"message\", message);\n window.removeEventListener(\"popstate\", route);\n window.removeEventListener(\"scroll\", scroll);\n history.pushState = pushState;\n history.replaceState = replaceState;\n };\n }, [mode]);\n const value = useMemo(() => ({ mode, preview }), [mode, preview]);\n return <Context.Provider value={value}>{children}</Context.Provider>;\n}\n\n/** Read effective merchant settings over the defaults declared by a template. */\nexport function useTemplateSettings<T extends Record<string, unknown>>(\n defaults: T,\n): T {\n const store = useStore();\n const { mode, preview } = useContext(Context);\n return templateSettings(\n defaults,\n store.data?.templateSettings,\n preview,\n mode,\n );\n}\n"],
|
|
5
|
+
"mappings": ";;;AACA,SAAS,gBAAgB;AACzB;AAAA,EACE;AAAA,EACA;AAAA,OAGK;AACP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAEK;AAgEE;AA9DT,IAAM,UAAU,cAGb,EAAE,MAAM,QAAQ,SAAS,KAAK,CAAC;AAG3B,SAAS,yBAAyB;AAAA,EACvC;AAAA,EACA;AACF,GAGG;AACD,QAAM,CAAC,SAAS,UAAU,IAAI,SAGpB,IAAI;AACd,YAAU,MAAM;AACd,eAAW,IAAI;AACf,QAAI,SAAS,QAAQ;AACnB;AAAA,IACF;AACA,UAAM,OAAO,CAACA,aAAoB,OAAO,OAAO,YAAYA,UAAS,GAAG;AACxE,UAAM,QAAQ,MACZ,KAAK,EAAE,QAAQ,SAAS,MAAM,SAAS,WAAW,SAAS,OAAO,CAAC;AACrE,UAAM,SAAS,MACb,KAAK;AAAA,MACH,QAAQ;AAAA,MACR,GAAG,OAAO;AAAA,MACV,QAAQ,SAAS,gBAAgB;AAAA,IACnC,CAAC;AACH,UAAM,UAAU,CAAC,UAAwB;AACvC,YAAM,SAAS,oBAAoB,MAAM,IAAI;AAC7C,UAAI,QAAQ;AACV,mBAAW,MAAM;AAAA,MACnB;AAAA,IACF;AACA,UAAM,YAAY,QAAQ;AAC1B,UAAM,eAAe,QAAQ;AAC7B,YAAQ,YAAY,YAAa,MAAM;AACrC,gBAAU,MAAM,MAAM,IAAI;AAC1B,YAAM;AAAA,IACR;AACA,YAAQ,eAAe,YAAa,MAAM;AACxC,mBAAa,MAAM,MAAM,IAAI;AAC7B,YAAM;AAAA,IACR;AACA,WAAO,iBAAiB,WAAW,OAAO;AAC1C,WAAO,iBAAiB,YAAY,KAAK;AACzC,WAAO,iBAAiB,UAAU,QAAQ,EAAE,SAAS,KAAK,CAAC;AAC3D,SAAK,EAAE,QAAQ,QAAQ,CAAC;AACxB,UAAM;AACN,WAAO;AACP,WAAO,MAAM;AACX,aAAO,oBAAoB,WAAW,OAAO;AAC7C,aAAO,oBAAoB,YAAY,KAAK;AAC5C,aAAO,oBAAoB,UAAU,MAAM;AAC3C,cAAQ,YAAY;AACpB,cAAQ,eAAe;AAAA,IACzB;AAAA,EACF,GAAG,CAAC,IAAI,CAAC;AACT,QAAM,QAAQ,QAAQ,OAAO,EAAE,MAAM,QAAQ,IAAI,CAAC,MAAM,OAAO,CAAC;AAChE,SAAO,oBAAC,QAAQ,UAAR,EAAiB,OAAe,UAAS;AACnD;AAGO,SAAS,oBACd,UACG;AACH,QAAM,QAAQ,SAAS;AACvB,QAAM,EAAE,MAAM,QAAQ,IAAI,WAAW,OAAO;AAC5C,SAAO;AAAA,IACL;AAAA,IACA,MAAM,MAAM;AAAA,IACZ;AAAA,IACA;AAAA,EACF;AACF;",
|
|
6
|
+
"names": ["message"]
|
|
7
|
+
}
|