@zalify/storefront-kit 0.3.1 → 0.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ export declare function resolveEditorOrigin(origins: readonly string[], win?: Window): string | null;
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Which editor, if any, is framing this document.
3
+ *
4
+ * `document.referrer` names the editor only on the first document the editor
5
+ * loads. In `interact` mode the bridge navigates the frame with
6
+ * `location.replace`, so the next document's referrer is the storefront
7
+ * itself. The trusted origin is therefore remembered for the tab on first
8
+ * contact, and later documents accept it as long as the editor-mode param is
9
+ * still present — always checked against the allowlist, never trusted bare.
10
+ */
11
+ const STORAGE_KEY = "zalify-editor-origin";
12
+ export function resolveEditorOrigin(origins, win = window) {
13
+ if (!origins.length || win.self === win.top)
14
+ return null;
15
+ try {
16
+ if (new URL(win.location.href).searchParams.get("zalify-editor") !== "1")
17
+ return null;
18
+ }
19
+ catch {
20
+ return null;
21
+ }
22
+ let referrer = null;
23
+ try {
24
+ referrer = new URL(win.document.referrer).origin;
25
+ }
26
+ catch {
27
+ referrer = null;
28
+ }
29
+ if (referrer && origins.includes(referrer)) {
30
+ try {
31
+ win.sessionStorage.setItem(STORAGE_KEY, referrer);
32
+ }
33
+ catch {
34
+ /* private mode: the first document still works */
35
+ }
36
+ return referrer;
37
+ }
38
+ try {
39
+ const remembered = win.sessionStorage.getItem(STORAGE_KEY);
40
+ return remembered && origins.includes(remembered) ? remembered : null;
41
+ }
42
+ catch {
43
+ return null;
44
+ }
45
+ }
@@ -37,3 +37,4 @@ export * from './components/VideoModal';
37
37
  export { Facets, COLLECTION_SORT_OPTIONS, SEARCH_SORT_OPTIONS, } from './components/Facets';
38
38
  export { builtinSections, builtinBlocks } from './registries';
39
39
  export { useEditorTemplate } from './useEditorTemplate';
40
+ export { resolveEditorOrigin } from './editor-origin';
@@ -38,3 +38,4 @@ export { Facets, COLLECTION_SORT_OPTIONS, SEARCH_SORT_OPTIONS, } from './compone
38
38
  // Server-only loaders live in '@zalify/storefront-kit/react/server'.
39
39
  export { builtinSections, builtinBlocks } from './registries';
40
40
  export { useEditorTemplate } from './useEditorTemplate';
41
+ export { resolveEditorOrigin } from './editor-origin';
@@ -1,24 +1,15 @@
1
1
  "use client";
2
2
  import { useEffect, useRef, useState } from "react";
3
3
  import { getThemeStore, setThemePreview } from "./engine/store";
4
+ import { resolveEditorOrigin } from "./editor-origin";
4
5
  /** No editor code or schema is fetched outside an explicitly allowed preview. */
