@replohq/sandbox-runtime 1.20.0 → 1.22.0
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/_vendor/schemas/comments.d.ts +906 -0
- package/_vendor/schemas/commentsOverlayRpc.d.ts +348 -0
- package/_vendor/schemas/commentsOverlayRpc.mjs +92 -0
- package/_vendor/schemas/generated/comments.d.ts +237 -0
- package/_vendor/schemas/generated/commentsOverlayRpc.d.ts +18 -0
- package/_vendor/schemas/generated/selectMode.d.ts +3 -1
- package/_vendor/schemas/selectMode.d.ts +113 -0
- package/_vendor/schemas/selectMode.mjs +1 -1
- package/_vendor/schemas/selectModeIframeRpc.d.ts +226 -0
- package/_vendor/schemas/selectModeIframeRpc.mjs +1 -1
- package/buildMetadata.js +3 -3
- package/chunk-CGRTGQO5.mjs +544 -0
- package/comments-overlay/CommentsOverlayMount.d.ts +1 -0
- package/comments-overlay/CommentsOverlayMount.js +44 -0
- package/comments-overlay/LICENSE-THIRD-PARTY.md +45 -0
- package/comments-overlay/bridge.d.ts +11 -0
- package/comments-overlay/bridge.js +136 -0
- package/comments-overlay/capture.d.ts +55 -0
- package/comments-overlay/capture.js +250 -0
- package/comments-overlay/originGate.d.ts +1 -0
- package/comments-overlay/originGate.js +11 -0
- package/comments-overlay/overlay.d.ts +52 -0
- package/comments-overlay/overlay.js +781 -0
- package/comments-overlay/resolve.d.ts +27 -0
- package/comments-overlay/resolve.js +201 -0
- package/comments-overlay/sourceLookup.d.ts +11 -0
- package/comments-overlay/sourceLookup.js +63 -0
- package/comments-overlay/textQuote.d.ts +17 -0
- package/comments-overlay/textQuote.js +87 -0
- package/comments-overlay/vendor/bippy.d.ts +53 -0
- package/comments-overlay/vendor/bippy.js +402 -0
- package/comments-overlay/vendor/elementAnchors.d.ts +9 -0
- package/comments-overlay/vendor/elementAnchors.js +144 -0
- package/comments-overlay/vendor/fiberElement.d.ts +21 -0
- package/comments-overlay/vendor/fiberElement.js +251 -0
- package/comments-overlay/vendor/htmlPreview.d.ts +13 -0
- package/comments-overlay/vendor/htmlPreview.js +179 -0
- package/comments-overlay/vendor/selector.d.ts +38 -0
- package/comments-overlay/vendor/selector.js +372 -0
- package/next/config.js +37 -1
- package/package.json +4 -4
- package/sandbox-runtime.js +2 -0
- package/select-mode/descriptor/elementDescriptor.d.ts +4 -3
- package/select-mode/descriptor/elementDescriptor.js +20 -30
- package/select-mode/iframeRpcChannel.d.ts +84 -0
- package/select-mode/symbolicate.js +25 -7
- package/chunk-QTGDSYSU.mjs +0 -249
|
@@ -0,0 +1,372 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Copyright (c) 2025 Aiden Bai
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
* https://github.com/aidenybai/react-grab
|
|
5
|
+
*/
|
|
6
|
+
const FINDER_TIMEOUT_MS = 200;
|
|
7
|
+
const MAX_SELECTOR_COMBINATIONS = 1e4;
|
|
8
|
+
const SELECTOR_ATTR_VALUE_MAX_LENGTH_CHARS = 120;
|
|
9
|
+
const SEED_MIN_LENGTH = 3;
|
|
10
|
+
const ATTR_VALUE_MAX_LENGTH_CHARS = 100;
|
|
11
|
+
const BROAD_SELECTOR_TARGET_DESCENDANT_RATIO = 0.5;
|
|
12
|
+
const PENALTY_ID = 0;
|
|
13
|
+
const PENALTY_CLASS = 1;
|
|
14
|
+
const PENALTY_ATTR = 2;
|
|
15
|
+
const PENALTY_TAG = 5;
|
|
16
|
+
const PENALTY_NTH_OF_TYPE = 10;
|
|
17
|
+
const PENALTY_NTH_CHILD = 50;
|
|
18
|
+
const PREFERRED_SELECTOR_ATTRIBUTE_NAMES = [
|
|
19
|
+
"data-testid",
|
|
20
|
+
"data-test-id",
|
|
21
|
+
"data-test",
|
|
22
|
+
"data-cy",
|
|
23
|
+
"data-qa",
|
|
24
|
+
"aria-label",
|
|
25
|
+
"href",
|
|
26
|
+
"src",
|
|
27
|
+
"role",
|
|
28
|
+
"name",
|
|
29
|
+
"title",
|
|
30
|
+
"alt"
|
|
31
|
+
];
|
|
32
|
+
const ACTIONABLE_SELECTOR_ROLES = /* @__PURE__ */ new Set([
|
|
33
|
+
"button",
|
|
34
|
+
"link",
|
|
35
|
+
"checkbox",
|
|
36
|
+
"radio",
|
|
37
|
+
"switch",
|
|
38
|
+
"tab",
|
|
39
|
+
"menuitem",
|
|
40
|
+
"option",
|
|
41
|
+
"textbox",
|
|
42
|
+
"combobox",
|
|
43
|
+
"slider",
|
|
44
|
+
"spinbutton"
|
|
45
|
+
]);
|
|
46
|
+
const ACCEPTED_ATTR_NAMES = /* @__PURE__ */ new Set([
|
|
47
|
+
"role",
|
|
48
|
+
"name",
|
|
49
|
+
"aria-label",
|
|
50
|
+
"rel",
|
|
51
|
+
"href"
|
|
52
|
+
]);
|
|
53
|
+
const UUID_ELEMENT_ID_PATTERN = /^[0-9a-f]{8}(?:-[0-9a-f]{4}){3}-[0-9a-f]{12}$/i;
|
|
54
|
+
const REACT_COLON_ELEMENT_ID_PATTERN = /:r[a-z0-9]+:/i;
|
|
55
|
+
const REACT_UNDERSCORE_ELEMENT_ID_PATTERN = /_r_[a-z0-9]+_(?:$|-)/i;
|
|
56
|
+
const REACT_GUILLEMET_ELEMENT_ID_PATTERN = /«r[a-z0-9]+»/i;
|
|
57
|
+
const GENERATED_FRAMEWORK_ELEMENT_ID_PATTERN = /^(?:downshift-\d+(?:-|$)|headlessui-[a-z-]+-\d+(?:-|$)|mui-\d+(?:-|$)|radix-\d+(?:-|$)|react-aria-\d+(?:-|$)|react-select-\d+(?:-|$))/i;
|
|
58
|
+
const EMBER_ELEMENT_ID_PATTERN = /^ember\d+$/i;
|
|
59
|
+
const NUMERIC_ELEMENT_ID_PATTERN = /^\d+$/;
|
|
60
|
+
function isStableElementId(elementId) {
|
|
61
|
+
return elementId.length > 0 && elementId.length <= SELECTOR_ATTR_VALUE_MAX_LENGTH_CHARS && !REACT_COLON_ELEMENT_ID_PATTERN.test(elementId) && !UUID_ELEMENT_ID_PATTERN.test(elementId) && !REACT_UNDERSCORE_ELEMENT_ID_PATTERN.test(elementId) && !REACT_GUILLEMET_ELEMENT_ID_PATTERN.test(elementId) && !GENERATED_FRAMEWORK_ELEMENT_ID_PATTERN.test(elementId) && !EMBER_ELEMENT_ID_PATTERN.test(elementId) && !NUMERIC_ELEMENT_ID_PATTERN.test(elementId);
|
|
62
|
+
}
|
|
63
|
+
function isPreferredSelectorAttribute(name, value) {
|
|
64
|
+
return PREFERRED_SELECTOR_ATTRIBUTE_NAMES.includes(name) && value.length > 0 && value.length <= SELECTOR_ATTR_VALUE_MAX_LENGTH_CHARS && (name !== "role" || value.split(/\s+/).some((role) => ACTIONABLE_SELECTOR_ROLES.has(role)));
|
|
65
|
+
}
|
|
66
|
+
function getSelectorRoot(element) {
|
|
67
|
+
const rootNode = element.getRootNode();
|
|
68
|
+
return rootNode instanceof Document || typeof ShadowRoot !== "undefined" && rootNode instanceof ShadowRoot ? rootNode : element.ownerDocument;
|
|
69
|
+
}
|
|
70
|
+
function isSelectorUniqueForElement(element, selector) {
|
|
71
|
+
try {
|
|
72
|
+
const matches = getSelectorRoot(element).querySelectorAll(selector);
|
|
73
|
+
return matches.length === 1 && matches[0] === element;
|
|
74
|
+
} catch {
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
function createSemanticSelector(element) {
|
|
79
|
+
const elementId = element.getAttribute("id");
|
|
80
|
+
if (elementId && isStableElementId(elementId)) {
|
|
81
|
+
const idSelector = `#${CSS.escape(elementId)}`;
|
|
82
|
+
if (isSelectorUniqueForElement(element, idSelector)) {
|
|
83
|
+
return idSelector;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
for (const attributeName of PREFERRED_SELECTOR_ATTRIBUTE_NAMES) {
|
|
87
|
+
const attributeValue = element.getAttribute(attributeName);
|
|
88
|
+
if (!attributeValue || !isPreferredSelectorAttribute(attributeName, attributeValue)) {
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
const attributeSelector = `[${attributeName}=${JSON.stringify(attributeValue)}]`;
|
|
92
|
+
if (isSelectorUniqueForElement(element, attributeSelector)) {
|
|
93
|
+
return attributeSelector;
|
|
94
|
+
}
|
|
95
|
+
const tagSelector = `${element.tagName.toLowerCase()}${attributeSelector}`;
|
|
96
|
+
if (isSelectorUniqueForElement(element, tagSelector)) {
|
|
97
|
+
return tagSelector;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
const SELECTOR_IDENTIFIER_QUERY = [
|
|
103
|
+
...PREFERRED_SELECTOR_ATTRIBUTE_NAMES.filter((name) => name !== "role").map(
|
|
104
|
+
(name) => `[${name}]`
|
|
105
|
+
),
|
|
106
|
+
...Array.from(ACTIONABLE_SELECTOR_ROLES).map((role) => `[role~="${role}"]`)
|
|
107
|
+
].join(",");
|
|
108
|
+
const GENERIC_SELECTOR_TARGET_QUERY = "button,input,select,textarea";
|
|
109
|
+
function hasSelectorIdentifier(element) {
|
|
110
|
+
const elementId = element.getAttribute("id");
|
|
111
|
+
return Boolean(
|
|
112
|
+
elementId && isStableElementId(elementId) || element.matches(SELECTOR_IDENTIFIER_QUERY)
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
function isSelectorTarget(element) {
|
|
116
|
+
return hasSelectorIdentifier(element) || element.matches(GENERIC_SELECTOR_TARGET_QUERY);
|
|
117
|
+
}
|
|
118
|
+
function isBroadSelectorTarget(element) {
|
|
119
|
+
const root = getSelectorRoot(element);
|
|
120
|
+
let bodyDescendantCount;
|
|
121
|
+
if (root instanceof Document) {
|
|
122
|
+
const { body, documentElement } = root;
|
|
123
|
+
if (element === body || element === documentElement) {
|
|
124
|
+
return true;
|
|
125
|
+
}
|
|
126
|
+
if (!body) {
|
|
127
|
+
return false;
|
|
128
|
+
}
|
|
129
|
+
bodyDescendantCount = body.getElementsByTagName("*").length;
|
|
130
|
+
} else {
|
|
131
|
+
bodyDescendantCount = root.querySelectorAll("*").length;
|
|
132
|
+
}
|
|
133
|
+
if (bodyDescendantCount === 0) {
|
|
134
|
+
return false;
|
|
135
|
+
}
|
|
136
|
+
return element.getElementsByTagName("*").length / bodyDescendantCount >= BROAD_SELECTOR_TARGET_DESCENDANT_RATIO;
|
|
137
|
+
}
|
|
138
|
+
function findSelectorTarget(element, isCandidateAccepted) {
|
|
139
|
+
let current = element;
|
|
140
|
+
while (current) {
|
|
141
|
+
if (isSelectorTarget(current)) {
|
|
142
|
+
const isBroad = isBroadSelectorTarget(current);
|
|
143
|
+
if (isBroad && current !== element) {
|
|
144
|
+
return element;
|
|
145
|
+
}
|
|
146
|
+
if (!isCandidateAccepted || isCandidateAccepted(current)) {
|
|
147
|
+
return current;
|
|
148
|
+
}
|
|
149
|
+
if (isBroad) {
|
|
150
|
+
return current;
|
|
151
|
+
}
|
|
152
|
+
if (!hasSelectorIdentifier(current) && current === element) {
|
|
153
|
+
return current;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
current = current.parentElement;
|
|
157
|
+
}
|
|
158
|
+
return element;
|
|
159
|
+
}
|
|
160
|
+
function buildRelativeNthOfTypePath(ancestor, element) {
|
|
161
|
+
const parts = [];
|
|
162
|
+
for (let current = element; current && current !== ancestor; current = current.parentElement) {
|
|
163
|
+
const tagName = current.tagName;
|
|
164
|
+
const parent = current.parentElement ?? current.parentNode;
|
|
165
|
+
const siblings = parent && "children" in parent ? Array.from(parent.children).filter((sibling) => {
|
|
166
|
+
return sibling.tagName === tagName;
|
|
167
|
+
}) : [];
|
|
168
|
+
parts.unshift(
|
|
169
|
+
siblings.length > 1 ? `${tagName.toLowerCase()}:nth-of-type(${siblings.indexOf(current) + 1})` : tagName.toLowerCase()
|
|
170
|
+
);
|
|
171
|
+
}
|
|
172
|
+
return parts.join(" > ");
|
|
173
|
+
}
|
|
174
|
+
function createNearestSemanticSelector(element) {
|
|
175
|
+
const ownSelector = createSemanticSelector(element);
|
|
176
|
+
if (ownSelector) {
|
|
177
|
+
return ownSelector;
|
|
178
|
+
}
|
|
179
|
+
const accepted = { selector: null };
|
|
180
|
+
const target = findSelectorTarget(element, (candidate) => {
|
|
181
|
+
accepted.selector = createSemanticSelector(candidate);
|
|
182
|
+
return accepted.selector !== null;
|
|
183
|
+
});
|
|
184
|
+
if (target === element || !accepted.selector) {
|
|
185
|
+
return null;
|
|
186
|
+
}
|
|
187
|
+
const selector = `${accepted.selector} > ${buildRelativeNthOfTypePath(target, element)}`;
|
|
188
|
+
return isSelectorUniqueForElement(element, selector) ? selector : null;
|
|
189
|
+
}
|
|
190
|
+
function isWordLike(text) {
|
|
191
|
+
if (!/^[a-z-]{3,}$/i.test(text)) {
|
|
192
|
+
return false;
|
|
193
|
+
}
|
|
194
|
+
return text.split(/-|[A-Z]/).every((segment) => {
|
|
195
|
+
return segment.length > 2 && !/[^aeiou]{4,}/i.test(segment);
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
function isAcceptedAttr(name, value) {
|
|
199
|
+
const nameIsAccepted = ACCEPTED_ATTR_NAMES.has(name) || name.startsWith("data-") && isWordLike(name);
|
|
200
|
+
const valueIsAccepted = isWordLike(value) && value.length < ATTR_VALUE_MAX_LENGTH_CHARS || value.startsWith("#") && isWordLike(value.slice(1));
|
|
201
|
+
return nameIsAccepted && valueIsAccepted;
|
|
202
|
+
}
|
|
203
|
+
function buildSelectorString(path) {
|
|
204
|
+
return path.map((node) => node.name).reverse().join(" > ");
|
|
205
|
+
}
|
|
206
|
+
function calculatePenalty(path) {
|
|
207
|
+
return path.reduce((total, node) => total + node.penalty, 0);
|
|
208
|
+
}
|
|
209
|
+
function getChildIndex(element, filterTagName) {
|
|
210
|
+
const parentNode = element.parentNode;
|
|
211
|
+
if (!parentNode) {
|
|
212
|
+
return void 0;
|
|
213
|
+
}
|
|
214
|
+
let position = 0;
|
|
215
|
+
for (const sibling of Array.from(parentNode.children)) {
|
|
216
|
+
if (filterTagName === void 0 || sibling.tagName.toLowerCase() === filterTagName) {
|
|
217
|
+
position++;
|
|
218
|
+
}
|
|
219
|
+
if (sibling === element) {
|
|
220
|
+
return position;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
return void 0;
|
|
224
|
+
}
|
|
225
|
+
function collectCandidateNodes(element) {
|
|
226
|
+
const candidates = [];
|
|
227
|
+
const elementId = element.getAttribute("id");
|
|
228
|
+
const tagName = element.tagName.toLowerCase();
|
|
229
|
+
if (elementId && isWordLike(elementId)) {
|
|
230
|
+
candidates.push({ name: `#${CSS.escape(elementId)}`, penalty: PENALTY_ID });
|
|
231
|
+
}
|
|
232
|
+
for (const className of Array.from(element.classList)) {
|
|
233
|
+
if (isWordLike(className)) {
|
|
234
|
+
candidates.push({
|
|
235
|
+
name: `.${CSS.escape(className)}`,
|
|
236
|
+
penalty: PENALTY_CLASS
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
for (const attribute of Array.from(element.attributes)) {
|
|
241
|
+
if (isAcceptedAttr(attribute.name, attribute.value) || isPreferredSelectorAttribute(attribute.name, attribute.value)) {
|
|
242
|
+
candidates.push({
|
|
243
|
+
name: `[${CSS.escape(attribute.name)}="${CSS.escape(attribute.value)}"]`,
|
|
244
|
+
penalty: PENALTY_ATTR
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
candidates.push({ name: tagName, penalty: PENALTY_TAG });
|
|
249
|
+
const typePosition = getChildIndex(element, tagName);
|
|
250
|
+
if (typePosition !== void 0) {
|
|
251
|
+
candidates.push({
|
|
252
|
+
name: tagName === "html" ? "html" : `${tagName}:nth-of-type(${typePosition})`,
|
|
253
|
+
penalty: PENALTY_NTH_OF_TYPE
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
const childPosition = getChildIndex(element);
|
|
257
|
+
if (childPosition !== void 0) {
|
|
258
|
+
candidates.push({
|
|
259
|
+
name: tagName === "html" ? "html" : `${tagName}:nth-child(${childPosition})`,
|
|
260
|
+
penalty: PENALTY_NTH_CHILD
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
return candidates;
|
|
264
|
+
}
|
|
265
|
+
function collectCombinations(stack, budget = MAX_SELECTOR_COMBINATIONS, currentPath = []) {
|
|
266
|
+
if (budget <= 0) {
|
|
267
|
+
return [];
|
|
268
|
+
}
|
|
269
|
+
const [level, ...rest] = stack;
|
|
270
|
+
if (!level) {
|
|
271
|
+
return [currentPath];
|
|
272
|
+
}
|
|
273
|
+
const results = [];
|
|
274
|
+
for (const node of level) {
|
|
275
|
+
const remainingBudget = budget - results.length;
|
|
276
|
+
if (remainingBudget <= 0) {
|
|
277
|
+
break;
|
|
278
|
+
}
|
|
279
|
+
results.push(
|
|
280
|
+
...collectCombinations(rest, remainingBudget, [...currentPath, node])
|
|
281
|
+
);
|
|
282
|
+
}
|
|
283
|
+
return results;
|
|
284
|
+
}
|
|
285
|
+
function isPathUnique(path, root) {
|
|
286
|
+
try {
|
|
287
|
+
return root.querySelectorAll(buildSelectorString(path)).length === 1;
|
|
288
|
+
} catch {
|
|
289
|
+
return false;
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
function findUniqueSelector(target, timeoutMs = FINDER_TIMEOUT_MS) {
|
|
293
|
+
if (target.tagName.toLowerCase() === "html") {
|
|
294
|
+
return "html";
|
|
295
|
+
}
|
|
296
|
+
const root = getSelectorRoot(target);
|
|
297
|
+
const stopElement = root instanceof Document ? root.documentElement : null;
|
|
298
|
+
const startTime = Date.now();
|
|
299
|
+
const ancestorStack = [];
|
|
300
|
+
let current = target;
|
|
301
|
+
let depth = 0;
|
|
302
|
+
const findIn = (paths) => {
|
|
303
|
+
paths.sort(
|
|
304
|
+
(pathA, pathB) => calculatePenalty(pathA) - calculatePenalty(pathB)
|
|
305
|
+
);
|
|
306
|
+
for (const path of paths) {
|
|
307
|
+
if (Date.now() - startTime > timeoutMs) {
|
|
308
|
+
return "timeout";
|
|
309
|
+
}
|
|
310
|
+
if (isPathUnique(path, root)) {
|
|
311
|
+
return path;
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
return null;
|
|
315
|
+
};
|
|
316
|
+
while (current && current !== stopElement) {
|
|
317
|
+
ancestorStack.push(collectCandidateNodes(current));
|
|
318
|
+
current = current.parentElement;
|
|
319
|
+
depth++;
|
|
320
|
+
if (depth >= SEED_MIN_LENGTH) {
|
|
321
|
+
const found = findIn(collectCombinations(ancestorStack));
|
|
322
|
+
if (found === "timeout") {
|
|
323
|
+
return null;
|
|
324
|
+
}
|
|
325
|
+
if (found) {
|
|
326
|
+
return buildSelectorString(found);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
if (depth < SEED_MIN_LENGTH) {
|
|
331
|
+
const found = findIn(collectCombinations(ancestorStack));
|
|
332
|
+
if (found && found !== "timeout") {
|
|
333
|
+
return buildSelectorString(found);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
return null;
|
|
337
|
+
}
|
|
338
|
+
function createNthChildSelector(element) {
|
|
339
|
+
const segments = [];
|
|
340
|
+
const body = element.ownerDocument.body;
|
|
341
|
+
let current = element;
|
|
342
|
+
while (current) {
|
|
343
|
+
const currentId = current.getAttribute("id");
|
|
344
|
+
if (currentId) {
|
|
345
|
+
segments.unshift(`#${CSS.escape(currentId)}`);
|
|
346
|
+
break;
|
|
347
|
+
}
|
|
348
|
+
const parent = current.parentElement;
|
|
349
|
+
if (!parent) {
|
|
350
|
+
segments.unshift(current.tagName.toLowerCase());
|
|
351
|
+
break;
|
|
352
|
+
}
|
|
353
|
+
const nthChild = Array.from(parent.children).indexOf(current) + 1;
|
|
354
|
+
segments.unshift(`${current.tagName.toLowerCase()}:nth-child(${nthChild})`);
|
|
355
|
+
if (parent === body) {
|
|
356
|
+
segments.unshift("body");
|
|
357
|
+
break;
|
|
358
|
+
}
|
|
359
|
+
current = parent;
|
|
360
|
+
}
|
|
361
|
+
return segments.join(" > ");
|
|
362
|
+
}
|
|
363
|
+
export {
|
|
364
|
+
FINDER_TIMEOUT_MS,
|
|
365
|
+
buildRelativeNthOfTypePath,
|
|
366
|
+
createNearestSemanticSelector,
|
|
367
|
+
createNthChildSelector,
|
|
368
|
+
createSemanticSelector,
|
|
369
|
+
findSelectorTarget,
|
|
370
|
+
findUniqueSelector,
|
|
371
|
+
isStableElementId
|
|
372
|
+
};
|
package/next/config.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { createHash } from "crypto";
|
|
2
|
+
import { existsSync, readFileSync } from "fs";
|
|
3
|
+
import { createRequire } from "module";
|
|
2
4
|
import path from "path";
|
|
3
5
|
const DEFAULT_PAGE_EXTENSIONS = ["tsx", "ts", "jsx", "js"];
|
|
4
6
|
const DEVELOPMENT_ONLY_PAGE_EXTENSIONS = ["dev.jsx", "dev.tsx"];
|
|
@@ -6,6 +8,33 @@ const BUILD_ONLY_PAGE_EXTENSIONS = ["published.tsx"];
|
|
|
6
8
|
const PUBLISH_TSCONFIG_FILENAME = "tsconfig.publish.json";
|
|
7
9
|
const PLATFORM_DEVICE_SIZES = [640, 828, 1080, 1200, 1920, 2048, 3840];
|
|
8
10
|
const PLATFORM_IMAGE_SIZES = [32, 48, 96, 128, 256, 384];
|
|
11
|
+
const VETTED_NEXT_POLYFILL_MODULE_SHA256 = "94c8008b70e41ac5b0360fd6f35ea7a59c20c6927aca7731ef8c6a3c26b9cddf";
|
|
12
|
+
function getPolyfillAlias() {
|
|
13
|
+
try {
|
|
14
|
+
const requireFromSite = createRequire(
|
|
15
|
+
path.join(process.cwd(), "package.json")
|
|
16
|
+
);
|
|
17
|
+
const source = readFileSync(
|
|
18
|
+
path.join(
|
|
19
|
+
path.dirname(requireFromSite.resolve("next/package.json")),
|
|
20
|
+
"dist/build/polyfills/polyfill-module.js"
|
|
21
|
+
)
|
|
22
|
+
);
|
|
23
|
+
requireFromSite.resolve("@replohq/sdk/next/polyfill-module");
|
|
24
|
+
if (createHash("sha256").update(source).digest("hex") === VETTED_NEXT_POLYFILL_MODULE_SHA256) {
|
|
25
|
+
return {
|
|
26
|
+
"../build/polyfills/polyfill-module": {
|
|
27
|
+
browser: "@replohq/sdk/next/polyfill-module"
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
} catch {
|
|
32
|
+
}
|
|
33
|
+
console.warn(
|
|
34
|
+
"[@replohq/sandbox-runtime] Skipped the Next.js polyfill alias, so Next's own polyfills ship. Either the installed @replohq/sdk has no next/polyfill-module (upgrade it), or next/dist/build/polyfills/polyfill-module.js is missing or differs from the vetted version (re-vet the polyfill list and update VETTED_NEXT_POLYFILL_MODULE_SHA256 in @replohq/sandbox-runtime/next/config)."
|
|
35
|
+
);
|
|
36
|
+
return {};
|
|
37
|
+
}
|
|
9
38
|
function getPageExtensions(configuredExtensions) {
|
|
10
39
|
const baseExtensions = (configuredExtensions ?? DEFAULT_PAGE_EXTENSIONS).filter((extension) => {
|
|
11
40
|
return !DEVELOPMENT_ONLY_PAGE_EXTENSIONS.includes(extension) && !BUILD_ONLY_PAGE_EXTENSIONS.includes(extension);
|
|
@@ -24,6 +53,13 @@ function withComponentPreviewPageExtension(config) {
|
|
|
24
53
|
...config,
|
|
25
54
|
pageExtensions: getPageExtensions(config.pageExtensions),
|
|
26
55
|
typescript: getTypescriptConfig(config.typescript),
|
|
56
|
+
turbopack: {
|
|
57
|
+
...config.turbopack,
|
|
58
|
+
resolveAlias: {
|
|
59
|
+
...getPolyfillAlias(),
|
|
60
|
+
...config.turbopack?.resolveAlias
|
|
61
|
+
}
|
|
62
|
+
},
|
|
27
63
|
images: {
|
|
28
64
|
...config.images,
|
|
29
65
|
deviceSizes: config.images?.deviceSizes ?? PLATFORM_DEVICE_SIZES,
|
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@replohq/sandbox-runtime",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.22.0",
|
|
4
4
|
"reploBuild": {
|
|
5
5
|
"packageName": "@replohq/sandbox-runtime",
|
|
6
|
-
"version": "1.
|
|
6
|
+
"version": "1.22.0",
|
|
7
7
|
"branch": "HEAD",
|
|
8
|
-
"commit": "
|
|
9
|
-
"builtAt": "2026-10-
|
|
8
|
+
"commit": "06e25b3341d1e79f6171365410c49bee41b1f3e0",
|
|
9
|
+
"builtAt": "2026-10-30T20:22:33.673Z"
|
|
10
10
|
},
|
|
11
11
|
"description": "Replo sandbox runtime — editor integration for agent-built Next.js sites.",
|
|
12
12
|
"license": "SEE LICENSE IN LICENSE",
|
package/sandbox-runtime.js
CHANGED
|
@@ -3,6 +3,7 @@ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
import { useViewportSnapshotResponder } from "./_vendor/replo-utils/react/use-viewport-snapshot-responder.mjs";
|
|
4
4
|
import { AnnotationLayerMount } from "./annotations/AnnotationLayerMount";
|
|
5
5
|
import { ClientErrorBoundary } from "./ClientErrorBoundary";
|
|
6
|
+
import { CommentsOverlayMount } from "./comments-overlay/CommentsOverlayMount";
|
|
6
7
|
import { DesignLibraryPreview } from "./design-library-preview/DesignLibraryPreview";
|
|
7
8
|
import { DocumentReadyBeacon } from "./DocumentReadyBeacon";
|
|
8
9
|
import { HeatLayerMount } from "./heatmaps/HeatLayerMount";
|
|
@@ -28,6 +29,7 @@ function SandboxRuntime({ children }) {
|
|
|
28
29
|
/* @__PURE__ */ jsx(PageCaptureResponder, {}),
|
|
29
30
|
/* @__PURE__ */ jsx(PagePreviewHeightResponder, {}),
|
|
30
31
|
/* @__PURE__ */ jsx(EditorSelection, {}),
|
|
32
|
+
/* @__PURE__ */ jsx(CommentsOverlayMount, {}),
|
|
31
33
|
/* @__PURE__ */ jsx(AnnotationLayerMount, {}),
|
|
32
34
|
/* @__PURE__ */ jsx(HeatLayerMount, {}),
|
|
33
35
|
/* @__PURE__ */ jsx(DesignLibraryPreview, {}),
|
|
@@ -5,9 +5,10 @@ import type { SelectableData, SelectModeClickPosition } from "../../_vendor/sche
|
|
|
5
5
|
*/
|
|
6
6
|
export declare function getNearestSelectableElement(element: Element): Element;
|
|
7
7
|
/**
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
8
|
+
* The cheap structural selector for selections, siblings, sections and override
|
|
9
|
+
* targets, so they stay comparable (e.g. `isSelectedSection`).
|
|
10
|
+
* Runs once per repeated sibling on every click, so it must not use the
|
|
11
|
+
* capture's uniqueness search, which costs up to 200ms per element.
|
|
11
12
|
*/
|
|
12
13
|
export declare function buildSelector(element: Element): string;
|
|
13
14
|
/**
|
|
@@ -4,6 +4,10 @@ import {
|
|
|
4
4
|
SELECT_MODE_SELECTABLE_PAYLOAD_ATTRIBUTE
|
|
5
5
|
} from "../../_vendor/schemas/selectMode.mjs";
|
|
6
6
|
import * as z from "zod";
|
|
7
|
+
import {
|
|
8
|
+
buildNthOfTypePath,
|
|
9
|
+
captureElementAnchor
|
|
10
|
+
} from "../../comments-overlay/capture";
|
|
7
11
|
import { getFiberChain } from "../fiber";
|
|
8
12
|
import { getImageDescriptor } from "../imageSources";
|
|
9
13
|
import { skipInlineTextMarkElements } from "../inlineTextMarks";
|
|
@@ -155,34 +159,19 @@ function identifyElement({
|
|
|
155
159
|
return tagName;
|
|
156
160
|
}
|
|
157
161
|
function buildSelector(element) {
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
parts.unshift(`#${CSS.escape(id)}`);
|
|
165
|
-
break;
|
|
166
|
-
}
|
|
167
|
-
const parentElement = currentElement.parentElement;
|
|
168
|
-
if (parentElement) {
|
|
169
|
-
const currentTag = currentElement.tagName;
|
|
170
|
-
const siblings = Array.from(parentElement.children);
|
|
171
|
-
const siblingElementsWithSameTag = siblings.filter((sibling) => {
|
|
172
|
-
return sibling.tagName === currentTag;
|
|
173
|
-
});
|
|
174
|
-
if (siblingElementsWithSameTag.length > 1) {
|
|
175
|
-
const index = siblingElementsWithSameTag.indexOf(currentElement) + 1;
|
|
176
|
-
parts.unshift(`${tag}:nth-of-type(${index})`);
|
|
177
|
-
} else {
|
|
178
|
-
parts.unshift(tag);
|
|
179
|
-
}
|
|
180
|
-
} else {
|
|
181
|
-
parts.unshift(tag);
|
|
182
|
-
}
|
|
183
|
-
currentElement = parentElement;
|
|
162
|
+
return buildNthOfTypePath(element);
|
|
163
|
+
}
|
|
164
|
+
function getAnchorSourceLocation(anchor) {
|
|
165
|
+
const source = anchor.source;
|
|
166
|
+
if (!source || source.line === void 0) {
|
|
167
|
+
return void 0;
|
|
184
168
|
}
|
|
185
|
-
return
|
|
169
|
+
return {
|
|
170
|
+
filePath: source.filePath,
|
|
171
|
+
lineNumber: source.line,
|
|
172
|
+
...source.col === void 0 ? {} : { columnNumber: source.col },
|
|
173
|
+
...source.componentChain[0] ? { componentName: source.componentChain[0] } : {}
|
|
174
|
+
};
|
|
186
175
|
}
|
|
187
176
|
function getTextMetrics(element) {
|
|
188
177
|
const text = element.textContent?.trim();
|
|
@@ -228,10 +217,10 @@ function buildElementDescriptor({
|
|
|
228
217
|
const video = z.instanceof(HTMLVideoElement).safeParse(element);
|
|
229
218
|
const textMetrics = getTextMetrics(element);
|
|
230
219
|
const fiberChain = element instanceof HTMLElement ? getFiberChain(element) : [];
|
|
231
|
-
const sourceLocation = fiberChain[0]?.sourceLocation;
|
|
232
220
|
const trimmedText = getRenderedText(element);
|
|
233
221
|
const sectionLabel = findSectionLabel(section);
|
|
234
222
|
const { definedStyles, authoredStyles } = getElementStyles(element);
|
|
223
|
+
const anchor = captureElementAnchor(element, clickPosition);
|
|
235
224
|
return {
|
|
236
225
|
kind: "react-element",
|
|
237
226
|
selector: buildSelector(element),
|
|
@@ -251,10 +240,11 @@ function buildElementDescriptor({
|
|
|
251
240
|
authoredStyles,
|
|
252
241
|
inheritedStyles: getInheritedStyles(element),
|
|
253
242
|
clickPosition,
|
|
254
|
-
sourceLocation,
|
|
243
|
+
sourceLocation: getAnchorSourceLocation(anchor),
|
|
255
244
|
fiberChain,
|
|
256
245
|
siblingSelectors,
|
|
257
|
-
...section ? { section: { selector: buildSelector(section) } } : {}
|
|
246
|
+
...section ? { section: { selector: buildSelector(section) } } : {},
|
|
247
|
+
anchor
|
|
258
248
|
};
|
|
259
249
|
}
|
|
260
250
|
export {
|
|
@@ -134,6 +134,34 @@ export declare function getSelectModeIframeRpcChannel(): import("../_vendor/sche
|
|
|
134
134
|
props: import("zod").ZodOptional<import("zod").ZodRecord<import("zod").ZodString, import("zod").ZodUnknown>>;
|
|
135
135
|
}, import("zod/v4/core").$strip>>>;
|
|
136
136
|
siblingSelectors: import("zod").ZodDefault<import("zod").ZodArray<import("zod").ZodString>>;
|
|
137
|
+
anchor: import("zod").ZodOptional<import("zod").ZodObject<{
|
|
138
|
+
type: import("zod").ZodLiteral<"element">;
|
|
139
|
+
semanticSelector: import("zod").ZodOptional<import("zod").ZodString>;
|
|
140
|
+
selector: import("zod").ZodString;
|
|
141
|
+
nthOfTypePath: import("zod").ZodString;
|
|
142
|
+
source: import("zod").ZodOptional<import("zod").ZodObject<{
|
|
143
|
+
filePath: import("zod").ZodString;
|
|
144
|
+
componentChain: import("zod").ZodArray<import("zod").ZodString>;
|
|
145
|
+
line: import("zod").ZodOptional<import("zod").ZodNumber>;
|
|
146
|
+
col: import("zod").ZodOptional<import("zod").ZodNumber>;
|
|
147
|
+
}, import("zod/v4/core").$strip>>;
|
|
148
|
+
listKey: import("zod").ZodOptional<import("zod").ZodString>;
|
|
149
|
+
tagName: import("zod").ZodString;
|
|
150
|
+
textPreview: import("zod").ZodString;
|
|
151
|
+
quote: import("zod").ZodOptional<import("zod").ZodObject<{
|
|
152
|
+
exact: import("zod").ZodString;
|
|
153
|
+
prefix: import("zod").ZodString;
|
|
154
|
+
suffix: import("zod").ZodString;
|
|
155
|
+
}, import("zod/v4/core").$strip>>;
|
|
156
|
+
offset: import("zod").ZodObject<{
|
|
157
|
+
x: import("zod").ZodNumber;
|
|
158
|
+
y: import("zod").ZodNumber;
|
|
159
|
+
}, import("zod/v4/core").$strip>;
|
|
160
|
+
context: import("zod").ZodOptional<import("zod").ZodObject<{
|
|
161
|
+
url: import("zod").ZodString;
|
|
162
|
+
locale: import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodString>>;
|
|
163
|
+
}, import("zod/v4/core").$strip>>;
|
|
164
|
+
}, import("zod/v4/core").$strip>>;
|
|
137
165
|
}, import("zod/v4/core").$strip>;
|
|
138
166
|
}, import("zod/v4/core").$strip>;
|
|
139
167
|
outputSchema: import("zod").ZodObject<{
|
|
@@ -235,6 +263,34 @@ export declare function getSelectModeIframeRpcChannel(): import("../_vendor/sche
|
|
|
235
263
|
props: import("zod").ZodOptional<import("zod").ZodRecord<import("zod").ZodString, import("zod").ZodUnknown>>;
|
|
236
264
|
}, import("zod/v4/core").$strip>>>;
|
|
237
265
|
siblingSelectors: import("zod").ZodDefault<import("zod").ZodArray<import("zod").ZodString>>;
|
|
266
|
+
anchor: import("zod").ZodOptional<import("zod").ZodObject<{
|
|
267
|
+
type: import("zod").ZodLiteral<"element">;
|
|
268
|
+
semanticSelector: import("zod").ZodOptional<import("zod").ZodString>;
|
|
269
|
+
selector: import("zod").ZodString;
|
|
270
|
+
nthOfTypePath: import("zod").ZodString;
|
|
271
|
+
source: import("zod").ZodOptional<import("zod").ZodObject<{
|
|
272
|
+
filePath: import("zod").ZodString;
|
|
273
|
+
componentChain: import("zod").ZodArray<import("zod").ZodString>;
|
|
274
|
+
line: import("zod").ZodOptional<import("zod").ZodNumber>;
|
|
275
|
+
col: import("zod").ZodOptional<import("zod").ZodNumber>;
|
|
276
|
+
}, import("zod/v4/core").$strip>>;
|
|
277
|
+
listKey: import("zod").ZodOptional<import("zod").ZodString>;
|
|
278
|
+
tagName: import("zod").ZodString;
|
|
279
|
+
textPreview: import("zod").ZodString;
|
|
280
|
+
quote: import("zod").ZodOptional<import("zod").ZodObject<{
|
|
281
|
+
exact: import("zod").ZodString;
|
|
282
|
+
prefix: import("zod").ZodString;
|
|
283
|
+
suffix: import("zod").ZodString;
|
|
284
|
+
}, import("zod/v4/core").$strip>>;
|
|
285
|
+
offset: import("zod").ZodObject<{
|
|
286
|
+
x: import("zod").ZodNumber;
|
|
287
|
+
y: import("zod").ZodNumber;
|
|
288
|
+
}, import("zod/v4/core").$strip>;
|
|
289
|
+
context: import("zod").ZodOptional<import("zod").ZodObject<{
|
|
290
|
+
url: import("zod").ZodString;
|
|
291
|
+
locale: import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodString>>;
|
|
292
|
+
}, import("zod/v4/core").$strip>>;
|
|
293
|
+
}, import("zod/v4/core").$strip>>;
|
|
238
294
|
}, import("zod/v4/core").$strip>, import("zod").ZodObject<{
|
|
239
295
|
kind: import("zod").ZodLiteral<"plan">;
|
|
240
296
|
planId: import("zod").ZodString;
|
|
@@ -424,6 +480,34 @@ export declare function getSelectModeIframeRpcChannel(): import("../_vendor/sche
|
|
|
424
480
|
props: import("zod").ZodOptional<import("zod").ZodRecord<import("zod").ZodString, import("zod").ZodUnknown>>;
|
|
425
481
|
}, import("zod/v4/core").$strip>>>;
|
|
426
482
|
siblingSelectors: import("zod").ZodDefault<import("zod").ZodArray<import("zod").ZodString>>;
|
|
483
|
+
anchor: import("zod").ZodOptional<import("zod").ZodObject<{
|
|
484
|
+
type: import("zod").ZodLiteral<"element">;
|
|
485
|
+
semanticSelector: import("zod").ZodOptional<import("zod").ZodString>;
|
|
486
|
+
selector: import("zod").ZodString;
|
|
487
|
+
nthOfTypePath: import("zod").ZodString;
|
|
488
|
+
source: import("zod").ZodOptional<import("zod").ZodObject<{
|
|
489
|
+
filePath: import("zod").ZodString;
|
|
490
|
+
componentChain: import("zod").ZodArray<import("zod").ZodString>;
|
|
491
|
+
line: import("zod").ZodOptional<import("zod").ZodNumber>;
|
|
492
|
+
col: import("zod").ZodOptional<import("zod").ZodNumber>;
|
|
493
|
+
}, import("zod/v4/core").$strip>>;
|
|
494
|
+
listKey: import("zod").ZodOptional<import("zod").ZodString>;
|
|
495
|
+
tagName: import("zod").ZodString;
|
|
496
|
+
textPreview: import("zod").ZodString;
|
|
497
|
+
quote: import("zod").ZodOptional<import("zod").ZodObject<{
|
|
498
|
+
exact: import("zod").ZodString;
|
|
499
|
+
prefix: import("zod").ZodString;
|
|
500
|
+
suffix: import("zod").ZodString;
|
|
501
|
+
}, import("zod/v4/core").$strip>>;
|
|
502
|
+
offset: import("zod").ZodObject<{
|
|
503
|
+
x: import("zod").ZodNumber;
|
|
504
|
+
y: import("zod").ZodNumber;
|
|
505
|
+
}, import("zod/v4/core").$strip>;
|
|
506
|
+
context: import("zod").ZodOptional<import("zod").ZodObject<{
|
|
507
|
+
url: import("zod").ZodString;
|
|
508
|
+
locale: import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodString>>;
|
|
509
|
+
}, import("zod/v4/core").$strip>>;
|
|
510
|
+
}, import("zod/v4/core").$strip>>;
|
|
427
511
|
}, import("zod/v4/core").$strip>, import("zod").ZodObject<{
|
|
428
512
|
kind: import("zod").ZodLiteral<"plan">;
|
|
429
513
|
planId: import("zod").ZodString;
|