@christianirack/brainllm-runtime 0.8.0 → 0.8.2
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/brain-form.js +6 -1
- package/package.json +1 -1
- package/server/server.mjs +12 -3
package/brain-form.js
CHANGED
|
@@ -35,7 +35,10 @@ const go = u => {
|
|
|
35
35
|
// submitted does not ask (#299): the user chose to save, and a submit() that navigates once the server accepted the
|
|
36
36
|
// data (`navigate('/products/' + p.id)`) leaves before submit() returns and marks the values saved.
|
|
37
37
|
const DIRTY = (globalThis.__brainDirty ||= new Set())
|
|
38
|
-
|
|
38
|
+
// registered with the first edit, not at load: a `beforeunload` listener can keep a page out of the back/forward cache,
|
|
39
|
+
// and most pages that load this file are never edited
|
|
40
|
+
const guardUnload = () => {
|
|
41
|
+
if (!globalThis.addEventListener || globalThis.__brainDirtyHook) return
|
|
39
42
|
globalThis.__brainDirtyHook = 1
|
|
40
43
|
globalThis.addEventListener('beforeunload', e => {
|
|
41
44
|
for (const f of DIRTY) if (f.dirty && !f.busy) return void (e.preventDefault(), (e.returnValue = ''))
|
|
@@ -315,6 +318,8 @@ function createForm(schema, o, readRule, validateRule, remote) {
|
|
|
315
318
|
},
|
|
316
319
|
}
|
|
317
320
|
DIRTY.add(f)
|
|
321
|
+
const watch = () => fx(() => void (f.dirty && guardUnload()))
|
|
322
|
+
owner ? I.root(watch, owner) : watch()
|
|
318
323
|
if (owner) I.root(() => fx(() => () => DIRTY.delete(f)), owner)
|
|
319
324
|
return f
|
|
320
325
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@christianirack/brainllm-runtime",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.2",
|
|
4
4
|
"description": "The BRAINLLM runtime for the Rust-built web compiler for LLMs: dependency-free ES modules (signals, DOM, router, data, forms, channels). The brain binary embeds this same version.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
package/server/server.mjs
CHANGED
|
@@ -1129,8 +1129,17 @@ export function createServer(o = {}) {
|
|
|
1129
1129
|
const loadOutcome = e => {
|
|
1130
1130
|
if (e && e.redirect) return send(res, e.status || 303, '', { location: withBase(linkTo(route, redirect(e.redirect, e.status ?? 303).redirect)), ...ctx.session.headers() })
|
|
1131
1131
|
if ((e instanceof HttpError && e.status === 404) || (e && e.notFound === true)) return missing(req, res, e) // `notFound()` 404 / `gone()` 410
|
|
1132
|
+
// the backend or another service said "not now": a temporary 503 tells crawlers to come back, a 500 tells them it broke
|
|
1133
|
+
// a status the page chose itself (`new HttpError(429)`) stays as it is, with the same hint to come back
|
|
1134
|
+
if (e && (e.status === 429 || e.status === 503) && !(e instanceof HttpError) && !res.headersSent) {
|
|
1135
|
+
log('warn', 'load temporarily unavailable', { rid, path: url.pathname, status: 503, ...trace(e) })
|
|
1136
|
+
res.setHeader('retry-after', String(Math.min(Math.ceil(e.retryAfter) || 5, 3600)))
|
|
1137
|
+
res.setHeader('cache-control', 'no-store')
|
|
1138
|
+
return errorPage(req, res, route, params, url, rid, 503)
|
|
1139
|
+
}
|
|
1132
1140
|
const status = intendedStatus(e) || 500
|
|
1133
1141
|
log('error', 'load failed', { rid, path: url.pathname, status, ...trace(e) })
|
|
1142
|
+
if ((status === 429 || status === 503) && !res.headersSent) res.setHeader('retry-after', String(Math.min(Math.ceil(e.retryAfter) || 5, 3600)))
|
|
1134
1143
|
return status === 500 ? errorPage(req, res, route, params, url, rid) : failPage(res, status, rid)
|
|
1135
1144
|
}
|
|
1136
1145
|
if (route.stream && route.server && !fixed && !forms && status === 200 && !res.$capture) return streamed(req, res, route, params, url, rid, ctx, keep(extra || {}), loadOutcome)
|
|
@@ -1272,9 +1281,9 @@ export function createServer(o = {}) {
|
|
|
1272
1281
|
return failPage(res, 403, rid)
|
|
1273
1282
|
}
|
|
1274
1283
|
// a failing load or guard → the folder's _error.brain (route `error`, props { message, rid }), else the generic page
|
|
1275
|
-
function errorPage(req, res, route, params, url, rid) {
|
|
1276
|
-
if (wantsHtml(req) && route.error) return ssr(req, res, route.error, params, url, rid, null,
|
|
1277
|
-
return failPage(res,
|
|
1284
|
+
function errorPage(req, res, route, params, url, rid, status = 500) {
|
|
1285
|
+
if (wantsHtml(req) && route.error) return ssr(req, res, route.error, params, url, rid, null, status, { message: 'Something went wrong.', rid })
|
|
1286
|
+
return failPage(res, status, rid)
|
|
1278
1287
|
}
|
|
1279
1288
|
async function render(route, props, forms, at = null) {
|
|
1280
1289
|
const { install } = await load('_server/dom.js')
|