5
6
  export function useEditorTemplate(name, options) {
6
7
  const [draft, setDraft] = useState(null);
7
8
  const controller = useRef(null);
8
9
  useEffect(() => {
9
10
  setDraft(null);
10
- if (!options.origins.length ||
11
- window.self === window.top ||
12
- new URL(window.location.href).searchParams.get("zalify-editor") !== "1")
13
- return;
14
- let parentOrigin;
15
- try {
16
- parentOrigin = new URL(document.referrer).origin;
17
- }
18
- catch {
19
- return;
20
- }
21
- if (!options.origins.includes(parentOrigin))
11
+ const parentOrigin = resolveEditorOrigin(options.origins);
12
+ if (!parentOrigin)
22
13
  return;
23
14
  let disposed = false;
24
15
  const owner = getThemeStore();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zalify/storefront-kit",
3
- "version": "0.3.1",
3
+ "version": "0.3.2",
4
4
  "type": "module",
5
5
  "description": "The Zalify storefront SDK: framework-agnostic commerce logic (/commerce), the theme contract types and validators (/schemas), the canvas-editor bridge (/editor), and the React theme engine + shared components (/ui, /react/server). Consumed as TypeScript source inside the zalify-storefronts monorepo; published as compiled ESM + d.ts.",
6
6
  "license": "SEE LICENSE IN LICENSE.md",
@@ -78,7 +78,7 @@
78
78
  ],
79
79
  "scripts": {
80
80
  "build": "rm -rf dist && tsc -p tsconfig.build.json",
81
- "test": "node --test \"src/schemas/*.test.ts\" \"src/editor/*.test.ts\"",
81
+ "test": "node --test \"src/schemas/*.test.ts\" \"src/editor/*.test.ts\" \"src/react/*.test.ts\"",
82
82
  "typecheck": "tsc -p tsconfig.json"
83
83
  }
84
84
  }
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Which editor, if any, is framing this document.
3
+ *
4
+ * `document.referrer` names the editor only on the first document the editor
5
+ * loads. In `interact` mode the bridge navigates the frame with
6
+ * `location.replace`, so the next document's referrer is the storefront
7
+ * itself. The trusted origin is therefore remembered for the tab on first
8
+ * contact, and later documents accept it as long as the editor-mode param is
9
+ * still present — always checked against the allowlist, never trusted bare.
10
+ */
11
+ const STORAGE_KEY = "zalify-editor-origin";
12
+
13
+ export function resolveEditorOrigin(
14
+ origins: readonly string[],
15
+ win: Window = window,
16
+ ): string | null {
17
+ if (!origins.length || win.self === win.top) return null;
18
+ try {
19
+ if (new URL(win.location.href).searchParams.get("zalify-editor") !== "1")
20
+ return null;
21
+ } catch {
22
+ return null;
23
+ }
24
+ let referrer: string | null = null;
25
+ try {
26
+ referrer = new URL(win.document.referrer).origin;
27
+ } catch {
28
+ referrer = null;
29
+ }
30
+ if (referrer && origins.includes(referrer)) {
31
+ try {
32
+ win.sessionStorage.setItem(STORAGE_KEY, referrer);
33
+ } catch {
34
+ /* private mode: the first document still works */
35
+ }
36
+ return referrer;
37
+ }
38
+ try {
39
+ const remembered = win.sessionStorage.getItem(STORAGE_KEY);
40
+ return remembered && origins.includes(remembered) ? remembered : null;
41
+ } catch {
42
+ return null;
43
+ }
44
+ }
@@ -79,3 +79,4 @@ export {
79
79
  export {builtinSections, builtinBlocks} from './registries';
80
80
 
81
81
  export {useEditorTemplate} from './useEditorTemplate';
82
+ export {resolveEditorOrigin} from './editor-origin';
@@ -6,6 +6,7 @@ import type { FrameBridgeController } from "../editor/frame";
6
6
  import type { PreviewContext } from "../schemas/bridge";
7
7
  import type { EditorDocuments } from "../editor/bootstrap";
8
8
  import { getThemeStore, setThemePreview } from "./engine/store";
9
+ import { resolveEditorOrigin } from "./editor-origin";
9
10
 
10
11
  type Options = {
11
12
  origins: readonly string[];
@@ -28,19 +29,8 @@ export function useEditorTemplate(
28
29
  const controller = useRef<FrameBridgeController | null>(null);
29
30
  useEffect(() => {
30
31
  setDraft(null);
31
- if (
32
- !options.origins.length ||
33
- window.self === window.top ||
34
- new URL(window.location.href).searchParams.get("zalify-editor") !== "1"
35
- )
36
- return;
37
- let parentOrigin: string;
38
- try {
39
- parentOrigin = new URL(document.referrer).origin;
40
- } catch {
41
- return;
42
- }
43
- if (!options.origins.includes(parentOrigin)) return;
32
+ const parentOrigin = resolveEditorOrigin(options.origins);
33
+ if (!parentOrigin) return;
44
34
  let disposed = false;
45
35
  const owner = getThemeStore();
46
36
  void Promise.all([