@replohq/sandbox-runtime 1.0.1 → 1.1.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/componentPreview.d.ts +10 -2
- package/_vendor/schemas/componentPreview.mjs +13 -4
- package/_vendor/schemas/generated/selectMode.d.ts +1 -1
- package/_vendor/schemas/selectMode.d.ts +1 -0
- package/_vendor/schemas/selectMode.mjs +1 -1
- package/_vendor/schemas/selectModeIframeRpc.mjs +1 -1
- package/buildMetadata.js +3 -3
- package/{chunk-3UFG7XTQ.mjs → chunk-P5NWJM64.mjs} +1 -1
- package/next/component-preview-client.d.ts +8 -0
- package/next/component-preview-client.js +174 -0
- package/next/component-preview-client.js.map +7 -0
- package/next/component-preview-props.d.ts +8 -0
- package/next/component-preview-props.js +36 -0
- package/next/component-preview-props.js.map +7 -0
- package/next/component-preview.d.ts +3 -1
- package/next/component-preview.js +32 -21
- package/next/component-preview.js.map +2 -2
- package/package.json +8 -4
- package/next/component-preview-navigation.d.ts +0 -1
- package/next/component-preview-navigation.js +0 -66
- package/next/component-preview-navigation.js.map +0 -7
|
@@ -3,14 +3,22 @@ export declare const MAX_COMPONENT_PREVIEW_PROPS_BYTES: number;
|
|
|
3
3
|
export declare const componentPreviewPropNameSchema: z.ZodString;
|
|
4
4
|
export declare const componentPreviewPropsSchema: z.ZodRecord<z.ZodString, z.ZodJSONSchema>;
|
|
5
5
|
export declare const COMPONENT_PREVIEW_IFRAME_RPC_CHANNEL_ID: "replo:component-preview-iframe-rpc";
|
|
6
|
-
export declare const COMPONENT_PREVIEW_IFRAME_RPC_CHANNEL_VERSION:
|
|
6
|
+
export declare const COMPONENT_PREVIEW_IFRAME_RPC_CHANNEL_VERSION: 2;
|
|
7
|
+
export declare const COMPONENT_PREVIEW_PARENT_ORIGIN_PARAM = "reploParentOrigin";
|
|
7
8
|
export declare const websiteBuilderToComponentPreviewMethodConfig: {
|
|
8
9
|
setProps: {
|
|
9
10
|
inputSchema: z.ZodObject<{
|
|
10
11
|
props: z.ZodRecord<z.ZodString, z.ZodJSONSchema>;
|
|
11
12
|
sourceRevision: z.ZodString;
|
|
12
13
|
}, z.core.$strip>;
|
|
13
|
-
outputSchema: z.
|
|
14
|
+
outputSchema: z.ZodObject<{
|
|
15
|
+
mode: z.ZodEnum<{
|
|
16
|
+
client: "client";
|
|
17
|
+
navigation: "navigation";
|
|
18
|
+
unchanged: "unchanged";
|
|
19
|
+
}>;
|
|
20
|
+
}, z.core.$strip>;
|
|
21
|
+
timeoutMs: number;
|
|
14
22
|
};
|
|
15
23
|
};
|
|
16
24
|
export declare const componentPreviewToWebsiteBuilderMethodConfig: {
|
|
@@ -32,16 +32,25 @@ var componentPreviewPropNameSchema = z.string().min(1).max(80).regex(/^[A-Za-z_$
|
|
|
32
32
|
name
|
|
33
33
|
);
|
|
34
34
|
}, "Unsafe prop name");
|
|
35
|
-
var componentPreviewPropsSchema = z.record(
|
|
35
|
+
var componentPreviewPropsSchema = z.record(
|
|
36
|
+
componentPreviewPropNameSchema,
|
|
37
|
+
z.json()
|
|
38
|
+
);
|
|
36
39
|
var COMPONENT_PREVIEW_IFRAME_RPC_CHANNEL_ID = "replo:component-preview-iframe-rpc";
|
|
37
|
-
var COMPONENT_PREVIEW_IFRAME_RPC_CHANNEL_VERSION =
|
|
40
|
+
var COMPONENT_PREVIEW_IFRAME_RPC_CHANNEL_VERSION = 2;
|
|
41
|
+
var COMPONENT_PREVIEW_PARENT_ORIGIN_PARAM = "reploParentOrigin";
|
|
38
42
|
var websiteBuilderToComponentPreviewMethodConfig = {
|
|
39
43
|
setProps: {
|
|
40
44
|
inputSchema: z.object({
|
|
41
45
|
props: componentPreviewPropsSchema,
|
|
42
46
|
sourceRevision: z.string().min(1)
|
|
43
47
|
}),
|
|
44
|
-
|
|
48
|
+
// How the frame applied the update: in place via the client prop store,
|
|
49
|
+
// via an in-frame server navigation, or not at all (already current).
|
|
50
|
+
outputSchema: z.object({
|
|
51
|
+
mode: z.enum(["client", "navigation", "unchanged"])
|
|
52
|
+
}),
|
|
53
|
+
timeoutMs: 1e4
|
|
45
54
|
}
|
|
46
55
|
};
|
|
47
56
|
var componentPreviewToWebsiteBuilderMethodConfig = {
|
|
@@ -94,4 +103,4 @@ var componentPreviewPreparationSchema = z.discriminatedUnion("status", [
|
|
|
94
103
|
z.object({ status: z.literal("siteUpdateRequired") })
|
|
95
104
|
]).describe("ComponentPreviewPreparation");
|
|
96
105
|
|
|
97
|
-
export { COMPONENT_PREVIEW_IFRAME_RPC_CHANNEL_ID, COMPONENT_PREVIEW_IFRAME_RPC_CHANNEL_VERSION, MAX_COMPONENT_PREVIEW_PROPS_BYTES, componentPreviewComponentSchema, componentPreviewIndexSchema, componentPreviewPathSchema, componentPreviewPreparationSchema, componentPreviewProductReferenceSchema, componentPreviewPropNameSchema, componentPreviewPropsSchema, componentPreviewSelectionSchema, componentPreviewSourceFileSchema, componentPreviewSourceSchema, componentPreviewToWebsiteBuilderMethodConfig, websiteBuilderToComponentPreviewMethodConfig };
|
|
106
|
+
export { COMPONENT_PREVIEW_IFRAME_RPC_CHANNEL_ID, COMPONENT_PREVIEW_IFRAME_RPC_CHANNEL_VERSION, COMPONENT_PREVIEW_PARENT_ORIGIN_PARAM, MAX_COMPONENT_PREVIEW_PROPS_BYTES, componentPreviewComponentSchema, componentPreviewIndexSchema, componentPreviewPathSchema, componentPreviewPreparationSchema, componentPreviewProductReferenceSchema, componentPreviewPropNameSchema, componentPreviewPropsSchema, componentPreviewSelectionSchema, componentPreviewSourceFileSchema, componentPreviewSourceSchema, componentPreviewToWebsiteBuilderMethodConfig, websiteBuilderToComponentPreviewMethodConfig };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export type DisplayMode = "browse" | "selection";
|
|
2
2
|
export type SelectModeSelectionType = "none" | "element" | "area" | "drag";
|
|
3
|
-
export type SelectModeInlineTextStatus = "editable" | "dynamic" | null;
|
|
3
|
+
export type SelectModeInlineTextStatus = "editable" | "dynamic" | "translated" | null;
|
|
4
4
|
export type SelectModeTextMetrics = {
|
|
5
5
|
tightWidth: number;
|
|
6
6
|
lineCount: number;
|
|
@@ -8,6 +8,7 @@ export declare const displayModeSchema: z.ZodEnum<{
|
|
|
8
8
|
export declare const selectModeInlineTextStatusSchema: z.ZodNullable<z.ZodEnum<{
|
|
9
9
|
editable: "editable";
|
|
10
10
|
dynamic: "dynamic";
|
|
11
|
+
translated: "translated";
|
|
11
12
|
}>>;
|
|
12
13
|
export declare const selectModeTextMetricsSchema: z.ZodObject<{
|
|
13
14
|
tightWidth: z.ZodNumber;
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export { SELECT_MODE_SELECTABLE_ATTRIBUTE, SELECT_MODE_SELECTABLE_PAYLOAD_ATTRIBUTE, displayModeSchema, getIframeOffset, readSelectablePayload, selectModeBoundingRectSchema, selectModeClickPositionSchema, selectModeDefinedStyleSchema, selectModeFiberChainEntrySchema, selectModeFormatCommandSchema, selectModeInlineTextStatusSchema, selectModeOptimisticUpdatePayloadSchema, selectModeSelectionRectSchema, selectModeSelectionSchema, selectModeSelectionTypeSchema, selectModeSourceLocationSchema, selectModeStyleAtRuleSchema, selectModeStyleVariantSchema, selectModeTextMetricsSchema, selectModeTiptapActiveMarksSchema, selectableDataPayloadSchema, selectableDataSchema, selectablePlanDataSchema, selectablePlanPayloadSchema, selectedElementSchema, transformPoint, transformRect, transformSelectableData } from '../../chunk-
|
|
1
|
+
export { SELECT_MODE_SELECTABLE_ATTRIBUTE, SELECT_MODE_SELECTABLE_PAYLOAD_ATTRIBUTE, displayModeSchema, getIframeOffset, readSelectablePayload, selectModeBoundingRectSchema, selectModeClickPositionSchema, selectModeDefinedStyleSchema, selectModeFiberChainEntrySchema, selectModeFormatCommandSchema, selectModeInlineTextStatusSchema, selectModeOptimisticUpdatePayloadSchema, selectModeSelectionRectSchema, selectModeSelectionSchema, selectModeSelectionTypeSchema, selectModeSourceLocationSchema, selectModeStyleAtRuleSchema, selectModeStyleVariantSchema, selectModeTextMetricsSchema, selectModeTiptapActiveMarksSchema, selectableDataPayloadSchema, selectableDataSchema, selectablePlanDataSchema, selectablePlanPayloadSchema, selectedElementSchema, transformPoint, transformRect, transformSelectableData } from '../../chunk-P5NWJM64.mjs';
|
|
2
2
|
import '../../chunk-N5EI2MOZ.mjs';
|
|
3
3
|
import '../../chunk-FNTGY7I5.mjs';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { selectModeFormatCommandSchema, selectModeOptimisticUpdatePayloadSchema, selectModeSelectionTypeSchema, displayModeSchema, selectModeTiptapActiveMarksSchema, selectModeBoundingRectSchema, selectableDataSchema, selectModeSelectionRectSchema, selectModeClickPositionSchema, selectedElementSchema } from '../../chunk-
|
|
1
|
+
import { selectModeFormatCommandSchema, selectModeOptimisticUpdatePayloadSchema, selectModeSelectionTypeSchema, displayModeSchema, selectModeTiptapActiveMarksSchema, selectModeBoundingRectSchema, selectableDataSchema, selectModeSelectionRectSchema, selectModeClickPositionSchema, selectedElementSchema } from '../../chunk-P5NWJM64.mjs';
|
|
2
2
|
import '../../chunk-N5EI2MOZ.mjs';
|
|
3
3
|
import '../../chunk-FNTGY7I5.mjs';
|
|
4
4
|
import { z } from 'zod';
|
package/buildMetadata.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export const BUILD_METADATA = {
|
|
2
2
|
"packageName": "@replohq/sandbox-runtime",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"branch": "HEAD",
|
|
5
|
-
"commit": "
|
|
6
|
-
"builtAt": "2026-08-
|
|
5
|
+
"commit": "9293953aa1a314e9bbfd7b614cfdd11181138f00",
|
|
6
|
+
"builtAt": "2026-08-20T04:27:18.976Z"
|
|
7
7
|
};
|
|
@@ -18,7 +18,7 @@ function parseSchemaOrNull(value, schema) {
|
|
|
18
18
|
var SELECT_MODE_SELECTABLE_ATTRIBUTE = "data-replo-selectable";
|
|
19
19
|
var SELECT_MODE_SELECTABLE_PAYLOAD_ATTRIBUTE = "data-replo-selectable-payload";
|
|
20
20
|
var displayModeSchema = z.enum(["browse", "selection"]).describe("DisplayMode");
|
|
21
|
-
var selectModeInlineTextStatusSchema = z.enum(["editable", "dynamic"]).nullable().describe("SelectModeInlineTextStatus");
|
|
21
|
+
var selectModeInlineTextStatusSchema = z.enum(["editable", "dynamic", "translated"]).nullable().describe("SelectModeInlineTextStatus");
|
|
22
22
|
var selectModeTextMetricsSchema = z.object({
|
|
23
23
|
tightWidth: z.number(),
|
|
24
24
|
lineCount: z.number(),
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { ComponentType } from "react";
|
|
2
|
+
export declare function ComponentPreviewNavigation(): null;
|
|
3
|
+
export declare function ComponentPreviewStage({ Component, initialProps, initialRawProps, initialSourceRevision, }: {
|
|
4
|
+
Component: ComponentType<Record<string, unknown>>;
|
|
5
|
+
initialProps: Record<string, unknown>;
|
|
6
|
+
initialRawProps: Record<string, unknown>;
|
|
7
|
+
initialSourceRevision: string;
|
|
8
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
import { useEffect, useMemo, useRef, useState, useTransition } from "react";
|
|
4
|
+
import { useRouter } from "next/navigation";
|
|
5
|
+
import { useIframeRpcChannel } from "../_vendor/replo-utils/react/use-iframe-rpc-channel.mjs";
|
|
6
|
+
import {
|
|
7
|
+
COMPONENT_PREVIEW_IFRAME_RPC_CHANNEL_ID,
|
|
8
|
+
COMPONENT_PREVIEW_IFRAME_RPC_CHANNEL_VERSION,
|
|
9
|
+
COMPONENT_PREVIEW_PARENT_ORIGIN_PARAM,
|
|
10
|
+
componentPreviewToWebsiteBuilderMethodConfig,
|
|
11
|
+
websiteBuilderToComponentPreviewMethodConfig
|
|
12
|
+
} from "../_vendor/schemas/componentPreview.mjs";
|
|
13
|
+
import {
|
|
14
|
+
createIframeRpcChannel,
|
|
15
|
+
createIframeRpcRemote
|
|
16
|
+
} from "../_vendor/schemas/iframeRpcChannel.mjs";
|
|
17
|
+
import { getComponentPreviewHref } from "./component-preview-href";
|
|
18
|
+
import {
|
|
19
|
+
mergeClientProps,
|
|
20
|
+
productReferencesChanged
|
|
21
|
+
} from "./component-preview-props";
|
|
22
|
+
function getParentOrigin() {
|
|
23
|
+
const fromQuery = new URLSearchParams(window.location.search).get(
|
|
24
|
+
COMPONENT_PREVIEW_PARENT_ORIGIN_PARAM
|
|
25
|
+
);
|
|
26
|
+
if (fromQuery) {
|
|
27
|
+
try {
|
|
28
|
+
return new URL(fromQuery).origin;
|
|
29
|
+
} catch {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return document.referrer ? new URL(document.referrer).origin : null;
|
|
34
|
+
}
|
|
35
|
+
function getCurrentHref() {
|
|
36
|
+
return `${window.location.pathname}${window.location.search}${window.location.hash}`;
|
|
37
|
+
}
|
|
38
|
+
function useComponentPreviewChannel(onSetProps) {
|
|
39
|
+
const onSetPropsRef = useRef(onSetProps);
|
|
40
|
+
onSetPropsRef.current = onSetProps;
|
|
41
|
+
useIframeRpcChannel(
|
|
42
|
+
useMemo(() => {
|
|
43
|
+
if (typeof window === "undefined") {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
const parentOrigin = getParentOrigin();
|
|
47
|
+
if (!parentOrigin) {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
const rpcChannel = createIframeRpcChannel({
|
|
51
|
+
subscriberWindow: window,
|
|
52
|
+
channelIdentifier: COMPONENT_PREVIEW_IFRAME_RPC_CHANNEL_ID,
|
|
53
|
+
version: COMPONENT_PREVIEW_IFRAME_RPC_CHANNEL_VERSION,
|
|
54
|
+
getTargetWindow: () => {
|
|
55
|
+
return window.parent === window ? null : window.parent;
|
|
56
|
+
},
|
|
57
|
+
targetOrigin: parentOrigin,
|
|
58
|
+
isExpectedMessage: (event) => {
|
|
59
|
+
return event.source === window.parent && event.origin === parentOrigin;
|
|
60
|
+
},
|
|
61
|
+
outgoingMethods: componentPreviewToWebsiteBuilderMethodConfig,
|
|
62
|
+
incomingMethods: websiteBuilderToComponentPreviewMethodConfig,
|
|
63
|
+
handlers: {
|
|
64
|
+
setProps: (input) => onSetPropsRef.current(input)
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
return {
|
|
68
|
+
...rpcChannel,
|
|
69
|
+
subscribe() {
|
|
70
|
+
const unsubscribe = rpcChannel.subscribe();
|
|
71
|
+
createIframeRpcRemote(rpcChannel).iframeReady();
|
|
72
|
+
return unsubscribe;
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
}, [])
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
function useNavigateAndSettle() {
|
|
79
|
+
const router = useRouter();
|
|
80
|
+
const [isNavigationPending, startNavigation] = useTransition();
|
|
81
|
+
const settleResolvers = useRef([]);
|
|
82
|
+
useEffect(() => {
|
|
83
|
+
if (!isNavigationPending) {
|
|
84
|
+
const resolvers = settleResolvers.current;
|
|
85
|
+
settleResolvers.current = [];
|
|
86
|
+
for (const resolve of resolvers) {
|
|
87
|
+
resolve();
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}, [isNavigationPending]);
|
|
91
|
+
return (href) => {
|
|
92
|
+
return new Promise((resolve) => {
|
|
93
|
+
settleResolvers.current.push(resolve);
|
|
94
|
+
startNavigation(() => {
|
|
95
|
+
router.replace(href, { scroll: false });
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
function ComponentPreviewNavigation() {
|
|
101
|
+
const navigateAndSettle = useNavigateAndSettle();
|
|
102
|
+
useComponentPreviewChannel(
|
|
103
|
+
async ({ props, sourceRevision }) => {
|
|
104
|
+
const nextHref = getComponentPreviewHref({
|
|
105
|
+
currentUrl: window.location.href,
|
|
106
|
+
props,
|
|
107
|
+
sourceRevision
|
|
108
|
+
});
|
|
109
|
+
if (nextHref === getCurrentHref()) {
|
|
110
|
+
return { mode: "unchanged" };
|
|
111
|
+
}
|
|
112
|
+
await navigateAndSettle(nextHref);
|
|
113
|
+
return { mode: "navigation" };
|
|
114
|
+
}
|
|
115
|
+
);
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
function ComponentPreviewStage({
|
|
119
|
+
Component,
|
|
120
|
+
initialProps,
|
|
121
|
+
initialRawProps,
|
|
122
|
+
initialSourceRevision
|
|
123
|
+
}) {
|
|
124
|
+
const [props, setProps] = useState(initialProps);
|
|
125
|
+
const [lastInitialProps, setLastInitialProps] = useState(initialProps);
|
|
126
|
+
if (initialProps !== lastInitialProps) {
|
|
127
|
+
setLastInitialProps(initialProps);
|
|
128
|
+
setProps(initialProps);
|
|
129
|
+
}
|
|
130
|
+
const propsRef = useRef(props);
|
|
131
|
+
propsRef.current = props;
|
|
132
|
+
const lastRawPropsRef = useRef(initialRawProps);
|
|
133
|
+
const lastSourceRevisionRef = useRef(initialSourceRevision);
|
|
134
|
+
const navigateAndSettle = useNavigateAndSettle();
|
|
135
|
+
useComponentPreviewChannel(
|
|
136
|
+
async ({
|
|
137
|
+
props: nextRawProps,
|
|
138
|
+
sourceRevision
|
|
139
|
+
}) => {
|
|
140
|
+
const nextHref = getComponentPreviewHref({
|
|
141
|
+
currentUrl: window.location.href,
|
|
142
|
+
props: nextRawProps,
|
|
143
|
+
sourceRevision
|
|
144
|
+
});
|
|
145
|
+
if (nextHref === getCurrentHref()) {
|
|
146
|
+
return { mode: "unchanged" };
|
|
147
|
+
}
|
|
148
|
+
if (sourceRevision !== lastSourceRevisionRef.current || productReferencesChanged({
|
|
149
|
+
previous: lastRawPropsRef.current,
|
|
150
|
+
next: nextRawProps
|
|
151
|
+
})) {
|
|
152
|
+
await navigateAndSettle(nextHref);
|
|
153
|
+
lastRawPropsRef.current = nextRawProps;
|
|
154
|
+
lastSourceRevisionRef.current = sourceRevision;
|
|
155
|
+
return { mode: "navigation" };
|
|
156
|
+
}
|
|
157
|
+
setProps(
|
|
158
|
+
mergeClientProps({
|
|
159
|
+
currentResolved: propsRef.current,
|
|
160
|
+
nextRaw: nextRawProps
|
|
161
|
+
})
|
|
162
|
+
);
|
|
163
|
+
lastRawPropsRef.current = nextRawProps;
|
|
164
|
+
window.history.replaceState(null, "", nextHref);
|
|
165
|
+
return { mode: "client" };
|
|
166
|
+
}
|
|
167
|
+
);
|
|
168
|
+
return /* @__PURE__ */ jsx(Component, { ...props });
|
|
169
|
+
}
|
|
170
|
+
export {
|
|
171
|
+
ComponentPreviewNavigation,
|
|
172
|
+
ComponentPreviewStage
|
|
173
|
+
};
|
|
174
|
+
//# sourceMappingURL=component-preview-client.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../next/component-preview-client.tsx"],
|
|
4
|
+
"sourcesContent": ["\"use client\";\n\nimport type { ComponentType } from \"react\";\nimport type { z } from \"zod\";\n\nimport { useEffect, useMemo, useRef, useState, useTransition } from \"react\";\n\nimport { useRouter } from \"next/navigation\";\n\nimport { useIframeRpcChannel } from \"replo-utils/react/use-iframe-rpc-channel\";\nimport {\n COMPONENT_PREVIEW_IFRAME_RPC_CHANNEL_ID,\n COMPONENT_PREVIEW_IFRAME_RPC_CHANNEL_VERSION,\n COMPONENT_PREVIEW_PARENT_ORIGIN_PARAM,\n componentPreviewToWebsiteBuilderMethodConfig,\n websiteBuilderToComponentPreviewMethodConfig,\n} from \"schemas/componentPreview\";\nimport {\n createIframeRpcChannel,\n createIframeRpcRemote,\n} from \"schemas/iframeRpcChannel\";\n\nimport { getComponentPreviewHref } from \"./component-preview-href\";\nimport {\n mergeClientProps,\n productReferencesChanged,\n} from \"./component-preview-props\";\n\ntype SetPropsInput = z.output<\n (typeof websiteBuilderToComponentPreviewMethodConfig)[\"setProps\"][\"inputSchema\"]\n>;\ntype SetPropsResult = z.output<\n (typeof websiteBuilderToComponentPreviewMethodConfig)[\"setProps\"][\"outputSchema\"]\n>;\n\nfunction getParentOrigin(): string | null {\n const fromQuery = new URLSearchParams(window.location.search).get(\n COMPONENT_PREVIEW_PARENT_ORIGIN_PARAM,\n );\n if (fromQuery) {\n try {\n return new URL(fromQuery).origin;\n } catch {\n return null;\n }\n }\n return document.referrer ? new URL(document.referrer).origin : null;\n}\n\nfunction getCurrentHref() {\n return `${window.location.pathname}${window.location.search}${window.location.hash}`;\n}\n\n// Child side of the preview channel: announces readiness on subscribe and\n// routes incoming setProps calls to the caller. React Doctor rejects passing a\n// useEffectEvent result into channel handlers, so freshness comes from a plain\n// ref the stable closure reads at call time.\nfunction useComponentPreviewChannel(\n onSetProps: (input: SetPropsInput) => Promise<SetPropsResult>,\n) {\n const onSetPropsRef = useRef(onSetProps);\n onSetPropsRef.current = onSetProps;\n useIframeRpcChannel(\n useMemo(() => {\n if (typeof window === \"undefined\") {\n return null;\n }\n const parentOrigin = getParentOrigin();\n if (!parentOrigin) {\n return null;\n }\n const rpcChannel = createIframeRpcChannel({\n subscriberWindow: window,\n channelIdentifier: COMPONENT_PREVIEW_IFRAME_RPC_CHANNEL_ID,\n version: COMPONENT_PREVIEW_IFRAME_RPC_CHANNEL_VERSION,\n getTargetWindow: () => {\n return window.parent === window ? null : window.parent;\n },\n targetOrigin: parentOrigin,\n isExpectedMessage: (event) => {\n return (\n event.source === window.parent && event.origin === parentOrigin\n );\n },\n outgoingMethods: componentPreviewToWebsiteBuilderMethodConfig,\n incomingMethods: websiteBuilderToComponentPreviewMethodConfig,\n handlers: {\n setProps: (input) => onSetPropsRef.current(input),\n },\n });\n return {\n ...rpcChannel,\n subscribe() {\n const unsubscribe = rpcChannel.subscribe();\n createIframeRpcRemote(rpcChannel).iframeReady();\n return unsubscribe;\n },\n };\n }, []),\n );\n}\n\n// Server-render an in-frame navigation and resolve once the transition\n// settles, so the parent's ack tracks the actual repaint.\nfunction useNavigateAndSettle() {\n const router = useRouter();\n const [isNavigationPending, startNavigation] = useTransition();\n const settleResolvers = useRef<(() => void)[]>([]);\n // eslint-disable-next-line replo/no-use-effect -- Resolves promises held by the RPC channel, an external system, once the navigation transition settles.\n useEffect(() => {\n if (!isNavigationPending) {\n const resolvers = settleResolvers.current;\n settleResolvers.current = [];\n for (const resolve of resolvers) {\n resolve();\n }\n }\n }, [isNavigationPending]);\n // A navigation that never settles (a hung RSC fetch) never resolves \u2014 the\n // parent's channel timeout turns that into the disconnected state, and the\n // caller's refs stay uncommitted so the next sync retries the navigation.\n return (href: string) => {\n return new Promise<void>((resolve) => {\n settleResolvers.current.push(resolve);\n startNavigation(() => {\n router.replace(href, { scroll: false });\n });\n });\n };\n}\n\nexport function ComponentPreviewNavigation() {\n const navigateAndSettle = useNavigateAndSettle();\n useComponentPreviewChannel(\n async ({ props, sourceRevision }): Promise<SetPropsResult> => {\n const nextHref = getComponentPreviewHref({\n currentUrl: window.location.href,\n props,\n sourceRevision,\n });\n if (nextHref === getCurrentHref()) {\n return { mode: \"unchanged\" };\n }\n await navigateAndSettle(nextHref);\n return { mode: \"navigation\" };\n },\n );\n return null;\n}\n\nexport function ComponentPreviewStage({\n Component,\n initialProps,\n initialRawProps,\n initialSourceRevision,\n}: {\n Component: ComponentType<Record<string, unknown>>;\n initialProps: Record<string, unknown>;\n initialRawProps: Record<string, unknown>;\n initialSourceRevision: string;\n}) {\n const [props, setProps] = useState(initialProps);\n // A fresh server render (mount or in-frame navigation) is authoritative;\n // its props replace whatever the client store held.\n const [lastInitialProps, setLastInitialProps] = useState(initialProps);\n if (initialProps !== lastInitialProps) {\n setLastInitialProps(initialProps);\n setProps(initialProps);\n }\n const propsRef = useRef(props);\n propsRef.current = props;\n const lastRawPropsRef = useRef(initialRawProps);\n const lastSourceRevisionRef = useRef(initialSourceRevision);\n const navigateAndSettle = useNavigateAndSettle();\n\n useComponentPreviewChannel(\n async ({\n props: nextRawProps,\n sourceRevision,\n }): Promise<SetPropsResult> => {\n const nextHref = getComponentPreviewHref({\n currentUrl: window.location.href,\n props: nextRawProps,\n sourceRevision,\n });\n if (nextHref === getCurrentHref()) {\n return { mode: \"unchanged\" };\n }\n if (\n sourceRevision !== lastSourceRevisionRef.current ||\n productReferencesChanged({\n previous: lastRawPropsRef.current,\n next: nextRawProps,\n })\n ) {\n await navigateAndSettle(nextHref);\n lastRawPropsRef.current = nextRawProps;\n lastSourceRevisionRef.current = sourceRevision;\n return { mode: \"navigation\" };\n }\n setProps(\n mergeClientProps({\n currentResolved: propsRef.current,\n nextRaw: nextRawProps,\n }),\n );\n lastRawPropsRef.current = nextRawProps;\n // Keep the URL canonical without a server round trip, so a manual frame\n // reload restores the current props and the unchanged-check stays exact.\n window.history.replaceState(null, \"\", nextHref);\n return { mode: \"client\" };\n },\n );\n\n return <Component {...props} />;\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export declare function productReferencesChanged({ previous, next, }: {
|
|
2
|
+
previous: Record<string, unknown>;
|
|
3
|
+
next: Record<string, unknown>;
|
|
4
|
+
}): boolean;
|
|
5
|
+
export declare function mergeClientProps({ currentResolved, nextRaw, }: {
|
|
6
|
+
currentResolved: Record<string, unknown>;
|
|
7
|
+
nextRaw: Record<string, unknown>;
|
|
8
|
+
}): Record<string, unknown>;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { componentPreviewProductReferenceSchema } from "../_vendor/schemas/componentPreview.mjs";
|
|
2
|
+
function isProductReference(value) {
|
|
3
|
+
return componentPreviewProductReferenceSchema.safeParse(value).success;
|
|
4
|
+
}
|
|
5
|
+
function productReferencesChanged({
|
|
6
|
+
previous,
|
|
7
|
+
next
|
|
8
|
+
}) {
|
|
9
|
+
const names = /* @__PURE__ */ new Set([...Object.keys(previous), ...Object.keys(next)]);
|
|
10
|
+
for (const name of names) {
|
|
11
|
+
const previousIsReference = isProductReference(previous[name]);
|
|
12
|
+
const nextIsReference = isProductReference(next[name]);
|
|
13
|
+
if (!previousIsReference && !nextIsReference) {
|
|
14
|
+
continue;
|
|
15
|
+
}
|
|
16
|
+
if (previousIsReference !== nextIsReference || JSON.stringify(previous[name]) !== JSON.stringify(next[name])) {
|
|
17
|
+
return true;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
function mergeClientProps({
|
|
23
|
+
currentResolved,
|
|
24
|
+
nextRaw
|
|
25
|
+
}) {
|
|
26
|
+
return Object.fromEntries(
|
|
27
|
+
Object.entries(nextRaw).map(([name, value]) => {
|
|
28
|
+
return isProductReference(value) ? [name, currentResolved[name]] : [name, value];
|
|
29
|
+
})
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
export {
|
|
33
|
+
mergeClientProps,
|
|
34
|
+
productReferencesChanged
|
|
35
|
+
};
|
|
36
|
+
//# sourceMappingURL=component-preview-props.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../next/component-preview-props.ts"],
|
|
4
|
+
"sourcesContent": ["import { componentPreviewProductReferenceSchema } from \"schemas/componentPreview\";\n\nfunction isProductReference(value: unknown) {\n return componentPreviewProductReferenceSchema.safeParse(value).success;\n}\n\n// Product references need the server loader, so any change to one forces the\n// navigation path; everything else can apply in place.\nexport function productReferencesChanged({\n previous,\n next,\n}: {\n previous: Record<string, unknown>;\n next: Record<string, unknown>;\n}): boolean {\n const names = new Set([...Object.keys(previous), ...Object.keys(next)]);\n for (const name of names) {\n const previousIsReference = isProductReference(previous[name]);\n const nextIsReference = isProductReference(next[name]);\n if (!previousIsReference && !nextIsReference) {\n continue;\n }\n if (\n previousIsReference !== nextIsReference ||\n JSON.stringify(previous[name]) !== JSON.stringify(next[name])\n ) {\n return true;\n }\n }\n return false;\n}\n\n// Builds the next client-store props from an incoming raw payload: raw product\n// references are swapped for the already-resolved values in the current store\n// (only unchanged references reach this path \u2014 changed ones forced navigation),\n// and every other prop takes the incoming value. Keys absent from the payload\n// are dropped so the component's own defaults return.\nexport function mergeClientProps({\n currentResolved,\n nextRaw,\n}: {\n currentResolved: Record<string, unknown>;\n nextRaw: Record<string, unknown>;\n}): Record<string, unknown> {\n return Object.fromEntries(\n Object.entries(nextRaw).map(([name, value]) => {\n return isProductReference(value)\n ? [name, currentResolved[name]]\n : [name, value];\n }),\n );\n}\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,8CAA8C;AAEvD,SAAS,mBAAmB,OAAgB;AAC1C,SAAO,uCAAuC,UAAU,KAAK,EAAE;AACjE;AAIO,SAAS,yBAAyB;AAAA,EACvC;AAAA,EACA;AACF,GAGY;AACV,QAAM,QAAQ,oBAAI,IAAI,CAAC,GAAG,OAAO,KAAK,QAAQ,GAAG,GAAG,OAAO,KAAK,IAAI,CAAC,CAAC;AACtE,aAAW,QAAQ,OAAO;AACxB,UAAM,sBAAsB,mBAAmB,SAAS,IAAI,CAAC;AAC7D,UAAM,kBAAkB,mBAAmB,KAAK,IAAI,CAAC;AACrD,QAAI,CAAC,uBAAuB,CAAC,iBAAiB;AAC5C;AAAA,IACF;AACA,QACE,wBAAwB,mBACxB,KAAK,UAAU,SAAS,IAAI,CAAC,MAAM,KAAK,UAAU,KAAK,IAAI,CAAC,GAC5D;AACA,aAAO;AAAA,IACT;AAAA,EACF;AACA,SAAO;AACT;AAOO,SAAS,iBAAiB;AAAA,EAC/B;AAAA,EACA;AACF,GAG4B;AAC1B,SAAO,OAAO;AAAA,IACZ,OAAO,QAAQ,OAAO,EAAE,IAAI,CAAC,CAAC,MAAM,KAAK,MAAM;AAC7C,aAAO,mBAAmB,KAAK,IAC3B,CAAC,MAAM,gBAAgB,IAAI,CAAC,IAC5B,CAAC,MAAM,KAAK;AAAA,IAClB,CAAC;AAAA,EACH;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import type { ComponentType } from "react";
|
|
2
2
|
import type { ComponentPreviewProductReference } from "../_vendor/schemas/generated/componentPreview";
|
|
3
3
|
export declare function createComponentPreviewPage(Component: ComponentType<Record<string, unknown>>, options?: {
|
|
4
|
-
getProduct
|
|
4
|
+
getProduct?: (reference: ComponentPreviewProductReference) => Promise<unknown>;
|
|
5
|
+
interactive?: boolean;
|
|
5
6
|
}): ({ searchParams, }: {
|
|
6
7
|
searchParams: Promise<{
|
|
7
8
|
props?: string | string[];
|
|
9
|
+
sourceRevision?: string | string[];
|
|
8
10
|
}>;
|
|
9
11
|
}) => Promise<import("react/jsx-runtime").JSX.Element>;
|
|
@@ -6,48 +6,43 @@ import {
|
|
|
6
6
|
} from "../_vendor/schemas/componentPreview.mjs";
|
|
7
7
|
import { DocumentReadyBeacon } from "../DocumentReadyBeacon";
|
|
8
8
|
import { NextErrorReporter } from "../NextErrorReporter";
|
|
9
|
-
import {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
"dangerouslySetInnerHTML"
|
|
14
|
-
]);
|
|
9
|
+
import {
|
|
10
|
+
ComponentPreviewNavigation,
|
|
11
|
+
ComponentPreviewStage
|
|
12
|
+
} from "./component-preview-client";
|
|
15
13
|
async function getPreviewProps({
|
|
16
14
|
value,
|
|
17
15
|
getProduct
|
|
18
16
|
}) {
|
|
19
17
|
const previewPropsResult = componentPreviewPropsSchema.safeParse(value);
|
|
20
18
|
if (!previewPropsResult.success) {
|
|
21
|
-
return {};
|
|
19
|
+
return { rawProps: {}, resolvedProps: {} };
|
|
22
20
|
}
|
|
23
|
-
const
|
|
21
|
+
const rawProps = {};
|
|
22
|
+
const resolvedProps = {};
|
|
24
23
|
for (const [name, propValue] of Object.entries(previewPropsResult.data)) {
|
|
25
|
-
|
|
26
|
-
continue;
|
|
27
|
-
}
|
|
24
|
+
rawProps[name] = propValue;
|
|
28
25
|
const productReferenceResult = componentPreviewProductReferenceSchema.safeParse(propValue);
|
|
29
26
|
if (!productReferenceResult.success) {
|
|
30
|
-
|
|
27
|
+
resolvedProps[name] = propValue;
|
|
31
28
|
continue;
|
|
32
29
|
}
|
|
33
|
-
|
|
34
|
-
previewProps[name] = await getProduct(productReferenceResult.data);
|
|
35
|
-
}
|
|
30
|
+
resolvedProps[name] = getProduct ? await getProduct(productReferenceResult.data) : propValue;
|
|
36
31
|
}
|
|
37
|
-
return
|
|
32
|
+
return { rawProps, resolvedProps };
|
|
38
33
|
}
|
|
39
34
|
async function parseProps({
|
|
40
35
|
value,
|
|
41
36
|
getProduct
|
|
42
37
|
}) {
|
|
43
38
|
if (typeof value !== "string" || new TextEncoder().encode(value).byteLength > MAX_COMPONENT_PREVIEW_PROPS_BYTES) {
|
|
44
|
-
return {};
|
|
39
|
+
return { rawProps: {}, resolvedProps: {} };
|
|
45
40
|
}
|
|
46
41
|
let parsed;
|
|
47
42
|
try {
|
|
48
43
|
parsed = JSON.parse(value);
|
|
49
44
|
} catch {
|
|
50
|
-
return {};
|
|
45
|
+
return { rawProps: {}, resolvedProps: {} };
|
|
51
46
|
}
|
|
52
47
|
return await getPreviewProps({ value: parsed, getProduct });
|
|
53
48
|
}
|
|
@@ -55,15 +50,31 @@ function createComponentPreviewPage(Component, options) {
|
|
|
55
50
|
return async function ComponentPreviewPage({
|
|
56
51
|
searchParams
|
|
57
52
|
}) {
|
|
58
|
-
const
|
|
59
|
-
|
|
53
|
+
const resolvedSearchParams = await searchParams;
|
|
54
|
+
const { rawProps, resolvedProps } = await parseProps({
|
|
55
|
+
value: resolvedSearchParams.props,
|
|
60
56
|
getProduct: options?.getProduct
|
|
61
57
|
});
|
|
58
|
+
if (options?.interactive) {
|
|
59
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
60
|
+
/* @__PURE__ */ jsx(DocumentReadyBeacon, {}),
|
|
61
|
+
/* @__PURE__ */ jsx(NextErrorReporter, {}),
|
|
62
|
+
/* @__PURE__ */ jsx(
|
|
63
|
+
ComponentPreviewStage,
|
|
64
|
+
{
|
|
65
|
+
Component,
|
|
66
|
+
initialProps: resolvedProps,
|
|
67
|
+
initialRawProps: rawProps,
|
|
68
|
+
initialSourceRevision: typeof resolvedSearchParams.sourceRevision === "string" ? resolvedSearchParams.sourceRevision : ""
|
|
69
|
+
}
|
|
70
|
+
)
|
|
71
|
+
] });
|
|
72
|
+
}
|
|
62
73
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
63
74
|
/* @__PURE__ */ jsx(ComponentPreviewNavigation, {}),
|
|
64
75
|
/* @__PURE__ */ jsx(DocumentReadyBeacon, {}),
|
|
65
76
|
/* @__PURE__ */ jsx(NextErrorReporter, {}),
|
|
66
|
-
/* @__PURE__ */ jsx(Component, { ...
|
|
77
|
+
/* @__PURE__ */ jsx(Component, { ...resolvedProps })
|
|
67
78
|
] });
|
|
68
79
|
};
|
|
69
80
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../next/component-preview.tsx"],
|
|
4
|
-
"sourcesContent": ["import type { ComponentType } from \"react\";\nimport type { ComponentPreviewProductReference } from \"schemas/generated/componentPreview\";\n\nimport {\n componentPreviewProductReferenceSchema,\n componentPreviewPropsSchema,\n MAX_COMPONENT_PREVIEW_PROPS_BYTES,\n} from \"schemas/componentPreview\";\n\nimport { DocumentReadyBeacon } from \"../DocumentReadyBeacon\";\nimport { NextErrorReporter } from \"../NextErrorReporter\";\nimport {
|
|
5
|
-
"mappings": "
|
|
4
|
+
"sourcesContent": ["import type { ComponentType } from \"react\";\nimport type { ComponentPreviewProductReference } from \"schemas/generated/componentPreview\";\n\nimport {\n componentPreviewProductReferenceSchema,\n componentPreviewPropsSchema,\n MAX_COMPONENT_PREVIEW_PROPS_BYTES,\n} from \"schemas/componentPreview\";\n\nimport { DocumentReadyBeacon } from \"../DocumentReadyBeacon\";\nimport { NextErrorReporter } from \"../NextErrorReporter\";\nimport {\n ComponentPreviewNavigation,\n ComponentPreviewStage,\n} from \"./component-preview-client\";\n\nasync function getPreviewProps({\n value,\n getProduct,\n}: {\n value: unknown;\n getProduct?: (\n reference: ComponentPreviewProductReference,\n ) => Promise<unknown>;\n}): Promise<{\n rawProps: Record<string, unknown>;\n resolvedProps: Record<string, unknown>;\n}> {\n const previewPropsResult = componentPreviewPropsSchema.safeParse(value);\n if (!previewPropsResult.success) {\n return { rawProps: {}, resolvedProps: {} };\n }\n\n const rawProps: Record<string, unknown> = {};\n const resolvedProps: Record<string, unknown> = {};\n for (const [name, propValue] of Object.entries(previewPropsResult.data)) {\n rawProps[name] = propValue;\n const productReferenceResult =\n componentPreviewProductReferenceSchema.safeParse(propValue);\n if (!productReferenceResult.success) {\n resolvedProps[name] = propValue;\n continue;\n }\n // Without a resolver the raw reference passes through rather than the prop\n // silently vanishing; the component sees exactly what the URL carried.\n resolvedProps[name] = getProduct\n ? await getProduct(productReferenceResult.data)\n : propValue;\n }\n return { rawProps, resolvedProps };\n}\n\nasync function parseProps({\n value,\n getProduct,\n}: {\n value: string | string[] | undefined;\n getProduct?: (\n reference: ComponentPreviewProductReference,\n ) => Promise<unknown>;\n}) {\n if (\n typeof value !== \"string\" ||\n new TextEncoder().encode(value).byteLength >\n MAX_COMPONENT_PREVIEW_PROPS_BYTES\n ) {\n return { rawProps: {}, resolvedProps: {} };\n }\n let parsed: unknown;\n try {\n parsed = JSON.parse(value);\n } catch {\n return { rawProps: {}, resolvedProps: {} };\n }\n return await getPreviewProps({ value: parsed, getProduct });\n}\n\nexport function createComponentPreviewPage(\n Component: ComponentType<Record<string, unknown>>,\n options?: {\n getProduct?: (\n reference: ComponentPreviewProductReference,\n ) => Promise<unknown>;\n // Interactive components (\"use client\" in the component file) render\n // inside the client prop stage so plain prop edits apply without a server\n // round trip. Server components can only re-render via navigation.\n interactive?: boolean;\n },\n) {\n return async function ComponentPreviewPage({\n searchParams,\n }: {\n searchParams: Promise<{\n props?: string | string[];\n sourceRevision?: string | string[];\n }>;\n }) {\n const resolvedSearchParams = await searchParams;\n const { rawProps, resolvedProps } = await parseProps({\n value: resolvedSearchParams.props,\n getProduct: options?.getProduct,\n });\n if (options?.interactive) {\n return (\n <>\n <DocumentReadyBeacon />\n <NextErrorReporter />\n <ComponentPreviewStage\n Component={Component}\n initialProps={resolvedProps}\n initialRawProps={rawProps}\n initialSourceRevision={\n typeof resolvedSearchParams.sourceRevision === \"string\"\n ? resolvedSearchParams.sourceRevision\n : \"\"\n }\n />\n </>\n );\n }\n return (\n <>\n <ComponentPreviewNavigation />\n <DocumentReadyBeacon />\n <NextErrorReporter />\n <Component {...resolvedProps} />\n </>\n );\n };\n}\n"],
|
|
5
|
+
"mappings": "AAwGQ,mBACE,KADF;AArGR;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAEP,SAAS,2BAA2B;AACpC,SAAS,yBAAyB;AAClC;AAAA,EACE;AAAA,EACA;AAAA,OACK;AAEP,eAAe,gBAAgB;AAAA,EAC7B;AAAA,EACA;AACF,GAQG;AACD,QAAM,qBAAqB,4BAA4B,UAAU,KAAK;AACtE,MAAI,CAAC,mBAAmB,SAAS;AAC/B,WAAO,EAAE,UAAU,CAAC,GAAG,eAAe,CAAC,EAAE;AAAA,EAC3C;AAEA,QAAM,WAAoC,CAAC;AAC3C,QAAM,gBAAyC,CAAC;AAChD,aAAW,CAAC,MAAM,SAAS,KAAK,OAAO,QAAQ,mBAAmB,IAAI,GAAG;AACvE,aAAS,IAAI,IAAI;AACjB,UAAM,yBACJ,uCAAuC,UAAU,SAAS;AAC5D,QAAI,CAAC,uBAAuB,SAAS;AACnC,oBAAc,IAAI,IAAI;AACtB;AAAA,IACF;AAGA,kBAAc,IAAI,IAAI,aAClB,MAAM,WAAW,uBAAuB,IAAI,IAC5C;AAAA,EACN;AACA,SAAO,EAAE,UAAU,cAAc;AACnC;AAEA,eAAe,WAAW;AAAA,EACxB;AAAA,EACA;AACF,GAKG;AACD,MACE,OAAO,UAAU,YACjB,IAAI,YAAY,EAAE,OAAO,KAAK,EAAE,aAC9B,mCACF;AACA,WAAO,EAAE,UAAU,CAAC,GAAG,eAAe,CAAC,EAAE;AAAA,EAC3C;AACA,MAAI;AACJ,MAAI;AACF,aAAS,KAAK,MAAM,KAAK;AAAA,EAC3B,QAAQ;AACN,WAAO,EAAE,UAAU,CAAC,GAAG,eAAe,CAAC,EAAE;AAAA,EAC3C;AACA,SAAO,MAAM,gBAAgB,EAAE,OAAO,QAAQ,WAAW,CAAC;AAC5D;AAEO,SAAS,2BACd,WACA,SASA;AACA,SAAO,eAAe,qBAAqB;AAAA,IACzC;AAAA,EACF,GAKG;AACD,UAAM,uBAAuB,MAAM;AACnC,UAAM,EAAE,UAAU,cAAc,IAAI,MAAM,WAAW;AAAA,MACnD,OAAO,qBAAqB;AAAA,MAC5B,YAAY,SAAS;AAAA,IACvB,CAAC;AACD,QAAI,SAAS,aAAa;AACxB,aACE,iCACE;AAAA,4BAAC,uBAAoB;AAAA,QACrB,oBAAC,qBAAkB;AAAA,QACnB;AAAA,UAAC;AAAA;AAAA,YACC;AAAA,YACA,cAAc;AAAA,YACd,iBAAiB;AAAA,YACjB,uBACE,OAAO,qBAAqB,mBAAmB,WAC3C,qBAAqB,iBACrB;AAAA;AAAA,QAER;AAAA,SACF;AAAA,IAEJ;AACA,WACE,iCACE;AAAA,0BAAC,8BAA2B;AAAA,MAC5B,oBAAC,uBAAoB;AAAA,MACrB,oBAAC,qBAAkB;AAAA,MACnB,oBAAC,aAAW,GAAG,eAAe;AAAA,OAChC;AAAA,EAEJ;AACF;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@replohq/sandbox-runtime",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"reploBuild": {
|
|
5
5
|
"packageName": "@replohq/sandbox-runtime",
|
|
6
|
-
"version": "1.0
|
|
6
|
+
"version": "1.1.0",
|
|
7
7
|
"branch": "HEAD",
|
|
8
|
-
"commit": "
|
|
9
|
-
"builtAt": "2026-08-
|
|
8
|
+
"commit": "9293953aa1a314e9bbfd7b614cfdd11181138f00",
|
|
9
|
+
"builtAt": "2026-08-20T04:27:18.976Z"
|
|
10
10
|
},
|
|
11
11
|
"description": "Replo sandbox runtime — editor integration for agent-built Next.js sites.",
|
|
12
12
|
"license": "SEE LICENSE IN LICENSE",
|
|
@@ -21,6 +21,10 @@
|
|
|
21
21
|
"types": "./next/component-preview.d.ts",
|
|
22
22
|
"default": "./next/component-preview.js"
|
|
23
23
|
},
|
|
24
|
+
"./next/component-preview-client": {
|
|
25
|
+
"types": "./next/component-preview-client.d.ts",
|
|
26
|
+
"default": "./next/component-preview-client.js"
|
|
27
|
+
},
|
|
24
28
|
"./next/config": {
|
|
25
29
|
"types": "./next/config.d.ts",
|
|
26
30
|
"default": "./next/config.js"
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare function ComponentPreviewNavigation(): null;
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
import { useMemo } from "react";
|
|
3
|
-
import { useRouter } from "next/navigation";
|
|
4
|
-
import { useIframeRpcChannel } from "../_vendor/replo-utils/react/use-iframe-rpc-channel.mjs";
|
|
5
|
-
import {
|
|
6
|
-
COMPONENT_PREVIEW_IFRAME_RPC_CHANNEL_ID,
|
|
7
|
-
COMPONENT_PREVIEW_IFRAME_RPC_CHANNEL_VERSION,
|
|
8
|
-
componentPreviewToWebsiteBuilderMethodConfig,
|
|
9
|
-
websiteBuilderToComponentPreviewMethodConfig
|
|
10
|
-
} from "../_vendor/schemas/componentPreview.mjs";
|
|
11
|
-
import {
|
|
12
|
-
createIframeRpcChannel,
|
|
13
|
-
createIframeRpcRemote
|
|
14
|
-
} from "../_vendor/schemas/iframeRpcChannel.mjs";
|
|
15
|
-
import { getComponentPreviewHref } from "./component-preview-href";
|
|
16
|
-
function ComponentPreviewNavigation() {
|
|
17
|
-
const router = useRouter();
|
|
18
|
-
useIframeRpcChannel(
|
|
19
|
-
useMemo(() => {
|
|
20
|
-
if (typeof window === "undefined" || !document.referrer) {
|
|
21
|
-
return null;
|
|
22
|
-
}
|
|
23
|
-
const parentOrigin = new URL(document.referrer).origin;
|
|
24
|
-
const rpcChannel = createIframeRpcChannel({
|
|
25
|
-
subscriberWindow: window,
|
|
26
|
-
channelIdentifier: COMPONENT_PREVIEW_IFRAME_RPC_CHANNEL_ID,
|
|
27
|
-
version: COMPONENT_PREVIEW_IFRAME_RPC_CHANNEL_VERSION,
|
|
28
|
-
getTargetWindow: () => {
|
|
29
|
-
return window.parent === window ? null : window.parent;
|
|
30
|
-
},
|
|
31
|
-
targetOrigin: parentOrigin,
|
|
32
|
-
isExpectedMessage: (event) => {
|
|
33
|
-
return event.source === window.parent && event.origin === parentOrigin;
|
|
34
|
-
},
|
|
35
|
-
outgoingMethods: componentPreviewToWebsiteBuilderMethodConfig,
|
|
36
|
-
incomingMethods: websiteBuilderToComponentPreviewMethodConfig,
|
|
37
|
-
handlers: {
|
|
38
|
-
setProps: ({ props, sourceRevision }) => {
|
|
39
|
-
const nextHref = getComponentPreviewHref({
|
|
40
|
-
currentUrl: window.location.href,
|
|
41
|
-
props,
|
|
42
|
-
sourceRevision
|
|
43
|
-
});
|
|
44
|
-
const currentHref = `${window.location.pathname}${window.location.search}${window.location.hash}`;
|
|
45
|
-
if (nextHref !== currentHref) {
|
|
46
|
-
router.replace(nextHref, { scroll: false });
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
});
|
|
51
|
-
return {
|
|
52
|
-
...rpcChannel,
|
|
53
|
-
subscribe() {
|
|
54
|
-
const unsubscribe = rpcChannel.subscribe();
|
|
55
|
-
createIframeRpcRemote(rpcChannel).iframeReady();
|
|
56
|
-
return unsubscribe;
|
|
57
|
-
}
|
|
58
|
-
};
|
|
59
|
-
}, [router])
|
|
60
|
-
);
|
|
61
|
-
return null;
|
|
62
|
-
}
|
|
63
|
-
export {
|
|
64
|
-
ComponentPreviewNavigation
|
|
65
|
-
};
|
|
66
|
-
//# sourceMappingURL=component-preview-navigation.js.map
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../next/component-preview-navigation.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\";\n\nimport { useMemo } from \"react\";\n\nimport { useRouter } from \"next/navigation\";\n\nimport { useIframeRpcChannel } from \"replo-utils/react/use-iframe-rpc-channel\";\nimport {\n COMPONENT_PREVIEW_IFRAME_RPC_CHANNEL_ID,\n COMPONENT_PREVIEW_IFRAME_RPC_CHANNEL_VERSION,\n componentPreviewToWebsiteBuilderMethodConfig,\n websiteBuilderToComponentPreviewMethodConfig,\n} from \"schemas/componentPreview\";\nimport {\n createIframeRpcChannel,\n createIframeRpcRemote,\n} from \"schemas/iframeRpcChannel\";\n\nimport { getComponentPreviewHref } from \"./component-preview-href\";\n\nexport function ComponentPreviewNavigation() {\n const router = useRouter();\n useIframeRpcChannel(\n useMemo(() => {\n if (typeof window === \"undefined\" || !document.referrer) {\n return null;\n }\n const parentOrigin = new URL(document.referrer).origin;\n const rpcChannel = createIframeRpcChannel({\n subscriberWindow: window,\n channelIdentifier: COMPONENT_PREVIEW_IFRAME_RPC_CHANNEL_ID,\n version: COMPONENT_PREVIEW_IFRAME_RPC_CHANNEL_VERSION,\n getTargetWindow: () => {\n return window.parent === window ? null : window.parent;\n },\n targetOrigin: parentOrigin,\n isExpectedMessage: (event) => {\n return (\n event.source === window.parent && event.origin === parentOrigin\n );\n },\n outgoingMethods: componentPreviewToWebsiteBuilderMethodConfig,\n incomingMethods: websiteBuilderToComponentPreviewMethodConfig,\n handlers: {\n setProps: ({ props, sourceRevision }) => {\n const nextHref = getComponentPreviewHref({\n currentUrl: window.location.href,\n props,\n sourceRevision,\n });\n const currentHref = `${window.location.pathname}${window.location.search}${window.location.hash}`;\n if (nextHref !== currentHref) {\n router.replace(nextHref, { scroll: false });\n }\n },\n },\n });\n return {\n ...rpcChannel,\n subscribe() {\n const unsubscribe = rpcChannel.subscribe();\n createIframeRpcRemote(rpcChannel).iframeReady();\n return unsubscribe;\n },\n };\n }, [router]),\n );\n\n return null;\n}\n"],
|
|
5
|
-
"mappings": ";AAEA,SAAS,eAAe;AAExB,SAAS,iBAAiB;AAE1B,SAAS,2BAA2B;AACpC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP;AAAA,EACE;AAAA,EACA;AAAA,OACK;AAEP,SAAS,+BAA+B;AAEjC,SAAS,6BAA6B;AAC3C,QAAM,SAAS,UAAU;AACzB;AAAA,IACE,QAAQ,MAAM;AACZ,UAAI,OAAO,WAAW,eAAe,CAAC,SAAS,UAAU;AACvD,eAAO;AAAA,MACT;AACA,YAAM,eAAe,IAAI,IAAI,SAAS,QAAQ,EAAE;AAChD,YAAM,aAAa,uBAAuB;AAAA,QACxC,kBAAkB;AAAA,QAClB,mBAAmB;AAAA,QACnB,SAAS;AAAA,QACT,iBAAiB,MAAM;AACrB,iBAAO,OAAO,WAAW,SAAS,OAAO,OAAO;AAAA,QAClD;AAAA,QACA,cAAc;AAAA,QACd,mBAAmB,CAAC,UAAU;AAC5B,iBACE,MAAM,WAAW,OAAO,UAAU,MAAM,WAAW;AAAA,QAEvD;AAAA,QACA,iBAAiB;AAAA,QACjB,iBAAiB;AAAA,QACjB,UAAU;AAAA,UACR,UAAU,CAAC,EAAE,OAAO,eAAe,MAAM;AACvC,kBAAM,WAAW,wBAAwB;AAAA,cACvC,YAAY,OAAO,SAAS;AAAA,cAC5B;AAAA,cACA;AAAA,YACF,CAAC;AACD,kBAAM,cAAc,GAAG,OAAO,SAAS,QAAQ,GAAG,OAAO,SAAS,MAAM,GAAG,OAAO,SAAS,IAAI;AAC/F,gBAAI,aAAa,aAAa;AAC5B,qBAAO,QAAQ,UAAU,EAAE,QAAQ,MAAM,CAAC;AAAA,YAC5C;AAAA,UACF;AAAA,QACF;AAAA,MACF,CAAC;AACD,aAAO;AAAA,QACL,GAAG;AAAA,QACH,YAAY;AACV,gBAAM,cAAc,WAAW,UAAU;AACzC,gCAAsB,UAAU,EAAE,YAAY;AAC9C,iBAAO;AAAA,QACT;AAAA,MACF;AAAA,IACF,GAAG,CAAC,MAAM,CAAC;AAAA,EACb;AAEA,SAAO;AACT;",
|
|
6
|
-
"names": []
|
|
7
|
-
}
|