@replohq/sandbox-runtime 0.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.
Files changed (139) hide show
  1. package/ClientErrorBoundary.d.ts +10 -0
  2. package/ClientErrorBoundary.js +61 -0
  3. package/ClientErrorBoundary.js.map +7 -0
  4. package/DocumentReadyBeacon.d.ts +1 -0
  5. package/DocumentReadyBeacon.js +9 -0
  6. package/DocumentReadyBeacon.js.map +7 -0
  7. package/LICENSE +11 -0
  8. package/LinkInterceptor.d.ts +1 -0
  9. package/LinkInterceptor.js +128 -0
  10. package/LinkInterceptor.js.map +7 -0
  11. package/NextErrorReporter.d.ts +1 -0
  12. package/NextErrorReporter.js +92 -0
  13. package/NextErrorReporter.js.map +7 -0
  14. package/_vendor/replo-utils/lib/crypto.d.ts +30 -0
  15. package/_vendor/replo-utils/lib/errors.d.ts +83 -0
  16. package/_vendor/replo-utils/lib/fonts.d.ts +49 -0
  17. package/_vendor/replo-utils/lib/fonts.mjs +221 -0
  18. package/_vendor/replo-utils/lib/geometry.d.ts +18 -0
  19. package/_vendor/replo-utils/lib/geometry.mjs +9 -0
  20. package/_vendor/replo-utils/lib/json.d.ts +6 -0
  21. package/_vendor/replo-utils/lib/log.d.ts +11 -0
  22. package/_vendor/replo-utils/lib/misc.d.ts +145 -0
  23. package/_vendor/replo-utils/lib/misc.mjs +2 -0
  24. package/_vendor/replo-utils/lib/object.d.ts +31 -0
  25. package/_vendor/replo-utils/lib/parse.d.ts +15 -0
  26. package/_vendor/replo-utils/lib/posthogReplay.d.ts +43 -0
  27. package/_vendor/replo-utils/lib/posthogReplay.mjs +121 -0
  28. package/_vendor/replo-utils/react/use-iframe-rpc-channel.d.ts +14 -0
  29. package/_vendor/replo-utils/react/use-iframe-rpc-channel.mjs +14 -0
  30. package/_vendor/schemas/designLibrary.d.ts +361 -0
  31. package/_vendor/schemas/designLibrary.mjs +126 -0
  32. package/_vendor/schemas/errors.d.ts +459 -0
  33. package/_vendor/schemas/generated/errors.d.ts +61 -0
  34. package/_vendor/schemas/generated/pageAnnotations.d.ts +48 -0
  35. package/_vendor/schemas/generated/pageAnnotations.mjs +1 -0
  36. package/_vendor/schemas/generated/selectMode.d.ts +119 -0
  37. package/_vendor/schemas/generated/selectMode.mjs +1 -0
  38. package/_vendor/schemas/generated/selectModeIframeRpc.d.ts +100 -0
  39. package/_vendor/schemas/generated/selectModeIframeRpc.mjs +1 -0
  40. package/_vendor/schemas/iframeRpcChannel.d.ts +82 -0
  41. package/_vendor/schemas/iframeRpcChannel.mjs +520 -0
  42. package/_vendor/schemas/pageAnnotations.d.ts +134 -0
  43. package/_vendor/schemas/pageAnnotations.mjs +61 -0
  44. package/_vendor/schemas/savedStyles.d.ts +140 -0
  45. package/_vendor/schemas/selectMode.d.ts +522 -0
  46. package/_vendor/schemas/selectMode.mjs +3 -0
  47. package/_vendor/schemas/selectModeIframeRpc.d.ts +900 -0
  48. package/_vendor/schemas/selectModeIframeRpc.mjs +223 -0
  49. package/annotations/AnnotationLayerMount.d.ts +1 -0
  50. package/annotations/AnnotationLayerMount.js +13 -0
  51. package/annotations/AnnotationLayerMount.js.map +7 -0
  52. package/annotations/annotationController.d.ts +1 -0
  53. package/annotations/annotationController.js +384 -0
  54. package/annotations/annotationController.js.map +7 -0
  55. package/annotations/bridgeGate.d.ts +7 -0
  56. package/annotations/bridgeGate.js +22 -0
  57. package/annotations/bridgeGate.js.map +7 -0
  58. package/chunk-3UFG7XTQ.mjs +205 -0
  59. package/chunk-FNTGY7I5.mjs +68 -0
  60. package/chunk-N5EI2MOZ.mjs +1535 -0
  61. package/design-library-preview/DesignLibraryPreview.d.ts +1 -0
  62. package/design-library-preview/DesignLibraryPreview.js +93 -0
  63. package/design-library-preview/DesignLibraryPreview.js.map +7 -0
  64. package/package.json +41 -0
  65. package/posthog-relay.d.ts +11 -0
  66. package/posthog-relay.js +72 -0
  67. package/posthog-relay.js.map +7 -0
  68. package/sandbox-runtime.d.ts +1 -0
  69. package/sandbox-runtime.js +29 -0
  70. package/sandbox-runtime.js.map +7 -0
  71. package/select-mode/EditorSelection.d.ts +24 -0
  72. package/select-mode/EditorSelection.js +95 -0
  73. package/select-mode/EditorSelection.js.map +7 -0
  74. package/select-mode/annotationFrames.d.ts +33 -0
  75. package/select-mode/annotationFrames.js +119 -0
  76. package/select-mode/annotationFrames.js.map +7 -0
  77. package/select-mode/descriptor/elementDescriptor.d.ts +26 -0
  78. package/select-mode/descriptor/elementDescriptor.js +201 -0
  79. package/select-mode/descriptor/elementDescriptor.js.map +7 -0
  80. package/select-mode/descriptor/styleExtraction.d.ts +3 -0
  81. package/select-mode/descriptor/styleExtraction.js +271 -0
  82. package/select-mode/descriptor/styleExtraction.js.map +7 -0
  83. package/select-mode/displayModeStore.d.ts +10 -0
  84. package/select-mode/displayModeStore.js +47 -0
  85. package/select-mode/displayModeStore.js.map +7 -0
  86. package/select-mode/fast-refresh-relay.d.ts +3 -0
  87. package/select-mode/fast-refresh-relay.js +33 -0
  88. package/select-mode/fast-refresh-relay.js.map +7 -0
  89. package/select-mode/fiber.d.ts +49 -0
  90. package/select-mode/fiber.js +306 -0
  91. package/select-mode/fiber.js.map +7 -0
  92. package/select-mode/hooks/useCrosshairCursor.d.ts +12 -0
  93. package/select-mode/hooks/useCrosshairCursor.js +20 -0
  94. package/select-mode/hooks/useCrosshairCursor.js.map +7 -0
  95. package/select-mode/hooks/useEditModeStateMachine.d.ts +16 -0
  96. package/select-mode/hooks/useEditModeStateMachine.js +235 -0
  97. package/select-mode/hooks/useEditModeStateMachine.js.map +7 -0
  98. package/select-mode/hooks/useOptimisticOverrides.d.ts +17 -0
  99. package/select-mode/hooks/useOptimisticOverrides.js +283 -0
  100. package/select-mode/hooks/useOptimisticOverrides.js.map +7 -0
  101. package/select-mode/hooks/useParentMessaging.d.ts +36 -0
  102. package/select-mode/hooks/useParentMessaging.js +74 -0
  103. package/select-mode/hooks/useParentMessaging.js.map +7 -0
  104. package/select-mode/hooks/useSelectModeController.d.ts +8 -0
  105. package/select-mode/hooks/useSelectModeController.js +46 -0
  106. package/select-mode/hooks/useSelectModeController.js.map +7 -0
  107. package/select-mode/hooks/useSelectionHover.d.ts +22 -0
  108. package/select-mode/hooks/useSelectionHover.js +110 -0
  109. package/select-mode/hooks/useSelectionHover.js.map +7 -0
  110. package/select-mode/hooks/useSelectionInteractions.d.ts +37 -0
  111. package/select-mode/hooks/useSelectionInteractions.js +353 -0
  112. package/select-mode/hooks/useSelectionInteractions.js.map +7 -0
  113. package/select-mode/hooks/useSelectionScrollSync.d.ts +28 -0
  114. package/select-mode/hooks/useSelectionScrollSync.js +70 -0
  115. package/select-mode/hooks/useSelectionScrollSync.js.map +7 -0
  116. package/select-mode/hooks/useSiblingResolution.d.ts +6 -0
  117. package/select-mode/hooks/useSiblingResolution.js +59 -0
  118. package/select-mode/hooks/useSiblingResolution.js.map +7 -0
  119. package/select-mode/iframeRpcChannel.d.ts +356 -0
  120. package/select-mode/iframeRpcChannel.js +92 -0
  121. package/select-mode/iframeRpcChannel.js.map +7 -0
  122. package/select-mode/inlineTextMarks.d.ts +6 -0
  123. package/select-mode/inlineTextMarks.js +25 -0
  124. package/select-mode/inlineTextMarks.js.map +7 -0
  125. package/select-mode/interactions/selectionGeometry.d.ts +105 -0
  126. package/select-mode/interactions/selectionGeometry.js +192 -0
  127. package/select-mode/interactions/selectionGeometry.js.map +7 -0
  128. package/select-mode/selectModeSession.d.ts +21 -0
  129. package/select-mode/selectModeSession.js +7 -0
  130. package/select-mode/selectModeSession.js.map +7 -0
  131. package/select-mode/selectionFrame.d.ts +81 -0
  132. package/select-mode/selectionFrame.js +132 -0
  133. package/select-mode/selectionFrame.js.map +7 -0
  134. package/select-mode/symbolicate.d.ts +2 -0
  135. package/select-mode/symbolicate.js +200 -0
  136. package/select-mode/symbolicate.js.map +7 -0
  137. package/select-mode/tiptapInlineEditor.d.ts +23 -0
  138. package/select-mode/tiptapInlineEditor.js +120 -0
  139. package/select-mode/tiptapInlineEditor.js.map +7 -0
