@shoppexio/builder-contracts 0.1.3 → 0.1.6

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 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';
@@ -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";
@@ -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 EdgeBlockRenderModeSchema: z.ZodLiteral<"edge-block-engine">;
70
- export type EdgeBlockRenderMode = z.infer<typeof EdgeBlockRenderModeSchema>;
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,12 +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<"edge-block-engine">;
81
+ renderMode: z.ZodLiteral<"server-liquid">;
82
82
  blocks: z.ZodArray<z.ZodObject<{
83
83
  blockId: z.ZodString;
84
84
  html: z.ZodString;
85
85
  }, z.core.$strict>>;
86
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>;
87
97
  export declare const PreviewReloadMessageSchema: z.ZodObject<{
88
98
  type: z.ZodLiteral<"RELOAD">;
89
99
  revision: z.ZodNumber;
@@ -187,11 +197,18 @@ export declare const PreviewMessageSchema: z.ZodDiscriminatedUnion<[z.ZodObject<
187
197
  revision: z.ZodNumber;
188
198
  pageId: z.ZodString;
189
199
  sourceRevision: z.ZodString;
190
- renderMode: z.ZodLiteral<"edge-block-engine">;
200
+ renderMode: z.ZodLiteral<"server-liquid">;
191
201
  blocks: z.ZodArray<z.ZodObject<{
192
202
  blockId: z.ZodString;
193
203
  html: z.ZodString;
194
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;
195
212
  }, z.core.$strict>, z.ZodObject<{
196
213
  type: z.ZodLiteral<"RELOAD">;
197
214
  revision: z.ZodNumber;
@@ -242,7 +259,7 @@ export declare const PreviewReadyHealthSchema: z.ZodDiscriminatedUnion<[z.ZodObj
242
259
  builderRuntimeProvider: z.ZodLiteral<true>;
243
260
  protocolVersion: z.ZodLiteral<2>;
244
261
  }, z.core.$strict>, z.ZodObject<{
245
- edgeBlockEngine: z.ZodLiteral<true>;
262
+ serverLiquidEngine: z.ZodLiteral<true>;
246
263
  blockHtmlReconcile: z.ZodLiteral<true>;
247
264
  protocolVersion: z.ZodLiteral<3>;
248
265
  }, z.core.$strict>], "protocolVersion">;
@@ -255,7 +272,7 @@ export declare const PreviewReadyResponseSchema: z.ZodObject<{
255
272
  builderRuntimeProvider: z.ZodLiteral<true>;
256
273
  protocolVersion: z.ZodLiteral<2>;
257
274
  }, z.core.$strict>, z.ZodObject<{
258
- edgeBlockEngine: z.ZodLiteral<true>;
275
+ serverLiquidEngine: z.ZodLiteral<true>;
259
276
  blockHtmlReconcile: z.ZodLiteral<true>;
260
277
  protocolVersion: z.ZodLiteral<3>;
261
278
  }, z.core.$strict>], "protocolVersion">>;
@@ -274,7 +291,7 @@ export declare const PreviewBlockHtmlAppliedResponseSchema: z.ZodObject<{
274
291
  revision: z.ZodNumber;
275
292
  pageId: z.ZodString;
276
293
  sourceRevision: z.ZodString;
277
- renderMode: z.ZodLiteral<"edge-block-engine">;
294
+ renderMode: z.ZodLiteral<"server-liquid">;
278
295
  blocks: z.ZodArray<z.ZodObject<{
279
296
  blockId: z.ZodString;
280
297
  html: z.ZodString;
@@ -285,7 +302,7 @@ export declare const PreviewBlockHtmlFailedResponseSchema: z.ZodObject<{
285
302
  revision: z.ZodNumber;
286
303
  pageId: z.ZodString;
287
304
  sourceRevision: z.ZodOptional<z.ZodString>;
288
- renderMode: z.ZodLiteral<"edge-block-engine">;
305
+ renderMode: z.ZodLiteral<"server-liquid">;
289
306
  blockIds: z.ZodArray<z.ZodString>;
290
307
  error: z.ZodString;
291
308
  }, z.core.$strict>;
@@ -418,7 +435,7 @@ export declare const PreviewResponseSchema: z.ZodDiscriminatedUnion<[z.ZodObject
418
435
  builderRuntimeProvider: z.ZodLiteral<true>;
419
436
  protocolVersion: z.ZodLiteral<2>;
420
437
  }, z.core.$strict>, z.ZodObject<{
421
- edgeBlockEngine: z.ZodLiteral<true>;
438
+ serverLiquidEngine: z.ZodLiteral<true>;
422
439
  blockHtmlReconcile: z.ZodLiteral<true>;
423
440
  protocolVersion: z.ZodLiteral<3>;
424
441
  }, z.core.$strict>], "protocolVersion">>;
@@ -434,7 +451,7 @@ export declare const PreviewResponseSchema: z.ZodDiscriminatedUnion<[z.ZodObject
434
451
  revision: z.ZodNumber;
435
452
  pageId: z.ZodString;
436
453
  sourceRevision: z.ZodString;
437
- renderMode: z.ZodLiteral<"edge-block-engine">;
454
+ renderMode: z.ZodLiteral<"server-liquid">;
438
455
  blocks: z.ZodArray<z.ZodObject<{
439
456
  blockId: z.ZodString;
440
457
  html: z.ZodString;
@@ -444,7 +461,7 @@ export declare const PreviewResponseSchema: z.ZodDiscriminatedUnion<[z.ZodObject
444
461
  revision: z.ZodNumber;
445
462
  pageId: z.ZodString;
446
463
  sourceRevision: z.ZodOptional<z.ZodString>;
447
- renderMode: z.ZodLiteral<"edge-block-engine">;
464
+ renderMode: z.ZodLiteral<"server-liquid">;
448
465
  blockIds: z.ZodArray<z.ZodString>;
449
466
  error: z.ZodString;
450
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,yBAAyB,mCAAiC,CAAC;AACxE,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,CAAC;AAE5E,eAAO,MAAM,2BAA2B;;;kBAK7B,CAAC;AACZ,MAAM,MAAM,qBAAqB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,2BAA2B,CAAC,CAAC;AAEhF,eAAO,MAAM,kCAAkC;;;;;;;;;;kBASpC,CAAC;AAEZ,eAAO,MAAM,0BAA0B;;;;;;;kBAM5B,CAAC;AAEZ,eAAO,MAAM,iCAAiC;;;;;;;;;;;;;;;;;;;;;kBAMnC,CAAC;AAEZ,eAAO,MAAM,gCAAgC;;kBAIlC,CAAC;AAEZ,eAAO,MAAM,qBAAqB;;;EAA8B,CAAC;AACjE,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAEpE;;;;;GAKG;AACH,eAAO,MAAM,sCAAsC;;;;;;kBAKxC,CAAC;AAEZ,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4BAO/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"}
1
+ {"version":3,"file":"preview-protocol.d.ts","sourceRoot":"","sources":["../src/preview-protocol.ts"],"names":[],"mappings":"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"}
@@ -20,7 +20,7 @@ export const PreviewApplyStateMessageSchema = z
20
20
  state: BuilderSettingsSchema,
21
21
  })
22
22
  .strict();
23
- export const EdgeBlockRenderModeSchema = z.literal('edge-block-engine');
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,8 +33,18 @@ 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: EdgeBlockRenderModeSchema,
37
- blocks: z.array(PreviewBlockHtmlPatchSchema).min(1),
36
+ renderMode: ServerLiquidRenderModeSchema,
37
+ blocks: z.array(PreviewBlockHtmlPatchSchema),
38
+ })
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),
38
48
  })
