@preprio/toolkit 0.5.0-rc.2 → 0.5.0-rc.4

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.
Files changed (50) hide show
  1. package/README.md +209 -10
  2. package/dist/astro/index.cjs +1194 -1
  3. package/dist/astro/index.d.cts +47 -1
  4. package/dist/astro/index.d.ts +47 -1
  5. package/dist/astro/index.js +1197 -1
  6. package/dist/chunk-46ISDRHT.js +26 -0
  7. package/dist/chunk-74KY5EAM.js +1219 -0
  8. package/dist/chunk-AHBN6FQP.js +184 -0
  9. package/dist/{chunk-FLPNN3CV.js → chunk-PDNCNTDB.js} +1370 -623
  10. package/dist/chunk-S3VT4UWM.js +75 -0
  11. package/dist/chunk-TG6N3C22.js +246 -0
  12. package/dist/chunk-URXSDJOU.js +356 -0
  13. package/dist/index.cjs +3166 -392
  14. package/dist/index.d.cts +238 -4
  15. package/dist/index.d.ts +238 -4
  16. package/dist/index.js +189 -149
  17. package/dist/live-preview-H4643QYF.js +18 -0
  18. package/dist/nextjs/components.cjs +1522 -359
  19. package/dist/nextjs/components.d.cts +1 -1
  20. package/dist/nextjs/components.d.ts +1 -1
  21. package/dist/nextjs/components.js +4 -1
  22. package/dist/nextjs/index.cjs +1085 -6
  23. package/dist/nextjs/index.d.cts +73 -1
  24. package/dist/nextjs/index.d.ts +73 -1
  25. package/dist/nextjs/index.js +1079 -7
  26. package/dist/nextjs/live-action.cjs +285 -0
  27. package/dist/nextjs/live-action.d.cts +12 -0
  28. package/dist/nextjs/live-action.d.ts +12 -0
  29. package/dist/nextjs/live-action.js +68 -0
  30. package/dist/nextjs/live.cjs +1896 -0
  31. package/dist/nextjs/live.d.cts +52 -0
  32. package/dist/nextjs/live.d.ts +52 -0
  33. package/dist/nextjs/live.js +265 -0
  34. package/dist/nuxt/index.cjs +1 -1
  35. package/dist/nuxt/index.js +1 -1
  36. package/dist/nuxt/live-preview-O2ZTJNW4.js +1520 -0
  37. package/dist/nuxt/live.cjs +1720 -0
  38. package/dist/nuxt/live.d.cts +43 -0
  39. package/dist/nuxt/live.d.ts +43 -0
  40. package/dist/nuxt/live.js +94 -0
  41. package/dist/{pixel-B3oQNbZ3.d.cts → pixel-CX422dCO.d.cts} +10 -1
  42. package/dist/{pixel-B3oQNbZ3.d.ts → pixel-CX422dCO.d.ts} +10 -1
  43. package/dist/react/components.cjs +1522 -359
  44. package/dist/react/components.d.cts +9 -0
  45. package/dist/react/components.d.ts +9 -0
  46. package/dist/react/components.js +1522 -359
  47. package/dist/sveltekit/index.cjs +1 -1
  48. package/dist/sveltekit/index.js +1 -1
  49. package/package.json +12 -1
  50. package/src/astro/PreprToolbar.astro +7 -1
