@shoppexio/builder-contracts 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/dist/builder-settings.d.ts +11 -666
- package/dist/builder-settings.d.ts.map +1 -1
- package/dist/builder-settings.js +2 -1
- package/dist/canonical-settings.d.ts +18 -0
- package/dist/canonical-settings.d.ts.map +1 -0
- package/dist/canonical-settings.js +106 -0
- package/dist/custom-pages.d.ts +15 -0
- package/dist/custom-pages.d.ts.map +1 -0
- package/dist/custom-pages.js +40 -0
- package/dist/dedicated-pages.d.ts +15 -0
- package/dist/dedicated-pages.d.ts.map +1 -0
- package/dist/dedicated-pages.js +142 -0
- package/dist/events.d.ts +35 -240
- package/dist/events.d.ts.map +1 -1
- package/dist/events.js +7 -0
- package/dist/fields.d.ts +229 -10
- package/dist/fields.d.ts.map +1 -1
- package/dist/fields.js +27 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10 -0
- package/dist/legacy-manifest.d.ts +18 -0
- package/dist/legacy-manifest.d.ts.map +1 -1
- package/dist/legacy-manifest.js +137 -22
- package/dist/migrations.d.ts.map +1 -1
- package/dist/migrations.js +55 -6
- package/dist/persistence.d.ts +7 -0
- package/dist/persistence.d.ts.map +1 -0
- package/dist/persistence.js +58 -0
- package/dist/preview-boot.d.ts +68 -0
- package/dist/preview-boot.d.ts.map +1 -0
- package/dist/preview-boot.js +38 -0
- package/dist/preview-protocol.d.ts +227 -459
- package/dist/preview-protocol.d.ts.map +1 -1
- package/dist/preview-protocol.js +112 -0
- package/dist/preview-session-resolve.d.ts +115 -0
- package/dist/preview-session-resolve.d.ts.map +1 -0
- package/dist/preview-session-resolve.js +25 -0
- package/dist/preview-trusted-origins.d.ts +4 -0
- package/dist/preview-trusted-origins.d.ts.map +1 -0
- package/dist/preview-trusted-origins.js +28 -0
- package/dist/storefront-initial-data-html.d.ts +17 -0
- package/dist/storefront-initial-data-html.d.ts.map +1 -0
- package/dist/storefront-initial-data-html.js +83 -0
- package/dist/storefront-typography-fonts.d.ts +18 -0
- package/dist/storefront-typography-fonts.d.ts.map +1 -0
- package/dist/storefront-typography-fonts.js +89 -0
- package/dist/style-slots.d.ts +50 -152
- package/dist/style-slots.d.ts.map +1 -1
- package/dist/style-slots.js +80 -32
- package/dist/theme-manifest.d.ts +287 -456
- package/dist/theme-manifest.d.ts.map +1 -1
- package/dist/theme-manifest.js +92 -0
- package/dist/theme-schemes.d.ts +10 -0
- package/dist/theme-schemes.d.ts.map +1 -0
- package/dist/theme-schemes.js +25 -0
- package/dist/validation.d.ts +1 -1
- package/dist/validation.d.ts.map +1 -1
- package/dist/validation.js +23 -12
- package/package.json +43 -1
- package/src/builder-contracts.test.ts +416 -3
- package/src/builder-settings.ts +4 -1
- package/src/canonical-settings.ts +156 -0
- package/src/custom-pages.test.ts +74 -0
- package/src/custom-pages.ts +70 -0
- package/src/dedicated-pages.test.ts +88 -0
- package/src/dedicated-pages.ts +173 -0
- package/src/events.ts +8 -0
- package/src/fields.ts +30 -0
- package/src/index.ts +10 -0
- package/src/legacy-manifest.ts +147 -23
- package/src/migrations.ts +70 -6
- package/src/persistence.ts +77 -0
- package/src/preview-boot.test.ts +72 -0
- package/src/preview-boot.ts +49 -0
- package/src/preview-protocol.test.ts +132 -0
- package/src/preview-protocol.ts +122 -0
- package/src/preview-session-resolve.test.ts +37 -0
- package/src/preview-session-resolve.ts +34 -0
- package/src/preview-trusted-origins.test.ts +24 -0
- package/src/preview-trusted-origins.ts +35 -0
- package/src/storefront-initial-data-html.test.ts +73 -0
- package/src/storefront-initial-data-html.ts +112 -0
- package/src/storefront-typography-fonts.test.ts +48 -0
- package/src/storefront-typography-fonts.ts +108 -0
- package/src/style-slots.ts +102 -34
- package/src/theme-manifest.ts +118 -1
- package/src/theme-schemes.ts +34 -0
- package/src/validation.ts +32 -13
- package/dist/builder-contracts.test.d.ts +0 -2
- package/dist/builder-contracts.test.d.ts.map +0 -1
- package/dist/builder-contracts.test.js +0 -361
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"preview-protocol.d.ts","sourceRoot":"","sources":["../src/preview-protocol.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,QAAQ,CAAC;AAG5B,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;kBAYxB,CAAC;AACZ,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAEtE,eAAO,MAAM,8BAA8B
|
|
1
|
+
{"version":3,"file":"preview-protocol.d.ts","sourceRoot":"","sources":["../src/preview-protocol.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,QAAQ,CAAC;AAG5B,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;kBAYxB,CAAC;AACZ,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAEtE,eAAO,MAAM,8BAA8B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAMhC,CAAC;AAEZ,eAAO,MAAM,0BAA0B;;;;;;;kBAM5B,CAAC;AAEZ,eAAO,MAAM,iCAAiC;;;;;;;;;;;;;;;;;;;;;kBAMnC,CAAC;AAEZ,eAAO,MAAM,gCAAgC;;kBAIlC,CAAC;AAEZ,eAAO,MAAM,qBAAqB;;;EAA8B,CAAC;AACjE,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAEpE;;;;;GAKG;AACH,eAAO,MAAM,sCAAsC;;;;;;kBAKxC,CAAC;AAEZ,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4BAM/B,CAAC;AACH,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAElE,eAAO,MAAM,gCAAgC;;;;;;kBAQlC,CAAC;AAEZ,eAAO,MAAM,0BAA0B;;;;;;;;kBAa5B,CAAC;AAEZ,eAAO,MAAM,4BAA4B;;;kBAK9B,CAAC;AAEZ,eAAO,MAAM,gCAAgC;;;;kBAMlC,CAAC;AAEZ,eAAO,MAAM,mCAAmC;;;;;;;;;;;;;;;;;;;;;kBAMrC,CAAC;AAEZ,eAAO,MAAM,0BAA0B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBA0B5B,CAAC;AAEZ;;;;;GAKG;AACH,eAAO,MAAM,iBAAiB;;;;;kBAOnB,CAAC;AACZ,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAE5D,eAAO,MAAM,8BAA8B;;;;;;;;;;kBAOhC,CAAC;AAEZ;;;;GAIG;AACH,eAAO,MAAM,kCAAkC;;;;;;;;;;kBAOpC,CAAC;AAEZ;;;;GAIG;AACH,eAAO,MAAM,qCAAqC;;;;;;kBAQvC,CAAC;AAEZ,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4BAUhC,CAAC;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC"}
|
package/dist/preview-protocol.js
CHANGED
|
@@ -39,16 +39,47 @@ export const PreviewRequestReadyMessageSchema = z
|
|
|
39
39
|
type: z.literal('REQUEST_READY'),
|
|
40
40
|
})
|
|
41
41
|
.strict();
|
|
42
|
+
export const InteractionModeSchema = z.enum(['edit', 'preview']);
|
|
43
|
+
/**
|
|
44
|
+
* Dashboard tells the runtime which interaction mode is active:
|
|
45
|
+
* - "edit" (default) intercepts clicks for selection.
|
|
46
|
+
* - "preview" lets the storefront react to clicks/links normally so
|
|
47
|
+
* a merchant can test buy-now flows or anchor links.
|
|
48
|
+
*/
|
|
49
|
+
export const PreviewSetInteractionModeMessageSchema = z
|
|
50
|
+
.object({
|
|
51
|
+
type: z.literal('SET_INTERACTION_MODE'),
|
|
52
|
+
mode: InteractionModeSchema,
|
|
53
|
+
})
|
|
54
|
+
.strict();
|
|
42
55
|
export const PreviewMessageSchema = z.discriminatedUnion('type', [
|
|
43
56
|
PreviewApplyStateMessageSchema,
|
|
44
57
|
PreviewReloadMessageSchema,
|
|
45
58
|
PreviewSelectElementMessageSchema,
|
|
46
59
|
PreviewRequestReadyMessageSchema,
|
|
60
|
+
PreviewSetInteractionModeMessageSchema,
|
|
47
61
|
]);
|
|
62
|
+
export const PreviewBootstrapOkResponseSchema = z
|
|
63
|
+
.object({
|
|
64
|
+
type: z.literal('BOOTSTRAP_OK'),
|
|
65
|
+
revision: z.number().int().nonnegative(),
|
|
66
|
+
shopSlug: z.string().min(1),
|
|
67
|
+
shopId: z.string().min(1),
|
|
68
|
+
artifactStale: z.boolean().optional(),
|
|
69
|
+
})
|
|
70
|
+
.strict();
|
|
48
71
|
export const PreviewReadyResponseSchema = z
|
|
49
72
|
.object({
|
|
50
73
|
type: z.literal('READY'),
|
|
51
74
|
revision: z.number().int().nonnegative(),
|
|
75
|
+
health: z
|
|
76
|
+
.object({
|
|
77
|
+
reactMounted: z.literal(true),
|
|
78
|
+
builderRuntimeProvider: z.literal(true),
|
|
79
|
+
protocolVersion: z.literal(2),
|
|
80
|
+
})
|
|
81
|
+
.strict()
|
|
82
|
+
.optional(),
|
|
52
83
|
})
|
|
53
84
|
.strict();
|
|
54
85
|
export const PreviewAppliedResponseSchema = z
|
|
@@ -71,9 +102,90 @@ export const PreviewElementClickedResponseSchema = z
|
|
|
71
102
|
selection: BuilderSelectionSchema,
|
|
72
103
|
})
|
|
73
104
|
.strict();
|
|
105
|
+
export const PreviewErrorResponseSchema = z
|
|
106
|
+
.object({
|
|
107
|
+
type: z.literal('PREVIEW_ERROR'),
|
|
108
|
+
revision: z.number().int().nonnegative().optional(),
|
|
109
|
+
message: z.string().min(1),
|
|
110
|
+
stack: z.string().optional(),
|
|
111
|
+
source: z.enum(['bootstrap', 'error', 'unhandledrejection']).optional(),
|
|
112
|
+
phase: z.enum(['bootstrap', 'runtime']).optional(),
|
|
113
|
+
diagnostics: z
|
|
114
|
+
.object({
|
|
115
|
+
name: z.string().optional(),
|
|
116
|
+
filename: z.string().optional(),
|
|
117
|
+
lineno: z.number().optional(),
|
|
118
|
+
colno: z.number().optional(),
|
|
119
|
+
href: z.string().optional(),
|
|
120
|
+
referrer: z.string().optional(),
|
|
121
|
+
parentOrigin: z.string().optional(),
|
|
122
|
+
previewMode: z.string().optional(),
|
|
123
|
+
userAgent: z.string().optional(),
|
|
124
|
+
localStorage: z.enum(['available', 'blocked', 'unknown']).optional(),
|
|
125
|
+
sessionStorage: z.enum(['available', 'blocked', 'unknown']).optional(),
|
|
126
|
+
historyScrollRestoration: z.enum(['available', 'blocked', 'unknown']).optional(),
|
|
127
|
+
})
|
|
128
|
+
.strict()
|
|
129
|
+
.optional(),
|
|
130
|
+
})
|
|
131
|
+
.strict();
|
|
132
|
+
/**
|
|
133
|
+
* Geometry of a block inside the preview iframe, expressed in the iframe's
|
|
134
|
+
* own coordinate space (viewport-relative). The dashboard converts these
|
|
135
|
+
* coordinates to its own overlay layer when positioning the floating
|
|
136
|
+
* toolbar / drag handle / insert affordances.
|
|
137
|
+
*/
|
|
138
|
+
export const PreviewRectSchema = z
|
|
139
|
+
.object({
|
|
140
|
+
top: z.number().finite(),
|
|
141
|
+
left: z.number().finite(),
|
|
142
|
+
width: z.number().finite(),
|
|
143
|
+
height: z.number().finite(),
|
|
144
|
+
})
|
|
145
|
+
.strict();
|
|
146
|
+
export const PreviewBlockRectResponseSchema = z
|
|
147
|
+
.object({
|
|
148
|
+
type: z.literal('BLOCK_RECT'),
|
|
149
|
+
revision: z.number().int().nonnegative().optional(),
|
|
150
|
+
blockId: z.string().min(1),
|
|
151
|
+
rect: PreviewRectSchema.nullable(),
|
|
152
|
+
})
|
|
153
|
+
.strict();
|
|
154
|
+
/**
|
|
155
|
+
* Preview reports a gap between two consecutive blocks that the user is
|
|
156
|
+
* hovering. The dashboard renders a "+" insert affordance there. When
|
|
157
|
+
* the user moves away from any gap, `index` is null.
|
|
158
|
+
*/
|
|
159
|
+
export const PreviewInserterHoverResponseSchema = z
|
|
160
|
+
.object({
|
|
161
|
+
type: z.literal('INSERTER_HOVER'),
|
|
162
|
+
revision: z.number().int().nonnegative().optional(),
|
|
163
|
+
index: z.number().int().nonnegative().nullable(),
|
|
164
|
+
rect: PreviewRectSchema.nullable(),
|
|
165
|
+
})
|
|
166
|
+
.strict();
|
|
167
|
+
/**
|
|
168
|
+
* Inline-edit commit. The runtime captures contenteditable text on
|
|
169
|
+
* blur/Enter and sends the result back to the dashboard, which writes
|
|
170
|
+
* it to the corresponding block/content path through the store.
|
|
171
|
+
*/
|
|
172
|
+
export const PreviewInlineEditCommitResponseSchema = z
|
|
173
|
+
.object({
|
|
174
|
+
type: z.literal('INLINE_EDIT_COMMIT'),
|
|
175
|
+
revision: z.number().int().nonnegative().optional(),
|
|
176
|
+
blockId: z.string().min(1),
|
|
177
|
+
contentPath: z.string().min(1),
|
|
178
|
+
value: z.string(),
|
|
179
|
+
})
|
|
180
|
+
.strict();
|
|
74
181
|
export const PreviewResponseSchema = z.discriminatedUnion('type', [
|
|
182
|
+
PreviewBootstrapOkResponseSchema,
|
|
75
183
|
PreviewReadyResponseSchema,
|
|
76
184
|
PreviewAppliedResponseSchema,
|
|
77
185
|
PreviewApplyFailedResponseSchema,
|
|
78
186
|
PreviewElementClickedResponseSchema,
|
|
187
|
+
PreviewErrorResponseSchema,
|
|
188
|
+
PreviewBlockRectResponseSchema,
|
|
189
|
+
PreviewInserterHoverResponseSchema,
|
|
190
|
+
PreviewInlineEditCommitResponseSchema,
|
|
79
191
|
]);
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import * as z from 'zod/v4';
|
|
2
|
+
export declare const BuilderPreviewResolveDataSchema: z.ZodObject<{
|
|
3
|
+
sessionId: z.ZodString;
|
|
4
|
+
shopId: z.ZodString;
|
|
5
|
+
shopSlug: z.ZodString;
|
|
6
|
+
themeId: z.ZodString;
|
|
7
|
+
artifactPrefix: z.ZodString;
|
|
8
|
+
builderSettings: z.ZodObject<{
|
|
9
|
+
version: z.ZodLiteral<2>;
|
|
10
|
+
revision: z.ZodNumber;
|
|
11
|
+
theme: z.ZodObject<{
|
|
12
|
+
content: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
13
|
+
layout: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
14
|
+
blocks: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
15
|
+
id: z.ZodString;
|
|
16
|
+
type: z.ZodString;
|
|
17
|
+
variant: z.ZodOptional<z.ZodString>;
|
|
18
|
+
visible: z.ZodDefault<z.ZodBoolean>;
|
|
19
|
+
settings: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
20
|
+
style_overrides: z.ZodOptional<z.ZodType<Partial<Record<import("./style-slots.ts").StyleSlotId, import("./style-slots.ts").StyleSlotValue>>, unknown, z.core.$ZodTypeInternals<Partial<Record<import("./style-slots.ts").StyleSlotId, import("./style-slots.ts").StyleSlotValue>>, unknown>>>;
|
|
21
|
+
}, z.core.$strict>>>;
|
|
22
|
+
}, z.core.$strict>>>;
|
|
23
|
+
style_slots: z.ZodDefault<z.ZodType<Partial<Record<import("./style-slots.ts").StyleSlotId, import("./style-slots.ts").StyleSlotValue>>, unknown, z.core.$ZodTypeInternals<Partial<Record<import("./style-slots.ts").StyleSlotId, import("./style-slots.ts").StyleSlotValue>>, unknown>>>;
|
|
24
|
+
pages: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
25
|
+
id: z.ZodString;
|
|
26
|
+
title: z.ZodString;
|
|
27
|
+
slug: z.ZodString;
|
|
28
|
+
visible: z.ZodDefault<z.ZodBoolean>;
|
|
29
|
+
layout: z.ZodDefault<z.ZodObject<{
|
|
30
|
+
blocks: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
31
|
+
id: z.ZodString;
|
|
32
|
+
type: z.ZodString;
|
|
33
|
+
variant: z.ZodOptional<z.ZodString>;
|
|
34
|
+
visible: z.ZodDefault<z.ZodBoolean>;
|
|
35
|
+
settings: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
36
|
+
style_overrides: z.ZodOptional<z.ZodType<Partial<Record<import("./style-slots.ts").StyleSlotId, import("./style-slots.ts").StyleSlotValue>>, unknown, z.core.$ZodTypeInternals<Partial<Record<import("./style-slots.ts").StyleSlotId, import("./style-slots.ts").StyleSlotValue>>, unknown>>>;
|
|
37
|
+
}, z.core.$strict>>>;
|
|
38
|
+
}, z.core.$strict>>;
|
|
39
|
+
seo: z.ZodOptional<z.ZodObject<{
|
|
40
|
+
title: z.ZodOptional<z.ZodString>;
|
|
41
|
+
description: z.ZodOptional<z.ZodString>;
|
|
42
|
+
}, z.core.$strict>>;
|
|
43
|
+
}, z.core.$strict>>>;
|
|
44
|
+
terms: z.ZodDefault<z.ZodObject<{
|
|
45
|
+
termsOfService: z.ZodOptional<z.ZodString>;
|
|
46
|
+
privacyPolicy: z.ZodOptional<z.ZodString>;
|
|
47
|
+
refundPolicy: z.ZodOptional<z.ZodString>;
|
|
48
|
+
imprint: z.ZodOptional<z.ZodString>;
|
|
49
|
+
}, z.core.$strict>>;
|
|
50
|
+
}, z.core.$strict>;
|
|
51
|
+
}, z.core.$strict>;
|
|
52
|
+
artifactStale: z.ZodOptional<z.ZodBoolean>;
|
|
53
|
+
sourceRevision: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
54
|
+
}, z.core.$strict>;
|
|
55
|
+
export type BuilderPreviewResolveData = z.infer<typeof BuilderPreviewResolveDataSchema>;
|
|
56
|
+
export declare const BuilderPreviewResolveResponseSchema: z.ZodObject<{
|
|
57
|
+
status: z.ZodNumber;
|
|
58
|
+
data: z.ZodNullable<z.ZodObject<{
|
|
59
|
+
sessionId: z.ZodString;
|
|
60
|
+
shopId: z.ZodString;
|
|
61
|
+
shopSlug: z.ZodString;
|
|
62
|
+
themeId: z.ZodString;
|
|
63
|
+
artifactPrefix: z.ZodString;
|
|
64
|
+
builderSettings: z.ZodObject<{
|
|
65
|
+
version: z.ZodLiteral<2>;
|
|
66
|
+
revision: z.ZodNumber;
|
|
67
|
+
theme: z.ZodObject<{
|
|
68
|
+
content: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
69
|
+
layout: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
70
|
+
blocks: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
71
|
+
id: z.ZodString;
|
|
72
|
+
type: z.ZodString;
|
|
73
|
+
variant: z.ZodOptional<z.ZodString>;
|
|
74
|
+
visible: z.ZodDefault<z.ZodBoolean>;
|
|
75
|
+
settings: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
76
|
+
style_overrides: z.ZodOptional<z.ZodType<Partial<Record<import("./style-slots.ts").StyleSlotId, import("./style-slots.ts").StyleSlotValue>>, unknown, z.core.$ZodTypeInternals<Partial<Record<import("./style-slots.ts").StyleSlotId, import("./style-slots.ts").StyleSlotValue>>, unknown>>>;
|
|
77
|
+
}, z.core.$strict>>>;
|
|
78
|
+
}, z.core.$strict>>>;
|
|
79
|
+
style_slots: z.ZodDefault<z.ZodType<Partial<Record<import("./style-slots.ts").StyleSlotId, import("./style-slots.ts").StyleSlotValue>>, unknown, z.core.$ZodTypeInternals<Partial<Record<import("./style-slots.ts").StyleSlotId, import("./style-slots.ts").StyleSlotValue>>, unknown>>>;
|
|
80
|
+
pages: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
81
|
+
id: z.ZodString;
|
|
82
|
+
title: z.ZodString;
|
|
83
|
+
slug: z.ZodString;
|
|
84
|
+
visible: z.ZodDefault<z.ZodBoolean>;
|
|
85
|
+
layout: z.ZodDefault<z.ZodObject<{
|
|
86
|
+
blocks: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
87
|
+
id: z.ZodString;
|
|
88
|
+
type: z.ZodString;
|
|
89
|
+
variant: z.ZodOptional<z.ZodString>;
|
|
90
|
+
visible: z.ZodDefault<z.ZodBoolean>;
|
|
91
|
+
settings: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
92
|
+
style_overrides: z.ZodOptional<z.ZodType<Partial<Record<import("./style-slots.ts").StyleSlotId, import("./style-slots.ts").StyleSlotValue>>, unknown, z.core.$ZodTypeInternals<Partial<Record<import("./style-slots.ts").StyleSlotId, import("./style-slots.ts").StyleSlotValue>>, unknown>>>;
|
|
93
|
+
}, z.core.$strict>>>;
|
|
94
|
+
}, z.core.$strict>>;
|
|
95
|
+
seo: z.ZodOptional<z.ZodObject<{
|
|
96
|
+
title: z.ZodOptional<z.ZodString>;
|
|
97
|
+
description: z.ZodOptional<z.ZodString>;
|
|
98
|
+
}, z.core.$strict>>;
|
|
99
|
+
}, z.core.$strict>>>;
|
|
100
|
+
terms: z.ZodDefault<z.ZodObject<{
|
|
101
|
+
termsOfService: z.ZodOptional<z.ZodString>;
|
|
102
|
+
privacyPolicy: z.ZodOptional<z.ZodString>;
|
|
103
|
+
refundPolicy: z.ZodOptional<z.ZodString>;
|
|
104
|
+
imprint: z.ZodOptional<z.ZodString>;
|
|
105
|
+
}, z.core.$strict>>;
|
|
106
|
+
}, z.core.$strict>;
|
|
107
|
+
}, z.core.$strict>;
|
|
108
|
+
artifactStale: z.ZodOptional<z.ZodBoolean>;
|
|
109
|
+
sourceRevision: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
110
|
+
}, z.core.$strict>>;
|
|
111
|
+
error: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
112
|
+
}, z.core.$strip>;
|
|
113
|
+
export type BuilderPreviewResolveResponse = z.infer<typeof BuilderPreviewResolveResponseSchema>;
|
|
114
|
+
export declare function parseBuilderPreviewResolveResponse(value: unknown): BuilderPreviewResolveResponse | null;
|
|
115
|
+
//# sourceMappingURL=preview-session-resolve.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"preview-session-resolve.d.ts","sourceRoot":"","sources":["../src/preview-session-resolve.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,QAAQ,CAAC;AAG5B,eAAO,MAAM,+BAA+B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAWjC,CAAC;AAEZ,MAAM,MAAM,yBAAyB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,+BAA+B,CAAC,CAAC;AAExF,eAAO,MAAM,mCAAmC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAMtC,CAAC;AAEX,MAAM,MAAM,6BAA6B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mCAAmC,CAAC,CAAC;AAEhG,wBAAgB,kCAAkC,CAChD,KAAK,EAAE,OAAO,GACb,6BAA6B,GAAG,IAAI,CAGtC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import * as z from 'zod/v4';
|
|
2
|
+
import { BuilderSettingsSchema } from "./builder-settings.js";
|
|
3
|
+
export const BuilderPreviewResolveDataSchema = z
|
|
4
|
+
.object({
|
|
5
|
+
sessionId: z.string().min(1),
|
|
6
|
+
shopId: z.string().min(1),
|
|
7
|
+
shopSlug: z.string().min(1),
|
|
8
|
+
themeId: z.string().min(1),
|
|
9
|
+
artifactPrefix: z.string().min(1),
|
|
10
|
+
builderSettings: BuilderSettingsSchema,
|
|
11
|
+
artifactStale: z.boolean().optional(),
|
|
12
|
+
sourceRevision: z.string().nullable().optional(),
|
|
13
|
+
})
|
|
14
|
+
.strict();
|
|
15
|
+
export const BuilderPreviewResolveResponseSchema = z
|
|
16
|
+
.object({
|
|
17
|
+
status: z.number(),
|
|
18
|
+
data: BuilderPreviewResolveDataSchema.nullable(),
|
|
19
|
+
error: z.string().nullable().optional(),
|
|
20
|
+
})
|
|
21
|
+
.strip();
|
|
22
|
+
export function parseBuilderPreviewResolveResponse(value) {
|
|
23
|
+
const parsed = BuilderPreviewResolveResponseSchema.safeParse(value);
|
|
24
|
+
return parsed.success ? parsed.data : null;
|
|
25
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export declare const TRUSTED_PREVIEW_PARENT_ORIGINS: readonly ["https://app.shoppex.io", "https://dashboard.shoppex.io", "https://dashboard.shoppex.test", "https://shoppex-dashboard.vercel.app", "http://localhost:3000", "http://127.0.0.1:3000"];
|
|
2
|
+
export type TrustedPreviewParentOrigin = (typeof TRUSTED_PREVIEW_PARENT_ORIGINS)[number];
|
|
3
|
+
export declare function isTrustedPreviewParentOrigin(origin: string): boolean;
|
|
4
|
+
//# sourceMappingURL=preview-trusted-origins.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"preview-trusted-origins.d.ts","sourceRoot":"","sources":["../src/preview-trusted-origins.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,8BAA8B,iMAOjC,CAAC;AAEX,MAAM,MAAM,0BAA0B,GAAG,CAAC,OAAO,8BAA8B,CAAC,CAAC,MAAM,CAAC,CAAC;AAEzF,wBAAgB,4BAA4B,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAuBpE"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export const TRUSTED_PREVIEW_PARENT_ORIGINS = [
|
|
2
|
+
'https://app.shoppex.io',
|
|
3
|
+
'https://dashboard.shoppex.io',
|
|
4
|
+
'https://dashboard.shoppex.test',
|
|
5
|
+
'https://shoppex-dashboard.vercel.app',
|
|
6
|
+
'http://localhost:3000',
|
|
7
|
+
'http://127.0.0.1:3000',
|
|
8
|
+
];
|
|
9
|
+
export function isTrustedPreviewParentOrigin(origin) {
|
|
10
|
+
if (TRUSTED_PREVIEW_PARENT_ORIGINS.includes(origin)) {
|
|
11
|
+
return true;
|
|
12
|
+
}
|
|
13
|
+
try {
|
|
14
|
+
const url = new URL(origin);
|
|
15
|
+
const hostname = url.hostname.toLowerCase();
|
|
16
|
+
const isHttp = url.protocol === 'http:' || url.protocol === 'https:';
|
|
17
|
+
return (isHttp
|
|
18
|
+
&& (hostname === 'dashboard.shoppex.test'
|
|
19
|
+
|| hostname === 'localhost'
|
|
20
|
+
|| hostname === '127.0.0.1'
|
|
21
|
+
|| hostname === '::1'
|
|
22
|
+
|| hostname === '[::1]'
|
|
23
|
+
|| hostname.endsWith('.localhost')));
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { PreviewBootPayload } from './preview-boot.ts';
|
|
2
|
+
export declare const HTML_INITIAL_BLOCK_PATTERN: RegExp;
|
|
3
|
+
export declare const LEGACY_HTML_INITIAL_PATTERN: RegExp;
|
|
4
|
+
export type InitialDataScriptMatch = {
|
|
5
|
+
json: string;
|
|
6
|
+
};
|
|
7
|
+
export declare function stripAllInitialDataScripts(html: string): string;
|
|
8
|
+
export declare function findInitialDataScriptJson(html: string): InitialDataScriptMatch | null;
|
|
9
|
+
export declare function findInitialDataPayload(html: string): Record<string, unknown> | null;
|
|
10
|
+
export declare function countInitialDataScripts(html: string): number;
|
|
11
|
+
export declare function buildPlainInitialDataScript(initialData: Record<string, unknown>): string;
|
|
12
|
+
export declare function buildWrappedStorefrontInitialDataScript(serialized: string, deployedThemeEntryPathPattern: RegExp): string;
|
|
13
|
+
export declare function injectPlainInitialDataBeforeHeadClose(html: string, script: string): string;
|
|
14
|
+
export declare function injectPreviewInitialData(html: string, payload: PreviewBootPayload): string;
|
|
15
|
+
export declare function assertSingleInitialDataScript(html: string, expectedSlug: string): void;
|
|
16
|
+
export declare function safeJson(value: unknown): string;
|
|
17
|
+
//# sourceMappingURL=storefront-initial-data-html.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storefront-initial-data-html.d.ts","sourceRoot":"","sources":["../src/storefront-initial-data-html.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAG5D,eAAO,MAAM,0BAA0B,QAC8K,CAAC;AAEtN,eAAO,MAAM,2BAA2B,QACuB,CAAC;AAOhE,MAAM,MAAM,sBAAsB,GAAG;IACnC,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAI/D;AAED,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,MAAM,GAAG,sBAAsB,GAAG,IAAI,CAYrF;AAED,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAWnF;AAED,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAK5D;AAED,wBAAgB,2BAA2B,CAAC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAExF;AAED,wBAAgB,uCAAuC,CACrD,UAAU,EAAE,MAAM,EAClB,6BAA6B,EAAE,MAAM,GACpC,MAAM,CAOR;AAED,wBAAgB,qCAAqC,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAK1F;AAED,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,kBAAkB,GAAG,MAAM,CAK1F;AAED,wBAAgB,6BAA6B,CAAC,IAAI,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,IAAI,CAmBtF;AAED,wBAAgB,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAE/C"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { mergePreviewBootIntoInitialData } from "./preview-boot.js";
|
|
2
|
+
export const HTML_INITIAL_BLOCK_PATTERN = /<!--shoppex-initial-data:start--><script>\(function\(\)\{[\s\S]*?window\.__SHOPPEX_INITIAL__=([\s\S]*?)(?=;\}\)\(\);<\/script><!--shoppex-initial-data:end-->);\}\)\(\);<\/script><!--shoppex-initial-data:end-->/;
|
|
3
|
+
export const LEGACY_HTML_INITIAL_PATTERN = /<script>\s*window\.__SHOPPEX_INITIAL__=([\s\S]*?)<\/script>/;
|
|
4
|
+
const HTML_INITIAL_BLOCK_GLOBAL_PATTERN = new RegExp(HTML_INITIAL_BLOCK_PATTERN.source, 'g');
|
|
5
|
+
const LEGACY_HTML_INITIAL_GLOBAL_PATTERN = new RegExp(LEGACY_HTML_INITIAL_PATTERN.source, 'g');
|
|
6
|
+
const CLOSING_HEAD_PATTERN = /<\/head>/i;
|
|
7
|
+
export function stripAllInitialDataScripts(html) {
|
|
8
|
+
return html
|
|
9
|
+
.replace(HTML_INITIAL_BLOCK_GLOBAL_PATTERN, '')
|
|
10
|
+
.replace(LEGACY_HTML_INITIAL_GLOBAL_PATTERN, '');
|
|
11
|
+
}
|
|
12
|
+
export function findInitialDataScriptJson(html) {
|
|
13
|
+
const blockMatch = html.match(HTML_INITIAL_BLOCK_PATTERN);
|
|
14
|
+
if (blockMatch?.[1]) {
|
|
15
|
+
return { json: blockMatch[1] };
|
|
16
|
+
}
|
|
17
|
+
const legacyMatch = html.match(LEGACY_HTML_INITIAL_PATTERN);
|
|
18
|
+
if (legacyMatch?.[1]) {
|
|
19
|
+
return { json: legacyMatch[1] };
|
|
20
|
+
}
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
export function findInitialDataPayload(html) {
|
|
24
|
+
const match = findInitialDataScriptJson(html);
|
|
25
|
+
if (!match) {
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
try {
|
|
29
|
+
return JSON.parse(match.json.replace(/;\s*$/, ''));
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
export function countInitialDataScripts(html) {
|
|
36
|
+
const blockMatches = html.match(HTML_INITIAL_BLOCK_GLOBAL_PATTERN)?.length ?? 0;
|
|
37
|
+
const stripped = html.replace(HTML_INITIAL_BLOCK_GLOBAL_PATTERN, '');
|
|
38
|
+
const legacyMatches = stripped.match(LEGACY_HTML_INITIAL_GLOBAL_PATTERN)?.length ?? 0;
|
|
39
|
+
return blockMatches + legacyMatches;
|
|
40
|
+
}
|
|
41
|
+
export function buildPlainInitialDataScript(initialData) {
|
|
42
|
+
return `<script>window.__SHOPPEX_INITIAL__=${safeJson(initialData)};</script>`;
|
|
43
|
+
}
|
|
44
|
+
export function buildWrappedStorefrontInitialDataScript(serialized, deployedThemeEntryPathPattern) {
|
|
45
|
+
return [
|
|
46
|
+
'<!--shoppex-initial-data:start--><script>(function(){',
|
|
47
|
+
'if(typeof window!=="undefined"){var path=window.location.pathname||"";',
|
|
48
|
+
`if(${deployedThemeEntryPathPattern}.test(path)){window.__SHOPPEX_DEPLOYED_THEME_ARTIFACT_PATH__=path;window.history.replaceState(window.history.state,"","/"+window.location.search+window.location.hash);}}`,
|
|
49
|
+
`window.__SHOPPEX_INITIAL__=${serialized};})();</script><!--shoppex-initial-data:end-->`,
|
|
50
|
+
].join('');
|
|
51
|
+
}
|
|
52
|
+
export function injectPlainInitialDataBeforeHeadClose(html, script) {
|
|
53
|
+
if (CLOSING_HEAD_PATTERN.test(html)) {
|
|
54
|
+
return html.replace(CLOSING_HEAD_PATTERN, `${script}\n</head>`);
|
|
55
|
+
}
|
|
56
|
+
return `${script}\n${html}`;
|
|
57
|
+
}
|
|
58
|
+
export function injectPreviewInitialData(html, payload) {
|
|
59
|
+
const withoutScripts = stripAllInitialDataScripts(html);
|
|
60
|
+
const initialData = mergePreviewBootIntoInitialData(payload);
|
|
61
|
+
const script = buildPlainInitialDataScript(initialData);
|
|
62
|
+
return injectPlainInitialDataBeforeHeadClose(withoutScripts, script);
|
|
63
|
+
}
|
|
64
|
+
export function assertSingleInitialDataScript(html, expectedSlug) {
|
|
65
|
+
const count = countInitialDataScripts(html);
|
|
66
|
+
if (count !== 1) {
|
|
67
|
+
throw new Error(`Expected exactly one initial-data script, found ${count}`);
|
|
68
|
+
}
|
|
69
|
+
const payload = findInitialDataPayload(html);
|
|
70
|
+
const store = payload?.store;
|
|
71
|
+
const slug = typeof store === 'object' && store !== null && !Array.isArray(store)
|
|
72
|
+
? store.slug
|
|
73
|
+
: null;
|
|
74
|
+
if (typeof slug !== 'string' || slug.trim().length === 0) {
|
|
75
|
+
throw new Error('Initial-data script is missing store.slug');
|
|
76
|
+
}
|
|
77
|
+
if (slug.trim() !== expectedSlug) {
|
|
78
|
+
throw new Error(`Initial-data slug mismatch: expected ${expectedSlug}, got ${slug}`);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
export function safeJson(value) {
|
|
82
|
+
return JSON.stringify(value).replace(/</g, '\\u003c');
|
|
83
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export type StorefrontCuratedFont = {
|
|
2
|
+
value: string;
|
|
3
|
+
label: string;
|
|
4
|
+
stack: string;
|
|
5
|
+
};
|
|
6
|
+
export declare const STOREFRONT_SYSTEM_FONTS: Set<string>;
|
|
7
|
+
export declare const STOREFRONT_CURATED_FONTS: StorefrontCuratedFont[];
|
|
8
|
+
export declare function resolvePrimaryFontFamilyName(raw: unknown): string | null;
|
|
9
|
+
export declare function resolveCuratedFontValue(raw: unknown, fallback: string): string;
|
|
10
|
+
export declare function curatedFontOptionsForValue(value: string): StorefrontCuratedFont[];
|
|
11
|
+
export declare function findStorefrontCuratedFont(value: string): StorefrontCuratedFont | undefined;
|
|
12
|
+
export declare function isValidGoogleFontFamily(value: unknown): value is string;
|
|
13
|
+
export declare function buildStorefrontGoogleFontHref(family: string): string;
|
|
14
|
+
export declare function getStorefrontGoogleFontHrefs(input: {
|
|
15
|
+
bodyFont?: unknown;
|
|
16
|
+
headingFont?: unknown;
|
|
17
|
+
}): string[];
|
|
18
|
+
//# sourceMappingURL=storefront-typography-fonts.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storefront-typography-fonts.d.ts","sourceRoot":"","sources":["../src/storefront-typography-fonts.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,qBAAqB,GAAG;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,eAAO,MAAM,uBAAuB,aAA6C,CAAC;AAElF,eAAO,MAAM,wBAAwB,EAAE,qBAAqB,EAwB3D,CAAC;AAIF,wBAAgB,4BAA4B,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAoBxE;AAED,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAE9E;AAED,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,MAAM,GAAG,qBAAqB,EAAE,CAajF;AAED,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,MAAM,GAAG,qBAAqB,GAAG,SAAS,CAE1F;AAED,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAIvE;AAED,wBAAgB,6BAA6B,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAGpE;AAED,wBAAgB,4BAA4B,CAAC,KAAK,EAAE;IAClD,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB,GAAG,MAAM,EAAE,CAYX"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
export const STOREFRONT_SYSTEM_FONTS = new Set(['Arial', 'Georgia', 'System UI']);
|
|
2
|
+
export const STOREFRONT_CURATED_FONTS = [
|
|
3
|
+
{ value: 'Inter', label: 'Inter', stack: 'Inter, system-ui, sans-serif' },
|
|
4
|
+
{ value: 'Geist', label: 'Geist', stack: 'Geist, system-ui, sans-serif' },
|
|
5
|
+
{ value: 'Manrope', label: 'Manrope', stack: 'Manrope, system-ui, sans-serif' },
|
|
6
|
+
{
|
|
7
|
+
value: 'Plus Jakarta Sans',
|
|
8
|
+
label: 'Plus Jakarta Sans',
|
|
9
|
+
stack: '"Plus Jakarta Sans", system-ui, sans-serif',
|
|
10
|
+
},
|
|
11
|
+
{ value: 'DM Sans', label: 'DM Sans', stack: '"DM Sans", system-ui, sans-serif' },
|
|
12
|
+
{
|
|
13
|
+
value: 'Space Grotesk',
|
|
14
|
+
label: 'Space Grotesk',
|
|
15
|
+
stack: '"Space Grotesk", system-ui, sans-serif',
|
|
16
|
+
},
|
|
17
|
+
{ value: 'Sora', label: 'Sora', stack: 'Sora, system-ui, sans-serif' },
|
|
18
|
+
{
|
|
19
|
+
value: 'IBM Plex Sans',
|
|
20
|
+
label: 'IBM Plex Sans',
|
|
21
|
+
stack: '"IBM Plex Sans", system-ui, sans-serif',
|
|
22
|
+
},
|
|
23
|
+
{ value: 'Arial', label: 'Arial', stack: 'Arial, sans-serif' },
|
|
24
|
+
{ value: 'Georgia', label: 'Georgia', stack: 'Georgia, serif' },
|
|
25
|
+
{ value: 'System UI', label: 'System UI', stack: 'system-ui, sans-serif' },
|
|
26
|
+
];
|
|
27
|
+
const GOOGLE_FONT_FAMILY_PATTERN = /^[A-Za-z0-9 +\-_]+$/;
|
|
28
|
+
export function resolvePrimaryFontFamilyName(raw) {
|
|
29
|
+
if (typeof raw !== 'string')
|
|
30
|
+
return null;
|
|
31
|
+
const trimmed = raw.trim();
|
|
32
|
+
if (!trimmed)
|
|
33
|
+
return null;
|
|
34
|
+
const exact = STOREFRONT_CURATED_FONTS.find((font) => font.value === trimmed || font.stack === trimmed);
|
|
35
|
+
if (exact)
|
|
36
|
+
return exact.value;
|
|
37
|
+
const prefix = STOREFRONT_CURATED_FONTS.find((font) => trimmed.startsWith(`${font.value},`));
|
|
38
|
+
if (prefix)
|
|
39
|
+
return prefix.value;
|
|
40
|
+
const quoted = trimmed.match(/^["'](.+?)["']/);
|
|
41
|
+
if (quoted?.[1]) {
|
|
42
|
+
return quoted[1].trim() || null;
|
|
43
|
+
}
|
|
44
|
+
const primary = trimmed.split(',')[0]?.trim();
|
|
45
|
+
return primary && primary.length > 0 ? primary : null;
|
|
46
|
+
}
|
|
47
|
+
export function resolveCuratedFontValue(raw, fallback) {
|
|
48
|
+
return resolvePrimaryFontFamilyName(raw) ?? fallback;
|
|
49
|
+
}
|
|
50
|
+
export function curatedFontOptionsForValue(value) {
|
|
51
|
+
if (STOREFRONT_CURATED_FONTS.some((font) => font.value === value)) {
|
|
52
|
+
return STOREFRONT_CURATED_FONTS;
|
|
53
|
+
}
|
|
54
|
+
return [
|
|
55
|
+
...STOREFRONT_CURATED_FONTS,
|
|
56
|
+
{
|
|
57
|
+
value,
|
|
58
|
+
label: value,
|
|
59
|
+
stack: value.includes(',') ? value : `${value}, system-ui, sans-serif`,
|
|
60
|
+
},
|
|
61
|
+
];
|
|
62
|
+
}
|
|
63
|
+
export function findStorefrontCuratedFont(value) {
|
|
64
|
+
return curatedFontOptionsForValue(value).find((font) => font.value === value);
|
|
65
|
+
}
|
|
66
|
+
export function isValidGoogleFontFamily(value) {
|
|
67
|
+
if (typeof value !== 'string')
|
|
68
|
+
return false;
|
|
69
|
+
const trimmed = value.trim();
|
|
70
|
+
return trimmed.length > 0 && trimmed.length <= 60 && GOOGLE_FONT_FAMILY_PATTERN.test(trimmed);
|
|
71
|
+
}
|
|
72
|
+
export function buildStorefrontGoogleFontHref(family) {
|
|
73
|
+
const familyParam = family.trim().replace(/\s+/g, '+');
|
|
74
|
+
return `https://fonts.googleapis.com/css2?family=${familyParam}:wght@400;500;600;700&display=swap`;
|
|
75
|
+
}
|
|
76
|
+
export function getStorefrontGoogleFontHrefs(input) {
|
|
77
|
+
const families = new Set();
|
|
78
|
+
for (const raw of [input.bodyFont, input.headingFont]) {
|
|
79
|
+
const family = resolvePrimaryFontFamilyName(raw);
|
|
80
|
+
if (!family)
|
|
81
|
+
continue;
|
|
82
|
+
if (STOREFRONT_SYSTEM_FONTS.has(family))
|
|
83
|
+
continue;
|
|
84
|
+
if (!isValidGoogleFontFamily(family))
|
|
85
|
+
continue;
|
|
86
|
+
families.add(family);
|
|
87
|
+
}
|
|
88
|
+
return [...families].map((family) => buildStorefrontGoogleFontHref(family));
|
|
89
|
+
}
|