39
49
  .strict();
40
50
  export const PreviewReloadMessageSchema = z
@@ -72,6 +82,7 @@ export const PreviewSetInteractionModeMessageSchema = z
72
82
  export const PreviewMessageSchema = z.discriminatedUnion('type', [
73
83
  PreviewApplyStateMessageSchema,
74
84
  PreviewApplyBlockHtmlMessageSchema,
85
+ PreviewApplyPageHtmlMessageSchema,
75
86
  PreviewReloadMessageSchema,
76
87
  PreviewSelectElementMessageSchema,
77
88
  PreviewRequestReadyMessageSchema,
@@ -96,7 +107,7 @@ export const PreviewReadyHealthSchema = z.discriminatedUnion('protocolVersion',
96
107
  .strict(),
97
108
  z
98
109
  .object({
99
- edgeBlockEngine: z.literal(true),
110
+ serverLiquidEngine: z.literal(true),
100
111
  blockHtmlReconcile: z.literal(true),
101
112
  protocolVersion: z.literal(3),
102
113
  })
@@ -128,8 +139,8 @@ export const PreviewBlockHtmlAppliedResponseSchema = z
128
139
  revision: z.number().int().nonnegative(),
129
140
  pageId: z.string().min(1),
130
141
  sourceRevision: z.string().min(1),
131
- renderMode: EdgeBlockRenderModeSchema,
132
- blocks: z.array(PreviewBlockHtmlPatchSchema).min(1),
142
+ renderMode: ServerLiquidRenderModeSchema,
143
+ blocks: z.array(PreviewBlockHtmlPatchSchema),
133
144
  })
134
145
  .strict();
135
146
  export const PreviewBlockHtmlFailedResponseSchema = z
@@ -138,8 +149,8 @@ export const PreviewBlockHtmlFailedResponseSchema = z
138
149
  revision: z.number().int().nonnegative(),
139
150
  pageId: z.string().min(1),
140
151
  sourceRevision: z.string().min(1).optional(),
141
- renderMode: EdgeBlockRenderModeSchema,
142
- blockIds: z.array(z.string().min(1)).min(1),
152
+ renderMode: ServerLiquidRenderModeSchema,
153
+ blockIds: z.array(z.string().min(1)),
143
154
  error: z.string().min(1),
144
155
  })
