@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.
- package/README.md +209 -10
- package/dist/astro/index.cjs +1194 -1
- package/dist/astro/index.d.cts +47 -1
- package/dist/astro/index.d.ts +47 -1
- package/dist/astro/index.js +1197 -1
- package/dist/chunk-46ISDRHT.js +26 -0
- package/dist/chunk-74KY5EAM.js +1219 -0
- package/dist/chunk-AHBN6FQP.js +184 -0
- package/dist/{chunk-FLPNN3CV.js → chunk-PDNCNTDB.js} +1370 -623
- package/dist/chunk-S3VT4UWM.js +75 -0
- package/dist/chunk-TG6N3C22.js +246 -0
- package/dist/chunk-URXSDJOU.js +356 -0
- package/dist/index.cjs +3166 -392
- package/dist/index.d.cts +238 -4
- package/dist/index.d.ts +238 -4
- package/dist/index.js +189 -149
- package/dist/live-preview-H4643QYF.js +18 -0
- package/dist/nextjs/components.cjs +1522 -359
- package/dist/nextjs/components.d.cts +1 -1
- package/dist/nextjs/components.d.ts +1 -1
- package/dist/nextjs/components.js +4 -1
- package/dist/nextjs/index.cjs +1085 -6
- package/dist/nextjs/index.d.cts +73 -1
- package/dist/nextjs/index.d.ts +73 -1
- package/dist/nextjs/index.js +1079 -7
- package/dist/nextjs/live-action.cjs +285 -0
- package/dist/nextjs/live-action.d.cts +12 -0
- package/dist/nextjs/live-action.d.ts +12 -0
- package/dist/nextjs/live-action.js +68 -0
- package/dist/nextjs/live.cjs +1896 -0
- package/dist/nextjs/live.d.cts +52 -0
- package/dist/nextjs/live.d.ts +52 -0
- package/dist/nextjs/live.js +265 -0
- package/dist/nuxt/index.cjs +1 -1
- package/dist/nuxt/index.js +1 -1
- package/dist/nuxt/live-preview-O2ZTJNW4.js +1520 -0
- package/dist/nuxt/live.cjs +1720 -0
- package/dist/nuxt/live.d.cts +43 -0
- package/dist/nuxt/live.d.ts +43 -0
- package/dist/nuxt/live.js +94 -0
- package/dist/{pixel-B3oQNbZ3.d.cts → pixel-CX422dCO.d.cts} +10 -1
- package/dist/{pixel-B3oQNbZ3.d.ts → pixel-CX422dCO.d.ts} +10 -1
- package/dist/react/components.cjs +1522 -359
- package/dist/react/components.d.cts +9 -0
- package/dist/react/components.d.ts +9 -0
- package/dist/react/components.js +1522 -359
- package/dist/sveltekit/index.cjs +1 -1
- package/dist/sveltekit/index.js +1 -1
- package/package.json +12 -1
- package/src/astro/PreprToolbar.astro +7 -1
package/dist/index.js
CHANGED
|
@@ -4,185 +4,220 @@ import {
|
|
|
4
4
|
COOKIE_SEGMENT,
|
|
5
5
|
COOKIE_UID,
|
|
6
6
|
COOKIE_VARIANT,
|
|
7
|
+
HEADER_LIVE_RENDER,
|
|
7
8
|
ONE_YEAR_SECONDS,
|
|
8
9
|
PARAM_HIDE_BAR,
|
|
9
10
|
PARAM_SEGMENT,
|
|
10
11
|
PARAM_VARIANT,
|
|
11
12
|
PreprToolbarElement,
|
|
12
|
-
VERSION,
|
|
13
13
|
createPreprPreview,
|
|
14
14
|
createToolbarStore,
|
|
15
15
|
definePreprToolbar,
|
|
16
16
|
loadTrackingPixel,
|
|
17
|
-
resolveFeatures,
|
|
18
17
|
setTrackingParam,
|
|
19
18
|
stegaClean,
|
|
20
19
|
trackEvent
|
|
21
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-PDNCNTDB.js";
|
|
21
|
+
import {
|
|
22
|
+
PreprError,
|
|
23
|
+
extractAccessToken,
|
|
24
|
+
getActiveSegmentFromHeaders,
|
|
25
|
+
getActiveVariantFromHeaders,
|
|
26
|
+
getPreprEnvironmentSegments,
|
|
27
|
+
getPreprHeadersFromHeaders,
|
|
28
|
+
getPreprUUIDFromHeaders,
|
|
29
|
+
getToolbarPropsFromHeaders,
|
|
30
|
+
validatePreprToken
|
|
31
|
+
} from "./chunk-AHBN6FQP.js";
|
|
32
|
+
import {
|
|
33
|
+
VERSION,
|
|
34
|
+
resolveFeatures
|
|
35
|
+
} from "./chunk-46ISDRHT.js";
|
|
36
|
+
import {
|
|
37
|
+
createLiveBridge,
|
|
38
|
+
createLiveStore
|
|
39
|
+
} from "./chunk-TG6N3C22.js";
|
|
40
|
+
import "./chunk-URXSDJOU.js";
|
|
41
|
+
import {
|
|
42
|
+
normalizeQuery,
|
|
43
|
+
parsePatchLog
|
|
44
|
+
} from "./chunk-74KY5EAM.js";
|
|
45
|
+
import {
|
|
46
|
+
createScopedLogger
|
|
47
|
+
} from "./chunk-S3VT4UWM.js";
|
|
22
48
|
|
|
23
|
-
// src/core/
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
return headers.get("prepr-customer-id");
|
|
49
|
+
// src/core/preview-attrs.ts
|
|
50
|
+
function previewAttrs(target, options) {
|
|
51
|
+
if (!options.enabled || !target.id || !target.field) return {};
|
|
52
|
+
const attrs = {
|
|
53
|
+
"data-prepr-encoded": "",
|
|
54
|
+
"data-prepr-id": target.id,
|
|
55
|
+
"data-prepr-field": target.field
|
|
56
|
+
};
|
|
57
|
+
if (target.locale) attrs["data-prepr-locale"] = target.locale;
|
|
58
|
+
if (target.entryId) attrs["data-prepr-entry-id"] = target.entryId;
|
|
59
|
+
return attrs;
|
|
35
60
|
}
|
|
36
|
-
|
|
37
|
-
|
|
61
|
+
|
|
62
|
+
// src/core/live-preview/morph.ts
|
|
63
|
+
function morphElement(current, next) {
|
|
64
|
+
syncAttributes(current, next);
|
|
65
|
+
morphChildren(current, next);
|
|
38
66
|
}
|
|
39
|
-
|
|
40
|
-
|
|
67
|
+
var SKIP_ATTR = "data-prepr-morph-skip";
|
|
68
|
+
var RUNTIME_ATTR_PREFIXES = ["data-prepr-"];
|
|
69
|
+
function isRuntimeAttribute(name) {
|
|
70
|
+
return RUNTIME_ATTR_PREFIXES.some((prefix) => name.startsWith(prefix));
|
|
41
71
|
}
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
"Prepr-Preview-Bar",
|
|
47
|
-
"Prepr-Context-utm_source",
|
|
48
|
-
"Prepr-Context-utm_medium",
|
|
49
|
-
"Prepr-Context-utm_term",
|
|
50
|
-
"Prepr-Context-utm_content",
|
|
51
|
-
"Prepr-Context-utm_campaign",
|
|
52
|
-
"Prepr-Context-initial_referral",
|
|
53
|
-
"Prepr-Visitor-IP",
|
|
54
|
-
"Prepr-Hubspot-Id",
|
|
55
|
-
"Prepr-Customer-Id-Created",
|
|
56
|
-
"User-Agent",
|
|
57
|
-
"Prepr-Package"
|
|
58
|
-
];
|
|
59
|
-
function getPreprHeadersFromHeaders(headers) {
|
|
60
|
-
const preprHeaders = {};
|
|
61
|
-
const copy = (key) => {
|
|
62
|
-
const value = headers.get(key);
|
|
63
|
-
if (value !== null) {
|
|
64
|
-
preprHeaders[key] = value;
|
|
72
|
+
function syncAttributes(current, next) {
|
|
73
|
+
for (const { name } of Array.from(current.attributes)) {
|
|
74
|
+
if (!next.hasAttribute(name) && !isRuntimeAttribute(name)) {
|
|
75
|
+
current.removeAttribute(name);
|
|
65
76
|
}
|
|
66
|
-
};
|
|
67
|
-
for (const key of PREPR_HEADER_KEYS) {
|
|
68
|
-
copy(key);
|
|
69
77
|
}
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
function validatePreprToken(token) {
|
|
73
|
-
if (!token) {
|
|
74
|
-
throw new PreprError(
|
|
75
|
-
"Token is required",
|
|
76
|
-
"MISSING_TOKEN",
|
|
77
|
-
"validatePreprToken"
|
|
78
|
-
);
|
|
79
|
-
}
|
|
80
|
-
if (!token.startsWith("https://")) {
|
|
81
|
-
throw new PreprError(
|
|
82
|
-
"Token must be a valid HTTPS URL",
|
|
83
|
-
"INVALID_TOKEN",
|
|
84
|
-
"validatePreprToken"
|
|
85
|
-
);
|
|
78
|
+
for (const { name, value } of Array.from(next.attributes)) {
|
|
79
|
+
if (current.getAttribute(name) !== value) current.setAttribute(name, value);
|
|
86
80
|
}
|
|
87
81
|
}
|
|
88
|
-
function
|
|
89
|
-
|
|
90
|
-
try {
|
|
91
|
-
url = new URL(graphqlUrl);
|
|
92
|
-
} catch (error) {
|
|
93
|
-
throw new PreprError(
|
|
94
|
-
"Token must be a valid URL",
|
|
95
|
-
"INVALID_TOKEN",
|
|
96
|
-
"extractAccessToken",
|
|
97
|
-
error instanceof Error ? error : new Error(String(error))
|
|
98
|
-
);
|
|
99
|
-
}
|
|
100
|
-
if (url.hostname !== "graphql.prepr.io") {
|
|
101
|
-
throw new PreprError(
|
|
102
|
-
"Token must be a graphql.prepr.io URL",
|
|
103
|
-
"INVALID_TOKEN",
|
|
104
|
-
"extractAccessToken"
|
|
105
|
-
);
|
|
106
|
-
}
|
|
107
|
-
const pathParts = url.pathname.split("/");
|
|
108
|
-
const token = pathParts[pathParts.length - 1];
|
|
109
|
-
if (!token) {
|
|
110
|
-
throw new PreprError(
|
|
111
|
-
"Token URL is missing an access token segment",
|
|
112
|
-
"INVALID_TOKEN",
|
|
113
|
-
"extractAccessToken"
|
|
114
|
-
);
|
|
115
|
-
}
|
|
116
|
-
return token;
|
|
82
|
+
function isSkipped(node) {
|
|
83
|
+
return node.nodeType === Node.ELEMENT_NODE && (node.hasAttribute(SKIP_ATTR) || node.localName === "prepr-toolbar");
|
|
117
84
|
}
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
"HTTP_ERROR",
|
|
144
|
-
"getPreprEnvironmentSegments"
|
|
145
|
-
);
|
|
146
|
-
}
|
|
147
|
-
const json = await response.json();
|
|
148
|
-
if (!json || !json.data || !json.data._Segments) {
|
|
149
|
-
throw new PreprError(
|
|
150
|
-
"Invalid response format from Prepr API",
|
|
151
|
-
"INVALID_RESPONSE",
|
|
152
|
-
"getPreprEnvironmentSegments"
|
|
153
|
-
);
|
|
154
|
-
}
|
|
155
|
-
return json.data._Segments;
|
|
156
|
-
} catch (error) {
|
|
157
|
-
if (error instanceof PreprError) {
|
|
158
|
-
throw error;
|
|
85
|
+
function sameKind(a, b) {
|
|
86
|
+
if (a.nodeType !== b.nodeType) return false;
|
|
87
|
+
if (a.nodeType !== Node.ELEMENT_NODE) return true;
|
|
88
|
+
const ea = a;
|
|
89
|
+
const eb = b;
|
|
90
|
+
return ea.localName === eb.localName && ea.id === eb.id;
|
|
91
|
+
}
|
|
92
|
+
function morphChildren(current, next) {
|
|
93
|
+
const wanted = Array.from(next.childNodes).filter((n) => !isSkipped(n));
|
|
94
|
+
const existing = Array.from(current.childNodes).filter((n) => !isSkipped(n));
|
|
95
|
+
wanted.forEach((target, i) => {
|
|
96
|
+
const node = existing[i];
|
|
97
|
+
if (node && sameKind(node, target)) {
|
|
98
|
+
if (node.nodeType === Node.ELEMENT_NODE) {
|
|
99
|
+
const element = node;
|
|
100
|
+
if (element.localName === "script") return;
|
|
101
|
+
if (element.localName === "astro-island" && !element.isEqualNode(target)) {
|
|
102
|
+
element.replaceWith(adopt(target, current));
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
morphElement(element, target);
|
|
106
|
+
} else if (node.nodeValue !== target.nodeValue) {
|
|
107
|
+
node.nodeValue = target.nodeValue;
|
|
108
|
+
}
|
|
109
|
+
return;
|
|
159
110
|
}
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
111
|
+
const replacement = adopt(target, current);
|
|
112
|
+
if (node) node.replaceWith(replacement);
|
|
113
|
+
else current.appendChild(replacement);
|
|
114
|
+
});
|
|
115
|
+
for (const extra of existing.slice(wanted.length)) extra.remove();
|
|
116
|
+
}
|
|
117
|
+
function adopt(node, parent) {
|
|
118
|
+
const copy = parent.ownerDocument.importNode(node, true);
|
|
119
|
+
if (copy.nodeType === Node.ELEMENT_NODE) {
|
|
120
|
+
const element = copy;
|
|
121
|
+
const scripts = element.localName === "script" ? [element] : Array.from(element.querySelectorAll("script"));
|
|
122
|
+
scripts.forEach((s) => s.setAttribute("data-prepr-morphed", ""));
|
|
166
123
|
}
|
|
124
|
+
return copy;
|
|
167
125
|
}
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
126
|
+
|
|
127
|
+
// src/core/live-preview/html-live.ts
|
|
128
|
+
var debug = createScopedLogger("live-preview:html");
|
|
129
|
+
var COMPLETE_DOCUMENT = /<\/html>\s*$/i;
|
|
130
|
+
var SYNC_DEBOUNCE_MS = 150;
|
|
131
|
+
function startHtmlLivePreview(options = {}) {
|
|
132
|
+
if (typeof window === "undefined" || window.self === window.top) {
|
|
133
|
+
return { stop() {
|
|
134
|
+
} };
|
|
135
|
+
}
|
|
136
|
+
const store = createLiveStore();
|
|
137
|
+
const bridge = createLiveBridge(store, {
|
|
138
|
+
allowedEditorOrigins: () => options.allowedEditorOrigins,
|
|
139
|
+
strictMode: () => options.strictMode
|
|
140
|
+
});
|
|
141
|
+
bridge.start();
|
|
142
|
+
let timer = null;
|
|
143
|
+
let inFlight = false;
|
|
144
|
+
let pending = null;
|
|
145
|
+
let stopped = false;
|
|
146
|
+
const render = async (log) => {
|
|
147
|
+
inFlight = true;
|
|
172
148
|
try {
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
149
|
+
const response = await fetch(window.location.href, {
|
|
150
|
+
method: log.length ? "POST" : "GET",
|
|
151
|
+
headers: log.length ? { "Content-Type": "application/json", [HEADER_LIVE_RENDER]: "1" } : {},
|
|
152
|
+
body: log.length ? JSON.stringify({ log }) : void 0,
|
|
153
|
+
credentials: "same-origin",
|
|
154
|
+
cache: "no-store"
|
|
155
|
+
});
|
|
156
|
+
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
|
157
|
+
const html = await response.text();
|
|
158
|
+
if (stopped) return;
|
|
159
|
+
if (!COMPLETE_DOCUMENT.test(html)) {
|
|
160
|
+
throw new Error("incomplete page; a component failed to render");
|
|
161
|
+
}
|
|
162
|
+
const next = new DOMParser().parseFromString(html, "text/html");
|
|
163
|
+
if (next.title && next.title !== document.title)
|
|
164
|
+
document.title = next.title;
|
|
165
|
+
morphElement(document.body, next.body);
|
|
166
|
+
} catch (error) {
|
|
167
|
+
debug.log("re-render failed", error);
|
|
168
|
+
} finally {
|
|
169
|
+
inFlight = false;
|
|
176
170
|
}
|
|
177
|
-
|
|
171
|
+
if (pending && !stopped) {
|
|
172
|
+
const queued = pending;
|
|
173
|
+
pending = null;
|
|
174
|
+
void render(queued);
|
|
175
|
+
}
|
|
176
|
+
};
|
|
177
|
+
const schedule = (log) => {
|
|
178
|
+
if (timer) clearTimeout(timer);
|
|
179
|
+
timer = setTimeout(() => {
|
|
180
|
+
timer = null;
|
|
181
|
+
if (inFlight) pending = log;
|
|
182
|
+
else void render(log);
|
|
183
|
+
}, SYNC_DEBOUNCE_MS);
|
|
184
|
+
};
|
|
185
|
+
const offLog = store.onLogChange((log) => {
|
|
186
|
+
if (log.length) schedule(log);
|
|
187
|
+
});
|
|
188
|
+
const offInvalidate = store.onInvalidate(() => schedule([]));
|
|
178
189
|
return {
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
190
|
+
stop() {
|
|
191
|
+
stopped = true;
|
|
192
|
+
if (timer) clearTimeout(timer);
|
|
193
|
+
offLog();
|
|
194
|
+
offInvalidate();
|
|
195
|
+
bridge.stop();
|
|
196
|
+
}
|
|
183
197
|
};
|
|
184
198
|
}
|
|
185
199
|
|
|
200
|
+
// src/core/live-preview/live-request.ts
|
|
201
|
+
var MAX_BODY_BYTES = 1e6;
|
|
202
|
+
async function readLiveLogFromRequest(request) {
|
|
203
|
+
try {
|
|
204
|
+
if (request.method !== "POST") return [];
|
|
205
|
+
if (request.headers.get(HEADER_LIVE_RENDER) !== "1") return [];
|
|
206
|
+
const origin = request.headers.get("origin");
|
|
207
|
+
if (!origin || origin !== new URL(request.url).origin) return [];
|
|
208
|
+
const type = request.headers.get("content-type") ?? "";
|
|
209
|
+
if (!type.toLowerCase().startsWith("application/json")) return [];
|
|
210
|
+
const length = Number(request.headers.get("content-length") ?? "0");
|
|
211
|
+
if (length > MAX_BODY_BYTES) return [];
|
|
212
|
+
const text = await request.clone().text();
|
|
213
|
+
if (text.length > MAX_BODY_BYTES) return [];
|
|
214
|
+
const body = JSON.parse(text);
|
|
215
|
+
return parsePatchLog(body?.log);
|
|
216
|
+
} catch {
|
|
217
|
+
return [];
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
186
221
|
// src/core/middleware.ts
|
|
187
222
|
var UTM_PARAMS = [
|
|
188
223
|
"utm_source",
|
|
@@ -352,9 +387,14 @@ export {
|
|
|
352
387
|
getPreprUUIDFromHeaders,
|
|
353
388
|
getToolbarPropsFromHeaders,
|
|
354
389
|
loadTrackingPixel,
|
|
390
|
+
normalizeQuery,
|
|
391
|
+
parsePatchLog,
|
|
392
|
+
previewAttrs,
|
|
355
393
|
processPreprRequest,
|
|
394
|
+
readLiveLogFromRequest,
|
|
356
395
|
resolveFeatures,
|
|
357
396
|
setTrackingParam,
|
|
397
|
+
startHtmlLivePreview,
|
|
358
398
|
stegaClean,
|
|
359
399
|
trackEvent,
|
|
360
400
|
validatePreprToken
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createLiveBridge,
|
|
3
|
+
createLiveStore
|
|
4
|
+
} from "./chunk-TG6N3C22.js";
|
|
5
|
+
import "./chunk-URXSDJOU.js";
|
|
6
|
+
import {
|
|
7
|
+
applyPatchLog,
|
|
8
|
+
normalizeQuery,
|
|
9
|
+
parsePatchLog
|
|
10
|
+
} from "./chunk-74KY5EAM.js";
|
|
11
|
+
import "./chunk-S3VT4UWM.js";
|
|
12
|
+
export {
|
|
13
|
+
applyPatchLog,
|
|
14
|
+
createLiveBridge,
|
|
15
|
+
createLiveStore,
|
|
16
|
+
normalizeQuery,
|
|
17
|
+
parsePatchLog
|
|
18
|
+
};
|