@replohq/sandbox-runtime 1.6.5 → 1.6.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/_vendor/replo-utils/lib/viewportSnapshot.d.ts +86 -0
- package/_vendor/replo-utils/lib/viewportSnapshot.mjs +1 -0
- package/_vendor/replo-utils/react/use-viewport-snapshot-responder.d.ts +9 -0
- package/_vendor/replo-utils/react/use-viewport-snapshot-responder.mjs +5262 -0
- package/buildMetadata.js +3 -3
- package/chunk-RXYLMSNM.mjs +134 -0
- package/package.json +4 -4
- package/sandbox-runtime.js +6 -0
- package/sandbox-runtime.js.map +2 -2
package/buildMetadata.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export const BUILD_METADATA = {
|
|
2
2
|
"packageName": "@replohq/sandbox-runtime",
|
|
3
|
-
"version": "1.6.
|
|
3
|
+
"version": "1.6.6",
|
|
4
4
|
"branch": "HEAD",
|
|
5
|
-
"commit": "
|
|
6
|
-
"builtAt": "2026-09-
|
|
5
|
+
"commit": "0a9a7d328a712b8935e4b6a199689919c3ec4392",
|
|
6
|
+
"builtAt": "2026-09-13T12:48:42.823Z"
|
|
7
7
|
};
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
// ../replo-utils/lib/viewportSnapshot.ts
|
|
2
|
+
var VIEWPORT_SNAPSHOT_REQUEST = "replo:viewport-snapshot:request";
|
|
3
|
+
var VIEWPORT_SNAPSHOT_RESPONSE = "replo:viewport-snapshot:response";
|
|
4
|
+
var VIEWPORT_SNAPSHOT_MAX_DEPTH = 2;
|
|
5
|
+
function isTrustedViewportSnapshotRequester(origin) {
|
|
6
|
+
let hostname;
|
|
7
|
+
try {
|
|
8
|
+
hostname = new URL(origin).hostname;
|
|
9
|
+
} catch {
|
|
10
|
+
return false;
|
|
11
|
+
}
|
|
12
|
+
if (hostname === "replo.app" || hostname.endsWith(".replo.app")) {
|
|
13
|
+
return true;
|
|
14
|
+
}
|
|
15
|
+
if (process.env.NODE_ENV === "production") {
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
return hostname === "localhost" || hostname === "127.0.0.1" || hostname.endsWith(".ngrok.app");
|
|
19
|
+
}
|
|
20
|
+
function canReceiveMessages(value) {
|
|
21
|
+
return typeof value === "object" && value !== null && "postMessage" in value && typeof value.postMessage === "function";
|
|
22
|
+
}
|
|
23
|
+
function isResponseMessage(data) {
|
|
24
|
+
return typeof data === "object" && data !== null && "type" in data && data.type === VIEWPORT_SNAPSHOT_RESPONSE && "requestId" in data && typeof data.requestId === "string" && "frameIndex" in data && typeof data.frameIndex === "number";
|
|
25
|
+
}
|
|
26
|
+
function isRequestMessage(data) {
|
|
27
|
+
return typeof data === "object" && data !== null && "type" in data && data.type === VIEWPORT_SNAPSHOT_REQUEST && "requestId" in data && typeof data.requestId === "string" && "frameIndex" in data && typeof data.frameIndex === "number" && "remainingDepth" in data && typeof data.remainingDepth === "number";
|
|
28
|
+
}
|
|
29
|
+
function requestChildViewportSnapshots({
|
|
30
|
+
targetWindow,
|
|
31
|
+
requestId,
|
|
32
|
+
timeoutMs,
|
|
33
|
+
remainingDepth = VIEWPORT_SNAPSHOT_MAX_DEPTH
|
|
34
|
+
}) {
|
|
35
|
+
const frameCount = targetWindow.frames.length;
|
|
36
|
+
if (frameCount === 0 || remainingDepth <= 0) {
|
|
37
|
+
return Promise.resolve([]);
|
|
38
|
+
}
|
|
39
|
+
return new Promise((resolve) => {
|
|
40
|
+
const collected = /* @__PURE__ */ new Map();
|
|
41
|
+
const settle = () => {
|
|
42
|
+
targetWindow.removeEventListener("message", onMessage);
|
|
43
|
+
targetWindow.clearTimeout(timeoutId);
|
|
44
|
+
resolve(
|
|
45
|
+
[...collected.entries()].map(([frameIndex, capture]) => ({ frameIndex, capture })).sort((first, second) => {
|
|
46
|
+
return first.frameIndex - second.frameIndex;
|
|
47
|
+
})
|
|
48
|
+
);
|
|
49
|
+
};
|
|
50
|
+
const onMessage = (event) => {
|
|
51
|
+
if (!isResponseMessage(event.data) || event.data.requestId !== requestId || // A frame may only answer for its own slot — anything else could
|
|
52
|
+
// overwrite a sibling's capture or force an early settle.
|
|
53
|
+
event.source !== targetWindow.frames[event.data.frameIndex]) {
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
if (event.data.capture !== null) {
|
|
57
|
+
collected.set(event.data.frameIndex, event.data.capture);
|
|
58
|
+
}
|
|
59
|
+
if (collected.size === frameCount) {
|
|
60
|
+
settle();
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
targetWindow.addEventListener("message", onMessage);
|
|
64
|
+
const timeoutId = targetWindow.setTimeout(settle, timeoutMs);
|
|
65
|
+
for (let frameIndex = 0; frameIndex < frameCount; frameIndex++) {
|
|
66
|
+
const message = {
|
|
67
|
+
type: VIEWPORT_SNAPSHOT_REQUEST,
|
|
68
|
+
requestId,
|
|
69
|
+
frameIndex,
|
|
70
|
+
remainingDepth
|
|
71
|
+
};
|
|
72
|
+
targetWindow.frames[frameIndex]?.postMessage(message, "*");
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
function startViewportSnapshotResponder({
|
|
77
|
+
targetWindow,
|
|
78
|
+
takeSnapshot,
|
|
79
|
+
measureFrameRect,
|
|
80
|
+
isTrustedRequester = isTrustedViewportSnapshotRequester,
|
|
81
|
+
cascadeTimeoutMs = 1500
|
|
82
|
+
}) {
|
|
83
|
+
if (targetWindow.parent === targetWindow) {
|
|
84
|
+
return () => {
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
const onMessage = (event) => {
|
|
88
|
+
if (!isRequestMessage(event.data) || event.source !== targetWindow.parent || !canReceiveMessages(event.source) || !isTrustedRequester(event.origin ?? "")) {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
const { requestId, frameIndex, remainingDepth } = event.data;
|
|
92
|
+
const requester = event.source;
|
|
93
|
+
const requesterOrigin = event.origin ?? "*";
|
|
94
|
+
const respond = (capture) => {
|
|
95
|
+
const response = {
|
|
96
|
+
type: VIEWPORT_SNAPSHOT_RESPONSE,
|
|
97
|
+
requestId,
|
|
98
|
+
frameIndex,
|
|
99
|
+
capture
|
|
100
|
+
};
|
|
101
|
+
requester.postMessage(response, requesterOrigin);
|
|
102
|
+
};
|
|
103
|
+
let snapshot = null;
|
|
104
|
+
try {
|
|
105
|
+
snapshot = takeSnapshot();
|
|
106
|
+
} catch {
|
|
107
|
+
}
|
|
108
|
+
if (snapshot === null) {
|
|
109
|
+
respond(null);
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
void requestChildViewportSnapshots({
|
|
113
|
+
targetWindow,
|
|
114
|
+
// Derived per level so a child's own fan-out can't collide with ours.
|
|
115
|
+
requestId: `${requestId}:${frameIndex}`,
|
|
116
|
+
timeoutMs: cascadeTimeoutMs,
|
|
117
|
+
remainingDepth: remainingDepth - 1
|
|
118
|
+
}).then((childCaptures) => {
|
|
119
|
+
respond({
|
|
120
|
+
snapshot,
|
|
121
|
+
childFrames: childCaptures.flatMap((child) => {
|
|
122
|
+
const rect = measureFrameRect(child.frameIndex);
|
|
123
|
+
return rect ? [{ frameIndex: child.frameIndex, rect, capture: child.capture }] : [];
|
|
124
|
+
})
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
};
|
|
128
|
+
targetWindow.addEventListener("message", onMessage);
|
|
129
|
+
return () => {
|
|
130
|
+
return targetWindow.removeEventListener("message", onMessage);
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export { VIEWPORT_SNAPSHOT_MAX_DEPTH, VIEWPORT_SNAPSHOT_REQUEST, VIEWPORT_SNAPSHOT_RESPONSE, isTrustedViewportSnapshotRequester, requestChildViewportSnapshots, startViewportSnapshotResponder };
|
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@replohq/sandbox-runtime",
|
|
3
|
-
"version": "1.6.
|
|
3
|
+
"version": "1.6.6",
|
|
4
4
|
"reploBuild": {
|
|
5
5
|
"packageName": "@replohq/sandbox-runtime",
|
|
6
|
-
"version": "1.6.
|
|
6
|
+
"version": "1.6.6",
|
|
7
7
|
"branch": "HEAD",
|
|
8
|
-
"commit": "
|
|
9
|
-
"builtAt": "2026-09-
|
|
8
|
+
"commit": "0a9a7d328a712b8935e4b6a199689919c3ec4392",
|
|
9
|
+
"builtAt": "2026-09-13T12:48:42.823Z"
|
|
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
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useViewportSnapshotResponder } from "./_vendor/replo-utils/react/use-viewport-snapshot-responder.mjs";
|
|
3
4
|
import { AnnotationLayerMount } from "./annotations/AnnotationLayerMount";
|
|
4
5
|
import { ClientErrorBoundary } from "./ClientErrorBoundary";
|
|
5
6
|
import { DesignLibraryPreview } from "./design-library-preview/DesignLibraryPreview";
|
|
@@ -9,6 +10,10 @@ import { NavigationReceiver } from "./NavigationReceiver";
|
|
|
9
10
|
import { NextErrorReporter } from "./NextErrorReporter";
|
|
10
11
|
import PostHogRelay from "./posthog-relay";
|
|
11
12
|
import { EditorSelection } from "./select-mode/EditorSelection";
|
|
13
|
+
function ViewportSnapshotResponder() {
|
|
14
|
+
useViewportSnapshotResponder();
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
12
17
|
function SandboxRuntime({ children }) {
|
|
13
18
|
const isDevServer = process.env.NODE_ENV === "development";
|
|
14
19
|
return isDevServer ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
@@ -18,6 +23,7 @@ function SandboxRuntime({ children }) {
|
|
|
18
23
|
/* @__PURE__ */ jsx(LinkInterceptor, {}),
|
|
19
24
|
/* @__PURE__ */ jsx(NavigationReceiver, {}),
|
|
20
25
|
/* @__PURE__ */ jsx(PostHogRelay, {}),
|
|
26
|
+
/* @__PURE__ */ jsx(ViewportSnapshotResponder, {}),
|
|
21
27
|
/* @__PURE__ */ jsx(EditorSelection, {}),
|
|
22
28
|
/* @__PURE__ */ jsx(AnnotationLayerMount, {}),
|
|
23
29
|
/* @__PURE__ */ jsx(DesignLibraryPreview, {}),
|
package/sandbox-runtime.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../sandbox-runtime.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\";\n\nimport { AnnotationLayerMount } from \"./annotations/AnnotationLayerMount\";\nimport { ClientErrorBoundary } from \"./ClientErrorBoundary\";\nimport { DesignLibraryPreview } from \"./design-library-preview/DesignLibraryPreview\";\nimport { DocumentReadyBeacon } from \"./DocumentReadyBeacon\";\nimport LinkInterceptor from \"./LinkInterceptor\";\nimport { NavigationReceiver } from \"./NavigationReceiver\";\nimport { NextErrorReporter } from \"./NextErrorReporter\";\nimport PostHogRelay from \"./posthog-relay\";\nimport { EditorSelection } from \"./select-mode/EditorSelection\";\n\nexport function SandboxRuntime({ children }: React.PropsWithChildren) {\n // The editor only embeds dev servers (`next dev`), so editor-integration components render there only \u2014 production builds ship none of them and behave like plain Next apps.\n const isDevServer = process.env.NODE_ENV === \"development\";\n\n return isDevServer ? (\n <>\n <DocumentReadyBeacon />\n {/* NOTE (Max, 2026-06-08): Outside ClientErrorBoundary on purpose \u2014 when\n the boundary catches a runtime error it swaps its children for a\n neutral cover, which would unmount the reporter and freeze it in the\n error state. It must stay mounted to observe Next's overlay across\n error <-> recover. */}\n <NextErrorReporter />\n <ClientErrorBoundary>\n <LinkInterceptor />\n <NavigationReceiver />\n <PostHogRelay />\n <EditorSelection />\n <AnnotationLayerMount />\n <DesignLibraryPreview />\n {children}\n </ClientErrorBoundary>\n </>\n ) : (\n children\n );\n}\n"],
|
|
5
|
-
"mappings": ";
|
|
4
|
+
"sourcesContent": ["\"use client\";\n\nimport { useViewportSnapshotResponder } from \"replo-utils/react/use-viewport-snapshot-responder\";\n\nimport { AnnotationLayerMount } from \"./annotations/AnnotationLayerMount\";\nimport { ClientErrorBoundary } from \"./ClientErrorBoundary\";\nimport { DesignLibraryPreview } from \"./design-library-preview/DesignLibraryPreview\";\nimport { DocumentReadyBeacon } from \"./DocumentReadyBeacon\";\nimport LinkInterceptor from \"./LinkInterceptor\";\nimport { NavigationReceiver } from \"./NavigationReceiver\";\nimport { NextErrorReporter } from \"./NextErrorReporter\";\nimport PostHogRelay from \"./posthog-relay\";\nimport { EditorSelection } from \"./select-mode/EditorSelection\";\n\nfunction ViewportSnapshotResponder() {\n useViewportSnapshotResponder();\n return null;\n}\n\nexport function SandboxRuntime({ children }: React.PropsWithChildren) {\n // The editor only embeds dev servers (`next dev`), so editor-integration components render there only \u2014 production builds ship none of them and behave like plain Next apps.\n const isDevServer = process.env.NODE_ENV === \"development\";\n\n return isDevServer ? (\n <>\n <DocumentReadyBeacon />\n {/* NOTE (Max, 2026-06-08): Outside ClientErrorBoundary on purpose \u2014 when\n the boundary catches a runtime error it swaps its children for a\n neutral cover, which would unmount the reporter and freeze it in the\n error state. It must stay mounted to observe Next's overlay across\n error <-> recover. */}\n <NextErrorReporter />\n <ClientErrorBoundary>\n <LinkInterceptor />\n <NavigationReceiver />\n <PostHogRelay />\n <ViewportSnapshotResponder />\n <EditorSelection />\n <AnnotationLayerMount />\n <DesignLibraryPreview />\n {children}\n </ClientErrorBoundary>\n </>\n ) : (\n children\n );\n}\n"],
|
|
5
|
+
"mappings": ";AAwBI,mBACE,KAOA,YARF;AAtBJ,SAAS,oCAAoC;AAE7C,SAAS,4BAA4B;AACrC,SAAS,2BAA2B;AACpC,SAAS,4BAA4B;AACrC,SAAS,2BAA2B;AACpC,OAAO,qBAAqB;AAC5B,SAAS,0BAA0B;AACnC,SAAS,yBAAyB;AAClC,OAAO,kBAAkB;AACzB,SAAS,uBAAuB;AAEhC,SAAS,4BAA4B;AACnC,+BAA6B;AAC7B,SAAO;AACT;AAEO,SAAS,eAAe,EAAE,SAAS,GAA4B;AAEpE,QAAM,cAAc,QAAQ,IAAI,aAAa;AAE7C,SAAO,cACL,iCACE;AAAA,wBAAC,uBAAoB;AAAA,IAMrB,oBAAC,qBAAkB;AAAA,IACnB,qBAAC,uBACC;AAAA,0BAAC,mBAAgB;AAAA,MACjB,oBAAC,sBAAmB;AAAA,MACpB,oBAAC,gBAAa;AAAA,MACd,oBAAC,6BAA0B;AAAA,MAC3B,oBAAC,mBAAgB;AAAA,MACjB,oBAAC,wBAAqB;AAAA,MACtB,oBAAC,wBAAqB;AAAA,MACrB;AAAA,OACH;AAAA,KACF,IAEA;AAEJ;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|