145
156
  .strict();
@@ -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
+ }
@@ -1,10 +1,15 @@
1
- export declare const PUBLIC_BUILDER_THEME_SCHEMES: readonly ["default", "classic", "nebula", "pulse", "phantom", "starlight", "apex", "vault", "vortex", "clean-minimal"];
2
- export declare const STARTER_BUILDER_THEME_SCHEMES: readonly ["blank"];
3
- export declare const BUILDER_READY_THEME_SCHEMES: readonly ["blank", "default", "classic", "nebula", "pulse", "phantom", "starlight", "apex", "vault", "vortex", "clean-minimal"];
1
+ export declare const PUBLIC_BUILDER_THEME_SCHEMES: readonly ["default", "classic", "nebula", "pulse", "phantom", "starlight", "apex", "vault", "clean-minimal"];
4
2
  export type PublicBuilderThemeScheme = (typeof PUBLIC_BUILDER_THEME_SCHEMES)[number];
3
+ export declare const STARTER_BUILDER_THEME_SCHEMES: readonly ["blank"];
4
+ export declare const THEME_STORE_DISABLED_SCHEMES: readonly ["classic", "vault", "nebula", "pulse", "phantom", "apex", "clean-minimal"];
5
+ export declare const THEME_STORE_INSTALLABLE_SCHEMES: readonly ["default", "starlight"];
6
+ export declare const BUILDER_READY_THEME_SCHEMES: readonly ["blank", "default", "classic", "nebula", "pulse", "phantom", "starlight", "apex", "vault", "clean-minimal"];
7
+ export type ThemeStoreInstallableScheme = (typeof THEME_STORE_INSTALLABLE_SCHEMES)[number];
5
8
  export type StarterBuilderThemeScheme = (typeof STARTER_BUILDER_THEME_SCHEMES)[number];
