@shoppexio/builder-contracts 0.1.0 → 0.1.1
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-contracts.test.js +71 -1
- package/dist/builder-settings.d.ts +9 -666
- package/dist/builder-settings.d.ts.map +1 -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/events.d.ts +35 -240
- package/dist/events.d.ts.map +1 -1
- package/dist/events.js +7 -0
- package/dist/fields.d.ts +169 -8
- package/dist/fields.d.ts.map +1 -1
- package/dist/fields.js +27 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -0
- package/dist/legacy-manifest.d.ts +11 -0
- package/dist/legacy-manifest.d.ts.map +1 -1
- package/dist/legacy-manifest.js +106 -16
- package/dist/migrations.d.ts.map +1 -1
- package/dist/migrations.js +50 -4
- 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 +36 -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 +26 -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/style-slots.d.ts +49 -151
- package/dist/style-slots.d.ts.map +1 -1
- package/dist/style-slots.js +75 -29
- package/dist/theme-manifest.d.ts +229 -454
- 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 +24 -0
- package/dist/validation.d.ts +1 -1
- package/dist/validation.d.ts.map +1 -1
- package/dist/validation.js +18 -9
- package/package.json +43 -1
- package/src/builder-contracts.test.ts +398 -3
- package/src/canonical-settings.ts +156 -0
- package/src/events.ts +8 -0
- package/src/fields.ts +30 -0
- package/src/index.ts +7 -0
- package/src/legacy-manifest.ts +107 -16
- package/src/migrations.ts +65 -4
- package/src/persistence.ts +77 -0
- package/src/preview-boot.ts +47 -0
- package/src/preview-protocol.test.ts +132 -0
- package/src/preview-protocol.ts +122 -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 +63 -0
- package/src/storefront-initial-data-html.ts +112 -0
- package/src/style-slots.ts +96 -31
- package/src/theme-manifest.ts +118 -1
- package/src/theme-schemes.ts +33 -0
- package/src/validation.ts +27 -10
package/src/theme-manifest.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as z from 'zod/v4';
|
|
2
2
|
import { BlockSettingsSchema } from './fields.ts';
|
|
3
|
-
import { StyleSlotDefaultsSchema, StyleSlotIdSchema } from './style-slots.ts';
|
|
3
|
+
import { StyleSlotDefaultsSchema, StyleSlotIdSchema, type StyleSlotDefaults } from './style-slots.ts';
|
|
4
4
|
|
|
5
5
|
export const ThemeIdSchema = z.string().min(1).regex(/^[a-z0-9][a-z0-9-_.]*$/);
|
|
6
6
|
export type ThemeId = z.infer<typeof ThemeIdSchema>;
|
|
@@ -62,6 +62,7 @@ export const ThemePresetSchema = z
|
|
|
62
62
|
.object({
|
|
63
63
|
label: z.string().min(1),
|
|
64
64
|
description: z.string().min(1).optional(),
|
|
65
|
+
preview: z.string().min(1).optional(),
|
|
65
66
|
content: z.record(z.string().min(1), z.unknown()).default({}),
|
|
66
67
|
layout: z.record(z.string().min(1), z.unknown()).default({}),
|
|
67
68
|
style_slots: StyleSlotDefaultsSchema.default({}),
|
|
@@ -69,15 +70,63 @@ export const ThemePresetSchema = z
|
|
|
69
70
|
.strict();
|
|
70
71
|
export type ThemePreset = z.infer<typeof ThemePresetSchema>;
|
|
71
72
|
|
|
73
|
+
export const ThemeManifestMetadataSchema = z
|
|
74
|
+
.object({
|
|
75
|
+
author: z.string().min(1).optional(),
|
|
76
|
+
description: z.string().min(1).optional(),
|
|
77
|
+
preview: z.string().min(1).optional(),
|
|
78
|
+
features: z.array(z.string().min(1)).optional(),
|
|
79
|
+
techStack: z.array(z.string().min(1)).optional(),
|
|
80
|
+
templates: z.array(z.string().min(1)).optional(),
|
|
81
|
+
createdAt: z.string().min(1).optional(),
|
|
82
|
+
demoUrl: z.string().min(1).optional(),
|
|
83
|
+
audience: z.string().min(1).optional(),
|
|
84
|
+
tags: z.array(z.string().min(1)).optional(),
|
|
85
|
+
hotfixPaths: z.array(z.string().min(1)).optional(),
|
|
86
|
+
})
|
|
87
|
+
.strict();
|
|
88
|
+
export type ThemeManifestMetadata = z.infer<typeof ThemeManifestMetadataSchema>;
|
|
89
|
+
|
|
90
|
+
export const ThemeManifestLinkGroupSchema = z
|
|
91
|
+
.object({
|
|
92
|
+
path: z.string().min(1),
|
|
93
|
+
label: z.string().min(1),
|
|
94
|
+
description: z.string().min(1).optional(),
|
|
95
|
+
pageIds: z.array(z.string().min(1)).optional(),
|
|
96
|
+
})
|
|
97
|
+
.strict();
|
|
98
|
+
export type ThemeManifestLinkGroup = z.infer<typeof ThemeManifestLinkGroupSchema>;
|
|
99
|
+
|
|
100
|
+
export const ThemeManifestLinkItemSchema = z
|
|
101
|
+
.object({
|
|
102
|
+
label: z.string().min(1),
|
|
103
|
+
href: z.string().min(1),
|
|
104
|
+
})
|
|
105
|
+
.strict();
|
|
106
|
+
export type ThemeManifestLinkItem = z.infer<typeof ThemeManifestLinkItemSchema>;
|
|
107
|
+
|
|
72
108
|
export const ThemeManifestSchema = z
|
|
73
109
|
.object({
|
|
74
110
|
id: ThemeIdSchema,
|
|
75
111
|
name: z.string().min(1),
|
|
76
112
|
version: z.string().min(1),
|
|
113
|
+
author: ThemeManifestMetadataSchema.shape.author,
|
|
114
|
+
description: ThemeManifestMetadataSchema.shape.description,
|
|
115
|
+
preview: ThemeManifestMetadataSchema.shape.preview,
|
|
116
|
+
features: ThemeManifestMetadataSchema.shape.features,
|
|
117
|
+
techStack: ThemeManifestMetadataSchema.shape.techStack,
|
|
118
|
+
templates: ThemeManifestMetadataSchema.shape.templates,
|
|
119
|
+
createdAt: ThemeManifestMetadataSchema.shape.createdAt,
|
|
120
|
+
demoUrl: ThemeManifestMetadataSchema.shape.demoUrl,
|
|
121
|
+
audience: ThemeManifestMetadataSchema.shape.audience,
|
|
122
|
+
tags: ThemeManifestMetadataSchema.shape.tags,
|
|
123
|
+
hotfixPaths: ThemeManifestMetadataSchema.shape.hotfixPaths,
|
|
77
124
|
pages: z.record(z.string().min(1), ManifestPageSchema),
|
|
78
125
|
blocks: z.record(z.string().min(1), ManifestBlockSchema),
|
|
79
126
|
styleSlots: StyleSlotDefaultsSchema.default({}),
|
|
80
127
|
presets: z.record(z.string().min(1), ThemePresetSchema).default({}),
|
|
128
|
+
linkGroups: z.array(ThemeManifestLinkGroupSchema).optional(),
|
|
129
|
+
defaultLinkItems: z.record(z.string().min(1), z.array(ThemeManifestLinkItemSchema)).optional(),
|
|
81
130
|
})
|
|
82
131
|
.strict()
|
|
83
132
|
.superRefine((manifest, ctx) => {
|
|
@@ -138,3 +187,71 @@ export type ThemeManifest = z.infer<typeof ThemeManifestSchema>;
|
|
|
138
187
|
export function parseThemeManifest(input: unknown): ThemeManifest {
|
|
139
188
|
return ThemeManifestSchema.parse(input);
|
|
140
189
|
}
|
|
190
|
+
|
|
191
|
+
export type ThemeManifestPresetListItem = {
|
|
192
|
+
id: string;
|
|
193
|
+
name: string;
|
|
194
|
+
description?: string;
|
|
195
|
+
preview?: string;
|
|
196
|
+
overrides: {
|
|
197
|
+
content?: Record<string, unknown>;
|
|
198
|
+
layout?: Record<string, unknown>;
|
|
199
|
+
tokens?: Record<string, unknown>;
|
|
200
|
+
style_slots?: StyleSlotDefaults;
|
|
201
|
+
};
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
export function listThemeManifestPresets(input: unknown): ThemeManifestPresetListItem[] {
|
|
205
|
+
if (!isRecord(input) || !isRecord(input.presets)) {
|
|
206
|
+
if (isRecord(input) && Array.isArray(input.presets)) {
|
|
207
|
+
return input.presets.flatMap((preset) => normalizeLegacyPresetListItem(preset));
|
|
208
|
+
}
|
|
209
|
+
return [];
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const manifest = ThemeManifestSchema.safeParse(input);
|
|
213
|
+
if (!manifest.success) {
|
|
214
|
+
return [];
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
return Object.entries(manifest.data.presets).map(([id, preset]) => ({
|
|
218
|
+
id,
|
|
219
|
+
name: preset.label,
|
|
220
|
+
...(preset.description ? { description: preset.description } : {}),
|
|
221
|
+
...(preset.preview ? { preview: preset.preview } : {}),
|
|
222
|
+
overrides: {
|
|
223
|
+
...(Object.keys(preset.content).length > 0 ? { content: preset.content } : {}),
|
|
224
|
+
...(Object.keys(preset.layout).length > 0 ? { layout: preset.layout } : {}),
|
|
225
|
+
...(Object.keys(preset.style_slots).length > 0 ? { style_slots: preset.style_slots } : {}),
|
|
226
|
+
},
|
|
227
|
+
}));
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
function normalizeLegacyPresetListItem(input: unknown): ThemeManifestPresetListItem[] {
|
|
231
|
+
if (!isRecord(input) || typeof input.id !== 'string') {
|
|
232
|
+
return [];
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
const overrides = isRecord(input.overrides) ? input.overrides : {};
|
|
236
|
+
const styleSlots = StyleSlotDefaultsSchema.safeParse(overrides.style_slots ?? overrides.styleSlots);
|
|
237
|
+
return [{
|
|
238
|
+
id: input.id,
|
|
239
|
+
name: typeof input.name === 'string'
|
|
240
|
+
? input.name
|
|
241
|
+
: typeof input.label === 'string'
|
|
242
|
+
? input.label
|
|
243
|
+
: input.id,
|
|
244
|
+
...(typeof input.description === 'string' ? { description: input.description } : {}),
|
|
245
|
+
...(typeof input.preview === 'string' ? { preview: input.preview } : {}),
|
|
246
|
+
overrides: {
|
|
247
|
+
...(isRecord(overrides.content) ? { content: overrides.content } : {}),
|
|
248
|
+
...(isRecord(overrides.layout) ? { layout: overrides.layout } : {}),
|
|
249
|
+
...(isRecord(overrides.tokens) ? { tokens: overrides.tokens } : {}),
|
|
250
|
+
...(styleSlots.success && Object.keys(styleSlots.data).length > 0 ? { style_slots: styleSlots.data } : {}),
|
|
251
|
+
},
|
|
252
|
+
}];
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
256
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
257
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export const PUBLIC_BUILDER_THEME_SCHEMES = [
|
|
2
|
+
'default',
|
|
3
|
+
'classic',
|
|
4
|
+
'nebula',
|
|
5
|
+
'pulse',
|
|
6
|
+
'phantom',
|
|
7
|
+
'starlight',
|
|
8
|
+
'apex',
|
|
9
|
+
'vault',
|
|
10
|
+
] as const;
|
|
11
|
+
|
|
12
|
+
export const STARTER_BUILDER_THEME_SCHEMES = ['blank'] as const;
|
|
13
|
+
|
|
14
|
+
export const BUILDER_READY_THEME_SCHEMES = [
|
|
15
|
+
...STARTER_BUILDER_THEME_SCHEMES,
|
|
16
|
+
...PUBLIC_BUILDER_THEME_SCHEMES,
|
|
17
|
+
] as const;
|
|
18
|
+
|
|
19
|
+
export type PublicBuilderThemeScheme = (typeof PUBLIC_BUILDER_THEME_SCHEMES)[number];
|
|
20
|
+
export type StarterBuilderThemeScheme = (typeof STARTER_BUILDER_THEME_SCHEMES)[number];
|
|
21
|
+
export type BuilderReadyThemeScheme = (typeof BUILDER_READY_THEME_SCHEMES)[number];
|
|
22
|
+
|
|
23
|
+
export function isPublicBuilderThemeScheme(value: string): value is PublicBuilderThemeScheme {
|
|
24
|
+
return (PUBLIC_BUILDER_THEME_SCHEMES as readonly string[]).includes(value);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function isStarterBuilderThemeScheme(value: string): value is StarterBuilderThemeScheme {
|
|
28
|
+
return (STARTER_BUILDER_THEME_SCHEMES as readonly string[]).includes(value);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function isBuilderReadyThemeScheme(value: string): value is BuilderReadyThemeScheme {
|
|
32
|
+
return (BUILDER_READY_THEME_SCHEMES as readonly string[]).includes(value);
|
|
33
|
+
}
|
package/src/validation.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { BlockInstance, BuilderSettings } from './builder-settings.ts';
|
|
2
|
+
import { ThemeStyleSlotIdSchema } from './style-slots.ts';
|
|
2
3
|
import type { ThemeManifest } from './theme-manifest.ts';
|
|
3
4
|
|
|
4
5
|
export type BuilderManifestValidationIssueCode =
|
|
@@ -9,6 +10,7 @@ export type BuilderManifestValidationIssueCode =
|
|
|
9
10
|
| 'too_many_block_instances'
|
|
10
11
|
| 'unknown_block_variant'
|
|
11
12
|
| 'unknown_block_setting'
|
|
13
|
+
| 'unknown_style_slot'
|
|
12
14
|
| 'unexposed_style_slot';
|
|
13
15
|
|
|
14
16
|
export type BuilderManifestValidationIssue = {
|
|
@@ -33,6 +35,8 @@ export function validateBuilderSettingsAgainstManifest(
|
|
|
33
35
|
): BuilderManifestValidationIssue[] {
|
|
34
36
|
const issues: BuilderManifestValidationIssue[] = [];
|
|
35
37
|
|
|
38
|
+
validateGlobalStyleSlots(settings, manifest, issues);
|
|
39
|
+
|
|
36
40
|
for (const [pageId, layout] of Object.entries(settings.theme.layout)) {
|
|
37
41
|
const page = manifest.pages[pageId];
|
|
38
42
|
if (!page) {
|
|
@@ -98,6 +102,28 @@ export function validateBuilderSettingsAgainstManifest(
|
|
|
98
102
|
return issues;
|
|
99
103
|
}
|
|
100
104
|
|
|
105
|
+
function validateGlobalStyleSlots(
|
|
106
|
+
settings: BuilderSettings,
|
|
107
|
+
manifest: ThemeManifest,
|
|
108
|
+
issues: BuilderManifestValidationIssue[],
|
|
109
|
+
): void {
|
|
110
|
+
const declaredThemeSlots = new Set(Object.keys(manifest.styleSlots));
|
|
111
|
+
|
|
112
|
+
for (const slotId of Object.keys(settings.theme.style_slots)) {
|
|
113
|
+
if (!ThemeStyleSlotIdSchema.safeParse(slotId).success) {
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (!declaredThemeSlots.has(slotId)) {
|
|
118
|
+
issues.push({
|
|
119
|
+
code: 'unknown_style_slot',
|
|
120
|
+
path: `theme.style_slots.${slotId}`,
|
|
121
|
+
message: `Theme style slot "${slotId}" is not declared by the manifest`,
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
101
127
|
export function assertBuilderSettingsMatchManifest(
|
|
102
128
|
settings: BuilderSettings,
|
|
103
129
|
manifest: ThemeManifest,
|
|
@@ -130,11 +156,7 @@ function validateBlockSettings(
|
|
|
130
156
|
return;
|
|
131
157
|
}
|
|
132
158
|
|
|
133
|
-
const allowedSettings = new Set
|
|
134
|
-
for (const path of settingPaths) {
|
|
135
|
-
allowedSettings.add(path);
|
|
136
|
-
allowedSettings.add(getShortSettingKey(path));
|
|
137
|
-
}
|
|
159
|
+
const allowedSettings = new Set(settingPaths);
|
|
138
160
|
|
|
139
161
|
for (const key of Object.keys(block.settings)) {
|
|
140
162
|
if (allowedSettings.has(key)) {
|
|
@@ -168,11 +190,6 @@ function validateBlockVariant(
|
|
|
168
190
|
}
|
|
169
191
|
}
|
|
170
192
|
|
|
171
|
-
function getShortSettingKey(path: string): string {
|
|
172
|
-
const parts = path.split('.').filter(Boolean);
|
|
173
|
-
return parts.at(-1) ?? path;
|
|
174
|
-
}
|
|
175
|
-
|
|
176
193
|
function validateBlockStyleOverrides(
|
|
177
194
|
block: BlockInstance,
|
|
178
195
|
exposedStyleSlots: string[],
|