@softr/sdk 0.0.43 → 0.0.44
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/dist/boot-error.js +42 -2
- package/dist/package.js +1 -1
- package/package.json +1 -1
package/dist/boot-error.js
CHANGED
|
@@ -23,6 +23,17 @@ function bootErrorMiddleware() {
|
|
|
23
23
|
}
|
|
24
24
|
});
|
|
25
25
|
}
|
|
26
|
+
/**
|
|
27
|
+
* Faults of the dev server rather than the app: a module graph swapped under a render already in
|
|
28
|
+
* flight, so React's dispatcher or a split chunk belongs to a generation that is gone. The next
|
|
29
|
+
* request reads the settled graph and succeeds, which is why these clear on their own — and why
|
|
30
|
+
* showing one is worse than useless. The page invites the reader to copy it to the agent, and an
|
|
31
|
+
* agent handed a phantom spends a repair round looking for it in code that is correct.
|
|
32
|
+
*/
|
|
33
|
+
const TRANSIENT = [/Cannot read properties of (?:null|undefined) \(reading 'use[A-Z]/, /Failed to fetch dynamically imported module/];
|
|
34
|
+
function isTransientBootError(message) {
|
|
35
|
+
return TRANSIENT.some((pattern) => pattern.test(message));
|
|
36
|
+
}
|
|
26
37
|
function errorPage(error) {
|
|
27
38
|
if (process.env.NODE_ENV === "production") return page("Something went wrong", "This page couldn’t be shown. Try again in a moment, and if it keeps happening, reach out to the owner of this app.", { note: "If you are the owner, you can check what happened in Studio." });
|
|
28
39
|
const cause = error instanceof Error ? error.cause : void 0;
|
|
@@ -31,6 +42,7 @@ function errorPage(error) {
|
|
|
31
42
|
const stack = root instanceof Error ? root.stack : void 0;
|
|
32
43
|
return page("This app crashed", "The app stopped before it could start. The technical details below show where it failed and can be copied to the agent to help fix the issue.", {
|
|
33
44
|
note: "These details are only shown because the app is running in development mode.",
|
|
45
|
+
autoRetry: isTransientBootError(message),
|
|
34
46
|
detail: `<pre class="message">${escapeHtml(message)}</pre>`,
|
|
35
47
|
actions: stack ? `<button type="button" id="toggle" aria-expanded="true" aria-controls="panel">
|
|
36
48
|
<svg class="chevron" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m9 18 6-6-6-6"/></svg>
|
|
@@ -71,7 +83,7 @@ function errorPage(error) {
|
|
|
71
83
|
<\/script>` : ""
|
|
72
84
|
});
|
|
73
85
|
}
|
|
74
|
-
function page(title, description, { note = "", detail = "", actions = "", panel = "" } = {}) {
|
|
86
|
+
function page(title, description, { note = "", detail = "", actions = "", panel = "", autoRetry = false } = {}) {
|
|
75
87
|
return `<!doctype html>
|
|
76
88
|
<html lang="en">
|
|
77
89
|
<head>
|
|
@@ -222,7 +234,7 @@ function page(title, description, { note = "", detail = "", actions = "", panel
|
|
|
222
234
|
</style>
|
|
223
235
|
</head>
|
|
224
236
|
<body>
|
|
225
|
-
<main>
|
|
237
|
+
<main${autoRetry ? " hidden" : ""}>
|
|
226
238
|
<div class="alert">
|
|
227
239
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3"/><path d="M12 9v4"/><path d="M12 17h.01"/></svg>
|
|
228
240
|
</div>
|
|
@@ -241,9 +253,37 @@ function page(title, description, { note = "", detail = "", actions = "", panel
|
|
|
241
253
|
</div>
|
|
242
254
|
${panel}
|
|
243
255
|
</main>
|
|
256
|
+
${autoRetry ? RETRY_SCRIPT : ""}
|
|
244
257
|
</body>
|
|
245
258
|
</html>`;
|
|
246
259
|
}
|
|
260
|
+
/**
|
|
261
|
+
* One reload, then the page reveals itself. The reload is what the reader would have done by hand,
|
|
262
|
+
* so a fault that clears is never seen; a fault that survives it shows the same page it always did.
|
|
263
|
+
* Session storage is what remembers the attempt, and a preview frame may refuse it — then there is
|
|
264
|
+
* no way to count, and the honest move is to show the error rather than risk reloading forever.
|
|
265
|
+
*/
|
|
266
|
+
const RETRY_SCRIPT = `<noscript><style>main[hidden] { display: flex !important }</style></noscript>
|
|
267
|
+
<script>
|
|
268
|
+
(() => {
|
|
269
|
+
const KEY = 'softr:boot-retry'
|
|
270
|
+
const reveal = () => { document.querySelector('main').hidden = false }
|
|
271
|
+
|
|
272
|
+
let store = null
|
|
273
|
+
try { store = sessionStorage } catch { /* blocked in this frame */ }
|
|
274
|
+
if (!store) return reveal()
|
|
275
|
+
|
|
276
|
+
const now = Date.now()
|
|
277
|
+
if (now - Number(store.getItem(KEY) ?? 0) < 15000) {
|
|
278
|
+
store.removeItem(KEY)
|
|
279
|
+
return reveal()
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
store.setItem(KEY, String(now))
|
|
283
|
+
// A beat, so the reload lands after the dev server has finished rebuilding rather than during.
|
|
284
|
+
setTimeout(() => location.reload(), 500)
|
|
285
|
+
})()
|
|
286
|
+
<\/script>`;
|
|
247
287
|
function escapeHtml(value) {
|
|
248
288
|
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
249
289
|
}
|
package/dist/package.js
CHANGED