@shoppexio/builder-contracts 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/builder-contracts.test.d.ts +2 -0
- package/dist/builder-contracts.test.d.ts.map +1 -0
- package/dist/builder-contracts.test.js +361 -0
- package/dist/builder-settings.d.ts +801 -0
- package/dist/builder-settings.d.ts.map +1 -0
- package/dist/builder-settings.js +65 -0
- package/dist/events.d.ts +512 -0
- package/dist/events.d.ts.map +1 -0
- package/dist/events.js +104 -0
- package/dist/fields.d.ts +300 -0
- package/dist/fields.d.ts.map +1 -0
- package/dist/fields.js +111 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +9 -0
- package/dist/legacy-manifest.d.ts +172 -0
- package/dist/legacy-manifest.d.ts.map +1 -0
- package/dist/legacy-manifest.js +272 -0
- package/dist/migrations.d.ts +31 -0
- package/dist/migrations.d.ts.map +1 -0
- package/dist/migrations.js +175 -0
- package/dist/preview-protocol.d.ts +687 -0
- package/dist/preview-protocol.d.ts.map +1 -0
- package/dist/preview-protocol.js +79 -0
- package/dist/style-slots.d.ts +209 -0
- package/dist/style-slots.d.ts.map +1 -0
- package/dist/style-slots.js +93 -0
- package/dist/theme-manifest.d.ts +845 -0
- package/dist/theme-manifest.d.ts.map +1 -0
- package/dist/theme-manifest.js +119 -0
- package/dist/validation.d.ts +16 -0
- package/dist/validation.d.ts.map +1 -0
- package/dist/validation.js +131 -0
- package/package.json +95 -0
- package/src/builder-contracts.test.ts +405 -0
- package/src/builder-settings.ts +85 -0
- package/src/events.ts +121 -0
- package/src/fields.ts +134 -0
- package/src/index.ts +9 -0
- package/src/legacy-manifest.ts +321 -0
- package/src/migrations.ts +240 -0
- package/src/preview-protocol.ts +93 -0
- package/src/style-slots.ts +111 -0
- package/src/theme-manifest.ts +140 -0
- package/src/validation.ts +196 -0
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import * as z from 'zod/v4';
|
|
2
|
+
import { type ThemeManifest } from './theme-manifest.ts';
|
|
3
|
+
export declare const LegacyBuilderFieldSchema: z.ZodObject<{
|
|
4
|
+
path: z.ZodString;
|
|
5
|
+
type: z.ZodOptional<z.ZodString>;
|
|
6
|
+
label: z.ZodString;
|
|
7
|
+
placeholder: z.ZodOptional<z.ZodString>;
|
|
8
|
+
defaultValue: z.ZodOptional<z.ZodUnknown>;
|
|
9
|
+
description: z.ZodOptional<z.ZodString>;
|
|
10
|
+
options: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
11
|
+
label: z.ZodString;
|
|
12
|
+
value: z.ZodString;
|
|
13
|
+
}, z.core.$loose>>>;
|
|
14
|
+
min: z.ZodOptional<z.ZodNumber>;
|
|
15
|
+
max: z.ZodOptional<z.ZodNumber>;
|
|
16
|
+
step: z.ZodOptional<z.ZodNumber>;
|
|
17
|
+
}, z.core.$loose>;
|
|
18
|
+
export type LegacyBuilderField = z.infer<typeof LegacyBuilderFieldSchema>;
|
|
19
|
+
export declare const LegacyBuilderListSchema: z.ZodObject<{
|
|
20
|
+
path: z.ZodString;
|
|
21
|
+
label: z.ZodString;
|
|
22
|
+
kind: z.ZodString;
|
|
23
|
+
description: z.ZodOptional<z.ZodString>;
|
|
24
|
+
}, z.core.$loose>;
|
|
25
|
+
export type LegacyBuilderList = z.infer<typeof LegacyBuilderListSchema>;
|
|
26
|
+
export declare const LegacyBuilderBlockSchema: z.ZodObject<{
|
|
27
|
+
id: z.ZodOptional<z.ZodString>;
|
|
28
|
+
label: z.ZodString;
|
|
29
|
+
maxInstances: z.ZodOptional<z.ZodNumber>;
|
|
30
|
+
mountable: z.ZodOptional<z.ZodBoolean>;
|
|
31
|
+
fields: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
32
|
+
path: z.ZodString;
|
|
33
|
+
type: z.ZodOptional<z.ZodString>;
|
|
34
|
+
label: z.ZodString;
|
|
35
|
+
placeholder: z.ZodOptional<z.ZodString>;
|
|
36
|
+
defaultValue: z.ZodOptional<z.ZodUnknown>;
|
|
37
|
+
description: z.ZodOptional<z.ZodString>;
|
|
38
|
+
options: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
39
|
+
label: z.ZodString;
|
|
40
|
+
value: z.ZodString;
|
|
41
|
+
}, z.core.$loose>>>;
|
|
42
|
+
min: z.ZodOptional<z.ZodNumber>;
|
|
43
|
+
max: z.ZodOptional<z.ZodNumber>;
|
|
44
|
+
step: z.ZodOptional<z.ZodNumber>;
|
|
45
|
+
}, z.core.$loose>>>;
|
|
46
|
+
lists: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
47
|
+
path: z.ZodString;
|
|
48
|
+
label: z.ZodString;
|
|
49
|
+
kind: z.ZodString;
|
|
50
|
+
description: z.ZodOptional<z.ZodString>;
|
|
51
|
+
}, z.core.$loose>>>;
|
|
52
|
+
}, z.core.$loose>;
|
|
53
|
+
export type LegacyBuilderBlock = z.infer<typeof LegacyBuilderBlockSchema>;
|
|
54
|
+
export declare const LegacyBuilderPageSchema: z.ZodObject<{
|
|
55
|
+
id: z.ZodString;
|
|
56
|
+
label: z.ZodString;
|
|
57
|
+
previewPath: z.ZodOptional<z.ZodString>;
|
|
58
|
+
blocks: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
59
|
+
fields: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
60
|
+
path: z.ZodString;
|
|
61
|
+
type: z.ZodOptional<z.ZodString>;
|
|
62
|
+
label: z.ZodString;
|
|
63
|
+
placeholder: z.ZodOptional<z.ZodString>;
|
|
64
|
+
defaultValue: z.ZodOptional<z.ZodUnknown>;
|
|
65
|
+
description: z.ZodOptional<z.ZodString>;
|
|
66
|
+
options: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
67
|
+
label: z.ZodString;
|
|
68
|
+
value: z.ZodString;
|
|
69
|
+
}, z.core.$loose>>>;
|
|
70
|
+
min: z.ZodOptional<z.ZodNumber>;
|
|
71
|
+
max: z.ZodOptional<z.ZodNumber>;
|
|
72
|
+
step: z.ZodOptional<z.ZodNumber>;
|
|
73
|
+
}, z.core.$loose>>>;
|
|
74
|
+
lists: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
75
|
+
path: z.ZodString;
|
|
76
|
+
label: z.ZodString;
|
|
77
|
+
kind: z.ZodString;
|
|
78
|
+
description: z.ZodOptional<z.ZodString>;
|
|
79
|
+
}, z.core.$loose>>>;
|
|
80
|
+
}, z.core.$loose>;
|
|
81
|
+
export type LegacyBuilderPage = z.infer<typeof LegacyBuilderPageSchema>;
|
|
82
|
+
declare const LegacyThemePresetSchema: z.ZodObject<{
|
|
83
|
+
id: z.ZodString;
|
|
84
|
+
name: z.ZodOptional<z.ZodString>;
|
|
85
|
+
label: z.ZodOptional<z.ZodString>;
|
|
86
|
+
description: z.ZodOptional<z.ZodString>;
|
|
87
|
+
overrides: z.ZodOptional<z.ZodObject<{
|
|
88
|
+
content: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
89
|
+
layout: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
90
|
+
style_slots: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
91
|
+
styleSlots: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
92
|
+
}, z.core.$loose>>;
|
|
93
|
+
}, z.core.$loose>;
|
|
94
|
+
export type LegacyThemePreset = z.infer<typeof LegacyThemePresetSchema>;
|
|
95
|
+
export declare const LegacyThemeManifestSchema: z.ZodObject<{
|
|
96
|
+
id: z.ZodString;
|
|
97
|
+
name: z.ZodString;
|
|
98
|
+
version: z.ZodString;
|
|
99
|
+
presets: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
100
|
+
id: z.ZodString;
|
|
101
|
+
name: z.ZodOptional<z.ZodString>;
|
|
102
|
+
label: z.ZodOptional<z.ZodString>;
|
|
103
|
+
description: z.ZodOptional<z.ZodString>;
|
|
104
|
+
overrides: z.ZodOptional<z.ZodObject<{
|
|
105
|
+
content: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
106
|
+
layout: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
107
|
+
style_slots: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
108
|
+
styleSlots: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
109
|
+
}, z.core.$loose>>;
|
|
110
|
+
}, z.core.$loose>>>;
|
|
111
|
+
builder: z.ZodObject<{
|
|
112
|
+
pages: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
113
|
+
id: z.ZodString;
|
|
114
|
+
label: z.ZodString;
|
|
115
|
+
previewPath: z.ZodOptional<z.ZodString>;
|
|
116
|
+
blocks: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
117
|
+
fields: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
118
|
+
path: z.ZodString;
|
|
119
|
+
type: z.ZodOptional<z.ZodString>;
|
|
120
|
+
label: z.ZodString;
|
|
121
|
+
placeholder: z.ZodOptional<z.ZodString>;
|
|
122
|
+
defaultValue: z.ZodOptional<z.ZodUnknown>;
|
|
123
|
+
description: z.ZodOptional<z.ZodString>;
|
|
124
|
+
options: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
125
|
+
label: z.ZodString;
|
|
126
|
+
value: z.ZodString;
|
|
127
|
+
}, z.core.$loose>>>;
|
|
128
|
+
min: z.ZodOptional<z.ZodNumber>;
|
|
129
|
+
max: z.ZodOptional<z.ZodNumber>;
|
|
130
|
+
step: z.ZodOptional<z.ZodNumber>;
|
|
131
|
+
}, z.core.$loose>>>;
|
|
132
|
+
lists: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
133
|
+
path: z.ZodString;
|
|
134
|
+
label: z.ZodString;
|
|
135
|
+
kind: z.ZodString;
|
|
136
|
+
description: z.ZodOptional<z.ZodString>;
|
|
137
|
+
}, z.core.$loose>>>;
|
|
138
|
+
}, z.core.$loose>>>;
|
|
139
|
+
blocks: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
140
|
+
id: z.ZodOptional<z.ZodString>;
|
|
141
|
+
label: z.ZodString;
|
|
142
|
+
maxInstances: z.ZodOptional<z.ZodNumber>;
|
|
143
|
+
mountable: z.ZodOptional<z.ZodBoolean>;
|
|
144
|
+
fields: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
145
|
+
path: z.ZodString;
|
|
146
|
+
type: z.ZodOptional<z.ZodString>;
|
|
147
|
+
label: z.ZodString;
|
|
148
|
+
placeholder: z.ZodOptional<z.ZodString>;
|
|
149
|
+
defaultValue: z.ZodOptional<z.ZodUnknown>;
|
|
150
|
+
description: z.ZodOptional<z.ZodString>;
|
|
151
|
+
options: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
152
|
+
label: z.ZodString;
|
|
153
|
+
value: z.ZodString;
|
|
154
|
+
}, z.core.$loose>>>;
|
|
155
|
+
min: z.ZodOptional<z.ZodNumber>;
|
|
156
|
+
max: z.ZodOptional<z.ZodNumber>;
|
|
157
|
+
step: z.ZodOptional<z.ZodNumber>;
|
|
158
|
+
}, z.core.$loose>>>;
|
|
159
|
+
lists: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
160
|
+
path: z.ZodString;
|
|
161
|
+
label: z.ZodString;
|
|
162
|
+
kind: z.ZodString;
|
|
163
|
+
description: z.ZodOptional<z.ZodString>;
|
|
164
|
+
}, z.core.$loose>>>;
|
|
165
|
+
}, z.core.$loose>>>;
|
|
166
|
+
presets: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
167
|
+
}, z.core.$loose>;
|
|
168
|
+
}, z.core.$loose>;
|
|
169
|
+
export type LegacyThemeManifest = z.infer<typeof LegacyThemeManifestSchema>;
|
|
170
|
+
export declare function convertLegacyThemeManifest(input: unknown): ThemeManifest;
|
|
171
|
+
export {};
|
|
172
|
+
//# sourceMappingURL=legacy-manifest.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"legacy-manifest.d.ts","sourceRoot":"","sources":["../src/legacy-manifest.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,QAAQ,CAAC;AAE5B,OAAO,EAAuB,KAAK,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAS9E,eAAO,MAAM,wBAAwB;;;;;;;;;;;;;;iBAarB,CAAC;AACjB,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,wBAAwB,CAAC,CAAC;AAE1E,eAAO,MAAM,uBAAuB;;;;;iBAOpB,CAAC;AACjB,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAC;AAExE,eAAO,MAAM,wBAAwB;;;;;;;;;;;;;;;;;;;;;;;;;;iBASrB,CAAC;AACjB,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,wBAAwB,CAAC,CAAC;AAE1E,eAAO,MAAM,uBAAuB;;;;;;;;;;;;;;;;;;;;;;;;;;iBASpB,CAAC;AACjB,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAC;AAExE,QAAA,MAAM,uBAAuB;;;;;;;;;;;iBAgBb,CAAC;AACjB,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAC;AAExE,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBActB,CAAC;AACjB,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,CAAC;AAE5E,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,OAAO,GAAG,aAAa,CAkExE"}
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
import * as z from 'zod/v4';
|
|
2
|
+
import { BuilderFieldSchema } from "./fields.js";
|
|
3
|
+
import { ThemeManifestSchema } from "./theme-manifest.js";
|
|
4
|
+
const LegacyFieldOptionSchema = z
|
|
5
|
+
.object({
|
|
6
|
+
label: z.string().min(1),
|
|
7
|
+
value: z.string().min(1),
|
|
8
|
+
})
|
|
9
|
+
.passthrough();
|
|
10
|
+
export const LegacyBuilderFieldSchema = z
|
|
11
|
+
.object({
|
|
12
|
+
path: z.string().min(1),
|
|
13
|
+
type: z.string().min(1).optional(),
|
|
14
|
+
label: z.string().min(1),
|
|
15
|
+
placeholder: z.string().optional(),
|
|
16
|
+
defaultValue: z.unknown().optional(),
|
|
17
|
+
description: z.string().optional(),
|
|
18
|
+
options: z.array(LegacyFieldOptionSchema).optional(),
|
|
19
|
+
min: z.number().optional(),
|
|
20
|
+
max: z.number().optional(),
|
|
21
|
+
step: z.number().positive().optional(),
|
|
22
|
+
})
|
|
23
|
+
.passthrough();
|
|
24
|
+
export const LegacyBuilderListSchema = z
|
|
25
|
+
.object({
|
|
26
|
+
path: z.string().min(1),
|
|
27
|
+
label: z.string().min(1),
|
|
28
|
+
kind: z.string().min(1),
|
|
29
|
+
description: z.string().optional(),
|
|
30
|
+
})
|
|
31
|
+
.passthrough();
|
|
32
|
+
export const LegacyBuilderBlockSchema = z
|
|
33
|
+
.object({
|
|
34
|
+
id: z.string().min(1).optional(),
|
|
35
|
+
label: z.string().min(1),
|
|
36
|
+
maxInstances: z.number().int().positive().optional(),
|
|
37
|
+
mountable: z.boolean().optional(),
|
|
38
|
+
fields: z.array(LegacyBuilderFieldSchema).optional(),
|
|
39
|
+
lists: z.array(LegacyBuilderListSchema).optional(),
|
|
40
|
+
})
|
|
41
|
+
.passthrough();
|
|
42
|
+
export const LegacyBuilderPageSchema = z
|
|
43
|
+
.object({
|
|
44
|
+
id: z.string().min(1),
|
|
45
|
+
label: z.string().min(1),
|
|
46
|
+
previewPath: z.string().min(1).optional(),
|
|
47
|
+
blocks: z.array(z.string().min(1)).default([]),
|
|
48
|
+
fields: z.array(LegacyBuilderFieldSchema).optional(),
|
|
49
|
+
lists: z.array(LegacyBuilderListSchema).optional(),
|
|
50
|
+
})
|
|
51
|
+
.passthrough();
|
|
52
|
+
const LegacyThemePresetSchema = z
|
|
53
|
+
.object({
|
|
54
|
+
id: z.string().min(1),
|
|
55
|
+
name: z.string().min(1).optional(),
|
|
56
|
+
label: z.string().min(1).optional(),
|
|
57
|
+
description: z.string().optional(),
|
|
58
|
+
overrides: z
|
|
59
|
+
.object({
|
|
60
|
+
content: z.record(z.string().min(1), z.unknown()).optional(),
|
|
61
|
+
layout: z.record(z.string().min(1), z.unknown()).optional(),
|
|
62
|
+
style_slots: z.record(z.string().min(1), z.unknown()).optional(),
|
|
63
|
+
styleSlots: z.record(z.string().min(1), z.unknown()).optional(),
|
|
64
|
+
})
|
|
65
|
+
.passthrough()
|
|
66
|
+
.optional(),
|
|
67
|
+
})
|
|
68
|
+
.passthrough();
|
|
69
|
+
export const LegacyThemeManifestSchema = z
|
|
70
|
+
.object({
|
|
71
|
+
id: z.string().min(1),
|
|
72
|
+
name: z.string().min(1),
|
|
73
|
+
version: z.string().min(1),
|
|
74
|
+
presets: z.array(LegacyThemePresetSchema).optional(),
|
|
75
|
+
builder: z
|
|
76
|
+
.object({
|
|
77
|
+
pages: z.array(LegacyBuilderPageSchema).default([]),
|
|
78
|
+
blocks: z.record(z.string().min(1), LegacyBuilderBlockSchema).default({}),
|
|
79
|
+
presets: z.record(z.string().min(1), z.unknown()).optional(),
|
|
80
|
+
})
|
|
81
|
+
.passthrough(),
|
|
82
|
+
})
|
|
83
|
+
.passthrough();
|
|
84
|
+
export function convertLegacyThemeManifest(input) {
|
|
85
|
+
const legacy = LegacyThemeManifestSchema.parse(input);
|
|
86
|
+
const blocks = Object.fromEntries(Object.entries(legacy.builder.blocks).map(([blockType, block]) => [
|
|
87
|
+
blockType,
|
|
88
|
+
{
|
|
89
|
+
label: block.label,
|
|
90
|
+
maxInstances: block.maxInstances,
|
|
91
|
+
variants: [],
|
|
92
|
+
settings: convertLegacyFields(block.fields ?? [], block.lists ?? []),
|
|
93
|
+
exposedStyleSlots: [],
|
|
94
|
+
presets: [],
|
|
95
|
+
},
|
|
96
|
+
]));
|
|
97
|
+
for (const page of legacy.builder.pages) {
|
|
98
|
+
const syntheticBlockType = createSyntheticPageBlockType(page.id);
|
|
99
|
+
if ((page.fields?.length ?? 0) === 0 && (page.lists?.length ?? 0) === 0) {
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
blocks[syntheticBlockType] = {
|
|
103
|
+
label: page.label,
|
|
104
|
+
maxInstances: 1,
|
|
105
|
+
variants: [],
|
|
106
|
+
settings: convertLegacyFields(page.fields ?? [], page.lists ?? []),
|
|
107
|
+
exposedStyleSlots: [],
|
|
108
|
+
presets: [],
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
const converted = {
|
|
112
|
+
id: normalizeLegacyThemeId(legacy.id),
|
|
113
|
+
name: legacy.name,
|
|
114
|
+
version: legacy.version,
|
|
115
|
+
pages: Object.fromEntries(legacy.builder.pages.map((page) => {
|
|
116
|
+
const syntheticBlockType = createSyntheticPageBlockType(page.id);
|
|
117
|
+
const hasSyntheticBlock = blocks[syntheticBlockType] !== undefined;
|
|
118
|
+
const allowedBlocks = page.blocks.length > 0
|
|
119
|
+
? page.blocks
|
|
120
|
+
: (hasSyntheticBlock ? [syntheticBlockType] : []);
|
|
121
|
+
// Seed synthetic page-level blocks as defaults so pages that
|
|
122
|
+
// only surface page.fields/lists still open with an editable
|
|
123
|
+
// block instance instead of an empty editor.
|
|
124
|
+
const defaultBlocks = page.blocks.length > 0
|
|
125
|
+
? page.blocks.map((type) => ({ type }))
|
|
126
|
+
: (hasSyntheticBlock ? [{ type: syntheticBlockType }] : []);
|
|
127
|
+
return [
|
|
128
|
+
page.id,
|
|
129
|
+
{
|
|
130
|
+
label: page.label,
|
|
131
|
+
previewPath: page.previewPath,
|
|
132
|
+
allowedBlocks,
|
|
133
|
+
defaultBlocks,
|
|
134
|
+
},
|
|
135
|
+
];
|
|
136
|
+
})),
|
|
137
|
+
blocks,
|
|
138
|
+
styleSlots: {},
|
|
139
|
+
presets: convertLegacyThemePresets(legacy.presets ?? []),
|
|
140
|
+
};
|
|
141
|
+
return ThemeManifestSchema.parse(converted);
|
|
142
|
+
}
|
|
143
|
+
function normalizeLegacyThemeId(rawId) {
|
|
144
|
+
const lowered = rawId.toLowerCase().replace(/[^a-z0-9\-_.]+/g, '-').replace(/^[-_.]+/, '');
|
|
145
|
+
if (lowered.length > 0 && /^[a-z0-9]/.test(lowered)) {
|
|
146
|
+
return lowered;
|
|
147
|
+
}
|
|
148
|
+
return `legacy-${lowered || 'theme'}`;
|
|
149
|
+
}
|
|
150
|
+
function createSyntheticPageBlockType(pageId) {
|
|
151
|
+
const normalizedPageId = pageId.toLowerCase().replace(/[^a-z0-9\-_.]+/g, '-').replace(/^[-_.]+/, '');
|
|
152
|
+
return `page-${normalizedPageId || 'custom'}`;
|
|
153
|
+
}
|
|
154
|
+
function convertLegacyThemePresets(presets) {
|
|
155
|
+
return Object.fromEntries(presets.map((preset) => [
|
|
156
|
+
preset.id,
|
|
157
|
+
{
|
|
158
|
+
label: preset.name ?? preset.label ?? preset.id,
|
|
159
|
+
description: preset.description,
|
|
160
|
+
content: flattenContentRecord(preset.overrides?.content ?? {}),
|
|
161
|
+
layout: preset.overrides?.layout ?? {},
|
|
162
|
+
style_slots: preset.overrides?.style_slots ?? preset.overrides?.styleSlots ?? {},
|
|
163
|
+
},
|
|
164
|
+
]));
|
|
165
|
+
}
|
|
166
|
+
function flattenContentRecord(content, prefix = '') {
|
|
167
|
+
const flattened = {};
|
|
168
|
+
for (const [key, value] of Object.entries(content)) {
|
|
169
|
+
const path = prefix ? `${prefix}.${key}` : key;
|
|
170
|
+
if (isPlainObject(value)) {
|
|
171
|
+
Object.assign(flattened, flattenContentRecord(value, path));
|
|
172
|
+
}
|
|
173
|
+
else {
|
|
174
|
+
flattened[path] = value;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
return flattened;
|
|
178
|
+
}
|
|
179
|
+
function isPlainObject(value) {
|
|
180
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
181
|
+
}
|
|
182
|
+
function convertLegacyFields(fields, lists) {
|
|
183
|
+
const convertedFields = Object.fromEntries(fields.map((field) => [field.path, convertLegacyField(field)]));
|
|
184
|
+
const convertedLists = Object.fromEntries(lists.map((list) => [
|
|
185
|
+
list.path,
|
|
186
|
+
{
|
|
187
|
+
type: 'list',
|
|
188
|
+
label: list.label,
|
|
189
|
+
description: list.description,
|
|
190
|
+
itemShape: createListItemShape(list.kind),
|
|
191
|
+
},
|
|
192
|
+
]));
|
|
193
|
+
return {
|
|
194
|
+
...convertedFields,
|
|
195
|
+
...convertedLists,
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
function convertLegacyField(field) {
|
|
199
|
+
const type = normalizeLegacyFieldType(field.type);
|
|
200
|
+
const base = {
|
|
201
|
+
label: field.label,
|
|
202
|
+
description: field.description,
|
|
203
|
+
defaultValue: field.defaultValue,
|
|
204
|
+
};
|
|
205
|
+
const candidate = type === 'select'
|
|
206
|
+
? {
|
|
207
|
+
...base,
|
|
208
|
+
type,
|
|
209
|
+
options: (field.options ?? []).map((option) => ({
|
|
210
|
+
label: option.label,
|
|
211
|
+
value: option.value,
|
|
212
|
+
})),
|
|
213
|
+
}
|
|
214
|
+
: type === 'range'
|
|
215
|
+
? {
|
|
216
|
+
...base,
|
|
217
|
+
type,
|
|
218
|
+
min: field.min ?? 0,
|
|
219
|
+
max: field.max ?? 100,
|
|
220
|
+
step: field.step,
|
|
221
|
+
}
|
|
222
|
+
: type === 'text'
|
|
223
|
+
? {
|
|
224
|
+
...base,
|
|
225
|
+
type,
|
|
226
|
+
placeholder: field.placeholder,
|
|
227
|
+
}
|
|
228
|
+
: {
|
|
229
|
+
...base,
|
|
230
|
+
type,
|
|
231
|
+
};
|
|
232
|
+
return BuilderFieldSchema.parse(candidate);
|
|
233
|
+
}
|
|
234
|
+
function normalizeLegacyFieldType(type) {
|
|
235
|
+
if (type === 'richtext' ||
|
|
236
|
+
type === 'image' ||
|
|
237
|
+
type === 'link' ||
|
|
238
|
+
type === 'boolean' ||
|
|
239
|
+
type === 'number' ||
|
|
240
|
+
type === 'range' ||
|
|
241
|
+
type === 'select' ||
|
|
242
|
+
type === 'color' ||
|
|
243
|
+
type === 'product' ||
|
|
244
|
+
type === 'products') {
|
|
245
|
+
return type;
|
|
246
|
+
}
|
|
247
|
+
return 'text';
|
|
248
|
+
}
|
|
249
|
+
function createListItemShape(kind) {
|
|
250
|
+
if (kind === 'faqItems') {
|
|
251
|
+
return {
|
|
252
|
+
question: { type: 'text', label: 'Question' },
|
|
253
|
+
answer: { type: 'richtext', label: 'Answer' },
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
if (kind === 'galleryItems') {
|
|
257
|
+
return {
|
|
258
|
+
image: { type: 'image', label: 'Image' },
|
|
259
|
+
alt: { type: 'text', label: 'Alt Text' },
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
if (kind === 'reviewItems') {
|
|
263
|
+
return {
|
|
264
|
+
quote: { type: 'richtext', label: 'Quote' },
|
|
265
|
+
author: { type: 'text', label: 'Author' },
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
return {
|
|
269
|
+
title: { type: 'text', label: 'Title' },
|
|
270
|
+
body: { type: 'richtext', label: 'Body' },
|
|
271
|
+
};
|
|
272
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { type BlockInstance, type BuilderSettings, type PageLayout } from './builder-settings.ts';
|
|
2
|
+
import type { StyleSlots } from './style-slots.ts';
|
|
3
|
+
import type { ThemeManifest } from './theme-manifest.ts';
|
|
4
|
+
export type LegacyBuilderSettingsInput = {
|
|
5
|
+
theme?: {
|
|
6
|
+
content?: Record<string, unknown>;
|
|
7
|
+
layout?: Record<string, unknown>;
|
|
8
|
+
tokens_override?: Record<string, unknown>;
|
|
9
|
+
style_slots?: Record<string, unknown>;
|
|
10
|
+
};
|
|
11
|
+
};
|
|
12
|
+
export type LegacyRedisOverride = {
|
|
13
|
+
target?: string;
|
|
14
|
+
property?: string;
|
|
15
|
+
value?: unknown;
|
|
16
|
+
className?: string;
|
|
17
|
+
};
|
|
18
|
+
export declare function createEmptyBuilderSettings(revision?: number): BuilderSettings;
|
|
19
|
+
export declare function createBlockInstance(input: {
|
|
20
|
+
id: string;
|
|
21
|
+
type: string;
|
|
22
|
+
variant?: string;
|
|
23
|
+
visible?: boolean;
|
|
24
|
+
settings?: Record<string, unknown>;
|
|
25
|
+
style_overrides?: StyleSlots;
|
|
26
|
+
}): BlockInstance;
|
|
27
|
+
export declare function createPageLayoutFromBlocks(blocks: BlockInstance[]): PageLayout;
|
|
28
|
+
export declare function migrateLegacyBuilderSettings(input: LegacyBuilderSettingsInput, revision?: number): BuilderSettings;
|
|
29
|
+
export declare function applyManifestDefaultLayout(settings: BuilderSettings, manifest: ThemeManifest): BuilderSettings;
|
|
30
|
+
export declare function mapLegacyTokenOverridesToStyleSlots(tokens: Record<string, unknown>): StyleSlots;
|
|
31
|
+
//# sourceMappingURL=migrations.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"migrations.d.ts","sourceRoot":"","sources":["../src/migrations.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,aAAa,EAClB,KAAK,eAAe,EAEpB,KAAK,UAAU,EAChB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEzD,MAAM,MAAM,0BAA0B,GAAG;IACvC,KAAK,CAAC,EAAE;QACN,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAClC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QACjC,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAC1C,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;KACvC,CAAC;CACH,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,wBAAgB,0BAA0B,CAAC,QAAQ,SAAI,GAAG,eAAe,CAYxE;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE;IACzC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACnC,eAAe,CAAC,EAAE,UAAU,CAAC;CAC9B,GAAG,aAAa,CAShB;AAED,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,aAAa,EAAE,GAAG,UAAU,CAE9E;AAED,wBAAgB,4BAA4B,CAAC,KAAK,EAAE,0BAA0B,EAAE,QAAQ,SAAI,GAAG,eAAe,CAkB7G;AAED,wBAAgB,0BAA0B,CAAC,QAAQ,EAAE,eAAe,EAAE,QAAQ,EAAE,aAAa,GAAG,eAAe,CAwB9G;AAED,wBAAgB,mCAAmC,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,UAAU,CAa/F"}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import { BUILDER_SETTINGS_VERSION, BuilderSettingsSchema, } from "./builder-settings.js";
|
|
2
|
+
export function createEmptyBuilderSettings(revision = 0) {
|
|
3
|
+
return {
|
|
4
|
+
version: BUILDER_SETTINGS_VERSION,
|
|
5
|
+
revision,
|
|
6
|
+
theme: {
|
|
7
|
+
content: {},
|
|
8
|
+
layout: {},
|
|
9
|
+
style_slots: {},
|
|
10
|
+
pages: [],
|
|
11
|
+
terms: {},
|
|
12
|
+
},
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
export function createBlockInstance(input) {
|
|
16
|
+
return {
|
|
17
|
+
id: input.id,
|
|
18
|
+
type: input.type,
|
|
19
|
+
variant: input.variant,
|
|
20
|
+
visible: input.visible ?? true,
|
|
21
|
+
settings: input.settings ?? {},
|
|
22
|
+
style_overrides: input.style_overrides,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
export function createPageLayoutFromBlocks(blocks) {
|
|
26
|
+
return { blocks };
|
|
27
|
+
}
|
|
28
|
+
export function migrateLegacyBuilderSettings(input, revision = 0) {
|
|
29
|
+
const tokensOverride = input.theme?.tokens_override ?? {};
|
|
30
|
+
const styleSlots = {
|
|
31
|
+
...mapLegacyTokenOverridesToStyleSlots(tokensOverride),
|
|
32
|
+
...(input.theme?.style_slots ?? {}),
|
|
33
|
+
};
|
|
34
|
+
return BuilderSettingsSchema.parse({
|
|
35
|
+
version: BUILDER_SETTINGS_VERSION,
|
|
36
|
+
revision,
|
|
37
|
+
theme: {
|
|
38
|
+
content: input.theme?.content ?? {},
|
|
39
|
+
layout: normalizeLegacyLayout(input.theme?.layout ?? {}),
|
|
40
|
+
style_slots: styleSlots,
|
|
41
|
+
pages: [],
|
|
42
|
+
terms: {},
|
|
43
|
+
},
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
export function applyManifestDefaultLayout(settings, manifest) {
|
|
47
|
+
const next = cloneBuilderSettings(settings);
|
|
48
|
+
for (const [pageId, page] of Object.entries(manifest.pages)) {
|
|
49
|
+
// Only seed defaults when the page has never been initialized
|
|
50
|
+
// (no layout entry at all). An explicit empty layout means the
|
|
51
|
+
// merchant intentionally removed all sections and must be
|
|
52
|
+
// preserved across reloads.
|
|
53
|
+
const hasPageLayoutEntry = Object.prototype.hasOwnProperty.call(next.theme.layout, pageId);
|
|
54
|
+
if (hasPageLayoutEntry || page.defaultBlocks.length === 0) {
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
next.theme.layout[pageId] = {
|
|
58
|
+
blocks: page.defaultBlocks.map((block, index) => createBlockInstance({
|
|
59
|
+
id: createDefaultBlockId(pageId, block.type, index),
|
|
60
|
+
type: block.type,
|
|
61
|
+
variant: block.variant,
|
|
62
|
+
settings: block.settings ?? {},
|
|
63
|
+
})),
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
return BuilderSettingsSchema.parse(next);
|
|
67
|
+
}
|
|
68
|
+
export function mapLegacyTokenOverridesToStyleSlots(tokens) {
|
|
69
|
+
const slots = {};
|
|
70
|
+
assignResponsiveNumber(tokens, slots, 'buttons.borderRadius', 'button.radius');
|
|
71
|
+
assignResponsiveNumber(tokens, slots, 'inputs.borderRadius', 'input.radius');
|
|
72
|
+
assignResponsiveNumber(tokens, slots, 'inputs.height', 'input.height');
|
|
73
|
+
assignColor(tokens, slots, 'colors.primary', 'color.primary');
|
|
74
|
+
assignColor(tokens, slots, 'colors.accent', 'color.accent');
|
|
75
|
+
assignColor(tokens, slots, 'colors.background', 'color.background');
|
|
76
|
+
assignColor(tokens, slots, 'colors.foreground', 'color.foreground');
|
|
77
|
+
assignColor(tokens, slots, 'colors.muted', 'color.muted');
|
|
78
|
+
return slots;
|
|
79
|
+
}
|
|
80
|
+
function normalizeLegacyLayout(layout) {
|
|
81
|
+
const normalized = {};
|
|
82
|
+
for (const [pageId, value] of Object.entries(layout)) {
|
|
83
|
+
if (isPageLayout(value)) {
|
|
84
|
+
normalized[pageId] = value;
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
if (Array.isArray(value)) {
|
|
88
|
+
normalized[pageId] = {
|
|
89
|
+
blocks: value.filter(isBlockInstance),
|
|
90
|
+
};
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
// Pre-v2 editor shape: { sections: [...] } with entries that
|
|
94
|
+
// commonly only carry { id, visible } (no type field). Use id as
|
|
95
|
+
// the type fallback so merchant layout/visibility survives the
|
|
96
|
+
// migration instead of being dropped by the stricter
|
|
97
|
+
// isBlockInstance check.
|
|
98
|
+
if (value && typeof value === 'object') {
|
|
99
|
+
const record = value;
|
|
100
|
+
const sections = Array.isArray(record.sections) ? record.sections : null;
|
|
101
|
+
if (sections) {
|
|
102
|
+
normalized[pageId] = {
|
|
103
|
+
blocks: sections.flatMap(normalizeLegacySection),
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return normalized;
|
|
109
|
+
}
|
|
110
|
+
function normalizeLegacySection(entry) {
|
|
111
|
+
if (!entry || typeof entry !== 'object')
|
|
112
|
+
return [];
|
|
113
|
+
const record = entry;
|
|
114
|
+
const rawType = typeof record.type === 'string' ? record.type.trim() : '';
|
|
115
|
+
const rawId = typeof record.id === 'string' ? record.id.trim() : '';
|
|
116
|
+
const type = rawType || rawId;
|
|
117
|
+
if (!type)
|
|
118
|
+
return [];
|
|
119
|
+
return [{
|
|
120
|
+
id: rawId || type,
|
|
121
|
+
type,
|
|
122
|
+
variant: typeof record.variant === 'string' ? record.variant : undefined,
|
|
123
|
+
visible: typeof record.visible === 'boolean' ? record.visible : true,
|
|
124
|
+
settings: isRecord(record.settings) ? record.settings : {},
|
|
125
|
+
}];
|
|
126
|
+
}
|
|
127
|
+
function isRecord(value) {
|
|
128
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
129
|
+
}
|
|
130
|
+
function assignResponsiveNumber(tokens, slots, tokenPath, slotId) {
|
|
131
|
+
const value = tokens[tokenPath];
|
|
132
|
+
if (typeof value === 'number' && Number.isFinite(value)) {
|
|
133
|
+
slots[slotId] = { base: value };
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
if (isResponsiveNumber(value)) {
|
|
137
|
+
slots[slotId] = value;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
function assignColor(tokens, slots, tokenPath, slotId) {
|
|
141
|
+
const value = tokens[tokenPath];
|
|
142
|
+
if (typeof value === 'string') {
|
|
143
|
+
slots[slotId] = value;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
function isResponsiveNumber(value) {
|
|
147
|
+
if (!value || typeof value !== 'object') {
|
|
148
|
+
return false;
|
|
149
|
+
}
|
|
150
|
+
const candidate = value;
|
|
151
|
+
return typeof candidate.base === 'number' && Number.isFinite(candidate.base);
|
|
152
|
+
}
|
|
153
|
+
function isBlockInstance(value) {
|
|
154
|
+
if (!value || typeof value !== 'object') {
|
|
155
|
+
return false;
|
|
156
|
+
}
|
|
157
|
+
const candidate = value;
|
|
158
|
+
return typeof candidate.id === 'string' && typeof candidate.type === 'string';
|
|
159
|
+
}
|
|
160
|
+
function isPageLayout(value) {
|
|
161
|
+
if (!value || typeof value !== 'object') {
|
|
162
|
+
return false;
|
|
163
|
+
}
|
|
164
|
+
const candidate = value;
|
|
165
|
+
return Array.isArray(candidate.blocks) && candidate.blocks.every(isBlockInstance);
|
|
166
|
+
}
|
|
167
|
+
function cloneBuilderSettings(settings) {
|
|
168
|
+
if (typeof structuredClone === 'function') {
|
|
169
|
+
return structuredClone(settings);
|
|
170
|
+
}
|
|
171
|
+
return JSON.parse(JSON.stringify(settings));
|
|
172
|
+
}
|
|
173
|
+
function createDefaultBlockId(pageId, blockType, index) {
|
|
174
|
+
return `${pageId}-${blockType}-${index + 1}`;
|
|
175
|
+
}
|