@@ -0,0 +1,75 @@
1
+ // src/core/utils.ts
2
+ var trustedParentOrigin = null;
3
+ function setTrustedParentOrigin(origin) {
4
+ trustedParentOrigin = origin;
5
+ }
6
+ function getTrustedParentOrigin() {
7
+ return trustedParentOrigin;
8
+ }
9
+ function sendPreprEvent(event, data, options) {
10
+ if (typeof window !== "undefined") {
11
+ const message = {
12
+ name: "prepr_preview_bar",
13
+ event,
14
+ ...data
15
+ };
16
+ window.dispatchEvent(
17
+ new CustomEvent("prepr_preview_bar", { detail: message })
18
+ );
19
+ if (window.parent && window.parent !== window) {
20
+ if (trustedParentOrigin) {
21
+ window.parent.postMessage(message, trustedParentOrigin);
22
+ } else if (options?.allowUntrustedTarget) {
23
+ window.parent.postMessage(message, "*");
24
+ }
25
+ }
26
+ }
27
+ }
28
+ var DebugLogger = class {
29
+ constructor(options) {
30
+ this.options = {
31
+ prefix: "[Prepr]",
32
+ ...options
33
+ };
34
+ }
35
+ // A local `enabled` wins; otherwise defer to the global logger so scoped
36
+ // loggers created before initDebugLogger() still pick up the setting.
37
+ isEnabled() {
38
+ if (this.options.enabled !== void 0) {
39
+ return this.options.enabled;
40
+ }
41
+ return globalDebugLogger?.options?.enabled ?? false;
42
+ }
43
+ log(message, ...args) {
44
+ if (!this.isEnabled()) return;
45
+ const prefix = this.options.prefix;
46
+ console.log(`${prefix} ${message}`, ...args);
47
+ }
48
+ warn(message, ...args) {
49
+ if (!this.isEnabled()) return;
50
+ const prefix = this.options.prefix;
51
+ console.warn(`${prefix} ${message}`, ...args);
52
+ }
53
+ error(message, ...args) {
54
+ if (!this.isEnabled()) return;
55
+ const prefix = this.options.prefix;
56
+ console.error(`${prefix} ${message}`, ...args);
57
+ }
58
+ };
59
+ var globalDebugLogger = null;
60
+ function initDebugLogger(enabled = false) {
61
+ globalDebugLogger = new DebugLogger({ enabled });
62
+ }
63
+ function createScopedLogger(scopeName) {
64
+ return new DebugLogger({
65
+ prefix: `[Prepr][${scopeName}]`
66
+ });
67
+ }
68
+
69
+ export {
70
+ setTrustedParentOrigin,
71
+ getTrustedParentOrigin,
72
+ sendPreprEvent,
73
+ initDebugLogger,
74
+ createScopedLogger
75
+ };
@@ -0,0 +1,246 @@
1
+ import {
2
+ isAllowedEditorOrigin
3
+ } from "./chunk-URXSDJOU.js";
4
+ import {
5
+ indexSnapshot,
6
+ isLiveFieldChange,
7
+ parseEditTarget,
8
+ parseListEntries,
9
+ patchSnapshot,
10
+ syncListSnapshot
11
+ } from "./chunk-74KY5EAM.js";
12
+ import {
13
+ createScopedLogger,
14
+ getTrustedParentOrigin,
15
+ sendPreprEvent
16
+ } from "./chunk-S3VT4UWM.js";
17
+
18
+ // src/core/live-preview/bridge-events.ts
19
+ var debug = createScopedLogger("live-preview:bridge");
20
+ function createLiveBridge(store, options = {}) {
21
+ let parentOrigin = null;
22
+ const onMessage = (evt) => {
23
+ const data = evt?.data;
24
+ if (data?.event === "prepr:initVE" && !parentOrigin) {
25
+ if (isAllowedEditorOrigin(evt.origin, options.allowedEditorOrigins?.())) {
26
+ parentOrigin = evt.origin;
27
+ } else {
28
+ debug.warn("ignored live preview handshake from origin", evt.origin);
29
+ }
30
+ return;
31
+ }
32
+ const trusted = parentOrigin ?? getTrustedParentOrigin();
33
+ if (!trusted || evt.origin !== trusted) return;
34
+ if (data?.event === "prepr:fieldChange") {
35
+ if (typeof data.itemId === "string" && typeof data.field === "string" && data.field && isLiveFieldChange(data.field, data.value)) {
36
+ const edit = parseEditTarget(data.edit);
37
+ if (edit) store.pushPatch(data.itemId, data.field, data.value, edit);
38
+ else store.pushPatch(data.itemId, data.field, data.value);
39
+ } else {
40
+ debug.log("dropped malformed fieldChange", data);
41
+ }
42
+ return;
43
+ }
44
+ if (data?.event === "prepr:listChange") {
45
+ const list = parseListEntries(data.entries);
46
+ if (typeof data.itemId === "string" && typeof data.field === "string" && data.field && list) {
47
+ store.pushList(data.itemId, data.field, list);
48
+ } else {
49
+ debug.log("dropped malformed listChange", data);
50
+ }
51
+ return;
52
+ }
53
+ if (data?.event === "prepr:itemChanged") {
54
+ if (typeof data.itemId === "string") {
55
+ store.invalidate(data.itemId);
56
+ } else {
57
+ debug.log("dropped malformed itemChanged", data);
58
+ }
59
+ }
60
+ };
61
+ return {
62
+ start() {
63
+ window.addEventListener("message", onMessage);
64
+ sendPreprEvent(
65
+ "live_ready",
66
+ options.strictMode?.() ? { strictMode: true } : void 0,
67
+ { allowUntrustedTarget: true }
68
+ );
69
+ },
70
+ stop() {
71
+ window.removeEventListener("message", onMessage);
72
+ }
73
+ };
74
+ }
75
+
76
+ // src/core/live-preview/live-store.ts
77
+ var debug2 = createScopedLogger("live-preview");
78
+ var INVALIDATE_DEBOUNCE_MS = 150;
79
+ var MAX_LOG_ENTRIES = 500;
80
+ function withPatch(log, patch) {
81
+ let previous = -1;
82
+ for (let i = log.length - 1; i >= 0; i--) {
83
+ const p = log[i];
84
+ if (p.type === patch.type && p.itemId === patch.itemId && p.field === patch.field) {
85
+ previous = i;
86
+ break;
87
+ }
88
+ }
89
+ if (previous === -1) return [...log, patch];
90
+ const since = log.slice(previous + 1);
91
+ const listPrefix = `${patch.field}.`;
92
+ const dependsOnPosition = since.some(
93
+ (p) => p.type === "list" || patch.type === "list" && p.itemId === patch.itemId && p.field.startsWith(listPrefix)
94
+ );
95
+ if (dependsOnPosition) return [...log, patch];
96
+ return [...log.slice(0, previous), ...since, patch];
97
+ }
98
+ function createLiveStore(options = {}) {
99
+ const accepts = (patch) => options.accept ? options.accept(patch) : true;
100
+ const entries = /* @__PURE__ */ new Set();
101
+ const invalidateListeners = /* @__PURE__ */ new Set();
102
+ const pendingInvalidates = /* @__PURE__ */ new Map();
103
+ let defaultFetcher = null;
104
+ let log = [];
105
+ const logListeners = /* @__PURE__ */ new Set();
106
+ function record(patch) {
107
+ log = withPatch(log, patch).slice(-MAX_LOG_ENTRIES);
108
+ logListeners.forEach((fn) => fn(log));
109
+ }
110
+ function notify(entry) {
111
+ entry.listeners.forEach((fn) => fn());
112
+ }
113
+ async function refetch(entry) {
114
+ const fetcher = entry.opts.fetcher ?? defaultFetcher;
115
+ if (!fetcher || entry.opts.query == null) return;
116
+ const token = ++entry.fetchToken;
117
+ try {
118
+ const result = await fetcher(entry.opts.query, entry.opts.variables);
119
+ if (token !== entry.fetchToken || !entries.has(entry)) return;
120
+ entry.snapshot = result;
121
+ entry.index = indexSnapshot(result);
122
+ notify(entry);
123
+ } catch (error) {
124
+ debug2.log("refetch failed, keeping previous snapshot", error);
125
+ }
126
+ }
127
+ function runInvalidate(itemId) {
128
+ pendingInvalidates.delete(itemId);
129
+ invalidateListeners.forEach((fn) => fn(itemId));
130
+ for (const entry of entries) {
131
+ if (entry.index.has(itemId)) void refetch(entry);
132
+ }
133
+ }
134
+ return {
135
+ register(data, opts = {}) {
136
+ const entry = {
137
+ snapshot: data,
138
+ index: indexSnapshot(data),
139
+ opts,
140
+ listeners: /* @__PURE__ */ new Set(),
141
+ fetchToken: 0
142
+ };
143
+ for (const patch of log) {
144
+ if (patch.type === "field") {
145
+ entry.snapshot = patchSnapshot(
146
+ entry.snapshot,
147
+ entry.index,
148
+ patch.itemId,
149
+ patch.field,
150
+ patch.value
151
+ );
152
+ } else {
153
+ const next = syncListSnapshot(
154
+ entry.snapshot,
155
+ entry.index,
156
+ patch.itemId,
157
+ patch.field,
158
+ patch.entries
159
+ );
160
+ if (next !== entry.snapshot) {
161
+ entry.snapshot = next;
162
+ entry.index = indexSnapshot(next);
163
+ }
164
+ }
165
+ }
166
+ entries.add(entry);
167
+ return {
168
+ getSnapshot: () => entry.snapshot,
169
+ subscribe(fn) {
170
+ entry.listeners.add(fn);
171
+ return () => entry.listeners.delete(fn);
172
+ },
173
+ unregister() {
174
+ entry.listeners.clear();
175
+ entries.delete(entry);
176
+ }
177
+ };
178
+ },
179
+ pushPatch(itemId, field, value, edit) {
180
+ const patch = edit ? { type: "field", itemId, field, value, edit } : { type: "field", itemId, field, value };
181
+ if (!accepts(patch)) return;
182
+ record(patch);
183
+ for (const entry of entries) {
184
+ const next = patchSnapshot(
185
+ entry.snapshot,
186
+ entry.index,
187
+ itemId,
188
+ field,
189
+ value
190
+ );
191
+ if (next !== entry.snapshot) {
192
+ entry.snapshot = next;
193
+ notify(entry);
194
+ }
195
+ }
196
+ },
197
+ pushList(itemId, field, list) {
198
+ const patch = { type: "list", itemId, field, entries: list };
199
+ if (!accepts(patch)) return;
200
+ record(patch);
201
+ for (const entry of entries) {
202
+ const next = syncListSnapshot(
203
+ entry.snapshot,
204
+ entry.index,
205
+ itemId,
206
+ field,
207
+ list
208
+ );
209
+ if (next !== entry.snapshot) {
210
+ entry.snapshot = next;
211
+ entry.index = indexSnapshot(next);
212
+ notify(entry);
213
+ }
214
+ }
215
+ },
216
+ invalidate(itemId) {
217
+ if (log.length) {
218
+ log = [];
219
+ logListeners.forEach((fn) => fn(log));
220
+ }
221
+ const pending = pendingInvalidates.get(itemId);
222
+ if (pending) clearTimeout(pending);
223
+ pendingInvalidates.set(
224
+ itemId,
225
+ setTimeout(() => runInvalidate(itemId), INVALIDATE_DEBOUNCE_MS)
226
+ );
227
+ },
228
+ setFetcher(fetcher) {
229
+ defaultFetcher = fetcher;
230
+ },
231
+ onInvalidate(fn) {
232
+ invalidateListeners.add(fn);
233
+ return () => invalidateListeners.delete(fn);
234
+ },
235
+ getLog: () => log,
236
+ onLogChange(fn) {
237
+ logListeners.add(fn);
238
+ return () => logListeners.delete(fn);
239
+ }
240
+ };
241
+ }
242
+
243
+ export {
244
+ createLiveBridge,
245
+ createLiveStore
246
+ };
@@ -0,0 +1,356 @@
1
+ import {
2
+ createScopedLogger,
3
+ sendPreprEvent,
4
+ setTrustedParentOrigin
5
+ } from "./chunk-S3VT4UWM.js";
6
+
7
+ // src/core/locales/de.json
8
+ var de_default = {
9
+ common: {
10
+ viewingAs: "Ansicht als:",
11
+ user: "Benutzer",
12
+ off: "Aus",
13
+ on: "An",
14
+ reset: "Zur\xFCcksetzen",
15
+ toolbar: "Symbolleiste"
16
+ },
17
+ adaptiveContent: {
18
+ adaptiveContent: "Adaptive Inhalte",
19
+ enablePreview: "Vorschaumodus",
20
+ segment: "Segment",
21
+ ABVariant: "A/B-Variante",
22
+ chooseSegment: "Segment w\xE4hlen",
23
+ searchSegments: "Segmente suchen",
24
+ none: "Keine Segmente",
25
+ allOtherUsers: "Alle anderen Benutzer",
26
+ offDescription: "Die Website als echter Benutzer ansehen (Cookies verwenden)",
27
+ onDescription: "Mit der Symbolleiste Segmente und A/B-Varianten simulieren"
28
+ },
29
+ editingTools: {
30
+ editingTools: "Bearbeitungswerkzeuge",
31
+ editMode: "Bearbeitungsmodus",
32
+ ariaCloseEditMode: "Bearbeitungsmodus schlie\xDFen"
33
+ },
34
+ visualEditor: {
35
+ edit: "Bearbeiten",
36
+ otherItem: "anderes Element",
37
+ linkedItem: "verkn\xFCpftes Element"
38
+ }
39
+ };
40
+
41
+ // src/core/locales/en.json
42
+ var en_default = {
43
+ common: {
44
+ viewingAs: "Viewing as:",
45
+ user: "User",
46
+ off: "Off",
47
+ on: "On",
48
+ reset: "Reset",
49
+ toolbar: "Toolbar"
50
+ },
51
+ adaptiveContent: {
52
+ adaptiveContent: "Adaptive content",
53
+ enablePreview: "Preview mode",
54
+ segment: "Segment",
55
+ ABVariant: "A/B variant",
56
+ chooseSegment: "Choose segment",
57
+ searchSegments: "Search segments",
58
+ none: "No segments",
59
+ allOtherUsers: "All other users",
60
+ offDescription: "View the site as a real user (use cookies)",
61
+ onDescription: "Use the toolbar to simulate segments and A/B variants"
62
+ },
63
+ editingTools: {
64
+ editingTools: "Editing tools",
65
+ editMode: "Edit mode",
66
+ ariaCloseEditMode: "Close edit mode"
67
+ },
68
+ visualEditor: {
69
+ edit: "Edit",
70
+ otherItem: "other item",
71
+ linkedItem: "linked item"
72
+ }
73
+ };
74
+
75
+ // src/core/locales/fr.json
76
+ var fr_default = {
77
+ common: {
78
+ viewingAs: "Affichage en tant que :",
79
+ user: "Utilisateur",
80
+ off: "D\xE9sactiv\xE9",
81
+ on: "Activ\xE9",
82
+ reset: "R\xE9initialiser",
83
+ toolbar: "Barre d'outils"
84
+ },
85
+ adaptiveContent: {
86
+ adaptiveContent: "Contenu adaptatif",
87
+ enablePreview: "Mode aper\xE7u",
88
+ segment: "Segment",
89
+ ABVariant: "Variante A/B",
90
+ chooseSegment: "Choisir un segment",
91
+ searchSegments: "Rechercher des segments",
92
+ none: "Aucun segment",
93
+ allOtherUsers: "Tous les autres utilisateurs",
94
+ offDescription: "Voir le site comme un vrai utilisateur (utiliser les cookies)",
95
+ onDescription: "Utilisez la barre d'outils pour simuler des segments et des variantes A/B"
96
+ },
97
+ editingTools: {
98
+ editingTools: "Outils d'\xE9dition",
99
+ editMode: "Mode \xE9dition",
100
+ ariaCloseEditMode: "Fermer le mode \xE9dition"
101
+ },
102
+ visualEditor: {
103
+ edit: "Modifier",
104
+ otherItem: "autre \xE9l\xE9ment",
105
+ linkedItem: "\xE9l\xE9ment li\xE9"
106
+ }
107
+ };
108
+
109
+ // src/core/locales/nl.json
110
+ var nl_default = {
111
+ common: {
112
+ viewingAs: "Bekijken als:",
113
+ user: "Gebruiker",
114
+ off: "Uit",
115
+ on: "Aan",
116
+ reset: "Resetten",
117
+ toolbar: "Toolbar"
118
+ },
119
+ adaptiveContent: {
120
+ adaptiveContent: "Adaptieve inhoud",
121
+ enablePreview: "Preview modus",
122
+ segment: "Segment",
123
+ ABVariant: "A/B-variant",
124
+ chooseSegment: "Kies segment",
125
+ searchSegments: "Zoek segmenten",
126
+ none: "Geen segmenten",
127
+ allOtherUsers: "Alle andere gebruikers",
128
+ offDescription: "Bekijk de site als een echte gebruiker (gebruik cookies)",
129
+ onDescription: "Gebruik de werkbalk om segmenten en A/B-varianten te simuleren"
130
+ },
131
+ editingTools: {
132
+ editingTools: "Editing Tools",
133
+ editMode: "Edit mode",
134
+ ariaCloseEditMode: "Edit mode sluiten"
135
+ },
136
+ visualEditor: {
137
+ edit: "Bewerken",
138
+ otherItem: "ander item",
139
+ linkedItem: "gekoppeld item"
140
+ }
141
+ };
142
+
143
+ // src/core/i18n.ts
144
+ var dictionaries = {
145
+ en: en_default,
146
+ nl: nl_default,
147
+ fr: fr_default,
148
+ de: de_default
149
+ };
150
+ var LOCALES = Object.keys(dictionaries);
151
+ function isLocale(value) {
152
+ return typeof value === "string" && value in dictionaries;
153
+ }
154
+ function getDict(locale) {
155
+ return isLocale(locale) ? dictionaries[locale] : dictionaries.en;
156
+ }
157
+ function getFromPath(obj, path) {
158
+ return path.split(".").reduce(
159
+ (acc, key) => acc && typeof acc === "object" ? acc[key] : void 0,
160
+ obj
161
+ );
162
+ }
163
+ function format(message, vars) {
164
+ if (!vars) return message;
165
+ return Object.keys(vars).reduce(
166
+ (acc, k) => acc.replace(new RegExp(`\\{${k}\\}`, "g"), String(vars[k])),
167
+ message
168
+ );
169
+ }
170
+ function t(key, locale, vars) {
171
+ const dict = getDict(locale);
172
+ const msg = getFromPath(dict, key);
173
+ if (typeof msg === "string") return format(msg, vars);
174
+ return key;
175
+ }
176
+
177
+ // src/core/iframe-bridge.ts
178
+ var debug = createScopedLogger("iframe-bridge");
179
+ var MAX_REFERENCES = 500;
180
+ var MAX_HREF_LENGTH = 8192;
181
+ var MAX_ASSETS = 500;
182
+ var MAX_ASSET_KEY_LENGTH = 2048;
183
+ var MAX_ASSET_FIELD_LENGTH = 512;
184
+ function parseIds(value) {
185
+ if (!Array.isArray(value)) return void 0;
186
+ return value.slice(0, MAX_REFERENCES).filter((id) => typeof id === "string" && id.length > 0);
187
+ }
188
+ function parseReferences(value) {
189
+ if (!Array.isArray(value)) return void 0;
190
+ const list = [];
191
+ for (const entry of value.slice(0, MAX_REFERENCES)) {
192
+ const id = entry?.id;
193
+ const field = entry?.field;
194
+ if (typeof id === "string" && id && typeof field === "string" && field) {
195
+ list.push({ id, field });
196
+ }
197
+ }
198
+ return list;
199
+ }
200
+ function boundedString(value, max) {
201
+ return typeof value === "string" && value && value.length <= max ? value : void 0;
202
+ }
203
+ function parseAssets(value) {
204
+ if (!Array.isArray(value)) return void 0;
205
+ const list = [];
206
+ for (const entry of value.slice(0, MAX_ASSETS)) {
207
+ if (!entry || typeof entry !== "object") continue;
208
+ const raw = entry;
209
+ const key = boundedString(raw.key, MAX_ASSET_KEY_LENGTH);
210
+ const field = boundedString(raw.field, MAX_ASSET_FIELD_LENGTH);
211
+ if (!key || !field) continue;
212
+ const entryId = boundedString(raw.entryId, MAX_ASSET_FIELD_LENGTH);
213
+ list.push(entryId ? { key, field, entryId } : { key, field });
214
+ }
215
+ return list;
216
+ }
217
+ var EDITOR_BASE_DOMAIN = "prepr.io";
218
+ function isAllowedEditorOrigin(origin, allowed) {
219
+ if (allowed) {
220
+ if (allowed.includes("*")) return origin !== "null";
221
+ return allowed.includes(origin);
222
+ }
223
+ let url;
224
+ try {
225
+ url = new URL(origin);
226
+ } catch {
227
+ return false;
228
+ }
229
+ if (url.protocol !== "https:" || url.port !== "") return false;
230
+ const suffix = `.${EDITOR_BASE_DOMAIN}`;
231
+ if (!url.hostname.endsWith(suffix)) return false;
232
+ const label = url.hostname.slice(0, -suffix.length);
233
+ return label.length > 0 && !label.includes(".");
234
+ }
235
+ function createIframeBridge(store, options = {}) {
236
+ const allowedOrigins = options.allowedEditorOrigins;
237
+ const onScrollToField = options.onScrollToField;
238
+ const onHandshake = options.onHandshake;
239
+ const onReferences = options.onReferences;
240
+ const onAssets = options.onAssets;
241
+ const onAllowNavigation = options.onAllowNavigation;
242
+ let parentOrigin = null;
243
+ const onKeyDown = (event) => {
244
+ const key = event.key.toLowerCase();
245
+ const blocked = (event.ctrlKey || event.metaKey) && ["s", "p", "l"].includes(key);
246
+ if (blocked) event.preventDefault();
247
+ };
248
+ const onMessage = (evt) => {
249
+ const data = evt?.data;
250
+ if (data?.event === "prepr:initVE" && !parentOrigin) {
251
+ if (!isAllowedEditorOrigin(evt.origin, allowedOrigins)) {
252
+ debug.warn("ignored editor handshake from origin", evt.origin);
253
+ return;
254
+ }
255
+ parentOrigin = evt.origin;
256
+ setTrustedParentOrigin(parentOrigin);
257
+ if (data.scrollPosition != null) {
258
+ const top = data.scrollPosition;
259
+ setTimeout(() => window.scrollTo(0, top), 1);
260
+ }
261
+ store?.set({ previewMode: true, editMode: data.editMode ?? true });
262
+ const itemId = typeof data.itemId === "string" && data.itemId ? data.itemId : void 0;
263
+ const referencedIds = parseReferences(data.referencedIds);
264
+ const componentIds = parseIds(data.componentIds);
265
+ const uiLocale = isLocale(data.uiLocale) ? data.uiLocale : void 0;
266
+ const navigationGuard = data.navigationGuard === true;
267
+ const assets = parseAssets(data.assets);
268
+ try {
269
+ onHandshake?.({
270
+ ...itemId && { itemId },
271
+ ...referencedIds && { referencedIds },
272
+ ...componentIds && { componentIds },
273
+ ...uiLocale && { uiLocale },
274
+ ...navigationGuard && { navigationGuard },
275
+ ...assets && { assets }
276
+ });
277
+ } catch (error) {
278
+ debug.warn("handshake handler failed", error);
279
+ }
280
+ }
281
+ if (!parentOrigin || evt.origin !== parentOrigin) return;
282
+ if (data?.event === "prepr:references") {
283
+ const assets = parseAssets(data.assets);
284
+ if (assets) {
285
+ try {
286
+ onAssets?.(assets);
287
+ } catch (error) {
288
+ debug.warn("assets handler failed", error);
289
+ }
290
+ }
291
+ const referencedIds = parseReferences(data.referencedIds);
292
+ if (!referencedIds) return;
293
+ try {
294
+ onReferences?.(referencedIds, parseIds(data.componentIds));
295
+ } catch (error) {
296
+ debug.warn("references handler failed", error);
297
+ }
298
+ }
299
+ if (data?.event === "prepr:getScrollPosition") {
300
+ const currentScrollY = window.scrollY || document.documentElement.scrollTop;
301
+ sendPreprEvent("getScrollPosition", { value: currentScrollY });
302
+ }
303
+ if (data?.event === "prepr:scrollToField") {
304
+ if (typeof data.field !== "string" || !data.field) return;
305
+ if (data.id !== void 0 && typeof data.id !== "string") return;
306
+ const block = typeof data.block === "number" && Number.isInteger(data.block) ? data.block : void 0;
307
+ const entryId = typeof data.entryId === "string" && data.entryId ? data.entryId : void 0;
308
+ const fieldById = typeof data.fieldById === "string" && data.fieldById ? data.fieldById : void 0;
309
+ try {
310
+ onScrollToField?.({
311
+ field: data.field,
312
+ id: data.id,
313
+ block,
314
+ entryId,
315
+ fieldById
316
+ });
317
+ } catch (error) {
318
+ debug.warn("scrollToField handler failed", error);
319
+ }
320
+ }
321
+ if (data?.event === "prepr:allowNavigation") {
322
+ const href = data.href;
323
+ if (typeof href !== "string" || !href || href.length > MAX_HREF_LENGTH) {
324
+ return;
325
+ }
326
+ try {
327
+ onAllowNavigation?.(href);
328
+ } catch (error) {
329
+ debug.warn("allowNavigation handler failed", error);
330
+ }
331
+ }
332
+ };
333
+ return {
334
+ start() {
335
+ const features = store?.get().features;
336
+ sendPreprEvent("loaded", features ? { features } : void 0, {
337
+ allowUntrustedTarget: true
338
+ });
339
+ window.addEventListener("keydown", onKeyDown);
340
+ window.addEventListener("message", onMessage);
341
+ },
342
+ stop() {
343
+ window.removeEventListener("keydown", onKeyDown);
344
+ window.removeEventListener("message", onMessage);
345
+ parentOrigin = null;
346
+ setTrustedParentOrigin(null);
347
+ }
348
+ };
349
+ }
350
+
351
+ export {
352
+ isLocale,
353
+ t,
354
+ isAllowedEditorOrigin,
355
+ createIframeBridge
356
+ };