@preprio/toolkit 0.5.0-rc.5 → 0.5.0-rc.7
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 +133 -26
- package/dist/astro/index.cjs +126 -30
- package/dist/astro/index.js +130 -30
- package/dist/{chunk-3X55JL52.js → chunk-HVS5QLWG.js} +1 -1
- package/dist/{chunk-TG6N3C22.js → chunk-IPMB32AZ.js} +1 -1
- package/dist/{chunk-CMUFTCG3.js → chunk-JUXP5KRX.js} +1 -1
- package/dist/{chunk-74KY5EAM.js → chunk-KD3GK6TO.js} +2 -0
- package/dist/{chunk-SBLZANL5.js → chunk-SUAFASBB.js} +11 -3
- package/dist/codegen/index.cjs +82 -0
- package/dist/codegen/index.d.cts +70 -0
- package/dist/codegen/index.d.ts +70 -0
- package/dist/codegen/index.js +64 -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.js +128 -28
- package/dist/nextjs/live-action.js +3 -3
- package/dist/nextjs/live.js +1 -1
- 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-O2ZTJNW4.js → live-preview-2RCU35W5.js} +6 -337
- package/dist/sveltekit/live.cjs +1008 -909
- package/dist/sveltekit/live.js +118 -11
- package/package.json +11 -1
package/dist/sveltekit/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(
|
|
@@ -624,537 +965,196 @@ function isAllowedEditorOrigin(origin, allowed) {
|
|
|
624
965
|
if (!url.hostname.endsWith(suffix)) return false;
|
|
625
966
|
const label = url.hostname.slice(0, -suffix.length);
|
|
626
967
|
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
|
-
};
|
|
808
|
-
}
|
|
809
|
-
var debug4;
|
|
810
|
-
var init_bridge_events = __esm({
|
|
811
|
-
"src/core/live-preview/bridge-events.ts"() {
|
|
968
|
+
}
|
|
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
|
-
if (s.kind === "InlineFragment") {
|
|
985
|
-
return {
|
|
986
|
-
kind: "inline",
|
|
987
|
-
on: s.typeCondition?.name.value ?? null,
|
|
988
|
-
selections: fromAstSelections(s.selectionSet)
|
|
989
|
-
};
|
|
990
|
-
}
|
|
991
|
-
const name = s.name.value;
|
|
992
|
-
return {
|
|
993
|
-
kind: "field",
|
|
994
|
-
name,
|
|
995
|
-
key: s.alias?.value ?? name,
|
|
996
|
-
hasArgs: Boolean(s.arguments?.length),
|
|
997
|
-
selections: s.selectionSet ? fromAstSelections(s.selectionSet) : null
|
|
998
|
-
};
|
|
999
|
-
});
|
|
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);
|
|
1000
996
|
}
|
|
1001
|
-
function
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
fragments.set(def.name.value, {
|
|
1007
|
-
on: def.typeCondition.name.value,
|
|
1008
|
-
selections: fromAstSelections(def.selectionSet)
|
|
1009
|
-
});
|
|
1010
|
-
} else if (def.kind === "OperationDefinition" && def.selectionSet) {
|
|
1011
|
-
operations.push({
|
|
1012
|
-
name: def.name?.value ?? null,
|
|
1013
|
-
selections: fromAstSelections(def.selectionSet)
|
|
1014
|
-
});
|
|
1015
|
-
}
|
|
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));
|
|
1016
1002
|
}
|
|
1017
|
-
|
|
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);
|
|
1011
|
+
});
|
|
1018
1012
|
}
|
|
1019
|
-
function
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
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;
|
|
1031
1030
|
}
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
const source = String(query);
|
|
1035
|
-
const sourceKey = `${cacheKey}
|
|
1036
|
-
${source}`;
|
|
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);
|
|
1031
|
+
if (typeof raw.typeName === "string" && raw.typeName.length <= 200) {
|
|
1032
|
+
entry.typeName = raw.typeName;
|
|
1042
1033
|
}
|
|
1043
|
-
|
|
1034
|
+
if (raw.fields !== void 0) {
|
|
1035
|
+
if (!isPlainRecord(raw.fields) || !isFieldValue(raw.fields, 0)) {
|
|
1036
|
+
return null;
|
|
1037
|
+
}
|
|
1038
|
+
entry.fields = raw.fields;
|
|
1039
|
+
}
|
|
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();
|
|
@@ -1631,6 +1631,102 @@ __export(live_exports, {
|
|
|
1631
1631
|
});
|
|
1632
1632
|
module.exports = __toCommonJS(live_exports);
|
|
1633
1633
|
|
|
1634
|
+
// src/core/live-preview/edit-links.ts
|
|
1635
|
+
var import_stega = require("@vercel/stega");
|
|
1636
|
+
init_query_document();
|
|
1637
|
+
var ENTRY_PARAM = "entry";
|
|
1638
|
+
var TOP_LEVEL_ENTRY = /^[A-Za-z0-9_]+\.\d+$/;
|
|
1639
|
+
function entryIdOf(element) {
|
|
1640
|
+
if (!isRecord(element)) return void 0;
|
|
1641
|
+
if (typeof element._id === "string") return element._id;
|
|
1642
|
+
if (element.__typename !== "Assets" || !Array.isArray(element.items)) {
|
|
1643
|
+
return void 0;
|
|
1644
|
+
}
|
|
1645
|
+
const first = element.items[0];
|
|
1646
|
+
return isRecord(first) && typeof first._id === "string" ? first._id : void 0;
|
|
1647
|
+
}
|
|
1648
|
+
function isRecord(value) {
|
|
1649
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1650
|
+
}
|
|
1651
|
+
function annotate(value, ctx) {
|
|
1652
|
+
if (!ctx.entryId) return value;
|
|
1653
|
+
const { cleaned, encoded } = (0, import_stega.vercelStegaSplit)(value);
|
|
1654
|
+
if (!encoded) return value;
|
|
1655
|
+
let payload;
|
|
1656
|
+
try {
|
|
1657
|
+
payload = (0, import_stega.vercelStegaDecode)(encoded);
|
|
1658
|
+
} catch {
|
|
1659
|
+
return value;
|
|
1660
|
+
}
|
|
1661
|
+
if (typeof payload?.href !== "string") return value;
|
|
1662
|
+
let url;
|
|
1663
|
+
try {
|
|
1664
|
+
url = new URL(payload.href);
|
|
1665
|
+
} catch {
|
|
1666
|
+
return value;
|
|
1667
|
+
}
|
|
1668
|
+
if (url.searchParams.has(ENTRY_PARAM)) return value;
|
|
1669
|
+
url.searchParams.set(ENTRY_PARAM, ctx.entryId);
|
|
1670
|
+
const itemId = decodeURIComponent(url.pathname.split("/").pop() ?? "");
|
|
1671
|
+
if (!url.searchParams.has("field") && ctx.entryField && TOP_LEVEL_ENTRY.test(ctx.entryField) && itemId === ctx.entryOwnerId) {
|
|
1672
|
+
url.searchParams.set("field", ctx.entryField);
|
|
1673
|
+
}
|
|
1674
|
+
return (0, import_stega.vercelStegaCombine)(
|
|
1675
|
+
cleaned,
|
|
1676
|
+
{ ...payload, href: url.toString() },
|
|
1677
|
+
false
|
|
1678
|
+
);
|
|
1679
|
+
}
|
|
1680
|
+
function walk(value, selections, ctx) {
|
|
1681
|
+
if (typeof value === "string") return annotate(value, ctx);
|
|
1682
|
+
if (Array.isArray(value)) {
|
|
1683
|
+
let changed2 = false;
|
|
1684
|
+
const next2 = value.map((element, index) => {
|
|
1685
|
+
const path = [...ctx.path, String(index)];
|
|
1686
|
+
const entryId = ctx.inAssets ? void 0 : entryIdOf(element);
|
|
1687
|
+
const entry = entryId ? {
|
|
1688
|
+
entryId,
|
|
1689
|
+
entryField: ctx.ownerId ? path.join(".") : void 0,
|
|
1690
|
+
entryOwnerId: ctx.ownerId,
|
|
1691
|
+
inAssets: isRecord(element) && element._id === void 0
|
|
1692
|
+
} : {};
|
|
1693
|
+
const out = walk(element, selections, { ...ctx, ...entry, path });
|
|
1694
|
+
if (out !== element) changed2 = true;
|
|
1695
|
+
return out;
|
|
1696
|
+
});
|
|
1697
|
+
return changed2 ? next2 : value;
|
|
1698
|
+
}
|
|
1699
|
+
if (!isRecord(value)) return value;
|
|
1700
|
+
const id = typeof value._id === "string" ? value._id : void 0;
|
|
1701
|
+
const typename = typeof value.__typename === "string" ? value.__typename : void 0;
|
|
1702
|
+
const fields = ctx.doc && selections ? collectFields(selections, typename, ctx.doc) : null;
|
|
1703
|
+
let changed = false;
|
|
1704
|
+
const next = {};
|
|
1705
|
+
for (const [key, child] of Object.entries(value)) {
|
|
1706
|
+
const field = fields?.get(key);
|
|
1707
|
+
const name = field?.name ?? key;
|
|
1708
|
+
const out = walk(child, field?.selections ?? null, {
|
|
1709
|
+
...ctx,
|
|
1710
|
+
ownerId: id ?? ctx.ownerId,
|
|
1711
|
+
path: id ? [name] : [...ctx.path, name]
|
|
1712
|
+
});
|
|
1713
|
+
if (out !== child) changed = true;
|
|
1714
|
+
next[key] = out;
|
|
1715
|
+
}
|
|
1716
|
+
return changed ? next : value;
|
|
1717
|
+
}
|
|
1718
|
+
function annotateEditLinks(result, query, operationName) {
|
|
1719
|
+
let doc = null;
|
|
1720
|
+
if (query !== void 0) {
|
|
1721
|
+
try {
|
|
1722
|
+
doc = readQueryDocument(query, operationName);
|
|
1723
|
+
} catch {
|
|
1724
|
+
doc = null;
|
|
1725
|
+
}
|
|
1726
|
+
}
|
|
1727
|
+
return walk(result, doc?.operation ?? null, { doc, path: [] });
|
|
1728
|
+
}
|
|
1729
|
+
|
|
1634
1730
|
// src/core/live-preview/test-reset.ts
|
|
1635
1731
|
var resets = /* @__PURE__ */ new Set();
|
|
1636
1732
|
function onTestReset(reset) {
|
|
@@ -1647,6 +1743,15 @@ var allowedEditorOrigins;
|
|
|
1647
1743
|
var strictMode;
|
|
1648
1744
|
var logListeners = /* @__PURE__ */ new Set();
|
|
1649
1745
|
var startListeners = /* @__PURE__ */ new Set();
|
|
1746
|
+
function constant(value) {
|
|
1747
|
+
return {
|
|
1748
|
+
subscribe(run) {
|
|
1749
|
+
run(value);
|
|
1750
|
+
return () => {
|
|
1751
|
+
};
|
|
1752
|
+
}
|
|
1753
|
+
};
|
|
1754
|
+
}
|
|
1650
1755
|
function isFramed() {
|
|
1651
1756
|
return typeof window !== "undefined" && window.self !== window.top;
|
|
1652
1757
|
}
|
|
@@ -1680,21 +1785,15 @@ function whenLive(listener) {
|
|
|
1680
1785
|
return () => startListeners.delete(listener);
|
|
1681
1786
|
}
|
|
1682
1787
|
function normalizedQuery(query, data, options = {}) {
|
|
1683
|
-
if (!options.enabled
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
run(data);
|
|
1687
|
-
return () => {
|
|
1688
|
-
};
|
|
1689
|
-
}
|
|
1690
|
-
};
|
|
1691
|
-
}
|
|
1788
|
+
if (!options.enabled) return constant(data);
|
|
1789
|
+
const annotated = annotateEditLinks(data, query);
|
|
1790
|
+
if (!isFramed()) return constant(annotated);
|
|
1692
1791
|
if (options.strictMode) strictMode = true;
|
|
1693
1792
|
if (options.allowedEditorOrigins) {
|
|
1694
1793
|
allowedEditorOrigins = [...options.allowedEditorOrigins];
|
|
1695
1794
|
}
|
|
1696
1795
|
startLive();
|
|
1697
|
-
const current = () => log.length && applyLog ? applyLog(query,
|
|
1796
|
+
const current = () => log.length && applyLog ? applyLog(query, annotated, log) : annotated;
|
|
1698
1797
|
return {
|
|
1699
1798
|
subscribe(run) {
|
|
1700
1799
|
run(current());
|