@replohq/sandbox-runtime 1.17.0 → 1.18.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.
@@ -1,17 +1,43 @@
1
1
  "use client";
2
2
  import { jsx } from "react/jsx-runtime";
3
3
  import * as React from "react";
4
+ import { v4 as uuidv4 } from "uuid";
5
+ import { getPreviewHealthActions } from "./previewHealth";
4
6
  class ErrorBoundary extends React.Component {
5
7
  state = {
6
8
  didCatch: false
7
9
  };
10
+ boundaryId = uuidv4();
11
+ caughtError = null;
8
12
  static getDerivedStateFromError() {
9
13
  return { didCatch: true };
10
14
  }
15
+ componentDidMount() {
16
+ if (!this.state.didCatch) {
17
+ getPreviewHealthActions()?.boundaryRendered(this.boundaryId);
18
+ } else if (this.caughtError) {
19
+ getPreviewHealthActions()?.boundaryCaughtError(
20
+ this.boundaryId,
21
+ this.caughtError
22
+ );
23
+ }
24
+ }
25
+ componentWillUnmount() {
26
+ getPreviewHealthActions()?.boundaryUnmounted(this.boundaryId);
27
+ }
11
28
  componentDidCatch(error, info) {
29
+ this.caughtError = { error, componentStack: info.componentStack ?? null };
30
+ getPreviewHealthActions()?.boundaryCaughtError(
31
+ this.boundaryId,
32
+ this.caughtError
33
+ );
12
34
  this.props.onError?.(error, info);
13
35
  }
14
36
  componentDidUpdate(previousProps, previousState) {
37
+ if (previousState.didCatch && !this.state.didCatch) {
38
+ this.caughtError = null;
39
+ getPreviewHealthActions()?.boundaryRendered(this.boundaryId);
40
+ }
15
41
  if (this.state.didCatch && previousState.didCatch && !Object.is(previousProps.resetKey, this.props.resetKey)) {
16
42
  this.setState({ didCatch: false });
17
43
  }
@@ -1,5 +1,9 @@
1
1
  import { jsx } from "react/jsx-runtime";
2
- const BEACON_SCRIPT = `if (window.parent !== window) { window.parent.postMessage({ type: "replo:preview:status", status: "ok", phase: "document", route: window.location.pathname }, "*"); }`;
2
+ import {
3
+ PREVIEW_HEALTH_IFRAME_RPC_CHANNEL_ID,
4
+ PREVIEW_HEALTH_IFRAME_RPC_CHANNEL_VERSION
5
+ } from "./_vendor/schemas/previewHealthIframeRpc.mjs";
6
+ const BEACON_SCRIPT = `if (window.parent !== window) { window.parent.postMessage({ channelIdentifier: ${JSON.stringify(PREVIEW_HEALTH_IFRAME_RPC_CHANNEL_ID)}, version: ${PREVIEW_HEALTH_IFRAME_RPC_CHANNEL_VERSION}, type: "notify", method: "documentArrived", data: { route: window.location.pathname } }, "*"); }`;
3
7
  function DocumentReadyBeacon() {
4
8
  return /* @__PURE__ */ jsx("script", { dangerouslySetInnerHTML: { __html: BEACON_SCRIPT } });
5
9
  }
@@ -1,544 +1,6 @@
1
- import { randomUUID } from '../../chunk-LIPH4O4F.mjs';
2
- import { isNullish } from '../../chunk-PH3S6LJI.mjs';
1
+ export { createIframeRpcChannel, createIframeRpcRemote, iframeRpcCallSchema } from '../../chunk-KRA4LUIR.mjs';
2
+ import '../../chunk-LIPH4O4F.mjs';
3
+ import '../../chunk-PH3S6LJI.mjs';
3
4
  import '../../chunk-FLYEIRFK.mjs';
4
5
  import '../../chunk-TESFJQID.mjs';
5
6
  import '../../chunk-G3S6ZNPF.mjs';
6
- import * as z from 'zod';
7
-
8
- var defaultLogger = {
9
- info: console.log,
10
- debug: console.debug,
11
- error: console.error
12
- };
13
-
14
- var ReploError = class _ReploError extends Error {
15
- config;
16
- id;
17
- constructor(config) {
18
- super(config.message);
19
- this.message = config.message ?? this.constructor.name;
20
- this.config = config;
21
- this.id = randomUUID();
22
- const cause = config.cause;
23
- if (cause) {
24
- this.cause = cause;
25
- if (cause instanceof _ReploError) {
26
- this.config.additionalData = {
27
- ...this.config.additionalData,
28
- ...cause.config.additionalData
29
- };
30
- }
31
- }
32
- }
33
- };
34
-
35
- var errorUserFacingDetailsMessageSchema = z.object({
36
- type: z.enum([
37
- "toast",
38
- "fullPageModal",
39
- "shopifyStorePassword",
40
- "billing.addPublishedElements",
41
- "billing.addShopifyThemeProject",
42
- "billing.addReploSite",
43
- "agentBilling.shopifyThemeBuilder.publishedElements.exceeded",
44
- "agentBilling.shopifyThemeBuilder.projects.exceeded",
45
- "agentBilling.sites.exceeded",
46
- "agentBilling.credits.exceeded",
47
- "agentBilling.upgradeRequired"
48
- ]),
49
- message: z.string(),
50
- isRecoverable: z.boolean().optional(),
51
- detail: z.array(z.string()).or(z.string()).optional(),
52
- key: z.string(),
53
- details: z.record(z.string(), z.unknown()).optional(),
54
- usage: z.object({
55
- current: z.number(),
56
- maximum: z.number()
57
- }).optional(),
58
- plan: z.string().optional(),
59
- callToAction: z.discriminatedUnion("type", [
60
- z.object({ type: z.literal("reload") }),
61
- z.object({ type: z.literal("installApp") }),
62
- z.object({ type: z.literal("goToHomepage") }),
63
- z.object({ type: z.literal("navigateToProjectEditor") }),
64
- z.object({ type: z.literal("closeModal") }),
65
- z.object({
66
- type: z.literal("navigateToLink"),
67
- link: z.discriminatedUnion("type", [
68
- z.object({ type: z.literal("errors.wrongJsonExtension") }),
69
- z.object({ type: z.literal("errors.chunkingError") }),
70
- z.object({ type: z.literal("errors.indexBackupError") }),
71
- z.object({
72
- type: z.literal("errors.shopifyIntegrationAlreadyAssigned")
73
- }),
74
- z.object({
75
- type: z.literal("errors.shopifyIntegrationMissingAccessScopes"),
76
- url: z.string()
77
- }),
78
- z.object({
79
- type: z.literal("customLink"),
80
- url: z.string()
81
- }),
82
- z.object({
83
- type: z.literal(
84
- "errors.projectConnectedToBillingIntegrationForShopifyWorkspace"
85
- )
86
- }),
87
- z.object({ type: z.literal("errors.themeAssetSizeLimitExceeded") }),
88
- z.object({ type: z.literal("errors.themeTotalSizeLimitExceeded") }),
89
- z.object({
90
- type: z.literal("errors.pageMetafieldDefinitionsChanged")
91
- }),
92
- z.object({ type: z.literal("settings.billing") })
93
- ])
94
- })
95
- ]).optional()
96
- }).describe("ErrorUserFacingDetailsWithMessage");
97
- z.discriminatedUnion("type", [
98
- errorUserFacingDetailsMessageSchema,
99
- z.object({
100
- type: z.literal("uncommon_none"),
101
- key: z.string(),
102
- usage: z.object({
103
- current: z.number(),
104
- maximum: z.number()
105
- }).optional(),
106
- message: z.string().optional(),
107
- isRecoverable: z.boolean().optional(),
108
- detail: z.array(z.string()).or(z.string()).optional()
109
- })
110
- ]).describe("ErrorUserFacingDetails");
111
- var IframeRpcChannelError = class extends ReploError {
112
- };
113
- var IframeRpcChannelTimeoutError = class extends IframeRpcChannelError {
114
- constructor({ method, timeoutMs }) {
115
- super({
116
- message: `RPC method '${method}' timed out after ${timeoutMs}ms`,
117
- additionalData: { method, timeoutMs }
118
- });
119
- }
120
- };
121
- var IframeRpcChannelTargetUnavailableError = class extends IframeRpcChannelError {
122
- constructor({
123
- callType,
124
- method
125
- }) {
126
- super({
127
- message: method ? `RPC target window was unavailable for '${method}'` : "RPC target window was unavailable for a response",
128
- additionalData: { callType, method }
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
- var iframeRpcCallResultSchema = z.discriminatedUnion("type", [
172
- z.object({
173
- type: z.literal("success"),
174
- data: z.unknown()
175
- }),
176
- z.object({
177
- type: z.literal("error"),
178
- errorMessage: z.string()
179
- })
180
- ]);
181
- var iframeRpcCallSchema = z.discriminatedUnion("type", [
182
- z.object({
183
- type: z.literal("notify"),
184
- method: z.string(),
185
- data: z.unknown().optional()
186
- }),
187
- z.object({
188
- type: z.literal("request"),
189
- id: z.uuid(),
190
- method: z.string(),
191
- data: z.unknown().optional()
192
- }),
193
- z.object({
194
- type: z.literal("response"),
195
- id: z.uuid(),
196
- result: iframeRpcCallResultSchema
197
- })
198
- ]);
199
- function isFireAndForgetOutputSchema(outputSchema) {
200
- return outputSchema.type === "void" || outputSchema instanceof z.ZodVoid;
201
- }
202
- function isUndefinedInputSchema(inputSchema) {
203
- return inputSchema.type === "undefined" || inputSchema instanceof z.ZodUndefined;
204
- }
205
- function coerceEmptyRpcInput({
206
- input,
207
- inputSchema
208
- }) {
209
- if (isNullish(input) && isUndefinedInputSchema(inputSchema)) {
210
- return void 0;
211
- }
212
- return input;
213
- }
214
- function createIframeRpcRemote(channelOrGetChannel, methods) {
215
- const getChannel = typeof channelOrGetChannel === "function" ? channelOrGetChannel : () => channelOrGetChannel;
216
- const outgoingMethods = methods ?? getChannel()?.outgoingMethods ?? null;
217
- if (!outgoingMethods) {
218
- throw new IframeRpcChannelConfigurationError({
219
- message: "createIframeRpcRemote requires methods when the channel may be null"
220
- });
221
- }
222
- return Object.fromEntries(
223
- Object.keys(outgoingMethods).map(
224
- (method) => {
225
- return [
226
- method,
227
- (input) => {
228
- const definition = outgoingMethods[method];
229
- const channel = getChannel();
230
- if (!channel) {
231
- if (definition && isFireAndForgetOutputSchema(definition.outputSchema)) {
232
- return void 0;
233
- }
234
- return Promise.reject(
235
- new IframeRpcChannelUnsubscribedError({ method })
236
- );
237
- }
238
- return channel.call({
239
- method,
240
- input
241
- });
242
- }
243
- ];
244
- }
245
- )
246
- );
247
- }
248
- function createIframeRpcChannel(options) {
249
- const {
250
- subscriberWindow,
251
- channelIdentifier,
252
- version,
253
- getTargetWindow,
254
- targetOrigin,
255
- isExpectedMessage,
256
- outgoingMethods,
257
- incomingMethods,
258
- handlers
259
- } = options;
260
- const pendingRequests = /* @__PURE__ */ new Map();
261
- let subscriberCount = 0;
262
- let handlerMeta;
263
- const postMessage = (call2, targetWindow = getTargetWindow()) => {
264
- targetWindow?.postMessage(
265
- {
266
- channelIdentifier,
267
- version,
268
- ...call2
269
- },
270
- targetOrigin
271
- );
272
- };
273
- const respondWithError = ({
274
- id,
275
- errorMessage
276
- }) => {
277
- postMessage({
278
- type: "response",
279
- id,
280
- result: { type: "error", errorMessage }
281
- });
282
- };
283
- const handleRequest = async (message) => {
284
- const definition = incomingMethods[message.method];
285
- if (!definition || isFireAndForgetOutputSchema(definition.outputSchema)) {
286
- respondWithError({
287
- id: message.id,
288
- errorMessage: `Unknown RPC method '${message.method}'`
289
- });
290
- return;
291
- }
292
- const parsedInput = definition.inputSchema.safeParse(
293
- coerceEmptyRpcInput({
294
- input: message.data,
295
- inputSchema: definition.inputSchema
296
- })
297
- );
298
- if (!parsedInput.success) {
299
- respondWithError({
300
- id: message.id,
301
- errorMessage: `Invalid request payload for '${message.method}': ${parsedInput.error.message}`
302
- });
303
- return;
304
- }
305
- try {
306
- const handler = handlers[
307
- message.method
308
- ];
309
- const result = await handler(parsedInput.data, handlerMeta);
310
- const parsedOutput = definition.outputSchema.safeParse(result);
311
- if (!parsedOutput.success) {
312
- respondWithError({
313
- id: message.id,
314
- errorMessage: `Invalid response payload for '${message.method}': ${parsedOutput.error.message}`
315
- });
316
- return;
317
- }
318
- postMessage({
319
- type: "response",
320
- id: message.id,
321
- result: { type: "success", data: parsedOutput.data }
322
- });
323
- } catch (error) {
324
- respondWithError({
325
- id: message.id,
326
- errorMessage: error instanceof Error ? error.message : String(error)
327
- });
328
- }
329
- };
330
- const handleNotification = (message) => {
331
- const definition = incomingMethods[message.method];
332
- if (!definition || !isFireAndForgetOutputSchema(definition.outputSchema)) {
333
- return;
334
- }
335
- const parsedInput = definition.inputSchema.safeParse(
336
- coerceEmptyRpcInput({
337
- input: message.data,
338
- inputSchema: definition.inputSchema
339
- })
340
- );
341
- if (!parsedInput.success) {
342
- defaultLogger.error(
343
- `[iframeRpcChannel] Dropping invalid notify for '${message.method}': ${parsedInput.error.message}`
344
- );
345
- return;
346
- }
347
- const handler = handlers[
348
- message.method
349
- ];
350
- handler(parsedInput.data, handlerMeta);
351
- };
352
- const handleResponse = (message) => {
353
- const pendingRequest = pendingRequests.get(message.id);
354
- if (!pendingRequest) {
355
- return;
356
- }
357
- clearTimeout(pendingRequest.timeoutId);
358
- pendingRequests.delete(message.id);
359
- if (message.result.type === "error") {
360
- pendingRequest.reject(
361
- new IframeRpcChannelRemoteError({
362
- method: pendingRequest.method,
363
- message: message.result.errorMessage
364
- })
365
- );
366
- return;
367
- }
368
- const definition = outgoingMethods[pendingRequest.method];
369
- const parsedOutput = definition?.outputSchema.safeParse(
370
- message.result.data
371
- );
372
- if (!parsedOutput?.success) {
373
- pendingRequest.reject(
374
- new IframeRpcChannelRemoteError({
375
- method: pendingRequest.method,
376
- message: `Invalid response payload: ${parsedOutput?.error.message ?? "unknown method"}`
377
- })
378
- );
379
- return;
380
- }
381
- pendingRequest.resolve(parsedOutput.data);
382
- };
383
- const handleMessage = (event) => {
384
- const parsedMetadata = z.object({
385
- channelIdentifier: z.literal(channelIdentifier),
386
- version: z.literal(version)
387
- }).safeParse(event.data);
388
- if (!parsedMetadata.success) {
389
- return;
390
- }
391
- if (!isExpectedMessage(event)) {
392
- const isSiblingFrame = typeof document !== "undefined" && Array.from(document.querySelectorAll("iframe")).some((frame) => {
393
- return event.source !== null && frame.contentWindow === event.source;
394
- });
395
- if (isSiblingFrame) {
396
- return;
397
- }
398
- defaultLogger.error("[iframeRpcChannel] Dropping unexpected message", {
399
- channelIdentifier,
400
- version,
401
- origin: event.origin,
402
- hasSource: Boolean(event.source)
403
- });
404
- return;
405
- }
406
- const parsedCall = iframeRpcCallSchema.safeParse(event.data);
407
- if (!parsedCall.success) {
408
- return;
409
- }
410
- const message = parsedCall.data;
411
- if (message.type === "notify") {
412
- handleNotification(message);
413
- return;
414
- }
415
- if (message.type === "request") {
416
- void handleRequest(message);
417
- return;
418
- }
419
- handleResponse(message);
420
- };
421
- const rejectPendingRequests = () => {
422
- for (const pendingRequest of pendingRequests.values()) {
423
- clearTimeout(pendingRequest.timeoutId);
424
- pendingRequest.reject(
425
- new IframeRpcChannelUnsubscribedError({
426
- method: pendingRequest.method
427
- })
428
- );
429
- }
430
- pendingRequests.clear();
431
- };
432
- const subscribe = () => {
433
- subscriberCount += 1;
434
- if (subscriberCount === 1) {
435
- subscriberWindow.addEventListener("message", handleMessage);
436
- }
437
- let isActive = true;
438
- return () => {
439
- if (!isActive) {
440
- return;
441
- }
442
- isActive = false;
443
- subscriberCount -= 1;
444
- if (subscriberCount === 0) {
445
- subscriberWindow.removeEventListener("message", handleMessage);
446
- rejectPendingRequests();
447
- }
448
- };
449
- };
450
- const call = ({
451
- method,
452
- input
453
- }) => {
454
- const definition = outgoingMethods[method];
455
- if (!definition) {
456
- return Promise.reject(
457
- new IframeRpcChannelUnknownMethodError({ method })
458
- );
459
- }
460
- const isFireAndForget = isFireAndForgetOutputSchema(
461
- definition.outputSchema
462
- );
463
- const parsedInput = definition.inputSchema.safeParse(
464
- coerceEmptyRpcInput({
465
- input,
466
- inputSchema: definition.inputSchema
467
- })
468
- );
469
- if (!parsedInput.success) {
470
- const error = new IframeRpcChannelInvalidInputError({
471
- method,
472
- cause: parsedInput.error
473
- });
474
- if (isFireAndForget) {
475
- throw error;
476
- }
477
- return Promise.reject(error);
478
- }
479
- if (isFireAndForget) {
480
- postMessage({
481
- type: "notify",
482
- method,
483
- data: parsedInput.data
484
- });
485
- return void 0;
486
- }
487
- if (subscriberCount === 0) {
488
- return Promise.reject(
489
- new IframeRpcChannelUnsubscribedError({ method })
490
- );
491
- }
492
- const targetWindow = getTargetWindow();
493
- if (!targetWindow) {
494
- return Promise.reject(
495
- new IframeRpcChannelTargetUnavailableError({
496
- callType: "request",
497
- method
498
- })
499
- );
500
- }
501
- const id = randomUUID();
502
- const timeoutMs = definition.timeoutMs ?? 3e4;
503
- return new Promise(
504
- (resolve, reject) => {
505
- const timeoutId = setTimeout(() => {
506
- pendingRequests.delete(id);
507
- reject(new IframeRpcChannelTimeoutError({ method, timeoutMs }));
508
- }, timeoutMs);
509
- pendingRequests.set(id, {
510
- method,
511
- resolve: (value) => {
512
- resolve(value);
513
- },
514
- reject,
515
- timeoutId
516
- });
517
- try {
518
- postMessage(
519
- {
520
- type: "request",
521
- id,
522
- method,
523
- data: parsedInput.data
524
- },
525
- targetWindow
526
- );
527
- } catch (error) {
528
- clearTimeout(timeoutId);
529
- pendingRequests.delete(id);
530
- reject(error);
531
- }
532
- }
533
- );
534
- };
535
- const channel = {
536
- subscribe,
537
- call,
538
- outgoingMethods
539
- };
540
- handlerMeta = { channel: createIframeRpcRemote(channel) };
541
- return channel;
542
- }
543
-
544
- export { createIframeRpcChannel, createIframeRpcRemote, iframeRpcCallSchema };