@shoppexio/builder-contracts 0.1.4 → 0.1.7
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/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/persistence.d.ts.map +1 -1
- package/dist/persistence.js +0 -3
- package/dist/preview-protocol.d.ts +28 -27
- package/dist/preview-protocol.d.ts.map +1 -1
- package/dist/preview-protocol.js +16 -7
- package/dist/storefront-render-frame-origins.d.ts +11 -0
- package/dist/storefront-render-frame-origins.d.ts.map +1 -0
- package/dist/storefront-render-frame-origins.js +23 -0
- package/dist/storefront-render-integrations.d.ts +10 -0
- package/dist/storefront-render-integrations.d.ts.map +1 -0
- package/dist/storefront-render-integrations.js +55 -0
- package/dist/storefront-render-seo.d.ts +11 -0
- package/dist/storefront-render-seo.d.ts.map +1 -0
- package/dist/storefront-render-seo.js +30 -0
- package/dist/theme-schemes.d.ts +2 -2
- package/dist/theme-schemes.d.ts.map +1 -1
- package/dist/theme-schemes.js +3 -3
- package/package.json +18 -6
- package/src/builder-contracts.test.ts +0 -50
- package/src/index.ts +2 -0
- package/src/persistence.ts +0 -3
- package/src/preview-protocol.test.ts +55 -7
- package/src/preview-protocol.ts +20 -8
- package/src/storefront-render-frame-origins.ts +26 -0
- package/src/storefront-render-integrations.test.ts +62 -0
- package/src/storefront-render-integrations.ts +81 -0
- package/src/storefront-render-seo.test.ts +33 -0
- package/src/storefront-render-seo.ts +47 -0
- package/src/theme-schemes.ts +3 -3
- package/dist/edge-block-engine-frame-origins.d.ts +0 -11
- package/dist/edge-block-engine-frame-origins.d.ts.map +0 -1
- package/dist/edge-block-engine-frame-origins.js +0 -23
- package/src/edge-block-engine-frame-origins.ts +0 -26
package/dist/index.d.ts
CHANGED
|
@@ -12,6 +12,8 @@ export * from './preview-protocol.ts';
|
|
|
12
12
|
export * from './preview-session-resolve.ts';
|
|
13
13
|
export * from './preview-trusted-origins.ts';
|
|
14
14
|
export * from './storefront-initial-data-html.ts';
|
|
15
|
+
export * from './storefront-render-integrations.ts';
|
|
16
|
+
export * from './storefront-render-seo.ts';
|
|
15
17
|
export * from './storefront-typography-fonts.ts';
|
|
16
18
|
export * from './style-slots.ts';
|
|
17
19
|
export * from './theme-manifest.ts';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC;AAClC,cAAc,sBAAsB,CAAC;AACrC,cAAc,uBAAuB,CAAC;AACtC,cAAc,yBAAyB,CAAC;AACxC,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,sBAAsB,CAAC;AACrC,cAAc,iBAAiB,CAAC;AAChC,cAAc,kBAAkB,CAAC;AACjC,cAAc,mBAAmB,CAAC;AAClC,cAAc,uBAAuB,CAAC;AACtC,cAAc,8BAA8B,CAAC;AAC7C,cAAc,8BAA8B,CAAC;AAC7C,cAAc,mCAAmC,CAAC;AAClD,cAAc,kCAAkC,CAAC;AACjD,cAAc,kBAAkB,CAAC;AACjC,cAAc,qBAAqB,CAAC;AACpC,cAAc,oBAAoB,CAAC;AACnC,cAAc,iBAAiB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC;AAClC,cAAc,sBAAsB,CAAC;AACrC,cAAc,uBAAuB,CAAC;AACtC,cAAc,yBAAyB,CAAC;AACxC,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,sBAAsB,CAAC;AACrC,cAAc,iBAAiB,CAAC;AAChC,cAAc,kBAAkB,CAAC;AACjC,cAAc,mBAAmB,CAAC;AAClC,cAAc,uBAAuB,CAAC;AACtC,cAAc,8BAA8B,CAAC;AAC7C,cAAc,8BAA8B,CAAC;AAC7C,cAAc,mCAAmC,CAAC;AAClD,cAAc,qCAAqC,CAAC;AACpD,cAAc,4BAA4B,CAAC;AAC3C,cAAc,kCAAkC,CAAC;AACjD,cAAc,kBAAkB,CAAC;AACjC,cAAc,qBAAqB,CAAC;AACpC,cAAc,oBAAoB,CAAC;AACnC,cAAc,iBAAiB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -12,6 +12,8 @@ export * from "./preview-protocol.js";
|
|
|
12
12
|
export * from "./preview-session-resolve.js";
|
|
13
13
|
export * from "./preview-trusted-origins.js";
|
|
14
14
|
export * from "./storefront-initial-data-html.js";
|
|
15
|
+
export * from "./storefront-render-integrations.js";
|
|
16
|
+
export * from "./storefront-render-seo.js";
|
|
15
17
|
export * from "./storefront-typography-fonts.js";
|
|
16
18
|
export * from "./style-slots.js";
|
|
17
19
|
export * from "./theme-manifest.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"persistence.d.ts","sourceRoot":"","sources":["../src/persistence.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,eAAe,EAErB,MAAM,uBAAuB,CAAC;AAQ/B,KAAK,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAmB1C,wBAAgB,+BAA+B,CAAC,KAAK,EAAE,OAAO,GAAG,eAAe,GAAG,IAAI,CAYtF;AA0BD;;;GAGG;AACH,wBAAgB,8BAA8B,CAAC,KAAK,EAAE,OAAO,GAAG,eAAe,GAAG,IAAI,CAkDrF;AAED;;GAEG;AACH,wBAAgB,gCAAgC,CAAC,mBAAmB,EAAE,OAAO,GAAG,UAAU,GAAG,IAAI,CAgBhG;AAED,wBAAgB,4BAA4B,CAAC,QAAQ,EAAE,eAAe,GAAG,eAAe,CASvF;AAaD,wBAAgB,qCAAqC,CACnD,eAAe,EAAE,OAAO,EACxB,eAAe,EAAE,eAAe,GAC/B,UAAU,
|
|
1
|
+
{"version":3,"file":"persistence.d.ts","sourceRoot":"","sources":["../src/persistence.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,eAAe,EAErB,MAAM,uBAAuB,CAAC;AAQ/B,KAAK,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAmB1C,wBAAgB,+BAA+B,CAAC,KAAK,EAAE,OAAO,GAAG,eAAe,GAAG,IAAI,CAYtF;AA0BD;;;GAGG;AACH,wBAAgB,8BAA8B,CAAC,KAAK,EAAE,OAAO,GAAG,eAAe,GAAG,IAAI,CAkDrF;AAED;;GAEG;AACH,wBAAgB,gCAAgC,CAAC,mBAAmB,EAAE,OAAO,GAAG,UAAU,GAAG,IAAI,CAgBhG;AAED,wBAAgB,4BAA4B,CAAC,QAAQ,EAAE,eAAe,GAAG,eAAe,CASvF;AAaD,wBAAgB,qCAAqC,CACnD,eAAe,EAAE,OAAO,EACxB,eAAe,EAAE,eAAe,GAC/B,UAAU,CAaZ"}
|
package/dist/persistence.js
CHANGED
|
@@ -128,9 +128,6 @@ export function mergeBuilderSettingsIntoThemeSettings(currentSettings, builderSe
|
|
|
128
128
|
const root = isRecord(currentSettings) ? { ...currentSettings } : {};
|
|
129
129
|
const currentBuilderSettings = isRecord(root.builder_settings) ? root.builder_settings : {};
|
|
130
130
|
const sanitizedBuilderSettings = sanitizeBuilderSettingsState(builderSettings);
|
|
131
|
-
root.version = sanitizedBuilderSettings.version;
|
|
132
|
-
root.revision = sanitizedBuilderSettings.revision;
|
|
133
|
-
root.theme = sanitizedBuilderSettings.theme;
|
|
134
131
|
root.builder_settings = {
|
|
135
132
|
...currentBuilderSettings,
|
|
136
133
|
version: sanitizedBuilderSettings.version,
|
|
@@ -66,8 +66,8 @@ export declare const PreviewApplyStateMessageSchema: z.ZodObject<{
|
|
|
66
66
|
}, z.core.$strict>;
|
|
67
67
|
}, z.core.$strict>;
|
|
68
68
|
}, z.core.$strict>;
|
|
69
|
-
export declare const
|
|
70
|
-
export type
|
|
69
|
+
export declare const ServerLiquidRenderModeSchema: z.ZodLiteral<"server-liquid">;
|
|
70
|
+
export type ServerLiquidRenderMode = z.infer<typeof ServerLiquidRenderModeSchema>;
|
|
71
71
|
export declare const PreviewBlockHtmlPatchSchema: z.ZodObject<{
|
|
72
72
|
blockId: z.ZodString;
|
|
73
73
|
html: z.ZodString;
|
|
@@ -78,16 +78,22 @@ export declare const PreviewApplyBlockHtmlMessageSchema: z.ZodObject<{
|
|
|
78
78
|
revision: z.ZodNumber;
|
|
79
79
|
pageId: z.ZodString;
|
|
80
80
|
sourceRevision: z.ZodString;
|
|
81
|
-
renderMode: z.ZodLiteral<"
|
|
82
|
-
scope: z.ZodOptional<z.ZodEnum<{
|
|
83
|
-
page: "page";
|
|
84
|
-
block: "block";
|
|
85
|
-
}>>;
|
|
81
|
+
renderMode: z.ZodLiteral<"server-liquid">;
|
|
86
82
|
blocks: z.ZodArray<z.ZodObject<{
|
|
87
83
|
blockId: z.ZodString;
|
|
88
84
|
html: z.ZodString;
|
|
89
85
|
}, z.core.$strict>>;
|
|
90
86
|
}, z.core.$strict>;
|
|
87
|
+
export type PreviewApplyBlockHtmlMessage = z.infer<typeof PreviewApplyBlockHtmlMessageSchema>;
|
|
88
|
+
export declare const PreviewApplyPageHtmlMessageSchema: z.ZodObject<{
|
|
89
|
+
type: z.ZodLiteral<"APPLY_PAGE_HTML">;
|
|
90
|
+
revision: z.ZodNumber;
|
|
91
|
+
pageId: z.ZodString;
|
|
92
|
+
sourceRevision: z.ZodString;
|
|
93
|
+
renderMode: z.ZodLiteral<"server-liquid">;
|
|
94
|
+
html: z.ZodString;
|
|
95
|
+
}, z.core.$strict>;
|
|
96
|
+
export type PreviewApplyPageHtmlMessage = z.infer<typeof PreviewApplyPageHtmlMessageSchema>;
|
|
91
97
|
export declare const PreviewReloadMessageSchema: z.ZodObject<{
|
|
92
98
|
type: z.ZodLiteral<"RELOAD">;
|
|
93
99
|
revision: z.ZodNumber;
|
|
@@ -191,15 +197,18 @@ export declare const PreviewMessageSchema: z.ZodDiscriminatedUnion<[z.ZodObject<
|
|
|
191
197
|
revision: z.ZodNumber;
|
|
192
198
|
pageId: z.ZodString;
|
|
193
199
|
sourceRevision: z.ZodString;
|
|
194
|
-
renderMode: z.ZodLiteral<"
|
|
195
|
-
scope: z.ZodOptional<z.ZodEnum<{
|
|
196
|
-
page: "page";
|
|
197
|
-
block: "block";
|
|
198
|
-
}>>;
|
|
200
|
+
renderMode: z.ZodLiteral<"server-liquid">;
|
|
199
201
|
blocks: z.ZodArray<z.ZodObject<{
|
|
200
202
|
blockId: z.ZodString;
|
|
201
203
|
html: z.ZodString;
|
|
202
204
|
}, z.core.$strict>>;
|
|
205
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
206
|
+
type: z.ZodLiteral<"APPLY_PAGE_HTML">;
|
|
207
|
+
revision: z.ZodNumber;
|
|
208
|
+
pageId: z.ZodString;
|
|
209
|
+
sourceRevision: z.ZodString;
|
|
210
|
+
renderMode: z.ZodLiteral<"server-liquid">;
|
|
211
|
+
html: z.ZodString;
|
|
203
212
|
}, z.core.$strict>, z.ZodObject<{
|
|
204
213
|
type: z.ZodLiteral<"RELOAD">;
|
|
205
214
|
revision: z.ZodNumber;
|
|
@@ -250,7 +259,7 @@ export declare const PreviewReadyHealthSchema: z.ZodDiscriminatedUnion<[z.ZodObj
|
|
|
250
259
|
builderRuntimeProvider: z.ZodLiteral<true>;
|
|
251
260
|
protocolVersion: z.ZodLiteral<2>;
|
|
252
261
|
}, z.core.$strict>, z.ZodObject<{
|
|
253
|
-
|
|
262
|
+
serverLiquidEngine: z.ZodLiteral<true>;
|
|
254
263
|
blockHtmlReconcile: z.ZodLiteral<true>;
|
|
255
264
|
protocolVersion: z.ZodLiteral<3>;
|
|
256
265
|
}, z.core.$strict>], "protocolVersion">;
|
|
@@ -263,7 +272,7 @@ export declare const PreviewReadyResponseSchema: z.ZodObject<{
|
|
|
263
272
|
builderRuntimeProvider: z.ZodLiteral<true>;
|
|
264
273
|
protocolVersion: z.ZodLiteral<2>;
|
|
265
274
|
}, z.core.$strict>, z.ZodObject<{
|
|
266
|
-
|
|
275
|
+
serverLiquidEngine: z.ZodLiteral<true>;
|
|
267
276
|
blockHtmlReconcile: z.ZodLiteral<true>;
|
|
268
277
|
protocolVersion: z.ZodLiteral<3>;
|
|
269
278
|
}, z.core.$strict>], "protocolVersion">>;
|
|
@@ -282,11 +291,7 @@ export declare const PreviewBlockHtmlAppliedResponseSchema: z.ZodObject<{
|
|
|
282
291
|
revision: z.ZodNumber;
|
|
283
292
|
pageId: z.ZodString;
|
|
284
293
|
sourceRevision: z.ZodString;
|
|
285
|
-
renderMode: z.ZodLiteral<"
|
|
286
|
-
scope: z.ZodOptional<z.ZodEnum<{
|
|
287
|
-
page: "page";
|
|
288
|
-
block: "block";
|
|
289
|
-
}>>;
|
|
294
|
+
renderMode: z.ZodLiteral<"server-liquid">;
|
|
290
295
|
blocks: z.ZodArray<z.ZodObject<{
|
|
291
296
|
blockId: z.ZodString;
|
|
292
297
|
html: z.ZodString;
|
|
@@ -297,7 +302,7 @@ export declare const PreviewBlockHtmlFailedResponseSchema: z.ZodObject<{
|
|
|
297
302
|
revision: z.ZodNumber;
|
|
298
303
|
pageId: z.ZodString;
|
|
299
304
|
sourceRevision: z.ZodOptional<z.ZodString>;
|
|
300
|
-
renderMode: z.ZodLiteral<"
|
|
305
|
+
renderMode: z.ZodLiteral<"server-liquid">;
|
|
301
306
|
blockIds: z.ZodArray<z.ZodString>;
|
|
302
307
|
error: z.ZodString;
|
|
303
308
|
}, z.core.$strict>;
|
|
@@ -430,7 +435,7 @@ export declare const PreviewResponseSchema: z.ZodDiscriminatedUnion<[z.ZodObject
|
|
|
430
435
|
builderRuntimeProvider: z.ZodLiteral<true>;
|
|
431
436
|
protocolVersion: z.ZodLiteral<2>;
|
|
432
437
|
}, z.core.$strict>, z.ZodObject<{
|
|
433
|
-
|
|
438
|
+
serverLiquidEngine: z.ZodLiteral<true>;
|
|
434
439
|
blockHtmlReconcile: z.ZodLiteral<true>;
|
|
435
440
|
protocolVersion: z.ZodLiteral<3>;
|
|
436
441
|
}, z.core.$strict>], "protocolVersion">>;
|
|
@@ -446,11 +451,7 @@ export declare const PreviewResponseSchema: z.ZodDiscriminatedUnion<[z.ZodObject
|
|
|
446
451
|
revision: z.ZodNumber;
|
|
447
452
|
pageId: z.ZodString;
|
|
448
453
|
sourceRevision: z.ZodString;
|
|
449
|
-
renderMode: z.ZodLiteral<"
|
|
450
|
-
scope: z.ZodOptional<z.ZodEnum<{
|
|
451
|
-
page: "page";
|
|
452
|
-
block: "block";
|
|
453
|
-
}>>;
|
|
454
|
+
renderMode: z.ZodLiteral<"server-liquid">;
|
|
454
455
|
blocks: z.ZodArray<z.ZodObject<{
|
|
455
456
|
blockId: z.ZodString;
|
|
456
457
|
html: z.ZodString;
|
|
@@ -460,7 +461,7 @@ export declare const PreviewResponseSchema: z.ZodDiscriminatedUnion<[z.ZodObject
|
|
|
460
461
|
revision: z.ZodNumber;
|
|
461
462
|
pageId: z.ZodString;
|
|
462
463
|
sourceRevision: z.ZodOptional<z.ZodString>;
|
|
463
|
-
renderMode: z.ZodLiteral<"
|
|
464
|
+
renderMode: z.ZodLiteral<"server-liquid">;
|
|
464
465
|
blockIds: z.ZodArray<z.ZodString>;
|
|
465
466
|
error: z.ZodString;
|
|
466
467
|
}, z.core.$strict>, z.ZodObject<{
|
|
@@ -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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAMhC,CAAC;AAEZ,eAAO,MAAM,
|
|
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,4BAA4B,+BAA6B,CAAC;AACvE,MAAM,MAAM,sBAAsB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,4BAA4B,CAAC,CAAC;AAElF,eAAO,MAAM,2BAA2B;;;kBAK7B,CAAC;AACZ,MAAM,MAAM,qBAAqB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,2BAA2B,CAAC,CAAC;AAEhF,eAAO,MAAM,kCAAkC;;;;;;;;;;kBASpC,CAAC;AACZ,MAAM,MAAM,4BAA4B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kCAAkC,CAAC,CAAC;AAE9F,eAAO,MAAM,iCAAiC;;;;;;;kBASnC,CAAC;AACZ,MAAM,MAAM,2BAA2B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iCAAiC,CAAC,CAAC;AAE5F,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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4BAQ/B,CAAC;AACH,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAElE,eAAO,MAAM,gCAAgC;;;;;;kBAQlC,CAAC;AAEZ,eAAO,MAAM,wBAAwB;;;;;;;;uCAenC,CAAC;AACH,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,wBAAwB,CAAC,CAAC;AAE1E,eAAO,MAAM,0BAA0B;;;;;;;;;;;;kBAM5B,CAAC;AAEZ,eAAO,MAAM,4BAA4B;;;kBAK9B,CAAC;AAEZ,eAAO,MAAM,gCAAgC;;;;kBAMlC,CAAC;AAEZ,eAAO,MAAM,qCAAqC;;;;;;;;;;kBASvC,CAAC;AAEZ,eAAO,MAAM,oCAAoC;;;;;;;;kBAUtC,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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4BAYhC,CAAC;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC"}
|
package/dist/preview-protocol.js
CHANGED
|
@@ -20,7 +20,7 @@ export const PreviewApplyStateMessageSchema = z
|
|
|
20
20
|
state: BuilderSettingsSchema,
|
|
21
21
|
})
|
|
22
22
|
.strict();
|
|
23
|
-
export const
|
|
23
|
+
export const ServerLiquidRenderModeSchema = z.literal('server-liquid');
|
|
24
24
|
export const PreviewBlockHtmlPatchSchema = z
|
|
25
25
|
.object({
|
|
26
26
|
blockId: z.string().min(1),
|
|
@@ -33,11 +33,20 @@ export const PreviewApplyBlockHtmlMessageSchema = z
|
|
|
33
33
|
revision: z.number().int().nonnegative(),
|
|
34
34
|
pageId: z.string().min(1),
|
|
35
35
|
sourceRevision: z.string().min(1),
|
|
36
|
-
renderMode:
|
|
37
|
-
scope: z.enum(['block', 'page']).optional(),
|
|
36
|
+
renderMode: ServerLiquidRenderModeSchema,
|
|
38
37
|
blocks: z.array(PreviewBlockHtmlPatchSchema),
|
|
39
38
|
})
|
|
40
39
|
.strict();
|
|
40
|
+
export const PreviewApplyPageHtmlMessageSchema = z
|
|
41
|
+
.object({
|
|
42
|
+
type: z.literal('APPLY_PAGE_HTML'),
|
|
43
|
+
revision: z.number().int().nonnegative(),
|
|
44
|
+
pageId: z.string().min(1),
|
|
45
|
+
sourceRevision: z.string().min(1),
|
|
46
|
+
renderMode: ServerLiquidRenderModeSchema,
|
|
47
|
+
html: z.string().min(1),
|
|
48
|
+
})
|
|
49
|
+
.strict();
|
|
41
50
|
export const PreviewReloadMessageSchema = z
|
|
42
51
|
.object({
|
|
43
52
|
type: z.literal('RELOAD'),
|
|
@@ -73,6 +82,7 @@ export const PreviewSetInteractionModeMessageSchema = z
|
|
|
73
82
|
export const PreviewMessageSchema = z.discriminatedUnion('type', [
|
|
74
83
|
PreviewApplyStateMessageSchema,
|
|
75
84
|
PreviewApplyBlockHtmlMessageSchema,
|
|
85
|
+
PreviewApplyPageHtmlMessageSchema,
|
|
76
86
|
PreviewReloadMessageSchema,
|
|
77
87
|
PreviewSelectElementMessageSchema,
|
|
78
88
|
PreviewRequestReadyMessageSchema,
|
|
@@ -97,7 +107,7 @@ export const PreviewReadyHealthSchema = z.discriminatedUnion('protocolVersion',
|
|
|
97
107
|
.strict(),
|
|
98
108
|
z
|
|
99
109
|
.object({
|
|
100
|
-
|
|
110
|
+
serverLiquidEngine: z.literal(true),
|
|
101
111
|
blockHtmlReconcile: z.literal(true),
|
|
102
112
|
protocolVersion: z.literal(3),
|
|
103
113
|
})
|
|
@@ -129,8 +139,7 @@ export const PreviewBlockHtmlAppliedResponseSchema = z
|
|
|
129
139
|
revision: z.number().int().nonnegative(),
|
|
130
140
|
pageId: z.string().min(1),
|
|
131
141
|
sourceRevision: z.string().min(1),
|
|
132
|
-
renderMode:
|
|
133
|
-
scope: z.enum(['block', 'page']).optional(),
|
|
142
|
+
renderMode: ServerLiquidRenderModeSchema,
|
|
134
143
|
blocks: z.array(PreviewBlockHtmlPatchSchema),
|
|
135
144
|
})
|
|
136
145
|
.strict();
|
|
@@ -140,7 +149,7 @@ export const PreviewBlockHtmlFailedResponseSchema = z
|
|
|
140
149
|
revision: z.number().int().nonnegative(),
|
|
141
150
|
pageId: z.string().min(1),
|
|
142
151
|
sourceRevision: z.string().min(1).optional(),
|
|
143
|
-
renderMode:
|
|
152
|
+
renderMode: ServerLiquidRenderModeSchema,
|
|
144
153
|
blockIds: z.array(z.string().min(1)),
|
|
145
154
|
error: z.string().min(1),
|
|
146
155
|
})
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SSOT for iframe embed origins allowed in the Storefront Render Service CSP (frame-src)
|
|
3
|
+
* and for custom-embed postMessage trust in storefront-commerce (when registered).
|
|
4
|
+
*/
|
|
5
|
+
export declare const STOREFRONT_RENDER_YOUTUBE_FRAME_ORIGINS: readonly ["https://www.youtube.com", "https://www.youtube-nocookie.com"];
|
|
6
|
+
/** Trusted origins for custom-embed resize postMessage (storefront-commerce). */
|
|
7
|
+
export declare const STOREFRONT_RENDER_CUSTOM_EMBED_TRUSTED_ORIGINS: readonly ["https://calendly.com", "https://substack.com", "https://shoppex.io"];
|
|
8
|
+
/** frame-src entries for custom embed blocks (includes Substack wildcard). */
|
|
9
|
+
export declare const STOREFRONT_RENDER_CUSTOM_EMBED_FRAME_SRC_ORIGINS: readonly ["https://calendly.com", "https://substack.com", "https://shoppex.io", "https://*.substack.com"];
|
|
10
|
+
export declare const STOREFRONT_RENDER_ALLOWED_FRAME_ORIGINS: readonly ["https://www.youtube.com", "https://www.youtube-nocookie.com", "https://calendly.com", "https://substack.com", "https://shoppex.io", "https://*.substack.com"];
|
|
11
|
+
//# sourceMappingURL=storefront-render-frame-origins.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storefront-render-frame-origins.d.ts","sourceRoot":"","sources":["../src/storefront-render-frame-origins.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,eAAO,MAAM,uCAAuC,0EAG1C,CAAC;AAEX,iFAAiF;AACjF,eAAO,MAAM,8CAA8C,iFAIjD,CAAC;AAEX,8EAA8E;AAC9E,eAAO,MAAM,gDAAgD,2GAGnD,CAAC;AAEX,eAAO,MAAM,uCAAuC,0KAG1C,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SSOT for iframe embed origins allowed in the Storefront Render Service CSP (frame-src)
|
|
3
|
+
* and for custom-embed postMessage trust in storefront-commerce (when registered).
|
|
4
|
+
*/
|
|
5
|
+
export const STOREFRONT_RENDER_YOUTUBE_FRAME_ORIGINS = [
|
|
6
|
+
'https://www.youtube.com',
|
|
7
|
+
'https://www.youtube-nocookie.com',
|
|
8
|
+
];
|
|
9
|
+
/** Trusted origins for custom-embed resize postMessage (storefront-commerce). */
|
|
10
|
+
export const STOREFRONT_RENDER_CUSTOM_EMBED_TRUSTED_ORIGINS = [
|
|
11
|
+
'https://calendly.com',
|
|
12
|
+
'https://substack.com',
|
|
13
|
+
'https://shoppex.io',
|
|
14
|
+
];
|
|
15
|
+
/** frame-src entries for custom embed blocks (includes Substack wildcard). */
|
|
16
|
+
export const STOREFRONT_RENDER_CUSTOM_EMBED_FRAME_SRC_ORIGINS = [
|
|
17
|
+
...STOREFRONT_RENDER_CUSTOM_EMBED_TRUSTED_ORIGINS,
|
|
18
|
+
'https://*.substack.com',
|
|
19
|
+
];
|
|
20
|
+
export const STOREFRONT_RENDER_ALLOWED_FRAME_ORIGINS = [
|
|
21
|
+
...STOREFRONT_RENDER_YOUTUBE_FRAME_ORIGINS,
|
|
22
|
+
...STOREFRONT_RENDER_CUSTOM_EMBED_FRAME_SRC_ORIGINS,
|
|
23
|
+
];
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export declare const STOREFRONT_RENDER_INTEGRATIONS_HEADER = "X-Shoppex-Storefront-Integrations";
|
|
2
|
+
export interface StorefrontRenderIntegrationEntry {
|
|
3
|
+
provider: string;
|
|
4
|
+
enabled: boolean;
|
|
5
|
+
config: Record<string, string>;
|
|
6
|
+
}
|
|
7
|
+
export declare function normalizeStorefrontRenderIntegrations(value: unknown): StorefrontRenderIntegrationEntry[];
|
|
8
|
+
export declare function encodeStorefrontRenderIntegrationsHeader(integrations: readonly StorefrontRenderIntegrationEntry[]): string;
|
|
9
|
+
export declare function decodeStorefrontRenderIntegrationsHeader(value: string | null): StorefrontRenderIntegrationEntry[];
|
|
10
|
+
//# sourceMappingURL=storefront-render-integrations.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storefront-render-integrations.d.ts","sourceRoot":"","sources":["../src/storefront-render-integrations.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,qCAAqC,sCAAsC,CAAC;AAIzF,MAAM,WAAW,gCAAgC;IAC/C,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAChC;AAsBD,wBAAgB,qCAAqC,CACnD,KAAK,EAAE,OAAO,GACb,gCAAgC,EAAE,CA+BpC;AAED,wBAAgB,wCAAwC,CACtD,YAAY,EAAE,SAAS,gCAAgC,EAAE,GACxD,MAAM,CAER;AAED,wBAAgB,wCAAwC,CACtD,KAAK,EAAE,MAAM,GAAG,IAAI,GACnB,gCAAgC,EAAE,CAOpC"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
export const STOREFRONT_RENDER_INTEGRATIONS_HEADER = 'X-Shoppex-Storefront-Integrations';
|
|
2
|
+
const MAX_STOREFRONT_RENDER_INTEGRATIONS = 20;
|
|
3
|
+
function isRecord(value) {
|
|
4
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
5
|
+
}
|
|
6
|
+
function readStringConfig(value) {
|
|
7
|
+
if (!isRecord(value)) {
|
|
8
|
+
return null;
|
|
9
|
+
}
|
|
10
|
+
const config = {};
|
|
11
|
+
for (const [key, rawValue] of Object.entries(value)) {
|
|
12
|
+
if (typeof key !== 'string' || typeof rawValue !== 'string') {
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
config[key] = rawValue;
|
|
16
|
+
}
|
|
17
|
+
return config;
|
|
18
|
+
}
|
|
19
|
+
export function normalizeStorefrontRenderIntegrations(value) {
|
|
20
|
+
if (!Array.isArray(value)) {
|
|
21
|
+
return [];
|
|
22
|
+
}
|
|
23
|
+
const integrations = [];
|
|
24
|
+
const seenProviders = new Set();
|
|
25
|
+
for (const rawEntry of value) {
|
|
26
|
+
if (integrations.length >= MAX_STOREFRONT_RENDER_INTEGRATIONS) {
|
|
27
|
+
break;
|
|
28
|
+
}
|
|
29
|
+
if (!isRecord(rawEntry)) {
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
const provider = rawEntry.provider;
|
|
33
|
+
const enabled = rawEntry.enabled;
|
|
34
|
+
const config = readStringConfig(rawEntry.config);
|
|
35
|
+
if (typeof provider !== 'string' || provider.length === 0 || typeof enabled !== 'boolean' || !config) {
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
if (seenProviders.has(provider)) {
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
seenProviders.add(provider);
|
|
42
|
+
integrations.push({ provider, enabled, config });
|
|
43
|
+
}
|
|
44
|
+
return integrations;
|
|
45
|
+
}
|
|
46
|
+
export function encodeStorefrontRenderIntegrationsHeader(integrations) {
|
|
47
|
+
return encodeURIComponent(JSON.stringify(normalizeStorefrontRenderIntegrations(integrations)));
|
|
48
|
+
}
|
|
49
|
+
export function decodeStorefrontRenderIntegrationsHeader(value) {
|
|
50
|
+
if (value === null) {
|
|
51
|
+
throw new Error(`${STOREFRONT_RENDER_INTEGRATIONS_HEADER} is required.`);
|
|
52
|
+
}
|
|
53
|
+
const parsed = JSON.parse(decodeURIComponent(value));
|
|
54
|
+
return normalizeStorefrontRenderIntegrations(parsed);
|
|
55
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export declare const STOREFRONT_RENDER_SEO_HEADER = "X-Shoppex-Storefront-Seo";
|
|
2
|
+
export interface StorefrontRenderSeoMetadata {
|
|
3
|
+
title: string;
|
|
4
|
+
description: string;
|
|
5
|
+
ogImage: string;
|
|
6
|
+
favicon: string;
|
|
7
|
+
}
|
|
8
|
+
export declare function normalizeStorefrontRenderSeoMetadata(value: unknown): StorefrontRenderSeoMetadata | null;
|
|
9
|
+
export declare function encodeStorefrontRenderSeoHeader(metadata: StorefrontRenderSeoMetadata): string;
|
|
10
|
+
export declare function decodeStorefrontRenderSeoHeader(value: string | null): StorefrontRenderSeoMetadata;
|
|
11
|
+
//# sourceMappingURL=storefront-render-seo.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storefront-render-seo.d.ts","sourceRoot":"","sources":["../src/storefront-render-seo.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,4BAA4B,6BAA6B,CAAC;AAEvE,MAAM,WAAW,2BAA2B;IAC1C,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;CACjB;AAMD,wBAAgB,oCAAoC,CAAC,KAAK,EAAE,OAAO,GAAG,2BAA2B,GAAG,IAAI,CAgBvG;AAED,wBAAgB,+BAA+B,CAAC,QAAQ,EAAE,2BAA2B,GAAG,MAAM,CAE7F;AAED,wBAAgB,+BAA+B,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,2BAA2B,CAWjG"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export const STOREFRONT_RENDER_SEO_HEADER = 'X-Shoppex-Storefront-Seo';
|
|
2
|
+
function isRecord(value) {
|
|
3
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
4
|
+
}
|
|
5
|
+
export function normalizeStorefrontRenderSeoMetadata(value) {
|
|
6
|
+
if (!isRecord(value)) {
|
|
7
|
+
return null;
|
|
8
|
+
}
|
|
9
|
+
const { title, description, ogImage, favicon } = value;
|
|
10
|
+
if (typeof title !== 'string'
|
|
11
|
+
|| typeof description !== 'string'
|
|
12
|
+
|| typeof ogImage !== 'string'
|
|
13
|
+
|| typeof favicon !== 'string') {
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
return { title, description, ogImage, favicon };
|
|
17
|
+
}
|
|
18
|
+
export function encodeStorefrontRenderSeoHeader(metadata) {
|
|
19
|
+
return encodeURIComponent(JSON.stringify(metadata));
|
|
20
|
+
}
|
|
21
|
+
export function decodeStorefrontRenderSeoHeader(value) {
|
|
22
|
+
if (value === null) {
|
|
23
|
+
throw new Error(`${STOREFRONT_RENDER_SEO_HEADER} is required.`);
|
|
24
|
+
}
|
|
25
|
+
const metadata = normalizeStorefrontRenderSeoMetadata(JSON.parse(decodeURIComponent(value)));
|
|
26
|
+
if (!metadata) {
|
|
27
|
+
throw new Error(`${STOREFRONT_RENDER_SEO_HEADER} is invalid.`);
|
|
28
|
+
}
|
|
29
|
+
return metadata;
|
|
30
|
+
}
|
package/dist/theme-schemes.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
export declare const PUBLIC_BUILDER_THEME_SCHEMES: readonly ["default", "classic", "nebula", "pulse", "phantom", "starlight", "apex", "vault", "clean-minimal"];
|
|
2
2
|
export type PublicBuilderThemeScheme = (typeof PUBLIC_BUILDER_THEME_SCHEMES)[number];
|
|
3
3
|
export declare const STARTER_BUILDER_THEME_SCHEMES: readonly ["blank"];
|
|
4
|
-
export declare const THEME_STORE_DISABLED_SCHEMES: readonly ["vault", "nebula", "phantom", "clean-minimal"];
|
|
5
|
-
export declare const THEME_STORE_INSTALLABLE_SCHEMES: readonly ["default", "
|
|
4
|
+
export declare const THEME_STORE_DISABLED_SCHEMES: readonly ["classic", "vault", "nebula", "phantom", "apex", "clean-minimal"];
|
|
5
|
+
export declare const THEME_STORE_INSTALLABLE_SCHEMES: readonly ["default", "starlight", "pulse"];
|
|
6
6
|
export declare const BUILDER_READY_THEME_SCHEMES: readonly ["blank", "default", "classic", "nebula", "pulse", "phantom", "starlight", "apex", "vault", "clean-minimal"];
|
|
7
7
|
export type ThemeStoreInstallableScheme = (typeof THEME_STORE_INSTALLABLE_SCHEMES)[number];
|
|
8
8
|
export type StarterBuilderThemeScheme = (typeof STARTER_BUILDER_THEME_SCHEMES)[number];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"theme-schemes.d.ts","sourceRoot":"","sources":["../src/theme-schemes.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,4BAA4B,8GAU/B,CAAC;AAEX,MAAM,MAAM,wBAAwB,GAAG,CAAC,OAAO,4BAA4B,CAAC,CAAC,MAAM,CAAC,CAAC;AAErF,eAAO,MAAM,6BAA6B,oBAAqB,CAAC;AAEhE,eAAO,MAAM,4BAA4B,
|
|
1
|
+
{"version":3,"file":"theme-schemes.d.ts","sourceRoot":"","sources":["../src/theme-schemes.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,4BAA4B,8GAU/B,CAAC;AAEX,MAAM,MAAM,wBAAwB,GAAG,CAAC,OAAO,4BAA4B,CAAC,CAAC,MAAM,CAAC,CAAC;AAErF,eAAO,MAAM,6BAA6B,oBAAqB,CAAC;AAEhE,eAAO,MAAM,4BAA4B,6EAOe,CAAC;AAIzD,eAAO,MAAM,+BAA+B,4CAIY,CAAC;AAEzD,eAAO,MAAM,2BAA2B,uHAG9B,CAAC;AAEX,MAAM,MAAM,2BAA2B,GAAG,CAAC,OAAO,+BAA+B,CAAC,CAAC,MAAM,CAAC,CAAC;AAC3F,MAAM,MAAM,yBAAyB,GAAG,CAAC,OAAO,6BAA6B,CAAC,CAAC,MAAM,CAAC,CAAC;AACvF,MAAM,MAAM,uBAAuB,GAAG,CAAC,OAAO,2BAA2B,CAAC,CAAC,MAAM,CAAC,CAAC;AAEnF,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,IAAI,wBAAwB,CAE3F;AAED,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAEjE;AAED,wBAAgB,6BAA6B,CAC3C,KAAK,EAAE,MAAM,GACZ,KAAK,IAAI,2BAA2B,CAEtC;AAED,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,IAAI,yBAAyB,CAE7F;AAED,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,IAAI,uBAAuB,CAEzF"}
|
package/dist/theme-schemes.js
CHANGED
|
@@ -11,18 +11,18 @@ export const PUBLIC_BUILDER_THEME_SCHEMES = [
|
|
|
11
11
|
];
|
|
12
12
|
export const STARTER_BUILDER_THEME_SCHEMES = ['blank'];
|
|
13
13
|
export const THEME_STORE_DISABLED_SCHEMES = [
|
|
14
|
+
'classic',
|
|
14
15
|
'vault',
|
|
15
16
|
'nebula',
|
|
16
17
|
'phantom',
|
|
18
|
+
'apex',
|
|
17
19
|
'clean-minimal',
|
|
18
20
|
];
|
|
19
21
|
const THEME_STORE_DISABLED_SCHEME_SET = new Set(THEME_STORE_DISABLED_SCHEMES);
|
|
20
22
|
export const THEME_STORE_INSTALLABLE_SCHEMES = [
|
|
21
23
|
'default',
|
|
22
|
-
'classic',
|
|
23
|
-
'pulse',
|
|
24
24
|
'starlight',
|
|
25
|
-
'
|
|
25
|
+
'pulse',
|
|
26
26
|
];
|
|
27
27
|
export const BUILDER_READY_THEME_SCHEMES = [
|
|
28
28
|
...STARTER_BUILDER_THEME_SCHEMES,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shoppexio/builder-contracts",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.7",
|
|
4
4
|
"description": "Shared Builder v2 contracts for Shoppex dashboard, backend, preview runtime, and themes",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|
|
@@ -37,11 +37,23 @@
|
|
|
37
37
|
"import": "./dist/builder-settings.js",
|
|
38
38
|
"default": "./dist/builder-settings.js"
|
|
39
39
|
},
|
|
40
|
-
"./
|
|
41
|
-
"bun": "./src/
|
|
42
|
-
"types": "./dist/
|
|
43
|
-
"import": "./dist/
|
|
44
|
-
"default": "./dist/
|
|
40
|
+
"./storefront-render-frame-origins": {
|
|
41
|
+
"bun": "./src/storefront-render-frame-origins.ts",
|
|
42
|
+
"types": "./dist/storefront-render-frame-origins.d.ts",
|
|
43
|
+
"import": "./dist/storefront-render-frame-origins.js",
|
|
44
|
+
"default": "./dist/storefront-render-frame-origins.js"
|
|
45
|
+
},
|
|
46
|
+
"./storefront-render-integrations": {
|
|
47
|
+
"bun": "./src/storefront-render-integrations.ts",
|
|
48
|
+
"types": "./dist/storefront-render-integrations.d.ts",
|
|
49
|
+
"import": "./dist/storefront-render-integrations.js",
|
|
50
|
+
"default": "./dist/storefront-render-integrations.js"
|
|
51
|
+
},
|
|
52
|
+
"./storefront-render-seo": {
|
|
53
|
+
"bun": "./src/storefront-render-seo.ts",
|
|
54
|
+
"types": "./dist/storefront-render-seo.d.ts",
|
|
55
|
+
"import": "./dist/storefront-render-seo.js",
|
|
56
|
+
"default": "./dist/storefront-render-seo.js"
|
|
45
57
|
},
|
|
46
58
|
"./events": {
|
|
47
59
|
"bun": "./src/events.ts",
|
|
@@ -219,59 +219,9 @@ describe('@shoppex/builder-contracts', () => {
|
|
|
219
219
|
expect((persisted.builder_settings as Record<string, unknown>).seo).toEqual({
|
|
220
220
|
default_title: 'Keep this title',
|
|
221
221
|
});
|
|
222
|
-
expect(persisted.version).toBe(settings.version);
|
|
223
|
-
expect(persisted.revision).toBe(settings.revision);
|
|
224
|
-
expect(persisted.theme).toEqual(settings.theme);
|
|
225
222
|
expect(extractPersistedBuilderSettings(persisted)).toEqual(settings);
|
|
226
223
|
});
|
|
227
224
|
|
|
228
|
-
test('updates stale root-level builder fields when publishing nested builder settings', () => {
|
|
229
|
-
const settings = createEmptyBuilderSettings(4);
|
|
230
|
-
settings.theme.layout.home = {
|
|
231
|
-
blocks: [
|
|
232
|
-
{
|
|
233
|
-
id: 'hero-1',
|
|
234
|
-
type: 'hero',
|
|
235
|
-
visible: true,
|
|
236
|
-
settings: { 'hero.title': 'Published title' },
|
|
237
|
-
},
|
|
238
|
-
],
|
|
239
|
-
};
|
|
240
|
-
|
|
241
|
-
const persisted = mergeBuilderSettingsIntoThemeSettings({
|
|
242
|
-
version: 2,
|
|
243
|
-
revision: 1,
|
|
244
|
-
theme: {
|
|
245
|
-
content: {},
|
|
246
|
-
layout: {
|
|
247
|
-
home: {
|
|
248
|
-
blocks: [
|
|
249
|
-
{
|
|
250
|
-
id: 'hero-1',
|
|
251
|
-
type: 'hero',
|
|
252
|
-
visible: true,
|
|
253
|
-
settings: { 'hero.title': 'Old title' },
|
|
254
|
-
},
|
|
255
|
-
],
|
|
256
|
-
},
|
|
257
|
-
},
|
|
258
|
-
style_slots: {},
|
|
259
|
-
pages: [],
|
|
260
|
-
terms: {},
|
|
261
|
-
},
|
|
262
|
-
builder_settings: {
|
|
263
|
-
seo: {
|
|
264
|
-
default_title: 'Keep this title',
|
|
265
|
-
},
|
|
266
|
-
},
|
|
267
|
-
}, settings);
|
|
268
|
-
|
|
269
|
-
expect(extractPersistedBuilderSettings(persisted)).toEqual(settings);
|
|
270
|
-
expect((persisted.builder_settings as Record<string, unknown>).seo).toEqual({
|
|
271
|
-
default_title: 'Keep this title',
|
|
272
|
-
});
|
|
273
|
-
});
|
|
274
|
-
|
|
275
225
|
test('coerces polluted v2 theme.typography into style_slots and strips invalid keys', () => {
|
|
276
226
|
const coerced = coercePersistedBuilderSettings({
|
|
277
227
|
version: 2,
|
package/src/index.ts
CHANGED
|
@@ -12,6 +12,8 @@ export * from './preview-protocol.ts';
|
|
|
12
12
|
export * from './preview-session-resolve.ts';
|
|
13
13
|
export * from './preview-trusted-origins.ts';
|
|
14
14
|
export * from './storefront-initial-data-html.ts';
|
|
15
|
+
export * from './storefront-render-integrations.ts';
|
|
16
|
+
export * from './storefront-render-seo.ts';
|
|
15
17
|
export * from './storefront-typography-fonts.ts';
|
|
16
18
|
export * from './style-slots.ts';
|
|
17
19
|
export * from './theme-manifest.ts';
|
package/src/persistence.ts
CHANGED
|
@@ -173,9 +173,6 @@ export function mergeBuilderSettingsIntoThemeSettings(
|
|
|
173
173
|
const currentBuilderSettings = isRecord(root.builder_settings) ? root.builder_settings : {};
|
|
174
174
|
const sanitizedBuilderSettings = sanitizeBuilderSettingsState(builderSettings);
|
|
175
175
|
|
|
176
|
-
root.version = sanitizedBuilderSettings.version;
|
|
177
|
-
root.revision = sanitizedBuilderSettings.revision;
|
|
178
|
-
root.theme = sanitizedBuilderSettings.theme;
|
|
179
176
|
root.builder_settings = {
|
|
180
177
|
...currentBuilderSettings,
|
|
181
178
|
version: sanitizedBuilderSettings.version,
|
|
@@ -11,7 +11,7 @@ describe('PreviewMessageSchema', () => {
|
|
|
11
11
|
revision: 12,
|
|
12
12
|
pageId: 'product',
|
|
13
13
|
sourceRevision: 'theme-source-12',
|
|
14
|
-
renderMode: '
|
|
14
|
+
renderMode: 'server-liquid',
|
|
15
15
|
blocks: [
|
|
16
16
|
{
|
|
17
17
|
blockId: 'product-form-1',
|
|
@@ -26,12 +26,28 @@ describe('PreviewMessageSchema', () => {
|
|
|
26
26
|
}
|
|
27
27
|
});
|
|
28
28
|
|
|
29
|
+
it('accepts v3 block HTML apply messages with no blocks', () => {
|
|
30
|
+
const parsed = PreviewMessageSchema.parse({
|
|
31
|
+
type: 'APPLY_BLOCK_HTML',
|
|
32
|
+
revision: 12,
|
|
33
|
+
pageId: 'empty',
|
|
34
|
+
sourceRevision: 'theme-source-12',
|
|
35
|
+
renderMode: 'server-liquid',
|
|
36
|
+
blocks: [],
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
expect(parsed.type).toBe('APPLY_BLOCK_HTML');
|
|
40
|
+
if (parsed.type === 'APPLY_BLOCK_HTML') {
|
|
41
|
+
expect(parsed.blocks).toEqual([]);
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
|
|
29
45
|
it('rejects v3 block HTML apply messages without sourceRevision', () => {
|
|
30
46
|
const result = PreviewMessageSchema.safeParse({
|
|
31
47
|
type: 'APPLY_BLOCK_HTML',
|
|
32
48
|
revision: 12,
|
|
33
49
|
pageId: 'product',
|
|
34
|
-
renderMode: '
|
|
50
|
+
renderMode: 'server-liquid',
|
|
35
51
|
blocks: [
|
|
36
52
|
{
|
|
37
53
|
blockId: 'product-form-1',
|
|
@@ -43,6 +59,22 @@ describe('PreviewMessageSchema', () => {
|
|
|
43
59
|
expect(result.success).toBe(false);
|
|
44
60
|
});
|
|
45
61
|
|
|
62
|
+
it('accepts v3 full page HTML apply messages', () => {
|
|
63
|
+
const parsed = PreviewMessageSchema.parse({
|
|
64
|
+
type: 'APPLY_PAGE_HTML',
|
|
65
|
+
revision: 13,
|
|
66
|
+
pageId: 'product',
|
|
67
|
+
sourceRevision: 'theme-source-13',
|
|
68
|
+
renderMode: 'server-liquid',
|
|
69
|
+
html: '<!doctype html><html><body><main data-page-id="product"></main></body></html>',
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
expect(parsed.type).toBe('APPLY_PAGE_HTML');
|
|
73
|
+
if (parsed.type === 'APPLY_PAGE_HTML') {
|
|
74
|
+
expect(parsed.html).toContain('data-page-id="product"');
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
|
|
46
78
|
it('accepts SET_INTERACTION_MODE with edit/preview', () => {
|
|
47
79
|
for (const mode of ['edit', 'preview'] as const) {
|
|
48
80
|
const parsed = PreviewMessageSchema.parse({
|
|
@@ -63,12 +95,12 @@ describe('PreviewMessageSchema', () => {
|
|
|
63
95
|
});
|
|
64
96
|
|
|
65
97
|
describe('PreviewResponseSchema', () => {
|
|
66
|
-
it('accepts READY health for
|
|
98
|
+
it('accepts READY health for server-liquid protocol v3', () => {
|
|
67
99
|
const parsed = PreviewResponseSchema.parse({
|
|
68
100
|
type: 'READY',
|
|
69
101
|
revision: 12,
|
|
70
102
|
health: {
|
|
71
|
-
|
|
103
|
+
serverLiquidEngine: true,
|
|
72
104
|
blockHtmlReconcile: true,
|
|
73
105
|
protocolVersion: 3,
|
|
74
106
|
},
|
|
@@ -86,7 +118,7 @@ describe('PreviewResponseSchema', () => {
|
|
|
86
118
|
revision: 12,
|
|
87
119
|
pageId: 'product',
|
|
88
120
|
sourceRevision: 'theme-source-12',
|
|
89
|
-
renderMode: '
|
|
121
|
+
renderMode: 'server-liquid',
|
|
90
122
|
blocks: [
|
|
91
123
|
{
|
|
92
124
|
blockId: 'footer-1',
|
|
@@ -97,7 +129,23 @@ describe('PreviewResponseSchema', () => {
|
|
|
97
129
|
|
|
98
130
|
expect(parsed.type).toBe('BLOCK_HTML_APPLIED');
|
|
99
131
|
if (parsed.type === 'BLOCK_HTML_APPLIED') {
|
|
100
|
-
expect(parsed.renderMode).toBe('
|
|
132
|
+
expect(parsed.renderMode).toBe('server-liquid');
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
it('accepts v3 block HTML applied responses with no blocks', () => {
|
|
137
|
+
const parsed = PreviewResponseSchema.parse({
|
|
138
|
+
type: 'BLOCK_HTML_APPLIED',
|
|
139
|
+
revision: 12,
|
|
140
|
+
pageId: 'empty',
|
|
141
|
+
sourceRevision: 'theme-source-12',
|
|
142
|
+
renderMode: 'server-liquid',
|
|
143
|
+
blocks: [],
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
expect(parsed.type).toBe('BLOCK_HTML_APPLIED');
|
|
147
|
+
if (parsed.type === 'BLOCK_HTML_APPLIED') {
|
|
148
|
+
expect(parsed.blocks).toEqual([]);
|
|
101
149
|
}
|
|
102
150
|
});
|
|
103
151
|
|
|
@@ -107,7 +155,7 @@ describe('PreviewResponseSchema', () => {
|
|
|
107
155
|
revision: 12,
|
|
108
156
|
pageId: 'product',
|
|
109
157
|
sourceRevision: 'theme-source-12',
|
|
110
|
-
renderMode: '
|
|
158
|
+
renderMode: 'server-liquid',
|
|
111
159
|
blockIds: ['footer-1'],
|
|
112
160
|
error: 'Rendered block root does not match block id.',
|
|
113
161
|
});
|
package/src/preview-protocol.ts
CHANGED
|
@@ -24,8 +24,8 @@ export const PreviewApplyStateMessageSchema = z
|
|
|
24
24
|
})
|
|
25
25
|
.strict();
|
|
26
26
|
|
|
27
|
-
export const
|
|
28
|
-
export type
|
|
27
|
+
export const ServerLiquidRenderModeSchema = z.literal('server-liquid');
|
|
28
|
+
export type ServerLiquidRenderMode = z.infer<typeof ServerLiquidRenderModeSchema>;
|
|
29
29
|
|
|
30
30
|
export const PreviewBlockHtmlPatchSchema = z
|
|
31
31
|
.object({
|
|
@@ -41,11 +41,23 @@ export const PreviewApplyBlockHtmlMessageSchema = z
|
|
|
41
41
|
revision: z.number().int().nonnegative(),
|
|
42
42
|
pageId: z.string().min(1),
|
|
43
43
|
sourceRevision: z.string().min(1),
|
|
44
|
-
renderMode:
|
|
45
|
-
scope: z.enum(['block', 'page']).optional(),
|
|
44
|
+
renderMode: ServerLiquidRenderModeSchema,
|
|
46
45
|
blocks: z.array(PreviewBlockHtmlPatchSchema),
|
|
47
46
|
})
|
|
48
47
|
.strict();
|
|
48
|
+
export type PreviewApplyBlockHtmlMessage = z.infer<typeof PreviewApplyBlockHtmlMessageSchema>;
|
|
49
|
+
|
|
50
|
+
export const PreviewApplyPageHtmlMessageSchema = z
|
|
51
|
+
.object({
|
|
52
|
+
type: z.literal('APPLY_PAGE_HTML'),
|
|
53
|
+
revision: z.number().int().nonnegative(),
|
|
54
|
+
pageId: z.string().min(1),
|
|
55
|
+
sourceRevision: z.string().min(1),
|
|
56
|
+
renderMode: ServerLiquidRenderModeSchema,
|
|
57
|
+
html: z.string().min(1),
|
|
58
|
+
})
|
|
59
|
+
.strict();
|
|
60
|
+
export type PreviewApplyPageHtmlMessage = z.infer<typeof PreviewApplyPageHtmlMessageSchema>;
|
|
49
61
|
|
|
50
62
|
export const PreviewReloadMessageSchema = z
|
|
51
63
|
.object({
|
|
@@ -88,6 +100,7 @@ export const PreviewSetInteractionModeMessageSchema = z
|
|
|
88
100
|
export const PreviewMessageSchema = z.discriminatedUnion('type', [
|
|
89
101
|
PreviewApplyStateMessageSchema,
|
|
90
102
|
PreviewApplyBlockHtmlMessageSchema,
|
|
103
|
+
PreviewApplyPageHtmlMessageSchema,
|
|
91
104
|
PreviewReloadMessageSchema,
|
|
92
105
|
PreviewSelectElementMessageSchema,
|
|
93
106
|
PreviewRequestReadyMessageSchema,
|
|
@@ -115,7 +128,7 @@ export const PreviewReadyHealthSchema = z.discriminatedUnion('protocolVersion',
|
|
|
115
128
|
.strict(),
|
|
116
129
|
z
|
|
117
130
|
.object({
|
|
118
|
-
|
|
131
|
+
serverLiquidEngine: z.literal(true),
|
|
119
132
|
blockHtmlReconcile: z.literal(true),
|
|
120
133
|
protocolVersion: z.literal(3),
|
|
121
134
|
})
|
|
@@ -152,8 +165,7 @@ export const PreviewBlockHtmlAppliedResponseSchema = z
|
|
|
152
165
|
revision: z.number().int().nonnegative(),
|
|
153
166
|
pageId: z.string().min(1),
|
|
154
167
|
sourceRevision: z.string().min(1),
|
|
155
|
-
renderMode:
|
|
156
|
-
scope: z.enum(['block', 'page']).optional(),
|
|
168
|
+
renderMode: ServerLiquidRenderModeSchema,
|
|
157
169
|
blocks: z.array(PreviewBlockHtmlPatchSchema),
|
|
158
170
|
})
|
|
159
171
|
.strict();
|
|
@@ -164,7 +176,7 @@ export const PreviewBlockHtmlFailedResponseSchema = z
|
|
|
164
176
|
revision: z.number().int().nonnegative(),
|
|
165
177
|
pageId: z.string().min(1),
|
|
166
178
|
sourceRevision: z.string().min(1).optional(),
|
|
167
|
-
renderMode:
|
|
179
|
+
renderMode: ServerLiquidRenderModeSchema,
|
|
168
180
|
blockIds: z.array(z.string().min(1)),
|
|
169
181
|
error: z.string().min(1),
|
|
170
182
|
})
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SSOT for iframe embed origins allowed in the Storefront Render Service CSP (frame-src)
|
|
3
|
+
* and for custom-embed postMessage trust in storefront-commerce (when registered).
|
|
4
|
+
*/
|
|
5
|
+
export const STOREFRONT_RENDER_YOUTUBE_FRAME_ORIGINS = [
|
|
6
|
+
'https://www.youtube.com',
|
|
7
|
+
'https://www.youtube-nocookie.com',
|
|
8
|
+
] as const;
|
|
9
|
+
|
|
10
|
+
/** Trusted origins for custom-embed resize postMessage (storefront-commerce). */
|
|
11
|
+
export const STOREFRONT_RENDER_CUSTOM_EMBED_TRUSTED_ORIGINS = [
|
|
12
|
+
'https://calendly.com',
|
|
13
|
+
'https://substack.com',
|
|
14
|
+
'https://shoppex.io',
|
|
15
|
+
] as const;
|
|
16
|
+
|
|
17
|
+
/** frame-src entries for custom embed blocks (includes Substack wildcard). */
|
|
18
|
+
export const STOREFRONT_RENDER_CUSTOM_EMBED_FRAME_SRC_ORIGINS = [
|
|
19
|
+
...STOREFRONT_RENDER_CUSTOM_EMBED_TRUSTED_ORIGINS,
|
|
20
|
+
'https://*.substack.com',
|
|
21
|
+
] as const;
|
|
22
|
+
|
|
23
|
+
export const STOREFRONT_RENDER_ALLOWED_FRAME_ORIGINS = [
|
|
24
|
+
...STOREFRONT_RENDER_YOUTUBE_FRAME_ORIGINS,
|
|
25
|
+
...STOREFRONT_RENDER_CUSTOM_EMBED_FRAME_SRC_ORIGINS,
|
|
26
|
+
] as const;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { describe, expect, test } from 'bun:test';
|
|
2
|
+
import {
|
|
3
|
+
decodeStorefrontRenderIntegrationsHeader,
|
|
4
|
+
encodeStorefrontRenderIntegrationsHeader,
|
|
5
|
+
normalizeStorefrontRenderIntegrations,
|
|
6
|
+
STOREFRONT_RENDER_INTEGRATIONS_HEADER,
|
|
7
|
+
} from './storefront-render-integrations.ts';
|
|
8
|
+
|
|
9
|
+
describe('storefront render integrations contract', () => {
|
|
10
|
+
test('round-trips integration metadata through an ASCII header value', () => {
|
|
11
|
+
const encoded = encodeStorefrontRenderIntegrationsHeader([
|
|
12
|
+
{
|
|
13
|
+
provider: 'google_tag_manager',
|
|
14
|
+
enabled: true,
|
|
15
|
+
config: { container_id: 'GTM-ABC123' },
|
|
16
|
+
},
|
|
17
|
+
]);
|
|
18
|
+
|
|
19
|
+
expect(encoded).toBe(
|
|
20
|
+
'%5B%7B%22provider%22%3A%22google_tag_manager%22%2C%22enabled%22%3Atrue%2C%22config%22%3A%7B%22container_id%22%3A%22GTM-ABC123%22%7D%7D%5D',
|
|
21
|
+
);
|
|
22
|
+
expect(decodeStorefrontRenderIntegrationsHeader(encoded)).toEqual([
|
|
23
|
+
{
|
|
24
|
+
provider: 'google_tag_manager',
|
|
25
|
+
enabled: true,
|
|
26
|
+
config: { container_id: 'GTM-ABC123' },
|
|
27
|
+
},
|
|
28
|
+
]);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
test('normalizes malformed entries without throwing', () => {
|
|
32
|
+
expect(normalizeStorefrontRenderIntegrations([
|
|
33
|
+
{
|
|
34
|
+
provider: 'crisp',
|
|
35
|
+
enabled: true,
|
|
36
|
+
config: { website_id: 'crisp_123' },
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
provider: 'crisp',
|
|
40
|
+
enabled: true,
|
|
41
|
+
config: { website_id: 'duplicate' },
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
provider: 'broken',
|
|
45
|
+
enabled: true,
|
|
46
|
+
config: { id: 123 },
|
|
47
|
+
},
|
|
48
|
+
])).toEqual([
|
|
49
|
+
{
|
|
50
|
+
provider: 'crisp',
|
|
51
|
+
enabled: true,
|
|
52
|
+
config: { website_id: 'crisp_123' },
|
|
53
|
+
},
|
|
54
|
+
]);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
test('fails when the required render header is missing', () => {
|
|
58
|
+
expect(() => decodeStorefrontRenderIntegrationsHeader(null)).toThrow(
|
|
59
|
+
`${STOREFRONT_RENDER_INTEGRATIONS_HEADER} is required.`,
|
|
60
|
+
);
|
|
61
|
+
});
|
|
62
|
+
});
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
export const STOREFRONT_RENDER_INTEGRATIONS_HEADER = 'X-Shoppex-Storefront-Integrations';
|
|
2
|
+
|
|
3
|
+
const MAX_STOREFRONT_RENDER_INTEGRATIONS = 20;
|
|
4
|
+
|
|
5
|
+
export interface StorefrontRenderIntegrationEntry {
|
|
6
|
+
provider: string;
|
|
7
|
+
enabled: boolean;
|
|
8
|
+
config: Record<string, string>;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
12
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function readStringConfig(value: unknown): Record<string, string> | null {
|
|
16
|
+
if (!isRecord(value)) {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const config: Record<string, string> = {};
|
|
21
|
+
for (const [key, rawValue] of Object.entries(value)) {
|
|
22
|
+
if (typeof key !== 'string' || typeof rawValue !== 'string') {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
config[key] = rawValue;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return config;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function normalizeStorefrontRenderIntegrations(
|
|
32
|
+
value: unknown,
|
|
33
|
+
): StorefrontRenderIntegrationEntry[] {
|
|
34
|
+
if (!Array.isArray(value)) {
|
|
35
|
+
return [];
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const integrations: StorefrontRenderIntegrationEntry[] = [];
|
|
39
|
+
const seenProviders = new Set<string>();
|
|
40
|
+
|
|
41
|
+
for (const rawEntry of value) {
|
|
42
|
+
if (integrations.length >= MAX_STOREFRONT_RENDER_INTEGRATIONS) {
|
|
43
|
+
break;
|
|
44
|
+
}
|
|
45
|
+
if (!isRecord(rawEntry)) {
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const provider = rawEntry.provider;
|
|
50
|
+
const enabled = rawEntry.enabled;
|
|
51
|
+
const config = readStringConfig(rawEntry.config);
|
|
52
|
+
if (typeof provider !== 'string' || provider.length === 0 || typeof enabled !== 'boolean' || !config) {
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
if (seenProviders.has(provider)) {
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
seenProviders.add(provider);
|
|
60
|
+
integrations.push({ provider, enabled, config });
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return integrations;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function encodeStorefrontRenderIntegrationsHeader(
|
|
67
|
+
integrations: readonly StorefrontRenderIntegrationEntry[],
|
|
68
|
+
): string {
|
|
69
|
+
return encodeURIComponent(JSON.stringify(normalizeStorefrontRenderIntegrations(integrations)));
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function decodeStorefrontRenderIntegrationsHeader(
|
|
73
|
+
value: string | null,
|
|
74
|
+
): StorefrontRenderIntegrationEntry[] {
|
|
75
|
+
if (value === null) {
|
|
76
|
+
throw new Error(`${STOREFRONT_RENDER_INTEGRATIONS_HEADER} is required.`);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const parsed = JSON.parse(decodeURIComponent(value)) as unknown;
|
|
80
|
+
return normalizeStorefrontRenderIntegrations(parsed);
|
|
81
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { describe, expect, test } from 'bun:test';
|
|
2
|
+
import {
|
|
3
|
+
decodeStorefrontRenderSeoHeader,
|
|
4
|
+
encodeStorefrontRenderSeoHeader,
|
|
5
|
+
STOREFRONT_RENDER_SEO_HEADER,
|
|
6
|
+
} from './storefront-render-seo.ts';
|
|
7
|
+
|
|
8
|
+
describe('storefront render SEO contract', () => {
|
|
9
|
+
test('round-trips SEO metadata through an ASCII header value', () => {
|
|
10
|
+
const encoded = encodeStorefrontRenderSeoHeader({
|
|
11
|
+
title: 'Demo Shop',
|
|
12
|
+
description: 'Premium digital products',
|
|
13
|
+
ogImage: 'https://cdn.example.com/og.png',
|
|
14
|
+
favicon: 'https://cdn.example.com/favicon.png',
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
expect(decodeStorefrontRenderSeoHeader(encoded)).toEqual({
|
|
18
|
+
title: 'Demo Shop',
|
|
19
|
+
description: 'Premium digital products',
|
|
20
|
+
ogImage: 'https://cdn.example.com/og.png',
|
|
21
|
+
favicon: 'https://cdn.example.com/favicon.png',
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
test('fails when the required SEO header is missing or malformed', () => {
|
|
26
|
+
expect(() => decodeStorefrontRenderSeoHeader(null)).toThrow(
|
|
27
|
+
`${STOREFRONT_RENDER_SEO_HEADER} is required.`,
|
|
28
|
+
);
|
|
29
|
+
expect(() => decodeStorefrontRenderSeoHeader(encodeURIComponent('{"title":"Missing fields"}'))).toThrow(
|
|
30
|
+
`${STOREFRONT_RENDER_SEO_HEADER} is invalid.`,
|
|
31
|
+
);
|
|
32
|
+
});
|
|
33
|
+
});
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
export const STOREFRONT_RENDER_SEO_HEADER = 'X-Shoppex-Storefront-Seo';
|
|
2
|
+
|
|
3
|
+
export interface StorefrontRenderSeoMetadata {
|
|
4
|
+
title: string;
|
|
5
|
+
description: string;
|
|
6
|
+
ogImage: string;
|
|
7
|
+
favicon: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
11
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function normalizeStorefrontRenderSeoMetadata(value: unknown): StorefrontRenderSeoMetadata | null {
|
|
15
|
+
if (!isRecord(value)) {
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const { title, description, ogImage, favicon } = value;
|
|
20
|
+
if (
|
|
21
|
+
typeof title !== 'string'
|
|
22
|
+
|| typeof description !== 'string'
|
|
23
|
+
|| typeof ogImage !== 'string'
|
|
24
|
+
|| typeof favicon !== 'string'
|
|
25
|
+
) {
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return { title, description, ogImage, favicon };
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function encodeStorefrontRenderSeoHeader(metadata: StorefrontRenderSeoMetadata): string {
|
|
33
|
+
return encodeURIComponent(JSON.stringify(metadata));
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function decodeStorefrontRenderSeoHeader(value: string | null): StorefrontRenderSeoMetadata {
|
|
37
|
+
if (value === null) {
|
|
38
|
+
throw new Error(`${STOREFRONT_RENDER_SEO_HEADER} is required.`);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const metadata = normalizeStorefrontRenderSeoMetadata(JSON.parse(decodeURIComponent(value)) as unknown);
|
|
42
|
+
if (!metadata) {
|
|
43
|
+
throw new Error(`${STOREFRONT_RENDER_SEO_HEADER} is invalid.`);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return metadata;
|
|
47
|
+
}
|
package/src/theme-schemes.ts
CHANGED
|
@@ -15,9 +15,11 @@ export type PublicBuilderThemeScheme = (typeof PUBLIC_BUILDER_THEME_SCHEMES)[num
|
|
|
15
15
|
export const STARTER_BUILDER_THEME_SCHEMES = ['blank'] as const;
|
|
16
16
|
|
|
17
17
|
export const THEME_STORE_DISABLED_SCHEMES = [
|
|
18
|
+
'classic',
|
|
18
19
|
'vault',
|
|
19
20
|
'nebula',
|
|
20
21
|
'phantom',
|
|
22
|
+
'apex',
|
|
21
23
|
'clean-minimal',
|
|
22
24
|
] as const satisfies readonly PublicBuilderThemeScheme[];
|
|
23
25
|
|
|
@@ -25,10 +27,8 @@ const THEME_STORE_DISABLED_SCHEME_SET = new Set<string>(THEME_STORE_DISABLED_SCH
|
|
|
25
27
|
|
|
26
28
|
export const THEME_STORE_INSTALLABLE_SCHEMES = [
|
|
27
29
|
'default',
|
|
28
|
-
'classic',
|
|
29
|
-
'pulse',
|
|
30
30
|
'starlight',
|
|
31
|
-
'
|
|
31
|
+
'pulse',
|
|
32
32
|
] as const satisfies readonly PublicBuilderThemeScheme[];
|
|
33
33
|
|
|
34
34
|
export const BUILDER_READY_THEME_SCHEMES = [
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* SSOT for iframe embed origins allowed in the Edge Block Engine CSP (frame-src)
|
|
3
|
-
* and for custom-embed postMessage trust in storefront-commerce (when registered).
|
|
4
|
-
*/
|
|
5
|
-
export declare const EDGE_BLOCK_YOUTUBE_FRAME_ORIGINS: readonly ["https://www.youtube.com", "https://www.youtube-nocookie.com"];
|
|
6
|
-
/** Trusted origins for custom-embed resize postMessage (storefront-commerce). */
|
|
7
|
-
export declare const EDGE_BLOCK_CUSTOM_EMBED_TRUSTED_ORIGINS: readonly ["https://calendly.com", "https://substack.com", "https://shoppex.io"];
|
|
8
|
-
/** frame-src entries for custom embed blocks (includes Substack wildcard). */
|
|
9
|
-
export declare const EDGE_BLOCK_CUSTOM_EMBED_FRAME_SRC_ORIGINS: readonly ["https://calendly.com", "https://substack.com", "https://shoppex.io", "https://*.substack.com"];
|
|
10
|
-
export declare const EDGE_BLOCK_ALLOWED_FRAME_ORIGINS: readonly ["https://www.youtube.com", "https://www.youtube-nocookie.com", "https://calendly.com", "https://substack.com", "https://shoppex.io", "https://*.substack.com"];
|
|
11
|
-
//# sourceMappingURL=edge-block-engine-frame-origins.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"edge-block-engine-frame-origins.d.ts","sourceRoot":"","sources":["../src/edge-block-engine-frame-origins.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,eAAO,MAAM,gCAAgC,0EAGnC,CAAC;AAEX,iFAAiF;AACjF,eAAO,MAAM,uCAAuC,iFAI1C,CAAC;AAEX,8EAA8E;AAC9E,eAAO,MAAM,yCAAyC,2GAG5C,CAAC;AAEX,eAAO,MAAM,gCAAgC,0KAGnC,CAAC"}
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* SSOT for iframe embed origins allowed in the Edge Block Engine CSP (frame-src)
|
|
3
|
-
* and for custom-embed postMessage trust in storefront-commerce (when registered).
|
|
4
|
-
*/
|
|
5
|
-
export const EDGE_BLOCK_YOUTUBE_FRAME_ORIGINS = [
|
|
6
|
-
'https://www.youtube.com',
|
|
7
|
-
'https://www.youtube-nocookie.com',
|
|
8
|
-
];
|
|
9
|
-
/** Trusted origins for custom-embed resize postMessage (storefront-commerce). */
|
|
10
|
-
export const EDGE_BLOCK_CUSTOM_EMBED_TRUSTED_ORIGINS = [
|
|
11
|
-
'https://calendly.com',
|
|
12
|
-
'https://substack.com',
|
|
13
|
-
'https://shoppex.io',
|
|
14
|
-
];
|
|
15
|
-
/** frame-src entries for custom embed blocks (includes Substack wildcard). */
|
|
16
|
-
export const EDGE_BLOCK_CUSTOM_EMBED_FRAME_SRC_ORIGINS = [
|
|
17
|
-
...EDGE_BLOCK_CUSTOM_EMBED_TRUSTED_ORIGINS,
|
|
18
|
-
'https://*.substack.com',
|
|
19
|
-
];
|
|
20
|
-
export const EDGE_BLOCK_ALLOWED_FRAME_ORIGINS = [
|
|
21
|
-
...EDGE_BLOCK_YOUTUBE_FRAME_ORIGINS,
|
|
22
|
-
...EDGE_BLOCK_CUSTOM_EMBED_FRAME_SRC_ORIGINS,
|
|
23
|
-
];
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* SSOT for iframe embed origins allowed in the Edge Block Engine CSP (frame-src)
|
|
3
|
-
* and for custom-embed postMessage trust in storefront-commerce (when registered).
|
|
4
|
-
*/
|
|
5
|
-
export const EDGE_BLOCK_YOUTUBE_FRAME_ORIGINS = [
|
|
6
|
-
'https://www.youtube.com',
|
|
7
|
-
'https://www.youtube-nocookie.com',
|
|
8
|
-
] as const;
|
|
9
|
-
|
|
10
|
-
/** Trusted origins for custom-embed resize postMessage (storefront-commerce). */
|
|
11
|
-
export const EDGE_BLOCK_CUSTOM_EMBED_TRUSTED_ORIGINS = [
|
|
12
|
-
'https://calendly.com',
|
|
13
|
-
'https://substack.com',
|
|
14
|
-
'https://shoppex.io',
|
|
15
|
-
] as const;
|
|
16
|
-
|
|
17
|
-
/** frame-src entries for custom embed blocks (includes Substack wildcard). */
|
|
18
|
-
export const EDGE_BLOCK_CUSTOM_EMBED_FRAME_SRC_ORIGINS = [
|
|
19
|
-
...EDGE_BLOCK_CUSTOM_EMBED_TRUSTED_ORIGINS,
|
|
20
|
-
'https://*.substack.com',
|
|
21
|
-
] as const;
|
|
22
|
-
|
|
23
|
-
export const EDGE_BLOCK_ALLOWED_FRAME_ORIGINS = [
|
|
24
|
-
...EDGE_BLOCK_YOUTUBE_FRAME_ORIGINS,
|
|
25
|
-
...EDGE_BLOCK_CUSTOM_EMBED_FRAME_SRC_ORIGINS,
|
|
26
|
-
] as const;
|