6
9
  export type BuilderReadyThemeScheme = (typeof BUILDER_READY_THEME_SCHEMES)[number];
7
10
  export declare function isPublicBuilderThemeScheme(value: string): value is PublicBuilderThemeScheme;
11
+ export declare function isThemeStoreDisabledScheme(value: string): boolean;
12
+ export declare function isThemeStoreInstallableScheme(value: string): value is ThemeStoreInstallableScheme;
8
13
  export declare function isStarterBuilderThemeScheme(value: string): value is StarterBuilderThemeScheme;
9
14
  export declare function isBuilderReadyThemeScheme(value: string): value is BuilderReadyThemeScheme;
10
15
  //# sourceMappingURL=theme-schemes.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"theme-schemes.d.ts","sourceRoot":"","sources":["../src/theme-schemes.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,4BAA4B,wHAW/B,CAAC;AAEX,eAAO,MAAM,6BAA6B,oBAAqB,CAAC;AAEhE,eAAO,MAAM,2BAA2B,iIAG9B,CAAC;AAEX,MAAM,MAAM,wBAAwB,GAAG,CAAC,OAAO,4BAA4B,CAAC,CAAC,MAAM,CAAC,CAAC;AACrF,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,2BAA2B,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,IAAI,yBAAyB,CAE7F;AAED,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,IAAI,uBAAuB,CAEzF"}
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,sFAQe,CAAC;AAIzD,eAAO,MAAM,+BAA+B,mCAGY,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"}
@@ -7,10 +7,23 @@ export const PUBLIC_BUILDER_THEME_SCHEMES = [
7
7
  'starlight',
8
8
  'apex',
9
9
  'vault',
10
- 'vortex',
11
10
  'clean-minimal',
12
11
  ];
13
12
  export const STARTER_BUILDER_THEME_SCHEMES = ['blank'];