@@ -0,0 +1,100 @@
1
+ import type { DisplayMode, SelectModeSelectionType, SelectModeOptimisticUpdatePayload, SelectModeFormatCommand, SelectableData, SelectModeBoundingRect, SelectModeClickPosition, SelectModeSelectionRect, SelectModeTiptapActiveMarks } from "./selectMode.ts";
2
+ export type SelectModeParentMessage = {
3
+ method: "setDisplayMode";
4
+ data: {
5
+ displayMode: DisplayMode;
6
+ };
7
+ } | {
8
+ method: "setSelectionType";
9
+ data: {
10
+ selectionType: SelectModeSelectionType;
11
+ };
12
+ } | {
13
+ method: "applyOptimisticUpdate";
14
+ data: {
15
+ editId: string;
16
+ payload: SelectModeOptimisticUpdatePayload;
17
+ targetSelector: string;
18
+ targetMode: "primary" | "group";
19
+ };
20
+ } | {
21
+ method: "markCommitted";
22
+ data: {
23
+ editId: string;
24
+ sessionId: string;
25
+ };
26
+ } | {
27
+ method: "resolveSessionOverrides";
28
+ data: {
29
+ sessionId: string;
30
+ outcome: "persisted" | "failed";
31
+ };
32
+ } | {
33
+ method: "revertOverride";
34
+ data: {
35
+ editId: string;
36
+ };
37
+ } | {
38
+ method: "enterEditMode";
39
+ } | {
40
+ method: "exitEditMode";
41
+ } | {
42
+ method: "formatCommand";
43
+ data: {
44
+ command: SelectModeFormatCommand;
45
+ };
46
+ };
47
+ export type SelectModeIframeMessage = {
48
+ method: "iframeReady";
49
+ } | {
50
+ method: "elementSelected";
51
+ data: {
52
+ data: SelectableData;
53
+ };
54
+ } | {
55
+ method: "hoverElement";
56
+ data: {
57
+ rect: SelectModeBoundingRect | null;
58
+ };
59
+ } | {
60
+ method: "selectedElementRectUpdate";
61
+ data: {
62
+ rect: SelectModeBoundingRect;
63
+ clickPosition: SelectModeClickPosition;
64
+ };
65
+ } | {
66
+ method: "selectedAreaRectUpdate";
67
+ data: {
68
+ rect: SelectModeBoundingRect;
69
+ };
70
+ } | {
71
+ method: "dragRectUpdated";
72
+ data: {
73
+ dragRect: SelectModeBoundingRect | null;
74
+ selectionRects: SelectModeSelectionRect[];
75
+ };
76
+ } | {
77
+ method: "areaSelected";
78
+ data: {
79
+ rect: SelectModeBoundingRect;
80
+ items: SelectableData[];
81
+ };
82
+ } | {
83
+ method: "textCommitted";
84
+ data: {
85
+ editId: string;
86
+ html: string;
87
+ json: {
88
+ [x: string]: unknown;
89
+ };
90
+ originalInnerHTML: string;
91
+ };
92
+ } | {
93
+ method: "editorStateUpdate";
94
+ data: {
95
+ activeMarks: SelectModeTiptapActiveMarks;
96
+ isEditing: boolean;
97
+ };
98
+ } | {
99
+ method: "fastRefreshComplete";
100
+ };
@@ -0,0 +1,82 @@
1
+ import { z } from "zod";
2
+ export type IframeRpcChannelMethodConfig = Record<string, {
3
+ inputSchema: z.ZodType;
4
+ outputSchema: z.ZodType;
5
+ timeoutMs?: number;
6
+ }>;
7
+ type MethodInput<Config extends IframeRpcChannelMethodConfig, Method extends keyof Config> = z.input<Config[Method]["inputSchema"]>;
8
+ type MethodOutput<Config extends IframeRpcChannelMethodConfig, Method extends keyof Config> = z.output<Config[Method]["outputSchema"]>;
9
+ type IsVoidMethod<Config extends IframeRpcChannelMethodConfig, Method extends keyof Config> = MethodOutput<Config, Method> extends void ? true : false;
10
+ type CallResult<Config extends IframeRpcChannelMethodConfig, Method extends keyof Config> = IsVoidMethod<Config, Method> extends true ? void : Promise<MethodOutput<Config, Method>>;
11
+ type MethodArgs<Config extends IframeRpcChannelMethodConfig, Method extends keyof Config> = undefined extends MethodInput<Config, Method> ? [input?: MethodInput<Config, Method>] : [input: MethodInput<Config, Method>];
12
+ export type IframeRpcChannelRemote<Config extends IframeRpcChannelMethodConfig> = {
13
+ [Method in keyof Config]: (...args: MethodArgs<Config, Method>) => CallResult<Config, Method>;
14
+ };
15
+ export type IframeRpcChannelHandlerMeta<OutgoingMethods extends IframeRpcChannelMethodConfig> = {
16
+ channel: IframeRpcChannelRemote<OutgoingMethods>;
17
+ };
18
+ export type IframeRpcChannelHandlers<IncomingMethods extends IframeRpcChannelMethodConfig, OutgoingMethods extends IframeRpcChannelMethodConfig = IframeRpcChannelMethodConfig> = {
19
+ [Method in keyof IncomingMethods]: (input: z.output<IncomingMethods[Method]["inputSchema"]>, meta: IframeRpcChannelHandlerMeta<OutgoingMethods>) => IsVoidMethod<IncomingMethods, Method> extends true ? void : // Valued handlers stay `unknown` so callers can return domain/generated
20
+ unknown | Promise<unknown>;
21
+ };
22
+ export type IframeRpcChannelInputHandlers<IncomingMethods extends IframeRpcChannelMethodConfig> = {
23
+ [Method in keyof IncomingMethods]: (input: z.output<IncomingMethods[Method]["inputSchema"]>) => IsVoidMethod<IncomingMethods, Method> extends true ? void : unknown | Promise<unknown>;
24
+ };
25
+ export declare const iframeRpcCallSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
26
+ type: z.ZodLiteral<"notify">;
27
+ method: z.ZodString;
28
+ data: z.ZodOptional<z.ZodUnknown>;
29
+ }, z.core.$strip>, z.ZodObject<{
30
+ type: z.ZodLiteral<"request">;
31
+ id: z.ZodUUID;
32
+ method: z.ZodString;
33
+ data: z.ZodOptional<z.ZodUnknown>;
34
+ }, z.core.$strip>, z.ZodObject<{
35
+ type: z.ZodLiteral<"response">;
36
+ id: z.ZodUUID;
37
+ result: z.ZodDiscriminatedUnion<[z.ZodObject<{
38
+ type: z.ZodLiteral<"success">;
39
+ data: z.ZodUnknown;
40
+ }, z.core.$strip>, z.ZodObject<{
41
+ type: z.ZodLiteral<"error">;
42
+ errorMessage: z.ZodString;
43
+ }, z.core.$strip>], "type">;
44
+ }, z.core.$strip>], "type">;
45
+ export type IframeRpcCall = z.infer<typeof iframeRpcCallSchema>;
46
+ type SubscriberWindow = {
47
+ addEventListener: (type: "message", listener: (event: MessageEvent) => void) => void;
48
+ removeEventListener: (type: "message", listener: (event: MessageEvent) => void) => void;
49
+ };
50
+ type TargetIframeWindow = {
51
+ postMessage: (message: unknown, targetOrigin: string) => void;
52
+ };
53
+ type IframeRpcChannelOptions<OutgoingMethods extends IframeRpcChannelMethodConfig, IncomingMethods extends IframeRpcChannelMethodConfig> = {
54
+ subscriberWindow: SubscriberWindow;
55
+ channelIdentifier: string;
56
+ version: number;
57
+ getTargetWindow: () => TargetIframeWindow | null;
58
+ targetOrigin: string;
59
+ isExpectedMessage: (event: MessageEvent) => boolean;
60
+ outgoingMethods: OutgoingMethods;
61
+ incomingMethods: IncomingMethods;
62
+ handlers: IframeRpcChannelHandlers<IncomingMethods, OutgoingMethods>;
63
+ };
64
+ export type IframeRpcChannel<OutgoingMethods extends IframeRpcChannelMethodConfig = IframeRpcChannelMethodConfig> = {
65
+ subscribe: () => () => void;
66
+ call: <Method extends keyof OutgoingMethods & string>(input: {
67
+ method: Method;
68
+ input: MethodInput<OutgoingMethods, Method>;
69
+ }) => CallResult<OutgoingMethods, Method>;
70
+ outgoingMethods: OutgoingMethods;
71
+ };
72
+ /**
73
+ * Flat method-per-key facade over `channel.call`. Prefer this at call sites
74
+ * over hand-writing `useMemo(() => ({ foo: (input) => channel.call(...) }))`.
75
+ *
76
+ * Pass a channel directly, or a getter (plus its method config) when the
77
+ * channel is created lazily / may be unavailable (e.g. SSR).
78
+ */
79
+ export declare function createIframeRpcRemote<OutgoingMethods extends IframeRpcChannelMethodConfig>(channel: IframeRpcChannel<OutgoingMethods>): IframeRpcChannelRemote<OutgoingMethods>;
80
+ export declare function createIframeRpcRemote<OutgoingMethods extends IframeRpcChannelMethodConfig>(getChannel: () => IframeRpcChannel<OutgoingMethods> | null, methods: OutgoingMethods): IframeRpcChannelRemote<OutgoingMethods>;
81
+ export declare function createIframeRpcChannel<OutgoingMethods extends IframeRpcChannelMethodConfig, IncomingMethods extends IframeRpcChannelMethodConfig>(options: IframeRpcChannelOptions<OutgoingMethods, IncomingMethods>): IframeRpcChannel<OutgoingMethods>;
82
+ export {};
@@ -0,0 +1,520 @@
1
+ import { isNullish } from '../../chunk-N5EI2MOZ.mjs';
2
+ import '../../chunk-FNTGY7I5.mjs';
3
+ import { v4 } from 'uuid';
4
+ import { z } from 'zod';
5
+
6
+ function randomUUID() {
7
+ return v4();
8
+ }
9
+
10
+ // ../replo-utils/lib/log.ts
11
+ var defaultLogger = {
12
+ info: console.log,
13
+ debug: console.debug,
14
+ error: console.error
15
+ };
16
+ var ReploError = class _ReploError extends Error {
17
+ config;
18
+ id;
19
+ constructor(config) {
20
+ super(config.message);
21
+ this.message = config.message ?? this.constructor.name;
22
+ this.config = config;
23
+ this.id = randomUUID();
24
+ const cause = config.cause;
25
+ if (cause) {
26
+ this.cause = cause;
27
+ if (cause instanceof _ReploError) {
28
+ this.config.additionalData = {
29
+ ...this.config.additionalData,
30
+ ...cause.config.additionalData
31
+ };
32
+ }
33
+ }
34
+ }
35
+ };
36
+ var errorUserFacingDetailsMessageSchema = z.object({
37
+ type: z.enum([
38
+ "toast",
39
+ "fullPageModal",
40
+ "shopifyStorePassword",
41
+ "billing.addPublishedElements",
42
+ "billing.addShopifyThemeProject",
43
+ "billing.addReploSite",
44
+ // Agent Billing ONLY
45
+ "agentBilling.shopifyThemeBuilder.publishedElements.exceeded",
46
+ "agentBilling.shopifyThemeBuilder.projects.exceeded",
47
+ "agentBilling.sites.exceeded",
48
+ "agentBilling.credits.exceeded",
49
+ "agentBilling.upgradeRequired"
50
+ ]),
51
+ message: z.string(),
52
+ isRecoverable: z.boolean().optional(),
53
+ // NOTE (Matt 2024-03-22): We support detail being an array of strings
54
+ // in FullPageModal only.
55
+ detail: z.array(z.string()).or(z.string()).optional(),
56
+ key: z.string(),
57
+ usage: z.object({
58
+ current: z.number(),
59
+ maximum: z.number()
60
+ }).optional(),
61
+ plan: z.string().optional(),
62
+ callToAction: z.discriminatedUnion("type", [
63
+ z.object({ type: z.literal("reload") }),
64
+ z.object({ type: z.literal("installApp") }),
65
+ z.object({ type: z.literal("goToHomepage") }),
66
+ z.object({ type: z.literal("navigateToProjectEditor") }),
67
+ z.object({ type: z.literal("closeModal") }),
68
+ z.object({
69
+ type: z.literal("navigateToLink"),
70
+ link: z.discriminatedUnion("type", [
71
+ z.object({ type: z.literal("errors.wrongJsonExtension") }),
72
+ z.object({ type: z.literal("errors.chunkingError") }),
73
+ z.object({ type: z.literal("errors.indexBackupError") }),
74
+ z.object({
75
+ type: z.literal("errors.shopifyIntegrationAlreadyAssigned")
76
+ }),
77
+ z.object({
78
+ type: z.literal("errors.shopifyIntegrationMissingAccessScopes"),
79
+ url: z.string()
80
+ }),
81
+ z.object({
82
+ type: z.literal("customLink"),
83
+ url: z.string()
84
+ }),
85
+ z.object({
86
+ type: z.literal(
87
+ "errors.projectConnectedToBillingIntegrationForShopifyWorkspace"
88
+ )
89
+ }),
90
+ z.object({ type: z.literal("errors.themeAssetSizeLimitExceeded") }),
91
+ z.object({ type: z.literal("errors.themeTotalSizeLimitExceeded") }),
92
+ z.object({
93
+ type: z.literal("errors.pageMetafieldDefinitionsChanged")
94
+ }),
95
+ z.object({ type: z.literal("settings.billing") })
96
+ ])
97
+ })
98
+ ]).optional()
99
+ }).describe("ErrorUserFacingDetailsWithMessage");
100
+ z.discriminatedUnion("type", [
101
+ /**
102
+ * Normal errors, show error UI depending on the type
103
+ */
104
+ errorUserFacingDetailsMessageSchema,
105
+ /**
106
+ * Errors which should be ignored by the frontend. This is uncommon, usually
107
+ * you want to show an error in the UI, and if you don't want to then you catch
108
+ * the error in your route.
109
+ */
110
+ z.object({
111
+ type: z.literal("uncommon_none"),
112
+ key: z.string(),
113
+ usage: z.object({
114
+ current: z.number(),
115
+ maximum: z.number()
116
+ }).optional(),
117
+ message: z.string().optional(),
118
+ isRecoverable: z.boolean().optional(),
119
+ detail: z.array(z.string()).or(z.string()).optional()
120
+ })
121
+ ]).describe("ErrorUserFacingDetails");
122
+ var IframeRpcChannelError = class extends ReploError {
123
+ };
124
+ var IframeRpcChannelTimeoutError = class extends IframeRpcChannelError {
125
+ constructor({ method, timeoutMs }) {
126
+ super({
127
+ message: `RPC method '${method}' timed out after ${timeoutMs}ms`,
128
+ additionalData: { method, timeoutMs }
129
+ });
130
+ }
131
+ };
132
+ var IframeRpcChannelRemoteError = class extends IframeRpcChannelError {
133
+ constructor({ method, message }) {
134
+ super({
135
+ message: `RPC method '${method}' failed: ${message}`,
136
+ additionalData: { method }
137
+ });
138
+ }
139
+ };
140
+ var IframeRpcChannelUnsubscribedError = class extends IframeRpcChannelError {
141
+ constructor({ method }) {
142
+ super({
143
+ message: `RPC was unsubscribed while '${method}' was pending`,
144
+ additionalData: { method }
145
+ });
146
+ }
147
+ };
148
+ var IframeRpcChannelUnknownMethodError = class extends IframeRpcChannelError {
149
+ constructor({ method }) {
150
+ super({
151
+ message: `Unknown RPC method '${method}'`,
152
+ additionalData: { method }
153
+ });
154
+ }
155
+ };
156
+ var IframeRpcChannelConfigurationError = class extends IframeRpcChannelError {
157
+ constructor({ message }) {
158
+ super({ message });
159
+ }
160
+ };
161
+ var IframeRpcChannelInvalidInputError = class extends IframeRpcChannelError {
162
+ constructor({ method, cause }) {
163
+ super({
164
+ message: `Invalid RPC input for '${method}': ${cause.message}`,
165
+ cause,
166
+ additionalData: { method }
167
+ });
168
+ }
169
+ };
170
+
171
+ // ../schemas/iframeRpcChannel.ts
172
+ var iframeRpcCallResultSchema = z.discriminatedUnion("type", [
173
+ z.object({
174
+ type: z.literal("success"),
175
+ data: z.unknown()
176
+ }),
177
+ z.object({
178
+ type: z.literal("error"),
179
+ errorMessage: z.string()
180
+ })
181
+ ]);
182
+ var iframeRpcCallSchema = z.discriminatedUnion("type", [
183
+ z.object({
184
+ type: z.literal("notify"),
185
+ method: z.string(),
186
+ data: z.unknown().optional()
187
+ }),
188
+ z.object({
189
+ type: z.literal("request"),
190
+ id: z.uuid(),
191
+ method: z.string(),
192
+ data: z.unknown().optional()
193
+ }),
194
+ z.object({
195
+ type: z.literal("response"),
196
+ id: z.uuid(),
197
+ result: iframeRpcCallResultSchema
198
+ })
199
+ ]);
200
+ function isFireAndForgetOutputSchema(outputSchema) {
201
+ return outputSchema.type === "void" || outputSchema instanceof z.ZodVoid;
202
+ }
203
+ function isUndefinedInputSchema(inputSchema) {
204
+ return inputSchema.type === "undefined" || inputSchema instanceof z.ZodUndefined;
205
+ }
206
+ function coerceEmptyRpcInput({
207
+ input,
208
+ inputSchema
209
+ }) {
210
+ if (isNullish(input) && isUndefinedInputSchema(inputSchema)) {
211
+ return void 0;
212
+ }
213
+ return input;
214
+ }
215
+ function createIframeRpcRemote(channelOrGetChannel, methods) {
216
+ const getChannel = typeof channelOrGetChannel === "function" ? channelOrGetChannel : () => channelOrGetChannel;
217
+ const outgoingMethods = methods ?? getChannel()?.outgoingMethods ?? null;
218
+ if (!outgoingMethods) {
219
+ throw new IframeRpcChannelConfigurationError({
220
+ message: "createIframeRpcRemote requires methods when the channel may be null"
221
+ });
222
+ }
223
+ return Object.fromEntries(
224
+ Object.keys(outgoingMethods).map(
225
+ (method) => {
226
+ return [
227
+ method,
228
+ (input) => {
229
+ const definition = outgoingMethods[method];
230
+ const channel = getChannel();
231
+ if (!channel) {
232
+ if (definition && isFireAndForgetOutputSchema(definition.outputSchema)) {
233
+ return void 0;
234
+ }
235
+ return Promise.reject(
236
+ new IframeRpcChannelUnsubscribedError({ method })
237
+ );
238
+ }
239
+ return channel.call({
240
+ method,
241
+ input
242
+ });
243
+ }
244
+ ];
245
+ }
246
+ )
247
+ );
248
+ }
249
+ function createIframeRpcChannel(options) {
250
+ const {
251
+ subscriberWindow,
252
+ channelIdentifier,
253
+ version,
254
+ getTargetWindow,
255
+ targetOrigin,
256
+ isExpectedMessage,
257
+ outgoingMethods,
258
+ incomingMethods,
259
+ handlers
260
+ } = options;
261
+ const pendingRequests = /* @__PURE__ */ new Map();
262
+ let subscriberCount = 0;
263
+ let handlerMeta;
264
+ const postMessage = (call2) => {
265
+ getTargetWindow()?.postMessage(
266
+ {
267
+ channelIdentifier,
268
+ version,
269
+ ...call2
270
+ },
271
+ targetOrigin
272
+ );
273
+ };
274
+ const respondWithError = ({
275
+ id,
276
+ errorMessage
277
+ }) => {
278
+ postMessage({
279
+ type: "response",
280
+ id,
281
+ result: { type: "error", errorMessage }
282
+ });
283
+ };
284
+ const handleRequest = async (message) => {
285
+ const definition = incomingMethods[message.method];
286
+ if (!definition || isFireAndForgetOutputSchema(definition.outputSchema)) {
287
+ respondWithError({
288
+ id: message.id,
289
+ errorMessage: `Unknown RPC method '${message.method}'`
290
+ });
291
+ return;
292
+ }
293
+ const parsedInput = definition.inputSchema.safeParse(
294
+ coerceEmptyRpcInput({
295
+ input: message.data,
296
+ inputSchema: definition.inputSchema
297
+ })
298
+ );
299
+ if (!parsedInput.success) {
300
+ respondWithError({
301
+ id: message.id,
302
+ errorMessage: `Invalid request payload for '${message.method}': ${parsedInput.error.message}`
303
+ });
304
+ return;
305
+ }
306
+ try {
307
+ const handler = handlers[
308
+ // eslint-disable-next-line replo/no-as-cast -- NOTE (Noah, 2026-07-25): The method existence check above narrows this dynamic wire string to the configured handler keys.
309
+ message.method
310
+ ];
311
+ const result = await handler(parsedInput.data, handlerMeta);
312
+ const parsedOutput = definition.outputSchema.safeParse(result);
313
+ if (!parsedOutput.success) {
314
+ respondWithError({
315
+ id: message.id,
316
+ errorMessage: `Invalid response payload for '${message.method}': ${parsedOutput.error.message}`
317
+ });
318
+ return;
319
+ }
320
+ postMessage({
321
+ type: "response",
322
+ id: message.id,
323
+ result: { type: "success", data: parsedOutput.data }
324
+ });
325
+ } catch (error) {
326
+ respondWithError({
327
+ id: message.id,
328
+ errorMessage: error instanceof Error ? error.message : String(error)
329
+ });
330
+ }
331
+ };
332
+ const handleNotification = (message) => {
333
+ const definition = incomingMethods[message.method];
334
+ if (!definition || !isFireAndForgetOutputSchema(definition.outputSchema)) {
335
+ return;
336
+ }
337
+ const parsedInput = definition.inputSchema.safeParse(
338
+ coerceEmptyRpcInput({
339
+ input: message.data,
340
+ inputSchema: definition.inputSchema
341
+ })
342
+ );
343
+ if (!parsedInput.success) {
344
+ defaultLogger.error(
345
+ `[iframeRpcChannel] Dropping invalid notify for '${message.method}': ${parsedInput.error.message}`
346
+ );
347
+ return;
348
+ }
349
+ const handler = handlers[
350
+ // eslint-disable-next-line replo/no-as-cast -- NOTE (Noah, 2026-08-07): Method existence check narrows this wire string to configured handler keys.
351
+ message.method
352
+ ];
353
+ handler(parsedInput.data, handlerMeta);
354
+ };
355
+ const handleResponse = (message) => {
356
+ const pendingRequest = pendingRequests.get(message.id);
357
+ if (!pendingRequest) {
358
+ return;
359
+ }
360
+ clearTimeout(pendingRequest.timeoutId);
361
+ pendingRequests.delete(message.id);
362
+ if (message.result.type === "error") {
363
+ pendingRequest.reject(
364
+ new IframeRpcChannelRemoteError({
365
+ method: pendingRequest.method,
366
+ message: message.result.errorMessage
367
+ })
368
+ );
369
+ return;
370
+ }
371
+ const definition = outgoingMethods[pendingRequest.method];
372
+ const parsedOutput = definition?.outputSchema.safeParse(
373
+ message.result.data
374
+ );
375
+ if (!parsedOutput?.success) {
376
+ pendingRequest.reject(
377
+ new IframeRpcChannelRemoteError({
378
+ method: pendingRequest.method,
379
+ message: `Invalid response payload: ${parsedOutput?.error.message ?? "unknown method"}`
380
+ })
381
+ );
382
+ return;
383
+ }
384
+ pendingRequest.resolve(parsedOutput.data);
385
+ };
386
+ const handleMessage = (event) => {
387
+ if (!isExpectedMessage(event)) {
388
+ return;
389
+ }
390
+ const parsedMetadata = z.object({
391
+ channelIdentifier: z.literal(channelIdentifier),
392
+ version: z.literal(version)
393
+ }).safeParse(event.data);
394
+ if (!parsedMetadata.success) {
395
+ return;
396
+ }
397
+ const parsedCall = iframeRpcCallSchema.safeParse(event.data);
398
+ if (!parsedCall.success) {
399
+ return;
400
+ }
401
+ const message = parsedCall.data;
402
+ if (message.type === "notify") {
403
+ handleNotification(message);
404
+ return;
405
+ }
406
+ if (message.type === "request") {
407
+ void handleRequest(message);
408
+ return;
409
+ }
410
+ handleResponse(message);
411
+ };
412
+ const rejectPendingRequests = () => {
413
+ for (const pendingRequest of pendingRequests.values()) {
414
+ clearTimeout(pendingRequest.timeoutId);
415
+ pendingRequest.reject(
416
+ new IframeRpcChannelUnsubscribedError({
417
+ method: pendingRequest.method
418
+ })
419
+ );
420
+ }
421
+ pendingRequests.clear();
422
+ };
423
+ const subscribe = () => {
424
+ subscriberCount += 1;
425
+ if (subscriberCount === 1) {
426
+ subscriberWindow.addEventListener("message", handleMessage);
427
+ }
428
+ let isActive = true;
429
+ return () => {
430
+ if (!isActive) {
431
+ return;
432
+ }
433
+ isActive = false;
434
+ subscriberCount -= 1;
435
+ if (subscriberCount === 0) {
436
+ subscriberWindow.removeEventListener("message", handleMessage);
437
+ rejectPendingRequests();
438
+ }
439
+ };
440
+ };
441
+ const call = ({
442
+ method,
443
+ input
444
+ }) => {
445
+ const definition = outgoingMethods[method];
446
+ if (!definition) {
447
+ return Promise.reject(
448
+ new IframeRpcChannelUnknownMethodError({ method })
449
+ );
450
+ }
451
+ const isFireAndForget = isFireAndForgetOutputSchema(
452
+ definition.outputSchema
453
+ );
454
+ const parsedInput = definition.inputSchema.safeParse(
455
+ coerceEmptyRpcInput({
456
+ input,
457
+ inputSchema: definition.inputSchema
458
+ })
459
+ );
460
+ if (!parsedInput.success) {
461
+ const error = new IframeRpcChannelInvalidInputError({
462
+ method,
463
+ cause: parsedInput.error
464
+ });
465
+ if (isFireAndForget) {
466
+ throw error;
467
+ }
468
+ return Promise.reject(error);
469
+ }
470
+ if (isFireAndForget) {
471
+ postMessage({
472
+ type: "notify",
473
+ method,
474
+ data: parsedInput.data
475
+ });
476
+ return void 0;
477
+ }
478
+ if (subscriberCount === 0) {
479
+ return Promise.reject(
480
+ new IframeRpcChannelUnsubscribedError({ method })
481
+ );
482
+ }
483
+ const id = randomUUID();
484
+ const timeoutMs = definition.timeoutMs ?? 3e4;
485
+ return new Promise(
486
+ (resolve, reject) => {
487
+ const timeoutId = setTimeout(() => {
488
+ pendingRequests.delete(id);
489
+ reject(new IframeRpcChannelTimeoutError({ method, timeoutMs }));
490
+ }, timeoutMs);
491
+ pendingRequests.set(id, {
492
+ method,
493
+ // PendingRequest is shared across methods, so resolve is typed as
494
+ // unknown; handleResponse already validated against this method's
495
+ // outputSchema before calling it.
496
+ resolve: (value) => {
497
+ resolve(value);
498
+ },
499
+ reject,
500
+ timeoutId
501
+ });
502
+ postMessage({
503
+ type: "request",
504
+ id,
505
+ method,
506
+ data: parsedInput.data
507
+ });
508
+ }
509
+ );
510
+ };
511
+ const channel = {
512
+ subscribe,
513
+ call,
514
+ outgoingMethods
515
+ };
516
+ handlerMeta = { channel: createIframeRpcRemote(channel) };
517
+ return channel;
518
+ }
519
+
520
+ export { createIframeRpcChannel, createIframeRpcRemote, iframeRpcCallSchema };