@preprio/toolkit 0.5.0-rc.4 → 0.5.0-rc.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/README.md +337 -165
- package/dist/astro/index.cjs +126 -30
- package/dist/astro/index.js +130 -30
- package/dist/{chunk-TG6N3C22.js → chunk-IPMB32AZ.js} +1 -1
- package/dist/{chunk-74KY5EAM.js → chunk-KD3GK6TO.js} +2 -0
- package/dist/{chunk-AHBN6FQP.js → chunk-KZIRM54F.js} +1 -1
- package/dist/{chunk-PDNCNTDB.js → chunk-L3SDL6MP.js} +11 -3
- package/dist/{chunk-46ISDRHT.js → chunk-WHZNCYUS.js} +1 -1
- package/dist/codegen/index.cjs +79 -0
- package/dist/codegen/index.d.cts +46 -0
- package/dist/codegen/index.d.ts +46 -0
- package/dist/codegen/index.js +62 -0
- package/dist/index.cjs +133 -28
- package/dist/index.d.cts +14 -2
- package/dist/index.d.ts +14 -2
- package/dist/index.js +108 -6
- package/dist/{live-preview-H4643QYF.js → live-preview-DLXGXRE5.js} +2 -2
- package/dist/nextjs/components.cjs +11 -3
- package/dist/nextjs/components.js +2 -2
- package/dist/nextjs/index.cjs +124 -28
- package/dist/nextjs/index.d.cts +2 -0
- package/dist/nextjs/index.d.ts +2 -0
- package/dist/nextjs/index.js +128 -28
- package/dist/nextjs/live-action.js +3 -3
- package/dist/nextjs/live.cjs +4 -1
- package/dist/nextjs/live.d.cts +3 -1
- package/dist/nextjs/live.d.ts +3 -1
- package/dist/nextjs/live.js +5 -2
- package/dist/nuxt/chunk-BWPGO25E.js +340 -0
- package/dist/nuxt/index.cjs +1 -1
- package/dist/nuxt/index.js +1 -1
- package/dist/nuxt/{live-preview-O2ZTJNW4.js → live-preview-2RCU35W5.js} +6 -337
- package/dist/nuxt/live.cjs +1007 -911
- package/dist/nuxt/live.js +109 -5
- package/dist/react/components.cjs +11 -3
- package/dist/react/components.js +11 -3
- package/dist/sveltekit/chunk-BWPGO25E.js +340 -0
- package/dist/sveltekit/index.cjs +1 -1
- package/dist/sveltekit/index.js +1 -1
- package/dist/sveltekit/live-preview-2RCU35W5.js +1189 -0
- package/dist/sveltekit/live.cjs +1830 -0
- package/dist/sveltekit/live.d.cts +59 -0
- package/dist/sveltekit/live.d.ts +59 -0
- package/dist/sveltekit/live.js +204 -0
- package/package.json +16 -1
package/dist/nuxt/live.cjs
CHANGED
|
@@ -25,423 +25,764 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
25
25
|
};
|
|
26
26
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
27
27
|
|
|
28
|
-
// src/core/
|
|
29
|
-
function
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
if (
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
28
|
+
// src/core/live-preview/query-document.ts
|
|
29
|
+
function tokenize(source) {
|
|
30
|
+
const tokens = [];
|
|
31
|
+
let i = 0;
|
|
32
|
+
const n = source.length;
|
|
33
|
+
while (i < n) {
|
|
34
|
+
const ch = source[i];
|
|
35
|
+
if (ch === " " || ch === " " || ch === "\n" || ch === "\r" || ch === "," || ch === "\uFEFF") {
|
|
36
|
+
i++;
|
|
37
|
+
} else if (ch === "#") {
|
|
38
|
+
while (i < n && source[i] !== "\n" && source[i] !== "\r") i++;
|
|
39
|
+
} else if (ch === "." && source.startsWith("...", i)) {
|
|
40
|
+
tokens.push({ type: "punct", value: "..." });
|
|
41
|
+
i += 3;
|
|
42
|
+
} else if (PUNCT.has(ch)) {
|
|
43
|
+
tokens.push({ type: "punct", value: ch });
|
|
44
|
+
i++;
|
|
45
|
+
} else if (NAME_START.test(ch)) {
|
|
46
|
+
const start = i;
|
|
47
|
+
while (i < n && NAME_CONTINUE.test(source[i])) i++;
|
|
48
|
+
tokens.push({ type: "name", value: source.slice(start, i) });
|
|
49
|
+
} else if (ch === '"') {
|
|
50
|
+
if (source.startsWith('"""', i)) {
|
|
51
|
+
i += 3;
|
|
52
|
+
while (i < n && !source.startsWith('"""', i)) {
|
|
53
|
+
i += source.startsWith('\\"""', i) ? 4 : 1;
|
|
54
|
+
}
|
|
55
|
+
if (i >= n) throw new SyntaxError("Unterminated block string");
|
|
56
|
+
i += 3;
|
|
57
|
+
} else {
|
|
58
|
+
i++;
|
|
59
|
+
while (i < n && source[i] !== '"') {
|
|
60
|
+
if (source[i] === "\n" || source[i] === "\r") {
|
|
61
|
+
throw new SyntaxError("Unterminated string");
|
|
62
|
+
}
|
|
63
|
+
i += source[i] === "\\" ? 2 : 1;
|
|
64
|
+
}
|
|
65
|
+
if (i >= n) throw new SyntaxError("Unterminated string");
|
|
66
|
+
i++;
|
|
47
67
|
}
|
|
68
|
+
tokens.push({ type: "value" });
|
|
69
|
+
} else if (ch === "-" || ch >= "0" && ch <= "9") {
|
|
70
|
+
i++;
|
|
71
|
+
while (i < n && /[0-9.eE+-]/.test(source[i])) i++;
|
|
72
|
+
tokens.push({ type: "value" });
|
|
73
|
+
} else {
|
|
74
|
+
throw new SyntaxError(`Unexpected character "${ch}"`);
|
|
48
75
|
}
|
|
49
76
|
}
|
|
77
|
+
return tokens;
|
|
50
78
|
}
|
|
51
|
-
function
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
79
|
+
function parseSource(source, operationName) {
|
|
80
|
+
const tokens = tokenize(source);
|
|
81
|
+
let pos = 0;
|
|
82
|
+
const peek = () => tokens[pos];
|
|
83
|
+
const isPunct = (value) => {
|
|
84
|
+
const t = tokens[pos];
|
|
85
|
+
return t?.type === "punct" && t.value === value;
|
|
86
|
+
};
|
|
87
|
+
const expectPunct = (value) => {
|
|
88
|
+
if (!isPunct(value)) throw new SyntaxError(`Expected "${value}"`);
|
|
89
|
+
pos++;
|
|
90
|
+
};
|
|
91
|
+
const expectName = () => {
|
|
92
|
+
const t = tokens[pos];
|
|
93
|
+
if (t?.type !== "name") throw new SyntaxError("Expected a name");
|
|
94
|
+
pos++;
|
|
95
|
+
return t.value;
|
|
96
|
+
};
|
|
97
|
+
const skipParens = () => {
|
|
98
|
+
expectPunct("(");
|
|
99
|
+
let depth = 1;
|
|
100
|
+
while (depth > 0) {
|
|
101
|
+
const t = tokens[pos++];
|
|
102
|
+
if (!t) throw new SyntaxError("Unbalanced parentheses");
|
|
103
|
+
if (t.type === "punct" && t.value === "(") depth++;
|
|
104
|
+
else if (t.type === "punct" && t.value === ")") depth--;
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
const skipDirectives = () => {
|
|
108
|
+
while (isPunct("@")) {
|
|
109
|
+
pos++;
|
|
110
|
+
expectName();
|
|
111
|
+
if (isPunct("(")) skipParens();
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
const parseSelectionSet = () => {
|
|
115
|
+
expectPunct("{");
|
|
116
|
+
const selections = [];
|
|
117
|
+
while (!isPunct("}")) {
|
|
118
|
+
if (!peek()) throw new SyntaxError("Unterminated selection set");
|
|
119
|
+
selections.push(parseSelection());
|
|
120
|
+
}
|
|
121
|
+
pos++;
|
|
122
|
+
return selections;
|
|
123
|
+
};
|
|
124
|
+
const parseSelection = () => {
|
|
125
|
+
if (isPunct("...")) {
|
|
126
|
+
pos++;
|
|
127
|
+
const t = peek();
|
|
128
|
+
if (t?.type === "name" && t.value !== "on") {
|
|
129
|
+
pos++;
|
|
130
|
+
skipDirectives();
|
|
131
|
+
return { kind: "spread", name: t.value };
|
|
90
132
|
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
// src/core/live-preview/index-snapshot.ts
|
|
97
|
-
function indexSnapshot(data) {
|
|
98
|
-
const index = /* @__PURE__ */ new Map();
|
|
99
|
-
const visited = /* @__PURE__ */ new WeakSet();
|
|
100
|
-
const path = [];
|
|
101
|
-
function walk(node) {
|
|
102
|
-
if (node === null || typeof node !== "object") return;
|
|
103
|
-
if (visited.has(node)) return;
|
|
104
|
-
visited.add(node);
|
|
105
|
-
if (Array.isArray(node)) {
|
|
106
|
-
for (let i = 0; i < node.length; i++) {
|
|
107
|
-
path.push(i);
|
|
108
|
-
walk(node[i]);
|
|
109
|
-
path.pop();
|
|
133
|
+
let on = null;
|
|
134
|
+
if (t?.type === "name") {
|
|
135
|
+
pos++;
|
|
136
|
+
on = expectName();
|
|
110
137
|
}
|
|
111
|
-
|
|
138
|
+
skipDirectives();
|
|
139
|
+
return { kind: "inline", on, selections: parseSelectionSet() };
|
|
112
140
|
}
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
} else if (typeof record.__typename === "string") {
|
|
119
|
-
debug.log("item without _id will not live-update", {
|
|
120
|
-
__typename: record.__typename
|
|
121
|
-
});
|
|
141
|
+
let name = expectName();
|
|
142
|
+
const key = name;
|
|
143
|
+
if (isPunct(":")) {
|
|
144
|
+
pos++;
|
|
145
|
+
name = expectName();
|
|
122
146
|
}
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
147
|
+
let hasArgs = false;
|
|
148
|
+
if (isPunct("(")) {
|
|
149
|
+
hasArgs = true;
|
|
150
|
+
skipParens();
|
|
151
|
+
}
|
|
152
|
+
skipDirectives();
|
|
153
|
+
const selections = isPunct("{") ? parseSelectionSet() : null;
|
|
154
|
+
return { kind: "field", name, key, hasArgs, selections };
|
|
155
|
+
};
|
|
156
|
+
const fragments = /* @__PURE__ */ new Map();
|
|
157
|
+
const operations = [];
|
|
158
|
+
while (pos < tokens.length) {
|
|
159
|
+
if (isPunct("{")) {
|
|
160
|
+
operations.push({ name: null, selections: parseSelectionSet() });
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
163
|
+
const keyword = expectName();
|
|
164
|
+
if (keyword === "fragment") {
|
|
165
|
+
const name = expectName();
|
|
166
|
+
if (expectName() !== "on") throw new SyntaxError('Expected "on"');
|
|
167
|
+
const on = expectName();
|
|
168
|
+
skipDirectives();
|
|
169
|
+
fragments.set(name, { on, selections: parseSelectionSet() });
|
|
170
|
+
} else if (keyword === "query" || keyword === "mutation" || keyword === "subscription") {
|
|
171
|
+
const t = peek();
|
|
172
|
+
let name = null;
|
|
173
|
+
if (t?.type === "name") name = expectName();
|
|
174
|
+
if (isPunct("(")) skipParens();
|
|
175
|
+
skipDirectives();
|
|
176
|
+
operations.push({ name, selections: parseSelectionSet() });
|
|
177
|
+
} else {
|
|
178
|
+
throw new SyntaxError(`Unexpected "${keyword}"`);
|
|
128
179
|
}
|
|
129
180
|
}
|
|
130
|
-
|
|
131
|
-
return index;
|
|
132
|
-
}
|
|
133
|
-
var debug;
|
|
134
|
-
var init_index_snapshot = __esm({
|
|
135
|
-
"src/core/live-preview/index-snapshot.ts"() {
|
|
136
|
-
"use strict";
|
|
137
|
-
init_utils();
|
|
138
|
-
debug = createScopedLogger("live-preview");
|
|
139
|
-
}
|
|
140
|
-
});
|
|
141
|
-
|
|
142
|
-
// src/core/live-preview/field-name.ts
|
|
143
|
-
function graphqlFieldName(apiId) {
|
|
144
|
-
return apiId.replace(UPPERCASE_BOUNDARY, "$1_").toLowerCase();
|
|
181
|
+
return { operation: pickOperation(operations, operationName), fragments };
|
|
145
182
|
}
|
|
146
|
-
function
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
return
|
|
183
|
+
function pickOperation(operations, operationName) {
|
|
184
|
+
const op = operationName ? operations.find((o) => o.name === operationName) : operations[0];
|
|
185
|
+
if (!op) throw new SyntaxError("The document has no operation");
|
|
186
|
+
return op.selections;
|
|
150
187
|
}
|
|
151
|
-
function
|
|
152
|
-
return
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
188
|
+
function fromAstSelections(set) {
|
|
189
|
+
return set.selections.map((s) => {
|
|
190
|
+
if (s.kind === "FragmentSpread")
|
|
191
|
+
return { kind: "spread", name: s.name.value };
|
|
192
|
+
if (s.kind === "InlineFragment") {
|
|
193
|
+
return {
|
|
194
|
+
kind: "inline",
|
|
195
|
+
on: s.typeCondition?.name.value ?? null,
|
|
196
|
+
selections: fromAstSelections(s.selectionSet)
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
const name = s.name.value;
|
|
200
|
+
return {
|
|
201
|
+
kind: "field",
|
|
202
|
+
name,
|
|
203
|
+
key: s.alias?.value ?? name,
|
|
204
|
+
hasArgs: Boolean(s.arguments?.length),
|
|
205
|
+
selections: s.selectionSet ? fromAstSelections(s.selectionSet) : null
|
|
206
|
+
};
|
|
207
|
+
});
|
|
158
208
|
}
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
209
|
+
function fromAst(doc, operationName) {
|
|
210
|
+
const fragments = /* @__PURE__ */ new Map();
|
|
211
|
+
const operations = [];
|
|
212
|
+
for (const def of doc.definitions ?? []) {
|
|
213
|
+
if (def.kind === "FragmentDefinition" && def.name && def.typeCondition && def.selectionSet) {
|
|
214
|
+
fragments.set(def.name.value, {
|
|
215
|
+
on: def.typeCondition.name.value,
|
|
216
|
+
selections: fromAstSelections(def.selectionSet)
|
|
217
|
+
});
|
|
218
|
+
} else if (def.kind === "OperationDefinition" && def.selectionSet) {
|
|
219
|
+
operations.push({
|
|
220
|
+
name: def.name?.value ?? null,
|
|
221
|
+
selections: fromAstSelections(def.selectionSet)
|
|
222
|
+
});
|
|
223
|
+
}
|
|
170
224
|
}
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
// src/core/live-preview/patch.ts
|
|
174
|
-
function isPlainObject(value) {
|
|
175
|
-
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
225
|
+
return { operation: pickOperation(operations, operationName), fragments };
|
|
176
226
|
}
|
|
177
|
-
function
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
if (!(
|
|
182
|
-
|
|
183
|
-
if (
|
|
184
|
-
|
|
185
|
-
|
|
227
|
+
function readQueryDocument(query, operationName) {
|
|
228
|
+
const cacheKey = operationName ?? "";
|
|
229
|
+
if (typeof query === "object" && query !== null && query.kind === "Document") {
|
|
230
|
+
let byName = astCache.get(query);
|
|
231
|
+
if (!byName) astCache.set(query, byName = /* @__PURE__ */ new Map());
|
|
232
|
+
let parsed2 = byName.get(cacheKey);
|
|
233
|
+
if (!parsed2) {
|
|
234
|
+
parsed2 = fromAst(
|
|
235
|
+
query,
|
|
236
|
+
operationName
|
|
237
|
+
);
|
|
238
|
+
byName.set(cacheKey, parsed2);
|
|
186
239
|
}
|
|
240
|
+
return parsed2;
|
|
187
241
|
}
|
|
188
|
-
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
242
|
+
const source = String(query);
|
|
243
|
+
const sourceKey = `${cacheKey}
|
|
244
|
+
${source}`;
|
|
245
|
+
let parsed = sourceCache.get(sourceKey);
|
|
246
|
+
if (!parsed) {
|
|
247
|
+
parsed = parseSource(source, operationName);
|
|
248
|
+
if (sourceCache.size >= SOURCE_CACHE_LIMIT) {
|
|
249
|
+
sourceCache.delete(sourceCache.keys().next().value);
|
|
250
|
+
}
|
|
251
|
+
sourceCache.set(sourceKey, parsed);
|
|
195
252
|
}
|
|
196
|
-
return
|
|
253
|
+
return parsed;
|
|
197
254
|
}
|
|
198
|
-
function
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
if (current === null || current === void 0) return incoming;
|
|
205
|
-
if (Array.isArray(current)) {
|
|
206
|
-
const template = current.find(isPlainObject);
|
|
207
|
-
return list.map((entry) => {
|
|
208
|
-
const match = current.find(
|
|
209
|
-
(c) => isPlainObject(c) && c._id !== void 0 && c._id === entry._id
|
|
210
|
-
);
|
|
211
|
-
if (isPlainObject(match)) return mergeObject(match, entry);
|
|
212
|
-
return isPlainObject(template) ? shapeLike(template, entry) : entry;
|
|
213
|
-
});
|
|
255
|
+
function collectFields(selections, typename, doc, options = {}) {
|
|
256
|
+
const cacheKey = `${options.strict ? "s" : "l"}:${typename ?? ""}`;
|
|
257
|
+
let cached = fieldsCache.get(selections);
|
|
258
|
+
if (!cached) {
|
|
259
|
+
cached = /* @__PURE__ */ new Map();
|
|
260
|
+
fieldsCache.set(selections, cached);
|
|
214
261
|
}
|
|
215
|
-
|
|
216
|
-
return
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
return
|
|
220
|
-
}
|
|
221
|
-
function cloneNode(node) {
|
|
222
|
-
return Array.isArray(node) ? [...node] : { ...node };
|
|
262
|
+
const hit = cached.get(cacheKey);
|
|
263
|
+
if (hit) return hit;
|
|
264
|
+
const fields = resolveFields(selections, typename, doc, options);
|
|
265
|
+
cached.set(cacheKey, fields);
|
|
266
|
+
return fields;
|
|
223
267
|
}
|
|
224
|
-
function
|
|
225
|
-
const
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
const
|
|
230
|
-
|
|
268
|
+
function resolveFields(selections, typename, doc, options) {
|
|
269
|
+
const exact = /* @__PURE__ */ new Map();
|
|
270
|
+
const loose = /* @__PURE__ */ new Map();
|
|
271
|
+
const merged = /* @__PURE__ */ new Map();
|
|
272
|
+
const walkedMatched = /* @__PURE__ */ new Set();
|
|
273
|
+
const walkedLoose = /* @__PURE__ */ new Set();
|
|
274
|
+
const add = (target, s) => {
|
|
275
|
+
let field = target.get(s.key);
|
|
276
|
+
if (!field) {
|
|
277
|
+
field = { name: s.name, key: s.key, hasArgs: s.hasArgs, selections: [] };
|
|
278
|
+
target.set(s.key, field);
|
|
279
|
+
merged.set(field, /* @__PURE__ */ new Set());
|
|
280
|
+
}
|
|
281
|
+
const done = merged.get(field);
|
|
282
|
+
if (s.selections && !done.has(s.selections)) {
|
|
283
|
+
done.add(s.selections);
|
|
284
|
+
field.selections = [...field.selections, ...s.selections];
|
|
285
|
+
}
|
|
231
286
|
};
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
287
|
+
const applies = (on, list) => conditionMatches(on, typename) || typename !== void 0 && typeConditions(list, doc).includes(typename);
|
|
288
|
+
const walk2 = (list, matched) => {
|
|
289
|
+
for (const s of list) {
|
|
290
|
+
if (s.kind === "field") {
|
|
291
|
+
add(matched ? exact : loose, s);
|
|
292
|
+
} else if (s.kind === "inline") {
|
|
293
|
+
walk2(s.selections, matched && applies(s.on, s.selections));
|
|
294
|
+
} else {
|
|
295
|
+
const fragment = doc.fragments.get(s.name);
|
|
296
|
+
if (!fragment) continue;
|
|
297
|
+
const asMatched = matched && applies(fragment.on, fragment.selections);
|
|
298
|
+
if (walkedMatched.has(fragment)) continue;
|
|
299
|
+
if (!asMatched && walkedLoose.has(fragment)) continue;
|
|
300
|
+
(asMatched ? walkedMatched : walkedLoose).add(fragment);
|
|
301
|
+
walk2(fragment.selections, asMatched);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
};
|
|
305
|
+
walk2(selections, true);
|
|
306
|
+
if (!options.strict) {
|
|
307
|
+
for (const [key, field] of loose) {
|
|
308
|
+
if (!exact.has(key)) exact.set(key, field);
|
|
240
309
|
}
|
|
241
310
|
}
|
|
242
|
-
return
|
|
311
|
+
return exact;
|
|
243
312
|
}
|
|
244
|
-
function
|
|
245
|
-
|
|
246
|
-
if (segments[segments.length - 1] !== "body" || typeof value !== "string") {
|
|
247
|
-
return [[field, value]];
|
|
248
|
-
}
|
|
249
|
-
let node = root;
|
|
250
|
-
for (const step of path) {
|
|
251
|
-
node = node[step];
|
|
252
|
-
}
|
|
253
|
-
for (const segment of segments.slice(0, -1)) {
|
|
254
|
-
if (node === null || typeof node !== "object") return [[field, value]];
|
|
255
|
-
const record2 = node;
|
|
256
|
-
node = INDEX_SEGMENT.test(segment) ? record2[Number(segment)] : record2[fieldKeyIn(record2, segment) ?? segment];
|
|
257
|
-
}
|
|
258
|
-
if (node === null || typeof node !== "object" || Array.isArray(node)) {
|
|
259
|
-
return [[field, value]];
|
|
260
|
-
}
|
|
261
|
-
const record = node;
|
|
262
|
-
if (!isTextEntry(record)) return [[field, value]];
|
|
263
|
-
const prefix = segments.slice(0, -1);
|
|
264
|
-
const writes = [];
|
|
265
|
-
for (const { name, plain } of TEXT_BODY_FIELDS) {
|
|
266
|
-
if (!(name in record)) continue;
|
|
267
|
-
writes.push([
|
|
268
|
-
[...prefix, name].join("."),
|
|
269
|
-
plain ? plainTextFromHtml(value) : value
|
|
270
|
-
]);
|
|
271
|
-
}
|
|
272
|
-
return writes.length ? writes : [[field, value]];
|
|
313
|
+
function conditionMatches(on, typename) {
|
|
314
|
+
return on === null || typename === void 0 || on === typename;
|
|
273
315
|
}
|
|
274
|
-
function
|
|
275
|
-
const
|
|
276
|
-
if (
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
316
|
+
function typeConditions(selections, doc) {
|
|
317
|
+
const hit = conditionsCache.get(selections);
|
|
318
|
+
if (hit) return hit;
|
|
319
|
+
const names = /* @__PURE__ */ new Set();
|
|
320
|
+
const seen = /* @__PURE__ */ new Set();
|
|
321
|
+
const walk2 = (list) => {
|
|
322
|
+
for (const s of list) {
|
|
323
|
+
if (s.kind === "inline") {
|
|
324
|
+
if (s.on) names.add(s.on);
|
|
325
|
+
walk2(s.selections);
|
|
326
|
+
} else if (s.kind === "spread") {
|
|
327
|
+
const fragment = doc.fragments.get(s.name);
|
|
328
|
+
if (!fragment || seen.has(fragment)) continue;
|
|
329
|
+
seen.add(fragment);
|
|
330
|
+
names.add(fragment.on);
|
|
331
|
+
walk2(fragment.selections);
|
|
332
|
+
}
|
|
288
333
|
}
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
334
|
+
};
|
|
335
|
+
walk2(selections);
|
|
336
|
+
const conditions = [...names];
|
|
337
|
+
conditionsCache.set(selections, conditions);
|
|
338
|
+
return conditions;
|
|
293
339
|
}
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
const child = cloneNode(cursor[fullPath[i]]);
|
|
321
|
-
cursor[fullPath[i]] = child;
|
|
322
|
-
cursor = child;
|
|
340
|
+
var NAME_START, NAME_CONTINUE, PUNCT, astCache, sourceCache, SOURCE_CACHE_LIMIT, fieldsCache, conditionsCache;
|
|
341
|
+
var init_query_document = __esm({
|
|
342
|
+
"src/core/live-preview/query-document.ts"() {
|
|
343
|
+
"use strict";
|
|
344
|
+
NAME_START = /[_A-Za-z]/;
|
|
345
|
+
NAME_CONTINUE = /[_0-9A-Za-z]/;
|
|
346
|
+
PUNCT = /* @__PURE__ */ new Set([
|
|
347
|
+
"!",
|
|
348
|
+
"$",
|
|
349
|
+
"&",
|
|
350
|
+
"(",
|
|
351
|
+
")",
|
|
352
|
+
":",
|
|
353
|
+
"=",
|
|
354
|
+
"@",
|
|
355
|
+
"[",
|
|
356
|
+
"]",
|
|
357
|
+
"{",
|
|
358
|
+
"|",
|
|
359
|
+
"}"
|
|
360
|
+
]);
|
|
361
|
+
astCache = /* @__PURE__ */ new WeakMap();
|
|
362
|
+
sourceCache = /* @__PURE__ */ new Map();
|
|
363
|
+
SOURCE_CACHE_LIMIT = 200;
|
|
364
|
+
fieldsCache = /* @__PURE__ */ new WeakMap();
|
|
365
|
+
conditionsCache = /* @__PURE__ */ new WeakMap();
|
|
323
366
|
}
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
// src/core/utils.ts
|
|
370
|
+
function getTrustedParentOrigin() {
|
|
371
|
+
return trustedParentOrigin;
|
|
327
372
|
}
|
|
328
|
-
function
|
|
329
|
-
if (
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
373
|
+
function sendPreprEvent(event, data, options) {
|
|
374
|
+
if (typeof window !== "undefined") {
|
|
375
|
+
const message = {
|
|
376
|
+
name: "prepr_preview_bar",
|
|
377
|
+
event,
|
|
378
|
+
...data
|
|
379
|
+
};
|
|
380
|
+
window.dispatchEvent(
|
|
381
|
+
new CustomEvent("prepr_preview_bar", { detail: message })
|
|
382
|
+
);
|
|
383
|
+
if (window.parent && window.parent !== window) {
|
|
384
|
+
if (trustedParentOrigin) {
|
|
385
|
+
window.parent.postMessage(message, trustedParentOrigin);
|
|
386
|
+
} else if (options?.allowUntrustedTarget) {
|
|
387
|
+
window.parent.postMessage(message, "*");
|
|
388
|
+
}
|
|
389
|
+
}
|
|
335
390
|
}
|
|
336
|
-
return keys;
|
|
337
391
|
}
|
|
338
|
-
function
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
_id: entry._id,
|
|
343
|
-
body,
|
|
344
|
-
html: body,
|
|
345
|
-
text: plainTextFromHtml(body),
|
|
346
|
-
format: entry.format ?? null
|
|
347
|
-
};
|
|
348
|
-
if (!template) return full;
|
|
349
|
-
const out = {};
|
|
350
|
-
for (const key of Object.keys(template)) {
|
|
351
|
-
out[key] = key in full ? full[key] : null;
|
|
352
|
-
}
|
|
353
|
-
return out;
|
|
392
|
+
function createScopedLogger(scopeName) {
|
|
393
|
+
return new DebugLogger({
|
|
394
|
+
prefix: `[Prepr][${scopeName}]`
|
|
395
|
+
});
|
|
354
396
|
}
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
397
|
+
var trustedParentOrigin, DebugLogger, globalDebugLogger;
|
|
398
|
+
var init_utils = __esm({
|
|
399
|
+
"src/core/utils.ts"() {
|
|
400
|
+
"use strict";
|
|
401
|
+
trustedParentOrigin = null;
|
|
402
|
+
DebugLogger = class {
|
|
403
|
+
constructor(options) {
|
|
404
|
+
this.options = {
|
|
405
|
+
prefix: "[Prepr]",
|
|
406
|
+
...options
|
|
407
|
+
};
|
|
408
|
+
}
|
|
409
|
+
// A local `enabled` wins; otherwise defer to the global logger so scoped
|
|
410
|
+
// loggers created before initDebugLogger() still pick up the setting.
|
|
411
|
+
isEnabled() {
|
|
412
|
+
if (this.options.enabled !== void 0) {
|
|
413
|
+
return this.options.enabled;
|
|
414
|
+
}
|
|
415
|
+
return globalDebugLogger?.options?.enabled ?? false;
|
|
416
|
+
}
|
|
417
|
+
log(message, ...args) {
|
|
418
|
+
if (!this.isEnabled()) return;
|
|
419
|
+
const prefix = this.options.prefix;
|
|
420
|
+
console.log(`${prefix} ${message}`, ...args);
|
|
421
|
+
}
|
|
422
|
+
warn(message, ...args) {
|
|
423
|
+
if (!this.isEnabled()) return;
|
|
424
|
+
const prefix = this.options.prefix;
|
|
425
|
+
console.warn(`${prefix} ${message}`, ...args);
|
|
426
|
+
}
|
|
427
|
+
error(message, ...args) {
|
|
428
|
+
if (!this.isEnabled()) return;
|
|
429
|
+
const prefix = this.options.prefix;
|
|
430
|
+
console.error(`${prefix} ${message}`, ...args);
|
|
431
|
+
}
|
|
432
|
+
};
|
|
433
|
+
globalDebugLogger = null;
|
|
359
434
|
}
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
435
|
+
});
|
|
436
|
+
|
|
437
|
+
// src/core/live-preview/index-snapshot.ts
|
|
438
|
+
function indexSnapshot(data) {
|
|
439
|
+
const index = /* @__PURE__ */ new Map();
|
|
440
|
+
const visited = /* @__PURE__ */ new WeakSet();
|
|
441
|
+
const path = [];
|
|
442
|
+
function walk2(node) {
|
|
443
|
+
if (node === null || typeof node !== "object") return;
|
|
444
|
+
if (visited.has(node)) return;
|
|
445
|
+
visited.add(node);
|
|
446
|
+
if (Array.isArray(node)) {
|
|
447
|
+
for (let i = 0; i < node.length; i++) {
|
|
448
|
+
path.push(i);
|
|
449
|
+
walk2(node[i]);
|
|
450
|
+
path.pop();
|
|
451
|
+
}
|
|
452
|
+
return;
|
|
367
453
|
}
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
454
|
+
const record = node;
|
|
455
|
+
if (typeof record._id === "string") {
|
|
456
|
+
const paths = index.get(record._id);
|
|
457
|
+
if (paths) paths.push([...path]);
|
|
458
|
+
else index.set(record._id, [[...path]]);
|
|
459
|
+
} else if (typeof record.__typename === "string") {
|
|
460
|
+
debug.log("item without _id will not live-update", {
|
|
461
|
+
__typename: record.__typename
|
|
462
|
+
});
|
|
463
|
+
}
|
|
464
|
+
for (const key in record) {
|
|
465
|
+
if (!Object.prototype.hasOwnProperty.call(record, key)) continue;
|
|
466
|
+
path.push(key);
|
|
467
|
+
walk2(record[key]);
|
|
468
|
+
path.pop();
|
|
382
469
|
}
|
|
383
470
|
}
|
|
384
|
-
|
|
385
|
-
return
|
|
386
|
-
}
|
|
387
|
-
function syncListSnapshot(snapshot, index, itemId, field, entries) {
|
|
388
|
-
const paths = index.get(itemId);
|
|
389
|
-
if (!paths?.length) return snapshot;
|
|
390
|
-
const fieldPath = field.split(".");
|
|
391
|
-
if (fieldPath.some((s) => FORBIDDEN_SEGMENTS.has(s))) return snapshot;
|
|
392
|
-
let root = snapshot;
|
|
393
|
-
for (const path of paths) {
|
|
394
|
-
root = syncListAtPath(root, [...path, ...fieldPath], entries);
|
|
395
|
-
}
|
|
396
|
-
return root;
|
|
397
|
-
}
|
|
398
|
-
function syncListAtPath(root, fullPath, entries, build) {
|
|
399
|
-
if (!fullPath.length) return root;
|
|
400
|
-
if (fullPath.some((s) => FORBIDDEN_SEGMENTS.has(String(s)))) return root;
|
|
401
|
-
let node = root;
|
|
402
|
-
for (const key of fullPath) {
|
|
403
|
-
node = node !== null && typeof node === "object" ? node[key] : void 0;
|
|
404
|
-
}
|
|
405
|
-
const next = syncList(node, entries, build);
|
|
406
|
-
if (next === node) return root;
|
|
407
|
-
const newRoot = cloneNode(root);
|
|
408
|
-
let cursor = newRoot;
|
|
409
|
-
for (let i = 0; i < fullPath.length - 1; i++) {
|
|
410
|
-
const child = cloneNode(cursor[fullPath[i]]);
|
|
411
|
-
cursor[fullPath[i]] = child;
|
|
412
|
-
cursor = child;
|
|
413
|
-
}
|
|
414
|
-
cursor[fullPath[fullPath.length - 1]] = next;
|
|
415
|
-
return newRoot;
|
|
416
|
-
}
|
|
417
|
-
function keepEditLink(current, value) {
|
|
418
|
-
if (typeof current !== "string" || typeof value !== "string") return value;
|
|
419
|
-
const { encoded } = (0, import_stega.vercelStegaSplit)(current);
|
|
420
|
-
if (!encoded || (0, import_stega.vercelStegaSplit)(value).encoded) return value;
|
|
421
|
-
return value + encoded;
|
|
471
|
+
walk2(data);
|
|
472
|
+
return index;
|
|
422
473
|
}
|
|
423
|
-
var
|
|
424
|
-
var
|
|
425
|
-
"src/core/live-preview/
|
|
474
|
+
var debug;
|
|
475
|
+
var init_index_snapshot = __esm({
|
|
476
|
+
"src/core/live-preview/index-snapshot.ts"() {
|
|
426
477
|
"use strict";
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
INDEX_SEGMENT = /^\d+$/;
|
|
430
|
-
FORBIDDEN_SEGMENTS = /* @__PURE__ */ new Set(["__proto__", "prototype", "constructor"]);
|
|
478
|
+
init_utils();
|
|
479
|
+
debug = createScopedLogger("live-preview");
|
|
431
480
|
}
|
|
432
481
|
});
|
|
433
482
|
|
|
434
|
-
// src/core/live-preview/
|
|
435
|
-
function
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
483
|
+
// src/core/live-preview/field-name.ts
|
|
484
|
+
function graphqlFieldName(apiId) {
|
|
485
|
+
return apiId.replace(UPPERCASE_BOUNDARY, "$1_").toLowerCase();
|
|
486
|
+
}
|
|
487
|
+
function fieldKeyIn(record, apiId) {
|
|
488
|
+
if (Object.prototype.hasOwnProperty.call(record, apiId)) return apiId;
|
|
489
|
+
const name = graphqlFieldName(apiId);
|
|
490
|
+
return Object.prototype.hasOwnProperty.call(record, name) ? name : void 0;
|
|
491
|
+
}
|
|
492
|
+
function plainTextFromHtml(html) {
|
|
493
|
+
return html.replace(TAG_PATTERN, "");
|
|
494
|
+
}
|
|
495
|
+
function isTextEntry(record) {
|
|
496
|
+
const typename = record.__typename;
|
|
497
|
+
if (typeof typename === "string") return typename === "Text";
|
|
498
|
+
return "html" in record || "text" in record && "format" in record;
|
|
499
|
+
}
|
|
500
|
+
var UPPERCASE_BOUNDARY, TEXT_BODY_FIELDS, TAG_PATTERN;
|
|
501
|
+
var init_field_name = __esm({
|
|
502
|
+
"src/core/live-preview/field-name.ts"() {
|
|
503
|
+
"use strict";
|
|
504
|
+
UPPERCASE_BOUNDARY = /(.)(?=[A-Z])/g;
|
|
505
|
+
TEXT_BODY_FIELDS = [
|
|
506
|
+
{ name: "body", plain: false },
|
|
507
|
+
{ name: "html", plain: false },
|
|
508
|
+
{ name: "text", plain: true }
|
|
509
|
+
];
|
|
510
|
+
TAG_PATTERN = /<[^>]+>/g;
|
|
511
|
+
}
|
|
512
|
+
});
|
|
513
|
+
|
|
514
|
+
// src/core/live-preview/patch.ts
|
|
515
|
+
function isPlainObject(value) {
|
|
516
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
517
|
+
}
|
|
518
|
+
function mergeObject(current, incoming) {
|
|
519
|
+
let changed = false;
|
|
520
|
+
const next = { ...current };
|
|
521
|
+
for (const key of Object.keys(current)) {
|
|
522
|
+
if (!(key in incoming) || incoming[key] === void 0) continue;
|
|
523
|
+
const value = isPlainObject(current[key]) && isPlainObject(incoming[key]) ? mergeObject(current[key], incoming[key]) : incoming[key];
|
|
524
|
+
if (value !== current[key]) {
|
|
525
|
+
next[key] = value;
|
|
526
|
+
changed = true;
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
return changed ? next : current;
|
|
530
|
+
}
|
|
531
|
+
function shapeLike(template, incoming) {
|
|
532
|
+
const out = {};
|
|
533
|
+
for (const key of Object.keys(template)) {
|
|
534
|
+
const value = incoming[key];
|
|
535
|
+
out[key] = value === void 0 ? null : isPlainObject(template[key]) && isPlainObject(value) ? shapeLike(template[key], value) : value;
|
|
536
|
+
}
|
|
537
|
+
return out;
|
|
538
|
+
}
|
|
539
|
+
function mergeStructured(current, incoming) {
|
|
540
|
+
if (incoming === null || Array.isArray(incoming) && !incoming.length) {
|
|
541
|
+
if (Array.isArray(current)) return current.length ? [] : current;
|
|
542
|
+
return null;
|
|
543
|
+
}
|
|
544
|
+
const list = Array.isArray(incoming) ? incoming : [incoming];
|
|
545
|
+
if (current === null || current === void 0) return incoming;
|
|
546
|
+
if (Array.isArray(current)) {
|
|
547
|
+
const template = current.find(isPlainObject);
|
|
548
|
+
return list.map((entry) => {
|
|
549
|
+
const match = current.find(
|
|
550
|
+
(c) => isPlainObject(c) && c._id !== void 0 && c._id === entry._id
|
|
551
|
+
);
|
|
552
|
+
if (isPlainObject(match)) return mergeObject(match, entry);
|
|
553
|
+
return isPlainObject(template) ? shapeLike(template, entry) : entry;
|
|
554
|
+
});
|
|
555
|
+
}
|
|
556
|
+
if (isPlainObject(current)) return mergeObject(current, list[0]);
|
|
557
|
+
return current;
|
|
558
|
+
}
|
|
559
|
+
function isPatchable(current) {
|
|
560
|
+
return current === null || typeof current === "string" || typeof current === "number" || typeof current === "boolean";
|
|
561
|
+
}
|
|
562
|
+
function cloneNode(node) {
|
|
563
|
+
return Array.isArray(node) ? [...node] : { ...node };
|
|
564
|
+
}
|
|
565
|
+
function patchSnapshot(snapshot, index, itemId, field, value) {
|
|
566
|
+
const paths = index.get(itemId);
|
|
567
|
+
if (!paths?.length) return snapshot;
|
|
568
|
+
let root = snapshot;
|
|
569
|
+
let changed = false;
|
|
570
|
+
const markChanged = () => {
|
|
571
|
+
changed = true;
|
|
572
|
+
};
|
|
573
|
+
for (const path of paths) {
|
|
574
|
+
for (const [target, targetValue] of expandTextBody(
|
|
575
|
+
root,
|
|
576
|
+
path,
|
|
577
|
+
field,
|
|
578
|
+
value
|
|
579
|
+
)) {
|
|
580
|
+
root = patchAtPath(root, path, target, targetValue, markChanged);
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
return changed ? root : snapshot;
|
|
584
|
+
}
|
|
585
|
+
function expandTextBody(root, path, field, value) {
|
|
586
|
+
const segments = field.split(".");
|
|
587
|
+
if (segments[segments.length - 1] !== "body" || typeof value !== "string") {
|
|
588
|
+
return [[field, value]];
|
|
589
|
+
}
|
|
590
|
+
let node = root;
|
|
591
|
+
for (const step of path) {
|
|
592
|
+
node = node[step];
|
|
593
|
+
}
|
|
594
|
+
for (const segment of segments.slice(0, -1)) {
|
|
595
|
+
if (node === null || typeof node !== "object") return [[field, value]];
|
|
596
|
+
const record2 = node;
|
|
597
|
+
node = INDEX_SEGMENT.test(segment) ? record2[Number(segment)] : record2[fieldKeyIn(record2, segment) ?? segment];
|
|
598
|
+
}
|
|
599
|
+
if (node === null || typeof node !== "object" || Array.isArray(node)) {
|
|
600
|
+
return [[field, value]];
|
|
601
|
+
}
|
|
602
|
+
const record = node;
|
|
603
|
+
if (!isTextEntry(record)) return [[field, value]];
|
|
604
|
+
const prefix = segments.slice(0, -1);
|
|
605
|
+
const writes = [];
|
|
606
|
+
for (const { name, plain } of TEXT_BODY_FIELDS) {
|
|
607
|
+
if (!(name in record)) continue;
|
|
608
|
+
writes.push([
|
|
609
|
+
[...prefix, name].join("."),
|
|
610
|
+
plain ? plainTextFromHtml(value) : value
|
|
611
|
+
]);
|
|
612
|
+
}
|
|
613
|
+
return writes.length ? writes : [[field, value]];
|
|
614
|
+
}
|
|
615
|
+
function patchAtPath(root, path, field, value, markChanged) {
|
|
616
|
+
const segments = field.split(".");
|
|
617
|
+
if (segments.some((s) => FORBIDDEN_SEGMENTS.has(s))) return root;
|
|
618
|
+
let node = root;
|
|
619
|
+
for (const step of path) {
|
|
620
|
+
node = node[step];
|
|
621
|
+
}
|
|
622
|
+
const fullPath = [...path];
|
|
623
|
+
for (const segment of segments) {
|
|
624
|
+
let step = segment;
|
|
625
|
+
if (INDEX_SEGMENT.test(segment)) {
|
|
626
|
+
step = Number(segment);
|
|
627
|
+
} else if (node !== null && typeof node === "object") {
|
|
628
|
+
step = fieldKeyIn(node, segment) ?? segment;
|
|
629
|
+
}
|
|
630
|
+
fullPath.push(step);
|
|
631
|
+
node = node !== null && typeof node === "object" ? node[step] : void 0;
|
|
632
|
+
}
|
|
633
|
+
return writeAtPath(root, fullPath, value, markChanged);
|
|
634
|
+
}
|
|
635
|
+
function writeAtPath(root, fullPath, value, markChanged = () => {
|
|
636
|
+
}) {
|
|
637
|
+
if (!fullPath.length) return root;
|
|
638
|
+
if (fullPath.some((s) => FORBIDDEN_SEGMENTS.has(String(s)))) return root;
|
|
639
|
+
const last = fullPath.length - 1;
|
|
640
|
+
let node = root;
|
|
641
|
+
for (let i = 0; i < last; i++) {
|
|
642
|
+
if (node === null || typeof node !== "object") return root;
|
|
643
|
+
node = node[fullPath[i]];
|
|
644
|
+
}
|
|
645
|
+
if (node === null || typeof node !== "object") return root;
|
|
646
|
+
const container = node;
|
|
647
|
+
const key = fullPath[last];
|
|
648
|
+
if (!(key in container)) return root;
|
|
649
|
+
let next;
|
|
650
|
+
if (value === null || typeof value === "object") {
|
|
651
|
+
next = mergeStructured(container[key], value);
|
|
652
|
+
} else if (!isPatchable(container[key])) {
|
|
653
|
+
return root;
|
|
654
|
+
} else {
|
|
655
|
+
next = keepEditLink(container[key], value);
|
|
656
|
+
}
|
|
657
|
+
if (container[key] === next) return root;
|
|
658
|
+
const newRoot = cloneNode(root);
|
|
659
|
+
let cursor = newRoot;
|
|
660
|
+
for (let i = 0; i < last; i++) {
|
|
661
|
+
const child = cloneNode(cursor[fullPath[i]]);
|
|
662
|
+
cursor[fullPath[i]] = child;
|
|
663
|
+
cursor = child;
|
|
664
|
+
}
|
|
665
|
+
cursor[key] = next;
|
|
666
|
+
markChanged();
|
|
667
|
+
return newRoot;
|
|
668
|
+
}
|
|
669
|
+
function listKeysOf(entry) {
|
|
670
|
+
if (!isPlainObject(entry)) return [];
|
|
671
|
+
const keys = [];
|
|
672
|
+
if (typeof entry._id === "string") keys.push(entry._id);
|
|
673
|
+
const first = Array.isArray(entry.items) ? entry.items[0] : null;
|
|
674
|
+
if (isPlainObject(first) && typeof first._id === "string") {
|
|
675
|
+
keys.push(first._id);
|
|
676
|
+
}
|
|
677
|
+
return keys;
|
|
678
|
+
}
|
|
679
|
+
function buildText(entry, template) {
|
|
680
|
+
const body = typeof entry.body === "string" ? entry.body : "";
|
|
681
|
+
const full = {
|
|
682
|
+
__typename: "Text",
|
|
683
|
+
_id: entry._id,
|
|
684
|
+
body,
|
|
685
|
+
html: body,
|
|
686
|
+
text: plainTextFromHtml(body),
|
|
687
|
+
format: entry.format ?? null
|
|
688
|
+
};
|
|
689
|
+
if (!template) return full;
|
|
690
|
+
const out = {};
|
|
691
|
+
for (const key of Object.keys(template)) {
|
|
692
|
+
out[key] = key in full ? full[key] : null;
|
|
693
|
+
}
|
|
694
|
+
return out;
|
|
695
|
+
}
|
|
696
|
+
function syncList(current, entries, build) {
|
|
697
|
+
if (current === null || current === void 0) {
|
|
698
|
+
const built = syncList([], entries, build);
|
|
699
|
+
return built.length ? built : current;
|
|
700
|
+
}
|
|
701
|
+
if (!Array.isArray(current)) return current;
|
|
702
|
+
const byKey = /* @__PURE__ */ new Map();
|
|
703
|
+
const templates = /* @__PURE__ */ new Map();
|
|
704
|
+
for (const c of current) {
|
|
705
|
+
for (const key of listKeysOf(c)) byKey.set(key, c);
|
|
706
|
+
if (isPlainObject(c) && typeof c.__typename === "string") {
|
|
707
|
+
if (!templates.has(c.__typename)) templates.set(c.__typename, c);
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
if (!entries.length) return current.length ? [] : current;
|
|
711
|
+
if (current.length && !byKey.size) return current;
|
|
712
|
+
if (current.length && !entries.some((e) => byKey.has(e._id))) return current;
|
|
713
|
+
const next = [];
|
|
714
|
+
for (const entry of entries) {
|
|
715
|
+
const existing = byKey.get(entry._id);
|
|
716
|
+
const built = existing === void 0 && entry.fields ? build?.(entry) : void 0;
|
|
717
|
+
if (existing !== void 0) {
|
|
718
|
+
next.push(existing);
|
|
719
|
+
} else if (built) {
|
|
720
|
+
next.push(built);
|
|
721
|
+
} else if (entry.__typename === "Text") {
|
|
722
|
+
next.push(buildText(entry, templates.get("Text")));
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
const same = next.length === current.length && next.every((n, i) => n === current[i]);
|
|
726
|
+
return same ? current : next;
|
|
727
|
+
}
|
|
728
|
+
function syncListSnapshot(snapshot, index, itemId, field, entries) {
|
|
729
|
+
const paths = index.get(itemId);
|
|
730
|
+
if (!paths?.length) return snapshot;
|
|
731
|
+
const fieldPath = field.split(".");
|
|
732
|
+
if (fieldPath.some((s) => FORBIDDEN_SEGMENTS.has(s))) return snapshot;
|
|
733
|
+
let root = snapshot;
|
|
734
|
+
for (const path of paths) {
|
|
735
|
+
root = syncListAtPath(root, [...path, ...fieldPath], entries);
|
|
736
|
+
}
|
|
737
|
+
return root;
|
|
738
|
+
}
|
|
739
|
+
function syncListAtPath(root, fullPath, entries, build) {
|
|
740
|
+
if (!fullPath.length) return root;
|
|
741
|
+
if (fullPath.some((s) => FORBIDDEN_SEGMENTS.has(String(s)))) return root;
|
|
742
|
+
let node = root;
|
|
743
|
+
for (const key of fullPath) {
|
|
744
|
+
node = node !== null && typeof node === "object" ? node[key] : void 0;
|
|
745
|
+
}
|
|
746
|
+
const next = syncList(node, entries, build);
|
|
747
|
+
if (next === node) return root;
|
|
748
|
+
const newRoot = cloneNode(root);
|
|
749
|
+
let cursor = newRoot;
|
|
750
|
+
for (let i = 0; i < fullPath.length - 1; i++) {
|
|
751
|
+
const child = cloneNode(cursor[fullPath[i]]);
|
|
752
|
+
cursor[fullPath[i]] = child;
|
|
753
|
+
cursor = child;
|
|
754
|
+
}
|
|
755
|
+
cursor[fullPath[fullPath.length - 1]] = next;
|
|
756
|
+
return newRoot;
|
|
757
|
+
}
|
|
758
|
+
function keepEditLink(current, value) {
|
|
759
|
+
if (typeof current !== "string" || typeof value !== "string") return value;
|
|
760
|
+
const { encoded } = (0, import_stega2.vercelStegaSplit)(current);
|
|
761
|
+
if (!encoded || (0, import_stega2.vercelStegaSplit)(value).encoded) return value;
|
|
762
|
+
return value + encoded;
|
|
763
|
+
}
|
|
764
|
+
var import_stega2, INDEX_SEGMENT, FORBIDDEN_SEGMENTS;
|
|
765
|
+
var init_patch = __esm({
|
|
766
|
+
"src/core/live-preview/patch.ts"() {
|
|
767
|
+
"use strict";
|
|
768
|
+
import_stega2 = require("@vercel/stega");
|
|
769
|
+
init_field_name();
|
|
770
|
+
INDEX_SEGMENT = /^\d+$/;
|
|
771
|
+
FORBIDDEN_SEGMENTS = /* @__PURE__ */ new Set(["__proto__", "prototype", "constructor"]);
|
|
772
|
+
}
|
|
773
|
+
});
|
|
774
|
+
|
|
775
|
+
// src/core/live-preview/live-store.ts
|
|
776
|
+
function withPatch(log2, patch) {
|
|
777
|
+
let previous = -1;
|
|
778
|
+
for (let i = log2.length - 1; i >= 0; i--) {
|
|
779
|
+
const p = log2[i];
|
|
780
|
+
if (p.type === patch.type && p.itemId === patch.itemId && p.field === patch.field) {
|
|
781
|
+
previous = i;
|
|
782
|
+
break;
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
if (previous === -1) return [...log2, patch];
|
|
445
786
|
const since = log2.slice(previous + 1);
|
|
446
787
|
const listPrefix = `${patch.field}.`;
|
|
447
788
|
const dependsOnPosition = since.some(
|
|
@@ -615,546 +956,205 @@ function isAllowedEditorOrigin(origin, allowed) {
|
|
|
615
956
|
}
|
|
616
957
|
let url;
|
|
617
958
|
try {
|
|
618
|
-
url = new URL(origin);
|
|
619
|
-
} catch {
|
|
620
|
-
return false;
|
|
621
|
-
}
|
|
622
|
-
if (url.protocol !== "https:" || url.port !== "") return false;
|
|
623
|
-
const suffix = `.${EDITOR_BASE_DOMAIN}`;
|
|
624
|
-
if (!url.hostname.endsWith(suffix)) return false;
|
|
625
|
-
const label = url.hostname.slice(0, -suffix.length);
|
|
626
|
-
return label.length > 0 && !label.includes(".");
|
|
627
|
-
}
|
|
628
|
-
var debug3, EDITOR_BASE_DOMAIN;
|
|
629
|
-
var init_iframe_bridge = __esm({
|
|
630
|
-
"src/core/iframe-bridge.ts"() {
|
|
631
|
-
"use strict";
|
|
632
|
-
init_utils();
|
|
633
|
-
debug3 = createScopedLogger("iframe-bridge");
|
|
634
|
-
EDITOR_BASE_DOMAIN = "prepr.io";
|
|
635
|
-
}
|
|
636
|
-
});
|
|
637
|
-
|
|
638
|
-
// src/core/live-preview/validate.ts
|
|
639
|
-
function isScalar(v) {
|
|
640
|
-
return typeof v === "string" || typeof v === "number" || typeof v === "boolean";
|
|
641
|
-
}
|
|
642
|
-
function isSafeLinkValue(value) {
|
|
643
|
-
const stripped = value.replace(URL_IGNORED_ANYWHERE, "");
|
|
644
|
-
let start = 0;
|
|
645
|
-
while (start < stripped.length && stripped.charCodeAt(start) <= 32) start++;
|
|
646
|
-
return !SCRIPT_SCHEME.test(stripped.slice(start));
|
|
647
|
-
}
|
|
648
|
-
function isUrlKey(key) {
|
|
649
|
-
return URL_KEY.test(key);
|
|
650
|
-
}
|
|
651
|
-
function isSafeKeyValue(key, value) {
|
|
652
|
-
if (typeof value !== "string") return true;
|
|
653
|
-
if (key === "url") return SAFE_URL.test(value);
|
|
654
|
-
return !isUrlKey(key) || isSafeLinkValue(value);
|
|
655
|
-
}
|
|
656
|
-
function isLiveValue(v, depth = 0) {
|
|
657
|
-
if (v === null || isScalar(v)) return true;
|
|
658
|
-
if (depth >= MAX_DEPTH) return false;
|
|
659
|
-
if (Array.isArray(v)) {
|
|
660
|
-
return depth === 0 && v.length <= MAX_ENTRIES && v.every((entry) => isPlainRecord(entry) && isLiveValue(entry, depth + 1));
|
|
661
|
-
}
|
|
662
|
-
if (!isPlainRecord(v)) return false;
|
|
663
|
-
const keys = Object.keys(v);
|
|
664
|
-
if (keys.length > MAX_ENTRIES) return false;
|
|
665
|
-
return keys.every((key) => {
|
|
666
|
-
if (FORBIDDEN_KEYS.has(key)) return false;
|
|
667
|
-
const value = v[key];
|
|
668
|
-
if (!isSafeKeyValue(key, value)) return false;
|
|
669
|
-
return !Array.isArray(value) && isLiveValue(value, depth + 1);
|
|
670
|
-
});
|
|
671
|
-
}
|
|
672
|
-
function isLiveFieldChange(field, value) {
|
|
673
|
-
if (!isLiveValue(value)) return false;
|
|
674
|
-
const name = field.slice(field.lastIndexOf(".") + 1);
|
|
675
|
-
return typeof value !== "string" || !isUrlKey(name) || isSafeLinkValue(value);
|
|
676
|
-
}
|
|
677
|
-
function parseListEntries(value) {
|
|
678
|
-
if (!Array.isArray(value) || value.length > MAX_LIST_ENTRIES) return null;
|
|
679
|
-
const list = [];
|
|
680
|
-
for (const raw of value) {
|
|
681
|
-
if (!isPlainRecord(raw) || typeof raw._id !== "string" || !raw._id) {
|
|
682
|
-
return null;
|
|
683
|
-
}
|
|
684
|
-
const entry = { _id: raw._id };
|
|
685
|
-
if (typeof raw.__typename === "string") entry.__typename = raw.__typename;
|
|
686
|
-
if (typeof raw.body === "string") entry.body = raw.body;
|
|
687
|
-
if (typeof raw.format === "string" || raw.format === null) {
|
|
688
|
-
entry.format = raw.format;
|
|
689
|
-
}
|
|
690
|
-
if (typeof raw.typeName === "string" && raw.typeName.length <= 200) {
|
|
691
|
-
entry.typeName = raw.typeName;
|
|
692
|
-
}
|
|
693
|
-
if (raw.fields !== void 0) {
|
|
694
|
-
if (!isPlainRecord(raw.fields) || !isFieldValue(raw.fields, 0)) {
|
|
695
|
-
return null;
|
|
696
|
-
}
|
|
697
|
-
entry.fields = raw.fields;
|
|
698
|
-
}
|
|
699
|
-
list.push(entry);
|
|
700
|
-
}
|
|
701
|
-
return list;
|
|
702
|
-
}
|
|
703
|
-
function isPlainRecord(v) {
|
|
704
|
-
return v !== null && typeof v === "object" && Object.getPrototypeOf(v) === Object.prototype;
|
|
705
|
-
}
|
|
706
|
-
function isFieldValue(v, depth) {
|
|
707
|
-
if (v === null || isScalar(v)) return true;
|
|
708
|
-
if (depth >= MAX_FIELD_DEPTH) return false;
|
|
709
|
-
if (Array.isArray(v)) {
|
|
710
|
-
return v.length <= MAX_ENTRIES && v.every(
|
|
711
|
-
(entry) => isScalar(entry) || isPlainRecord(entry) && isFieldValue(entry, depth + 1)
|
|
712
|
-
);
|
|
713
|
-
}
|
|
714
|
-
if (!isPlainRecord(v)) return false;
|
|
715
|
-
const keys = Object.keys(v);
|
|
716
|
-
if (keys.length > MAX_FIELD_KEYS) return false;
|
|
717
|
-
return keys.every((key) => {
|
|
718
|
-
if (FORBIDDEN_KEYS.has(key)) return false;
|
|
719
|
-
const value = v[key];
|
|
720
|
-
if (!isSafeKeyValue(key, value)) return false;
|
|
721
|
-
return isFieldValue(value, depth + 1);
|
|
722
|
-
});
|
|
723
|
-
}
|
|
724
|
-
function parseEditTarget(raw) {
|
|
725
|
-
if (!isPlainRecord(raw)) return void 0;
|
|
726
|
-
const { item, field } = raw;
|
|
727
|
-
if (typeof item !== "string" || !item || item.length > MAX_EDIT_LENGTH) {
|
|
728
|
-
return void 0;
|
|
729
|
-
}
|
|
730
|
-
if (typeof field !== "string" || !field || field.length > MAX_EDIT_LENGTH) {
|
|
731
|
-
return void 0;
|
|
732
|
-
}
|
|
733
|
-
return { item, field };
|
|
734
|
-
}
|
|
735
|
-
var MAX_DEPTH, MAX_ENTRIES, FORBIDDEN_KEYS, SAFE_URL, URL_KEY, URL_IGNORED_ANYWHERE, SCRIPT_SCHEME, MAX_LIST_ENTRIES, MAX_FIELD_DEPTH, MAX_FIELD_KEYS, MAX_EDIT_LENGTH;
|
|
736
|
-
var init_validate = __esm({
|
|
737
|
-
"src/core/live-preview/validate.ts"() {
|
|
738
|
-
"use strict";
|
|
739
|
-
MAX_DEPTH = 3;
|
|
740
|
-
MAX_ENTRIES = 50;
|
|
741
|
-
FORBIDDEN_KEYS = /* @__PURE__ */ new Set(["__proto__", "prototype", "constructor"]);
|
|
742
|
-
SAFE_URL = /^https?:\/\//i;
|
|
743
|
-
URL_KEY = /^(?:url|href|src|link)$|(?:_url|Url)$/;
|
|
744
|
-
URL_IGNORED_ANYWHERE = /[\t\n\r]/g;
|
|
745
|
-
SCRIPT_SCHEME = /^(?:javascript|vbscript|data):/i;
|
|
746
|
-
MAX_LIST_ENTRIES = 200;
|
|
747
|
-
MAX_FIELD_DEPTH = 6;
|
|
748
|
-
MAX_FIELD_KEYS = 100;
|
|
749
|
-
MAX_EDIT_LENGTH = 1e3;
|
|
750
|
-
}
|
|
751
|
-
});
|
|
752
|
-
|
|
753
|
-
// src/core/live-preview/bridge-events.ts
|
|
754
|
-
function createLiveBridge(store2, options = {}) {
|
|
755
|
-
let parentOrigin = null;
|
|
756
|
-
const onMessage = (evt) => {
|
|
757
|
-
const data = evt?.data;
|
|
758
|
-
if (data?.event === "prepr:initVE" && !parentOrigin) {
|
|
759
|
-
if (isAllowedEditorOrigin(evt.origin, options.allowedEditorOrigins?.())) {
|
|
760
|
-
parentOrigin = evt.origin;
|
|
761
|
-
} else {
|
|
762
|
-
debug4.warn("ignored live preview handshake from origin", evt.origin);
|
|
763
|
-
}
|
|
764
|
-
return;
|
|
765
|
-
}
|
|
766
|
-
const trusted = parentOrigin ?? getTrustedParentOrigin();
|
|
767
|
-
if (!trusted || evt.origin !== trusted) return;
|
|
768
|
-
if (data?.event === "prepr:fieldChange") {
|
|
769
|
-
if (typeof data.itemId === "string" && typeof data.field === "string" && data.field && isLiveFieldChange(data.field, data.value)) {
|
|
770
|
-
const edit = parseEditTarget(data.edit);
|
|
771
|
-
if (edit) store2.pushPatch(data.itemId, data.field, data.value, edit);
|
|
772
|
-
else store2.pushPatch(data.itemId, data.field, data.value);
|
|
773
|
-
} else {
|
|
774
|
-
debug4.log("dropped malformed fieldChange", data);
|
|
775
|
-
}
|
|
776
|
-
return;
|
|
777
|
-
}
|
|
778
|
-
if (data?.event === "prepr:listChange") {
|
|
779
|
-
const list = parseListEntries(data.entries);
|
|
780
|
-
if (typeof data.itemId === "string" && typeof data.field === "string" && data.field && list) {
|
|
781
|
-
store2.pushList(data.itemId, data.field, list);
|
|
782
|
-
} else {
|
|
783
|
-
debug4.log("dropped malformed listChange", data);
|
|
784
|
-
}
|
|
785
|
-
return;
|
|
786
|
-
}
|
|
787
|
-
if (data?.event === "prepr:itemChanged") {
|
|
788
|
-
if (typeof data.itemId === "string") {
|
|
789
|
-
store2.invalidate(data.itemId);
|
|
790
|
-
} else {
|
|
791
|
-
debug4.log("dropped malformed itemChanged", data);
|
|
792
|
-
}
|
|
793
|
-
}
|
|
794
|
-
};
|
|
795
|
-
return {
|
|
796
|
-
start() {
|
|
797
|
-
window.addEventListener("message", onMessage);
|
|
798
|
-
sendPreprEvent(
|
|
799
|
-
"live_ready",
|
|
800
|
-
options.strictMode?.() ? { strictMode: true } : void 0,
|
|
801
|
-
{ allowUntrustedTarget: true }
|
|
802
|
-
);
|
|
803
|
-
},
|
|
804
|
-
stop() {
|
|
805
|
-
window.removeEventListener("message", onMessage);
|
|
806
|
-
}
|
|
807
|
-
};
|
|
959
|
+
url = new URL(origin);
|
|
960
|
+
} catch {
|
|
961
|
+
return false;
|
|
962
|
+
}
|
|
963
|
+
if (url.protocol !== "https:" || url.port !== "") return false;
|
|
964
|
+
const suffix = `.${EDITOR_BASE_DOMAIN}`;
|
|
965
|
+
if (!url.hostname.endsWith(suffix)) return false;
|
|
966
|
+
const label = url.hostname.slice(0, -suffix.length);
|
|
967
|
+
return label.length > 0 && !label.includes(".");
|
|
808
968
|
}
|
|
809
|
-
var
|
|
810
|
-
var
|
|
811
|
-
"src/core/
|
|
969
|
+
var debug3, EDITOR_BASE_DOMAIN;
|
|
970
|
+
var init_iframe_bridge = __esm({
|
|
971
|
+
"src/core/iframe-bridge.ts"() {
|
|
812
972
|
"use strict";
|
|
813
973
|
init_utils();
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
debug4 = createScopedLogger("live-preview:bridge");
|
|
974
|
+
debug3 = createScopedLogger("iframe-bridge");
|
|
975
|
+
EDITOR_BASE_DOMAIN = "prepr.io";
|
|
817
976
|
}
|
|
818
977
|
});
|
|
819
978
|
|
|
820
|
-
// src/core/live-preview/
|
|
821
|
-
function
|
|
822
|
-
|
|
823
|
-
let i = 0;
|
|
824
|
-
const n = source.length;
|
|
825
|
-
while (i < n) {
|
|
826
|
-
const ch = source[i];
|
|
827
|
-
if (ch === " " || ch === " " || ch === "\n" || ch === "\r" || ch === "," || ch === "\uFEFF") {
|
|
828
|
-
i++;
|
|
829
|
-
} else if (ch === "#") {
|
|
830
|
-
while (i < n && source[i] !== "\n" && source[i] !== "\r") i++;
|
|
831
|
-
} else if (ch === "." && source.startsWith("...", i)) {
|
|
832
|
-
tokens.push({ type: "punct", value: "..." });
|
|
833
|
-
i += 3;
|
|
834
|
-
} else if (PUNCT.has(ch)) {
|
|
835
|
-
tokens.push({ type: "punct", value: ch });
|
|
836
|
-
i++;
|
|
837
|
-
} else if (NAME_START.test(ch)) {
|
|
838
|
-
const start = i;
|
|
839
|
-
while (i < n && NAME_CONTINUE.test(source[i])) i++;
|
|
840
|
-
tokens.push({ type: "name", value: source.slice(start, i) });
|
|
841
|
-
} else if (ch === '"') {
|
|
842
|
-
if (source.startsWith('"""', i)) {
|
|
843
|
-
i += 3;
|
|
844
|
-
while (i < n && !source.startsWith('"""', i)) {
|
|
845
|
-
i += source.startsWith('\\"""', i) ? 4 : 1;
|
|
846
|
-
}
|
|
847
|
-
if (i >= n) throw new SyntaxError("Unterminated block string");
|
|
848
|
-
i += 3;
|
|
849
|
-
} else {
|
|
850
|
-
i++;
|
|
851
|
-
while (i < n && source[i] !== '"') {
|
|
852
|
-
if (source[i] === "\n" || source[i] === "\r") {
|
|
853
|
-
throw new SyntaxError("Unterminated string");
|
|
854
|
-
}
|
|
855
|
-
i += source[i] === "\\" ? 2 : 1;
|
|
856
|
-
}
|
|
857
|
-
if (i >= n) throw new SyntaxError("Unterminated string");
|
|
858
|
-
i++;
|
|
859
|
-
}
|
|
860
|
-
tokens.push({ type: "value" });
|
|
861
|
-
} else if (ch === "-" || ch >= "0" && ch <= "9") {
|
|
862
|
-
i++;
|
|
863
|
-
while (i < n && /[0-9.eE+-]/.test(source[i])) i++;
|
|
864
|
-
tokens.push({ type: "value" });
|
|
865
|
-
} else {
|
|
866
|
-
throw new SyntaxError(`Unexpected character "${ch}"`);
|
|
867
|
-
}
|
|
868
|
-
}
|
|
869
|
-
return tokens;
|
|
979
|
+
// src/core/live-preview/validate.ts
|
|
980
|
+
function isScalar(v) {
|
|
981
|
+
return typeof v === "string" || typeof v === "number" || typeof v === "boolean";
|
|
870
982
|
}
|
|
871
|
-
function
|
|
872
|
-
const
|
|
873
|
-
let
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
const t = tokens[pos];
|
|
877
|
-
return t?.type === "punct" && t.value === value;
|
|
878
|
-
};
|
|
879
|
-
const expectPunct = (value) => {
|
|
880
|
-
if (!isPunct(value)) throw new SyntaxError(`Expected "${value}"`);
|
|
881
|
-
pos++;
|
|
882
|
-
};
|
|
883
|
-
const expectName = () => {
|
|
884
|
-
const t = tokens[pos];
|
|
885
|
-
if (t?.type !== "name") throw new SyntaxError("Expected a name");
|
|
886
|
-
pos++;
|
|
887
|
-
return t.value;
|
|
888
|
-
};
|
|
889
|
-
const skipParens = () => {
|
|
890
|
-
expectPunct("(");
|
|
891
|
-
let depth = 1;
|
|
892
|
-
while (depth > 0) {
|
|
893
|
-
const t = tokens[pos++];
|
|
894
|
-
if (!t) throw new SyntaxError("Unbalanced parentheses");
|
|
895
|
-
if (t.type === "punct" && t.value === "(") depth++;
|
|
896
|
-
else if (t.type === "punct" && t.value === ")") depth--;
|
|
897
|
-
}
|
|
898
|
-
};
|
|
899
|
-
const skipDirectives = () => {
|
|
900
|
-
while (isPunct("@")) {
|
|
901
|
-
pos++;
|
|
902
|
-
expectName();
|
|
903
|
-
if (isPunct("(")) skipParens();
|
|
904
|
-
}
|
|
905
|
-
};
|
|
906
|
-
const parseSelectionSet = () => {
|
|
907
|
-
expectPunct("{");
|
|
908
|
-
const selections = [];
|
|
909
|
-
while (!isPunct("}")) {
|
|
910
|
-
if (!peek()) throw new SyntaxError("Unterminated selection set");
|
|
911
|
-
selections.push(parseSelection());
|
|
912
|
-
}
|
|
913
|
-
pos++;
|
|
914
|
-
return selections;
|
|
915
|
-
};
|
|
916
|
-
const parseSelection = () => {
|
|
917
|
-
if (isPunct("...")) {
|
|
918
|
-
pos++;
|
|
919
|
-
const t = peek();
|
|
920
|
-
if (t?.type === "name" && t.value !== "on") {
|
|
921
|
-
pos++;
|
|
922
|
-
skipDirectives();
|
|
923
|
-
return { kind: "spread", name: t.value };
|
|
924
|
-
}
|
|
925
|
-
let on = null;
|
|
926
|
-
if (t?.type === "name") {
|
|
927
|
-
pos++;
|
|
928
|
-
on = expectName();
|
|
929
|
-
}
|
|
930
|
-
skipDirectives();
|
|
931
|
-
return { kind: "inline", on, selections: parseSelectionSet() };
|
|
932
|
-
}
|
|
933
|
-
let name = expectName();
|
|
934
|
-
const key = name;
|
|
935
|
-
if (isPunct(":")) {
|
|
936
|
-
pos++;
|
|
937
|
-
name = expectName();
|
|
938
|
-
}
|
|
939
|
-
let hasArgs = false;
|
|
940
|
-
if (isPunct("(")) {
|
|
941
|
-
hasArgs = true;
|
|
942
|
-
skipParens();
|
|
943
|
-
}
|
|
944
|
-
skipDirectives();
|
|
945
|
-
const selections = isPunct("{") ? parseSelectionSet() : null;
|
|
946
|
-
return { kind: "field", name, key, hasArgs, selections };
|
|
947
|
-
};
|
|
948
|
-
const fragments = /* @__PURE__ */ new Map();
|
|
949
|
-
const operations = [];
|
|
950
|
-
while (pos < tokens.length) {
|
|
951
|
-
if (isPunct("{")) {
|
|
952
|
-
operations.push({ name: null, selections: parseSelectionSet() });
|
|
953
|
-
continue;
|
|
954
|
-
}
|
|
955
|
-
const keyword = expectName();
|
|
956
|
-
if (keyword === "fragment") {
|
|
957
|
-
const name = expectName();
|
|
958
|
-
if (expectName() !== "on") throw new SyntaxError('Expected "on"');
|
|
959
|
-
const on = expectName();
|
|
960
|
-
skipDirectives();
|
|
961
|
-
fragments.set(name, { on, selections: parseSelectionSet() });
|
|
962
|
-
} else if (keyword === "query" || keyword === "mutation" || keyword === "subscription") {
|
|
963
|
-
const t = peek();
|
|
964
|
-
let name = null;
|
|
965
|
-
if (t?.type === "name") name = expectName();
|
|
966
|
-
if (isPunct("(")) skipParens();
|
|
967
|
-
skipDirectives();
|
|
968
|
-
operations.push({ name, selections: parseSelectionSet() });
|
|
969
|
-
} else {
|
|
970
|
-
throw new SyntaxError(`Unexpected "${keyword}"`);
|
|
971
|
-
}
|
|
972
|
-
}
|
|
973
|
-
return { operation: pickOperation(operations, operationName), fragments };
|
|
983
|
+
function isSafeLinkValue(value) {
|
|
984
|
+
const stripped = value.replace(URL_IGNORED_ANYWHERE, "");
|
|
985
|
+
let start = 0;
|
|
986
|
+
while (start < stripped.length && stripped.charCodeAt(start) <= 32) start++;
|
|
987
|
+
return !SCRIPT_SCHEME.test(stripped.slice(start));
|
|
974
988
|
}
|
|
975
|
-
function
|
|
976
|
-
|
|
977
|
-
if (!op) throw new SyntaxError("The document has no operation");
|
|
978
|
-
return op.selections;
|
|
989
|
+
function isUrlKey(key) {
|
|
990
|
+
return URL_KEY.test(key);
|
|
979
991
|
}
|
|
980
|
-
function
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
992
|
+
function isSafeKeyValue(key, value) {
|
|
993
|
+
if (typeof value !== "string") return true;
|
|
994
|
+
if (key === "url") return SAFE_URL.test(value);
|
|
995
|
+
return !isUrlKey(key) || isSafeLinkValue(value);
|
|
996
|
+
}
|
|
997
|
+
function isLiveValue(v, depth = 0) {
|
|
998
|
+
if (v === null || isScalar(v)) return true;
|
|
999
|
+
if (depth >= MAX_DEPTH) return false;
|
|
1000
|
+
if (Array.isArray(v)) {
|
|
1001
|
+
return depth === 0 && v.length <= MAX_ENTRIES && v.every((entry) => isPlainRecord(entry) && isLiveValue(entry, depth + 1));
|
|
1002
|
+
}
|
|
1003
|
+
if (!isPlainRecord(v)) return false;
|
|
1004
|
+
const keys = Object.keys(v);
|
|
1005
|
+
if (keys.length > MAX_ENTRIES) return false;
|
|
1006
|
+
return keys.every((key) => {
|
|
1007
|
+
if (FORBIDDEN_KEYS.has(key)) return false;
|
|
1008
|
+
const value = v[key];
|
|
1009
|
+
if (!isSafeKeyValue(key, value)) return false;
|
|
1010
|
+
return !Array.isArray(value) && isLiveValue(value, depth + 1);
|
|
999
1011
|
});
|
|
1000
1012
|
}
|
|
1001
|
-
function
|
|
1002
|
-
|
|
1003
|
-
const
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1013
|
+
function isLiveFieldChange(field, value) {
|
|
1014
|
+
if (!isLiveValue(value)) return false;
|
|
1015
|
+
const name = field.slice(field.lastIndexOf(".") + 1);
|
|
1016
|
+
return typeof value !== "string" || !isUrlKey(name) || isSafeLinkValue(value);
|
|
1017
|
+
}
|
|
1018
|
+
function parseListEntries(value) {
|
|
1019
|
+
if (!Array.isArray(value) || value.length > MAX_LIST_ENTRIES) return null;
|
|
1020
|
+
const list = [];
|
|
1021
|
+
for (const raw of value) {
|
|
1022
|
+
if (!isPlainRecord(raw) || typeof raw._id !== "string" || !raw._id) {
|
|
1023
|
+
return null;
|
|
1024
|
+
}
|
|
1025
|
+
const entry = { _id: raw._id };
|
|
1026
|
+
if (typeof raw.__typename === "string") entry.__typename = raw.__typename;
|
|
1027
|
+
if (typeof raw.body === "string") entry.body = raw.body;
|
|
1028
|
+
if (typeof raw.format === "string" || raw.format === null) {
|
|
1029
|
+
entry.format = raw.format;
|
|
1015
1030
|
}
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
}
|
|
1019
|
-
function readQueryDocument(query, operationName) {
|
|
1020
|
-
const cacheKey = operationName ?? "";
|
|
1021
|
-
if (typeof query === "object" && query !== null && query.kind === "Document") {
|
|
1022
|
-
let byName = astCache.get(query);
|
|
1023
|
-
if (!byName) astCache.set(query, byName = /* @__PURE__ */ new Map());
|
|
1024
|
-
let parsed2 = byName.get(cacheKey);
|
|
1025
|
-
if (!parsed2) {
|
|
1026
|
-
parsed2 = fromAst(
|
|
1027
|
-
query,
|
|
1028
|
-
operationName
|
|
1029
|
-
);
|
|
1030
|
-
byName.set(cacheKey, parsed2);
|
|
1031
|
+
if (typeof raw.typeName === "string" && raw.typeName.length <= 200) {
|
|
1032
|
+
entry.typeName = raw.typeName;
|
|
1031
1033
|
}
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
let parsed = sourceCache.get(sourceKey);
|
|
1038
|
-
if (!parsed) {
|
|
1039
|
-
parsed = parseSource(source, operationName);
|
|
1040
|
-
if (sourceCache.size >= SOURCE_CACHE_LIMIT) {
|
|
1041
|
-
sourceCache.delete(sourceCache.keys().next().value);
|
|
1034
|
+
if (raw.fields !== void 0) {
|
|
1035
|
+
if (!isPlainRecord(raw.fields) || !isFieldValue(raw.fields, 0)) {
|
|
1036
|
+
return null;
|
|
1037
|
+
}
|
|
1038
|
+
entry.fields = raw.fields;
|
|
1042
1039
|
}
|
|
1043
|
-
|
|
1040
|
+
list.push(entry);
|
|
1044
1041
|
}
|
|
1045
|
-
return
|
|
1042
|
+
return list;
|
|
1046
1043
|
}
|
|
1047
|
-
function
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1044
|
+
function isPlainRecord(v) {
|
|
1045
|
+
return v !== null && typeof v === "object" && Object.getPrototypeOf(v) === Object.prototype;
|
|
1046
|
+
}
|
|
1047
|
+
function isFieldValue(v, depth) {
|
|
1048
|
+
if (v === null || isScalar(v)) return true;
|
|
1049
|
+
if (depth >= MAX_FIELD_DEPTH) return false;
|
|
1050
|
+
if (Array.isArray(v)) {
|
|
1051
|
+
return v.length <= MAX_ENTRIES && v.every(
|
|
1052
|
+
(entry) => isScalar(entry) || isPlainRecord(entry) && isFieldValue(entry, depth + 1)
|
|
1053
|
+
);
|
|
1053
1054
|
}
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1055
|
+
if (!isPlainRecord(v)) return false;
|
|
1056
|
+
const keys = Object.keys(v);
|
|
1057
|
+
if (keys.length > MAX_FIELD_KEYS) return false;
|
|
1058
|
+
return keys.every((key) => {
|
|
1059
|
+
if (FORBIDDEN_KEYS.has(key)) return false;
|
|
1060
|
+
const value = v[key];
|
|
1061
|
+
if (!isSafeKeyValue(key, value)) return false;
|
|
1062
|
+
return isFieldValue(value, depth + 1);
|
|
1063
|
+
});
|
|
1059
1064
|
}
|
|
1060
|
-
function
|
|
1061
|
-
|
|
1062
|
-
const
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1065
|
+
function parseEditTarget(raw) {
|
|
1066
|
+
if (!isPlainRecord(raw)) return void 0;
|
|
1067
|
+
const { item, field } = raw;
|
|
1068
|
+
if (typeof item !== "string" || !item || item.length > MAX_EDIT_LENGTH) {
|
|
1069
|
+
return void 0;
|
|
1070
|
+
}
|
|
1071
|
+
if (typeof field !== "string" || !field || field.length > MAX_EDIT_LENGTH) {
|
|
1072
|
+
return void 0;
|
|
1073
|
+
}
|
|
1074
|
+
return { item, field };
|
|
1075
|
+
}
|
|
1076
|
+
var MAX_DEPTH, MAX_ENTRIES, FORBIDDEN_KEYS, SAFE_URL, URL_KEY, URL_IGNORED_ANYWHERE, SCRIPT_SCHEME, MAX_LIST_ENTRIES, MAX_FIELD_DEPTH, MAX_FIELD_KEYS, MAX_EDIT_LENGTH;
|
|
1077
|
+
var init_validate = __esm({
|
|
1078
|
+
"src/core/live-preview/validate.ts"() {
|
|
1079
|
+
"use strict";
|
|
1080
|
+
MAX_DEPTH = 3;
|
|
1081
|
+
MAX_ENTRIES = 50;
|
|
1082
|
+
FORBIDDEN_KEYS = /* @__PURE__ */ new Set(["__proto__", "prototype", "constructor"]);
|
|
1083
|
+
SAFE_URL = /^https?:\/\//i;
|
|
1084
|
+
URL_KEY = /^(?:url|href|src|link)$|(?:_url|Url)$/;
|
|
1085
|
+
URL_IGNORED_ANYWHERE = /[\t\n\r]/g;
|
|
1086
|
+
SCRIPT_SCHEME = /^(?:javascript|vbscript|data):/i;
|
|
1087
|
+
MAX_LIST_ENTRIES = 200;
|
|
1088
|
+
MAX_FIELD_DEPTH = 6;
|
|
1089
|
+
MAX_FIELD_KEYS = 100;
|
|
1090
|
+
MAX_EDIT_LENGTH = 1e3;
|
|
1091
|
+
}
|
|
1092
|
+
});
|
|
1093
|
+
|
|
1094
|
+
// src/core/live-preview/bridge-events.ts
|
|
1095
|
+
function createLiveBridge(store2, options = {}) {
|
|
1096
|
+
let parentOrigin = null;
|
|
1097
|
+
const onMessage = (evt) => {
|
|
1098
|
+
const data = evt?.data;
|
|
1099
|
+
if (data?.event === "prepr:initVE" && !parentOrigin) {
|
|
1100
|
+
if (isAllowedEditorOrigin(evt.origin, options.allowedEditorOrigins?.())) {
|
|
1101
|
+
parentOrigin = evt.origin;
|
|
1102
|
+
} else {
|
|
1103
|
+
debug4.warn("ignored live preview handshake from origin", evt.origin);
|
|
1104
|
+
}
|
|
1105
|
+
return;
|
|
1077
1106
|
}
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
walk(s.selections, matched && applies(s.on, s.selections));
|
|
1107
|
+
const trusted = parentOrigin ?? getTrustedParentOrigin();
|
|
1108
|
+
if (!trusted || evt.origin !== trusted) return;
|
|
1109
|
+
if (data?.event === "prepr:fieldChange") {
|
|
1110
|
+
if (typeof data.itemId === "string" && typeof data.field === "string" && data.field && isLiveFieldChange(data.field, data.value)) {
|
|
1111
|
+
const edit = parseEditTarget(data.edit);
|
|
1112
|
+
if (edit) store2.pushPatch(data.itemId, data.field, data.value, edit);
|
|
1113
|
+
else store2.pushPatch(data.itemId, data.field, data.value);
|
|
1086
1114
|
} else {
|
|
1087
|
-
|
|
1088
|
-
if (!fragment) continue;
|
|
1089
|
-
const asMatched = matched && applies(fragment.on, fragment.selections);
|
|
1090
|
-
if (walkedMatched.has(fragment)) continue;
|
|
1091
|
-
if (!asMatched && walkedLoose.has(fragment)) continue;
|
|
1092
|
-
(asMatched ? walkedMatched : walkedLoose).add(fragment);
|
|
1093
|
-
walk(fragment.selections, asMatched);
|
|
1115
|
+
debug4.log("dropped malformed fieldChange", data);
|
|
1094
1116
|
}
|
|
1117
|
+
return;
|
|
1095
1118
|
}
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1119
|
+
if (data?.event === "prepr:listChange") {
|
|
1120
|
+
const list = parseListEntries(data.entries);
|
|
1121
|
+
if (typeof data.itemId === "string" && typeof data.field === "string" && data.field && list) {
|
|
1122
|
+
store2.pushList(data.itemId, data.field, list);
|
|
1123
|
+
} else {
|
|
1124
|
+
debug4.log("dropped malformed listChange", data);
|
|
1125
|
+
}
|
|
1126
|
+
return;
|
|
1101
1127
|
}
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
}
|
|
1108
|
-
function typeConditions(selections, doc) {
|
|
1109
|
-
const hit = conditionsCache.get(selections);
|
|
1110
|
-
if (hit) return hit;
|
|
1111
|
-
const names = /* @__PURE__ */ new Set();
|
|
1112
|
-
const seen = /* @__PURE__ */ new Set();
|
|
1113
|
-
const walk = (list) => {
|
|
1114
|
-
for (const s of list) {
|
|
1115
|
-
if (s.kind === "inline") {
|
|
1116
|
-
if (s.on) names.add(s.on);
|
|
1117
|
-
walk(s.selections);
|
|
1118
|
-
} else if (s.kind === "spread") {
|
|
1119
|
-
const fragment = doc.fragments.get(s.name);
|
|
1120
|
-
if (!fragment || seen.has(fragment)) continue;
|
|
1121
|
-
seen.add(fragment);
|
|
1122
|
-
names.add(fragment.on);
|
|
1123
|
-
walk(fragment.selections);
|
|
1128
|
+
if (data?.event === "prepr:itemChanged") {
|
|
1129
|
+
if (typeof data.itemId === "string") {
|
|
1130
|
+
store2.invalidate(data.itemId);
|
|
1131
|
+
} else {
|
|
1132
|
+
debug4.log("dropped malformed itemChanged", data);
|
|
1124
1133
|
}
|
|
1125
1134
|
}
|
|
1126
1135
|
};
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1136
|
+
return {
|
|
1137
|
+
start() {
|
|
1138
|
+
window.addEventListener("message", onMessage);
|
|
1139
|
+
sendPreprEvent(
|
|
1140
|
+
"live_ready",
|
|
1141
|
+
options.strictMode?.() ? { strictMode: true } : void 0,
|
|
1142
|
+
{ allowUntrustedTarget: true }
|
|
1143
|
+
);
|
|
1144
|
+
},
|
|
1145
|
+
stop() {
|
|
1146
|
+
window.removeEventListener("message", onMessage);
|
|
1147
|
+
}
|
|
1148
|
+
};
|
|
1131
1149
|
}
|
|
1132
|
-
var
|
|
1133
|
-
var
|
|
1134
|
-
"src/core/live-preview/
|
|
1150
|
+
var debug4;
|
|
1151
|
+
var init_bridge_events = __esm({
|
|
1152
|
+
"src/core/live-preview/bridge-events.ts"() {
|
|
1135
1153
|
"use strict";
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
"$",
|
|
1141
|
-
"&",
|
|
1142
|
-
"(",
|
|
1143
|
-
")",
|
|
1144
|
-
":",
|
|
1145
|
-
"=",
|
|
1146
|
-
"@",
|
|
1147
|
-
"[",
|
|
1148
|
-
"]",
|
|
1149
|
-
"{",
|
|
1150
|
-
"|",
|
|
1151
|
-
"}"
|
|
1152
|
-
]);
|
|
1153
|
-
astCache = /* @__PURE__ */ new WeakMap();
|
|
1154
|
-
sourceCache = /* @__PURE__ */ new Map();
|
|
1155
|
-
SOURCE_CACHE_LIMIT = 200;
|
|
1156
|
-
fieldsCache = /* @__PURE__ */ new WeakMap();
|
|
1157
|
-
conditionsCache = /* @__PURE__ */ new WeakMap();
|
|
1154
|
+
init_utils();
|
|
1155
|
+
init_iframe_bridge();
|
|
1156
|
+
init_validate();
|
|
1157
|
+
debug4 = createScopedLogger("live-preview:bridge");
|
|
1158
1158
|
}
|
|
1159
1159
|
});
|
|
1160
1160
|
|
|
@@ -1265,7 +1265,7 @@ function applyPatch(root, index, patch, doc, result) {
|
|
|
1265
1265
|
for (const target of targets) {
|
|
1266
1266
|
const pushed = plain.has(target) && typeof patch.value === "string" ? plainTextFromHtml(patch.value) : patch.value;
|
|
1267
1267
|
let value = keepEditLink(readAtPath(root, target), pushed);
|
|
1268
|
-
if (patch.edit && typeof value === "string" && !(0,
|
|
1268
|
+
if (patch.edit && typeof value === "string" && !(0, import_stega3.vercelStegaSplit)(value).encoded) {
|
|
1269
1269
|
value = editLinksFor(result, patch.edit.item)?.encode(
|
|
1270
1270
|
value,
|
|
1271
1271
|
patch.edit.field
|
|
@@ -1332,7 +1332,7 @@ function resolveTargets(root, doc, ownerPath, segments) {
|
|
|
1332
1332
|
let current = root;
|
|
1333
1333
|
const out = [];
|
|
1334
1334
|
const plain = /* @__PURE__ */ new Set();
|
|
1335
|
-
const
|
|
1335
|
+
const walk2 = (node, sel, i, path) => {
|
|
1336
1336
|
if (i === segments.length) {
|
|
1337
1337
|
out.push(path);
|
|
1338
1338
|
return;
|
|
@@ -1342,10 +1342,10 @@ function resolveTargets(root, doc, ownerPath, segments) {
|
|
|
1342
1342
|
if (INDEX_SEGMENT2.test(segment)) {
|
|
1343
1343
|
const position = Number(segment);
|
|
1344
1344
|
if (position < node.length) {
|
|
1345
|
-
|
|
1345
|
+
walk2(node[position], sel, i + 1, [...path, position]);
|
|
1346
1346
|
}
|
|
1347
1347
|
} else if (segment === "items") {
|
|
1348
|
-
|
|
1348
|
+
walk2(node, sel, i + 1, path);
|
|
1349
1349
|
}
|
|
1350
1350
|
return;
|
|
1351
1351
|
}
|
|
@@ -1378,11 +1378,11 @@ function resolveTargets(root, doc, ownerPath, segments) {
|
|
|
1378
1378
|
current = writeAtPath(current, fieldPath, value);
|
|
1379
1379
|
}
|
|
1380
1380
|
}
|
|
1381
|
-
|
|
1381
|
+
walk2(value, field.selections, i + 1, fieldPath);
|
|
1382
1382
|
}
|
|
1383
|
-
if (!matched && segment === "items")
|
|
1383
|
+
if (!matched && segment === "items") walk2(node, sel, i + 1, path);
|
|
1384
1384
|
};
|
|
1385
|
-
|
|
1385
|
+
walk2(owner, selections, 0, ownerPath);
|
|
1386
1386
|
return { root: current, targets: out, plain };
|
|
1387
1387
|
}
|
|
1388
1388
|
function resolveTypename(doc, selections, typename, typeName) {
|
|
@@ -1501,11 +1501,11 @@ function listKeysIn(result) {
|
|
|
1501
1501
|
if (!byType) {
|
|
1502
1502
|
byType = /* @__PURE__ */ new Map();
|
|
1503
1503
|
const seen = /* @__PURE__ */ new WeakSet();
|
|
1504
|
-
const
|
|
1504
|
+
const walk2 = (node) => {
|
|
1505
1505
|
if (node === null || typeof node !== "object" || seen.has(node)) return;
|
|
1506
1506
|
seen.add(node);
|
|
1507
1507
|
if (Array.isArray(node)) {
|
|
1508
|
-
node.forEach(
|
|
1508
|
+
node.forEach(walk2);
|
|
1509
1509
|
return;
|
|
1510
1510
|
}
|
|
1511
1511
|
const record = node;
|
|
@@ -1516,10 +1516,10 @@ function listKeysIn(result) {
|
|
|
1516
1516
|
if (!keys) byType.set(type, keys = /* @__PURE__ */ new Set());
|
|
1517
1517
|
keys.add(key);
|
|
1518
1518
|
}
|
|
1519
|
-
|
|
1519
|
+
walk2(value);
|
|
1520
1520
|
}
|
|
1521
1521
|
};
|
|
1522
|
-
|
|
1522
|
+
walk2(result);
|
|
1523
1523
|
listKeysCache.set(result, byType);
|
|
1524
1524
|
}
|
|
1525
1525
|
return byType.get(typename) ?? NO_KEYS;
|
|
@@ -1536,7 +1536,7 @@ function editLinksFor(result, item) {
|
|
|
1536
1536
|
if (!template) return null;
|
|
1537
1537
|
return {
|
|
1538
1538
|
encode(text, field) {
|
|
1539
|
-
if ((0,
|
|
1539
|
+
if ((0, import_stega3.vercelStegaSplit)(text).encoded) return text;
|
|
1540
1540
|
let href;
|
|
1541
1541
|
try {
|
|
1542
1542
|
href = new URL(template.href);
|
|
@@ -1549,7 +1549,7 @@ function editLinksFor(result, item) {
|
|
|
1549
1549
|
);
|
|
1550
1550
|
href.searchParams.set("field", field);
|
|
1551
1551
|
const link = href.toString().replace(/%2E/gi, ".").replace(/%2F/gi, "/");
|
|
1552
|
-
return (0,
|
|
1552
|
+
return (0, import_stega3.vercelStegaCombine)(text, { origin: template.origin, href: link });
|
|
1553
1553
|
}
|
|
1554
1554
|
};
|
|
1555
1555
|
}
|
|
@@ -1558,12 +1558,12 @@ function findEditLink(root) {
|
|
|
1558
1558
|
if (linkCache.has(root)) return linkCache.get(root) ?? null;
|
|
1559
1559
|
let found = null;
|
|
1560
1560
|
const seen = /* @__PURE__ */ new WeakSet();
|
|
1561
|
-
const
|
|
1561
|
+
const walk2 = (node) => {
|
|
1562
1562
|
if (found) return;
|
|
1563
1563
|
if (typeof node === "string") {
|
|
1564
|
-
if (!(0,
|
|
1564
|
+
if (!(0, import_stega3.vercelStegaSplit)(node).encoded) return;
|
|
1565
1565
|
try {
|
|
1566
|
-
const decoded = (0,
|
|
1566
|
+
const decoded = (0, import_stega3.vercelStegaDecode)(node);
|
|
1567
1567
|
if (typeof decoded?.origin === "string" && typeof decoded.href === "string" && EDIT_PATH.test(decoded.href)) {
|
|
1568
1568
|
found = { origin: decoded.origin, href: decoded.href };
|
|
1569
1569
|
}
|
|
@@ -1573,17 +1573,17 @@ function findEditLink(root) {
|
|
|
1573
1573
|
}
|
|
1574
1574
|
if (node === null || typeof node !== "object" || seen.has(node)) return;
|
|
1575
1575
|
seen.add(node);
|
|
1576
|
-
for (const value of Object.values(node))
|
|
1576
|
+
for (const value of Object.values(node)) walk2(value);
|
|
1577
1577
|
};
|
|
1578
|
-
|
|
1578
|
+
walk2(root);
|
|
1579
1579
|
linkCache.set(root, found);
|
|
1580
1580
|
return found;
|
|
1581
1581
|
}
|
|
1582
|
-
var
|
|
1582
|
+
var import_stega3, debug5, MAX_LOG_ENTRIES2, MAX_LOG_SOURCE_LENGTH, INDEX_SEGMENT2, FORBIDDEN_SEGMENTS2, ENVELOPE_KEYS, NON_ALPHANUMERIC, typeKey, NO_KEYS, listKeysCache, EDIT_PATH, ITEM_ID, linkCache;
|
|
1583
1583
|
var init_normalize_query = __esm({
|
|
1584
1584
|
"src/core/live-preview/normalize-query.ts"() {
|
|
1585
1585
|
"use strict";
|
|
1586
|
-
|
|
1586
|
+
import_stega3 = require("@vercel/stega");
|
|
1587
1587
|
init_utils();
|
|
1588
1588
|
init_field_name();
|
|
1589
1589
|
init_index_snapshot();
|
|
@@ -1632,6 +1632,102 @@ __export(live_exports, {
|
|
|
1632
1632
|
module.exports = __toCommonJS(live_exports);
|
|
1633
1633
|
var import_vue = require("vue");
|
|
1634
1634
|
|
|
1635
|
+
// src/core/live-preview/edit-links.ts
|
|
1636
|
+
var import_stega = require("@vercel/stega");
|
|
1637
|
+
init_query_document();
|
|
1638
|
+
var ENTRY_PARAM = "entry";
|
|
1639
|
+
var TOP_LEVEL_ENTRY = /^[A-Za-z0-9_]+\.\d+$/;
|
|
1640
|
+
function entryIdOf(element) {
|
|
1641
|
+
if (!isRecord(element)) return void 0;
|
|
1642
|
+
if (typeof element._id === "string") return element._id;
|
|
1643
|
+
if (element.__typename !== "Assets" || !Array.isArray(element.items)) {
|
|
1644
|
+
return void 0;
|
|
1645
|
+
}
|
|
1646
|
+
const first = element.items[0];
|
|
1647
|
+
return isRecord(first) && typeof first._id === "string" ? first._id : void 0;
|
|
1648
|
+
}
|
|
1649
|
+
function isRecord(value) {
|
|
1650
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1651
|
+
}
|
|
1652
|
+
function annotate(value, ctx) {
|
|
1653
|
+
if (!ctx.entryId) return value;
|
|
1654
|
+
const { cleaned, encoded } = (0, import_stega.vercelStegaSplit)(value);
|
|
1655
|
+
if (!encoded) return value;
|
|
1656
|
+
let payload;
|
|
1657
|
+
try {
|
|
1658
|
+
payload = (0, import_stega.vercelStegaDecode)(encoded);
|
|
1659
|
+
} catch {
|
|
1660
|
+
return value;
|
|
1661
|
+
}
|
|
1662
|
+
if (typeof payload?.href !== "string") return value;
|
|
1663
|
+
let url;
|
|
1664
|
+
try {
|
|
1665
|
+
url = new URL(payload.href);
|
|
1666
|
+
} catch {
|
|
1667
|
+
return value;
|
|
1668
|
+
}
|
|
1669
|
+
if (url.searchParams.has(ENTRY_PARAM)) return value;
|
|
1670
|
+
url.searchParams.set(ENTRY_PARAM, ctx.entryId);
|
|
1671
|
+
const itemId = decodeURIComponent(url.pathname.split("/").pop() ?? "");
|
|
1672
|
+
if (!url.searchParams.has("field") && ctx.entryField && TOP_LEVEL_ENTRY.test(ctx.entryField) && itemId === ctx.entryOwnerId) {
|
|
1673
|
+
url.searchParams.set("field", ctx.entryField);
|
|
1674
|
+
}
|
|
1675
|
+
return (0, import_stega.vercelStegaCombine)(
|
|
1676
|
+
cleaned,
|
|
1677
|
+
{ ...payload, href: url.toString() },
|
|
1678
|
+
false
|
|
1679
|
+
);
|
|
1680
|
+
}
|
|
1681
|
+
function walk(value, selections, ctx) {
|
|
1682
|
+
if (typeof value === "string") return annotate(value, ctx);
|
|
1683
|
+
if (Array.isArray(value)) {
|
|
1684
|
+
let changed2 = false;
|
|
1685
|
+
const next2 = value.map((element, index) => {
|
|
1686
|
+
const path = [...ctx.path, String(index)];
|
|
1687
|
+
const entryId = ctx.inAssets ? void 0 : entryIdOf(element);
|
|
1688
|
+
const entry = entryId ? {
|
|
1689
|
+
entryId,
|
|
1690
|
+
entryField: ctx.ownerId ? path.join(".") : void 0,
|
|
1691
|
+
entryOwnerId: ctx.ownerId,
|
|
1692
|
+
inAssets: isRecord(element) && element._id === void 0
|
|
1693
|
+
} : {};
|
|
1694
|
+
const out = walk(element, selections, { ...ctx, ...entry, path });
|
|
1695
|
+
if (out !== element) changed2 = true;
|
|
1696
|
+
return out;
|
|
1697
|
+
});
|
|
1698
|
+
return changed2 ? next2 : value;
|
|
1699
|
+
}
|
|
1700
|
+
if (!isRecord(value)) return value;
|
|
1701
|
+
const id = typeof value._id === "string" ? value._id : void 0;
|
|
1702
|
+
const typename = typeof value.__typename === "string" ? value.__typename : void 0;
|
|
1703
|
+
const fields = ctx.doc && selections ? collectFields(selections, typename, ctx.doc) : null;
|
|
1704
|
+
let changed = false;
|
|
1705
|
+
const next = {};
|
|
1706
|
+
for (const [key, child] of Object.entries(value)) {
|
|
1707
|
+
const field = fields?.get(key);
|
|
1708
|
+
const name = field?.name ?? key;
|
|
1709
|
+
const out = walk(child, field?.selections ?? null, {
|
|
1710
|
+
...ctx,
|
|
1711
|
+
ownerId: id ?? ctx.ownerId,
|
|
1712
|
+
path: id ? [name] : [...ctx.path, name]
|
|
1713
|
+
});
|
|
1714
|
+
if (out !== child) changed = true;
|
|
1715
|
+
next[key] = out;
|
|
1716
|
+
}
|
|
1717
|
+
return changed ? next : value;
|
|
1718
|
+
}
|
|
1719
|
+
function annotateEditLinks(result, query, operationName) {
|
|
1720
|
+
let doc = null;
|
|
1721
|
+
if (query !== void 0) {
|
|
1722
|
+
try {
|
|
1723
|
+
doc = readQueryDocument(query, operationName);
|
|
1724
|
+
} catch {
|
|
1725
|
+
doc = null;
|
|
1726
|
+
}
|
|
1727
|
+
}
|
|
1728
|
+
return walk(result, doc?.operation ?? null, { doc, path: [] });
|
|
1729
|
+
}
|
|
1730
|
+
|
|
1635
1731
|
// src/core/live-preview/test-reset.ts
|
|
1636
1732
|
var resets = /* @__PURE__ */ new Set();
|
|
1637
1733
|
function onTestReset(reset) {
|
|
@@ -1679,9 +1775,9 @@ function whenLive(listener) {
|
|
|
1679
1775
|
return () => startListeners.delete(listener);
|
|
1680
1776
|
}
|
|
1681
1777
|
function useNormalizedQuery(query, data, options = {}) {
|
|
1682
|
-
if (!options.enabled
|
|
1683
|
-
|
|
1684
|
-
|
|
1778
|
+
if (!options.enabled) return (0, import_vue.computed)(() => (0, import_vue.unref)(data));
|
|
1779
|
+
const annotated = (0, import_vue.computed)(() => annotateEditLinks((0, import_vue.unref)(data), query));
|
|
1780
|
+
if (!isFramed()) return annotated;
|
|
1685
1781
|
if (options.strictMode) strictMode = true;
|
|
1686
1782
|
if (options.allowedEditorOrigins) {
|
|
1687
1783
|
allowedEditorOrigins = [...options.allowedEditorOrigins];
|
|
@@ -1699,7 +1795,7 @@ function useNormalizedQuery(query, data, options = {}) {
|
|
|
1699
1795
|
});
|
|
1700
1796
|
}
|
|
1701
1797
|
return (0, import_vue.computed)(() => {
|
|
1702
|
-
const value =
|
|
1798
|
+
const value = annotated.value;
|
|
1703
1799
|
return log.value.length && applyLog ? applyLog(query, value, log.value) : value;
|
|
1704
1800
|
});
|
|
1705
1801
|
}
|