13
+ export const THEME_STORE_DISABLED_SCHEMES = [
14
+ 'classic',
15
+ 'vault',
16
+ 'nebula',
17
+ 'pulse',
18
+ 'phantom',
19
+ 'apex',
20
+ 'clean-minimal',
21
+ ];
22
+ const THEME_STORE_DISABLED_SCHEME_SET = new Set(THEME_STORE_DISABLED_SCHEMES);
23
+ export const THEME_STORE_INSTALLABLE_SCHEMES = [
24
+ 'default',
25
+ 'starlight',
26
+ ];
14
27
  export const BUILDER_READY_THEME_SCHEMES = [
15
28
  ...STARTER_BUILDER_THEME_SCHEMES,
16
29
  ...PUBLIC_BUILDER_THEME_SCHEMES,
@@ -18,6 +31,12 @@ export const BUILDER_READY_THEME_SCHEMES = [
18
31
  export function isPublicBuilderThemeScheme(value) {
19
32
  return PUBLIC_BUILDER_THEME_SCHEMES.includes(value);
20
33
  }
34
+ export function isThemeStoreDisabledScheme(value) {
35
+ return THEME_STORE_DISABLED_SCHEME_SET.has(value);
36
+ }
37
+ export function isThemeStoreInstallableScheme(value) {
38
+ return THEME_STORE_INSTALLABLE_SCHEMES.includes(value);
39
+ }
21
40
  export function isStarterBuilderThemeScheme(value) {
22
41
  return STARTER_BUILDER_THEME_SCHEMES.includes(value);
23
42
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shoppexio/builder-contracts",
3
- "version": "0.1.3",
3
+ "version": "0.1.6",
4
4
  "description": "Shared Builder v2 contracts for Shoppex dashboard, backend, preview runtime, and themes",
5
5
  "type": "module",
6
6
  "repository": {
@@ -37,6 +37,24 @@
37
37
  "import": "./dist/builder-settings.js",
38
38
  "default": "./dist/builder-settings.js"
39
39
  },
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"
57
+ },
40
58
  "./events": {
41
59
  "bun": "./src/events.ts",
42
60
  "types": "./dist/events.d.ts",
@@ -857,7 +857,7 @@ describe('@shoppex/builder-contracts', () => {
857
857
 
858
858
  test('parses official theme manifests with transparent contact-form backgrounds', () => {
859
859
  const repoRoot = join(import.meta.dir, '../../..');
860
- for (const theme of ['default', 'nebula', 'classic', 'pulse', 'starlight', 'vault', 'phantom', 'apex', 'vortex']) {
860
+ for (const theme of ['default', 'nebula', 'classic', 'pulse', 'starlight', 'vault', 'phantom', 'apex']) {
861
861
  const manifestPath = join(repoRoot, 'themes', theme, 'theme.manifest.json');
862
862
  const raw = JSON.parse(readFileSync(manifestPath, 'utf8'));
863
863
  expect(ThemeManifestSchema.safeParse(raw).success, `${theme} manifest should validate`).toBe(true);
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';
@@ -11,7 +11,7 @@ describe('PreviewMessageSchema', () => {
11
11
  revision: 12,
12
12
  pageId: 'product',
13
13
  sourceRevision: 'theme-source-12',
14
- renderMode: 'edge-block-engine',
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: 'edge-block-engine',
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 edge block protocol v3', () => {
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
- edgeBlockEngine: true,
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: 'edge-block-engine',
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('edge-block-engine');
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: 'edge-block-engine',
158
+ renderMode: 'server-liquid',
111
159
  blockIds: ['footer-1'],
112
160
  error: 'Rendered block root does not match block id.',
113
161
  });
@@ -24,8 +24,8 @@ export const PreviewApplyStateMessageSchema = z
24
24
  })
25
25
  .strict();
26
26
 
27
- export const EdgeBlockRenderModeSchema = z.literal('edge-block-engine');
28
- export type EdgeBlockRenderMode = z.infer<typeof EdgeBlockRenderModeSchema>;
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,10 +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: EdgeBlockRenderModeSchema,
45
- blocks: z.array(PreviewBlockHtmlPatchSchema).min(1),
44
+ renderMode: ServerLiquidRenderModeSchema,
45
+ blocks: z.array(PreviewBlockHtmlPatchSchema),
46
46
  })
47
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>;
48
61
 
49
62
  export const PreviewReloadMessageSchema = z
50
63
  .object({
@@ -87,6 +100,7 @@ export const PreviewSetInteractionModeMessageSchema = z
87
100
  export const PreviewMessageSchema = z.discriminatedUnion('type', [
88
101
  PreviewApplyStateMessageSchema,
89
102
  PreviewApplyBlockHtmlMessageSchema,
103
+ PreviewApplyPageHtmlMessageSchema,
90
104
  PreviewReloadMessageSchema,
91
105
  PreviewSelectElementMessageSchema,
92
106
  PreviewRequestReadyMessageSchema,
@@ -114,7 +128,7 @@ export const PreviewReadyHealthSchema = z.discriminatedUnion('protocolVersion',
114
128
  .strict(),
115
129
  z
116
130
  .object({
117
- edgeBlockEngine: z.literal(true),
131
+ serverLiquidEngine: z.literal(true),
118
132
  blockHtmlReconcile: z.literal(true),
119
133
  protocolVersion: z.literal(3),
120
134
  })
@@ -151,8 +165,8 @@ export const PreviewBlockHtmlAppliedResponseSchema = z
151
165
  revision: z.number().int().nonnegative(),
152
166
  pageId: z.string().min(1),
153
167
  sourceRevision: z.string().min(1),
154
- renderMode: EdgeBlockRenderModeSchema,
155
- blocks: z.array(PreviewBlockHtmlPatchSchema).min(1),
168
+ renderMode: ServerLiquidRenderModeSchema,
169
+ blocks: z.array(PreviewBlockHtmlPatchSchema),
156
170
  })
157
171
  .strict();
158
172
 
@@ -162,8 +176,8 @@ export const PreviewBlockHtmlFailedResponseSchema = z
162
176
  revision: z.number().int().nonnegative(),
163
177
  pageId: z.string().min(1),
164
178
  sourceRevision: z.string().min(1).optional(),
165
- renderMode: EdgeBlockRenderModeSchema,
166
- blockIds: z.array(z.string().min(1)).min(1),
179
+ renderMode: ServerLiquidRenderModeSchema,
180
+ blockIds: z.array(z.string().min(1)),
167
181
  error: z.string().min(1),
168
182
  })
169
183
  .strict();
@@ -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
+ }
@@ -7,18 +7,36 @@ export const PUBLIC_BUILDER_THEME_SCHEMES = [
7
7
  'starlight',
8
8
  'apex',
9
9
  'vault',
10
- 'vortex',
11
10
  'clean-minimal',
12
11
  ] as const;
13
12
 
13
+ export type PublicBuilderThemeScheme = (typeof PUBLIC_BUILDER_THEME_SCHEMES)[number];
14
+
14
15
  export const STARTER_BUILDER_THEME_SCHEMES = ['blank'] as const;
15
16
 
17
+ export const THEME_STORE_DISABLED_SCHEMES = [
18
+ 'classic',
19
+ 'vault',
20
+ 'nebula',
21
+ 'pulse',
22
+ 'phantom',
23
+ 'apex',
24
+ 'clean-minimal',
25
+ ] as const satisfies readonly PublicBuilderThemeScheme[];
26
+
27
+ const THEME_STORE_DISABLED_SCHEME_SET = new Set<string>(THEME_STORE_DISABLED_SCHEMES);
28
+
29
+ export const THEME_STORE_INSTALLABLE_SCHEMES = [
30
+ 'default',
31
+ 'starlight',
32
+ ] as const satisfies readonly PublicBuilderThemeScheme[];
33
+
16
34
  export const BUILDER_READY_THEME_SCHEMES = [
17
35
  ...STARTER_BUILDER_THEME_SCHEMES,
18
36
  ...PUBLIC_BUILDER_THEME_SCHEMES,
19
37
  ] as const;
20
38
 
21
- export type PublicBuilderThemeScheme = (typeof PUBLIC_BUILDER_THEME_SCHEMES)[number];
39
+ export type ThemeStoreInstallableScheme = (typeof THEME_STORE_INSTALLABLE_SCHEMES)[number];
22
40
  export type StarterBuilderThemeScheme = (typeof STARTER_BUILDER_THEME_SCHEMES)[number];
23
41
  export type BuilderReadyThemeScheme = (typeof BUILDER_READY_THEME_SCHEMES)[number];
24
42
 
@@ -26,6 +44,16 @@ export function isPublicBuilderThemeScheme(value: string): value is PublicBuilde
26
44
  return (PUBLIC_BUILDER_THEME_SCHEMES as readonly string[]).includes(value);
27
45
  }
28
46
 
47
+ export function isThemeStoreDisabledScheme(value: string): boolean {
48
+ return THEME_STORE_DISABLED_SCHEME_SET.has(value);
49
+ }
50
+
51
+ export function isThemeStoreInstallableScheme(
52
+ value: string,
53
+ ): value is ThemeStoreInstallableScheme {
54
+ return (THEME_STORE_INSTALLABLE_SCHEMES as readonly string[]).includes(value);
55
+ }
56
+
29
57
  export function isStarterBuilderThemeScheme(value: string): value is StarterBuilderThemeScheme {
30
58
  return (STARTER_BUILDER_THEME_SCHEMES as readonly string[]).includes(value);
31
59
  }