@christianirack/brainllm-runtime 0.8.8 → 0.8.9
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/package.json +1 -1
- package/server/server.mjs +21 -11
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@christianirack/brainllm-runtime",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.9",
|
|
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
|
@@ -193,7 +193,7 @@ const cacheSearch = url => {
|
|
|
193
193
|
return kept.size ? '?' + kept : ''
|
|
194
194
|
}
|
|
195
195
|
// Messages this file writes itself, safe to log as the cause; anything else in an error message may hold private data.
|
|
196
|
-
const OWN_CAUSES = new Set(['Backend request unavailable', 'Backend request timed out', 'Backend response exceeds its byte budget', 'Backend request capacity is unavailable', 'Backend stream capacity is unavailable', 'Native file request failed', 'Native file response is invalid', 'Native
|
|
196
|
+
const OWN_CAUSES = new Set(['Backend request unavailable', 'Backend request timed out', 'Backend response exceeds its byte budget', 'Backend request capacity is unavailable', 'Backend stream capacity is unavailable', 'Native file request failed', 'Native file response is invalid', 'Native resource response must be JSON', 'Native resource response is invalid', 'Native resource request failed', 'Invalid final response status', 'Invalid configured redirect location', 'timeout'])
|
|
197
197
|
const CODE_SHAPE = /^[A-Z][A-Z0-9_]{0,63}$/
|
|
198
198
|
const varyEncoding = v => (/accept-encoding/i.test(v || '') ? v : [v, 'Accept-Encoding'].filter(Boolean).join(', '))
|
|
199
199
|
function bodyEncoding(req, status, headers) {
|
|
@@ -308,9 +308,9 @@ export function nativeAuthBridge(raw, refused, serviceKey, report, ownSession) {
|
|
|
308
308
|
if (!user || typeof user.id !== 'string' || !/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(user.id) || typeof user.email !== 'string' || user.email.length > 254 || typeof user.role !== 'string' || !/^[a-z][a-z0-9_]{0,47}$/.test(user.role) || ['public','owner'].includes(user.role) || typeof user.verified !== 'boolean') throw new HttpError(503,'Native authentication response is unavailable')
|
|
309
309
|
return Object.freeze({id:user.id,email:user.email,role:user.role,verified:user.verified})
|
|
310
310
|
}
|
|
311
|
-
const unavailable = () => new HttpError(503,
|
|
311
|
+
const unavailable = (why = 'the backend did not answer') => new HttpError(503, `Native authentication is unavailable: ${why}`)
|
|
312
312
|
const call = (incoming, response, path, method, rid, origin) => {
|
|
313
|
-
if (stopped || active >= 8) return Promise.reject(unavailable())
|
|
313
|
+
if (stopped || active >= 8) return Promise.reject(unavailable(stopped ? 'the server is shutting down' : '8 session checks are already in flight'))
|
|
314
314
|
const cookie = cookies(incoming, method === 'POST')
|
|
315
315
|
const client = forwardedFor(incoming)
|
|
316
316
|
active++
|
|
@@ -350,7 +350,7 @@ export function nativeAuthBridge(raw, refused, serviceKey, report, ownSession) {
|
|
|
350
350
|
})
|
|
351
351
|
})
|
|
352
352
|
owned.add(outgoing); incoming.once('aborted', cancel); response.once('close', disconnected)
|
|
353
|
-
timer = setTimeout(() => outgoing.destroy(unavailable()), 2000)
|
|
353
|
+
timer = setTimeout(() => outgoing.destroy(unavailable('the backend took more than 2 s')), 2000)
|
|
354
354
|
outgoing.once('error', () => finish(true)); outgoing.end()
|
|
355
355
|
} catch { outgoing?.destroy(); finish(true) }
|
|
356
356
|
})
|
|
@@ -363,7 +363,7 @@ export function nativeAuthBridge(raw, refused, serviceKey, report, ownSession) {
|
|
|
363
363
|
let origin
|
|
364
364
|
try { origin = new URL(process.env.PUBLIC_SITE_URL).origin } catch { origin = 'https://' + (req.headers['x-forwarded-host'] || req.headers.host) }
|
|
365
365
|
return new Promise((resolve, reject) => {
|
|
366
|
-
if (stopped || active >= 8) return reject(unavailable())
|
|
366
|
+
if (stopped || active >= 8) return reject(unavailable(stopped ? 'the server is shutting down' : '8 session checks are already in flight'))
|
|
367
367
|
active++
|
|
368
368
|
let settled = false
|
|
369
369
|
const done = (error, value) => { if (settled) return; settled = true; active--; clearTimeout(timer); error ? reject(unavailable()) : resolve(value) }
|
|
@@ -388,7 +388,7 @@ export function nativeAuthBridge(raw, refused, serviceKey, report, ownSession) {
|
|
|
388
388
|
} catch { done(true) }
|
|
389
389
|
})
|
|
390
390
|
})
|
|
391
|
-
const timer = setTimeout(() => outgoing.destroy(unavailable()), 2000)
|
|
391
|
+
const timer = setTimeout(() => outgoing.destroy(unavailable('the backend took more than 2 s')), 2000)
|
|
392
392
|
owned.add(outgoing); outgoing.once('close', () => owned.delete(outgoing))
|
|
393
393
|
outgoing.once('error', () => done(true)); outgoing.end()
|
|
394
394
|
})
|
|
@@ -949,7 +949,8 @@ export function createServer(o = {}) {
|
|
|
949
949
|
// log fields for a thrown value: its stack (sanitized by log) plus, for B09xx budget failures, the typed fields only
|
|
950
950
|
const trace = e => {
|
|
951
951
|
const out = { stack: String((e && e.stack) || e) }
|
|
952
|
-
if (e instanceof HttpError && OWN_CAUSES.has(e.message)) out.cause = e.message
|
|
952
|
+
if (e instanceof HttpError && (OWN_CAUSES.has(e.message) || e.message.startsWith('Native authentication is unavailable:'))) out.cause = e.message
|
|
953
|
+
if (e && e.code === 'ERR_MODULE_NOT_FOUND') out.fix = 'this process runs an older build whose files are gone; restart it after brain build'
|
|
953
954
|
if (e && CODE_SHAPE.test(e.transport || '')) out.transport = e.transport
|
|
954
955
|
if (e && typeof e.code === 'string' && CODE_SHAPE.test(e.code)) out.backendCode = e.code
|
|
955
956
|
if (e && e.kind === 'budget' && /^B09\d\d$/.test(e.code) && typeof e.budget === 'string' && /^[a-zA-Z]{1,16}$/.test(e.budget)) {
|
|
@@ -1706,6 +1707,15 @@ export function createServer(o = {}) {
|
|
|
1706
1707
|
const href = tag && /\bhref="(https?:\/\/[^"<>\s]+)"/.exec(tag)?.[1].replaceAll('&', '&')
|
|
1707
1708
|
return href && URL.canParse(href) && !/\/[:*]/.test(new URL(href).pathname) ? { link: `<${href}>; rel="canonical"` } : {}
|
|
1708
1709
|
}
|
|
1710
|
+
// What exists whatever the routes say: generated sitemaps, built files and the icon browsers ask for by habit.
|
|
1711
|
+
async function serveFixed(req, res, url) {
|
|
1712
|
+
if (DYN.length && /^\/sitemap(-static|-dynamic(-\d+)?)?\.xml$/.test(url.pathname) && (await serveSitemap(req, res, url))) return true
|
|
1713
|
+
if (serveStatic(req, res, url)) return true
|
|
1714
|
+
if (url.pathname !== '/favicon.ico' || !file('favicon.svg')) return false
|
|
1715
|
+
send(res, 301, '', { location: withBase('/favicon.svg'), 'cache-control': 'public, max-age=86400' })
|
|
1716
|
+
return true
|
|
1717
|
+
}
|
|
1718
|
+
|
|
1709
1719
|
function serveStatic(req, res, url) {
|
|
1710
1720
|
const p = publicStaticPath(url.pathname)
|
|
1711
1721
|
if (p === null) return false
|
|
@@ -1867,6 +1877,8 @@ export function createServer(o = {}) {
|
|
|
1867
1877
|
if (!m) continue
|
|
1868
1878
|
// a form page without load is prerendered: GET serves that file (build ETag); only its POST needs the server
|
|
1869
1879
|
if (!post && route.forms && serveStatic(req, res, url)) return
|
|
1880
|
+
// a parameter never hides a real file: `/[code]` must not swallow /favicon.svg, /sitemap.xml or a prerendered /login
|
|
1881
|
+
if (!post && m.groups && (await serveFixed(req, res, url))) return
|
|
1870
1882
|
if (!post && serveTwin(req, res, url)) return
|
|
1871
1883
|
const paged = post ? null : pageParameter(route, url)
|
|
1872
1884
|
if (paged === 404) return notFound(req, res)
|
|
@@ -1874,15 +1886,13 @@ export function createServer(o = {}) {
|
|
|
1874
1886
|
return await (post ? formPost : ssr)(req, res, route, m.groups ? { ...m.groups } : {}, url, rid)
|
|
1875
1887
|
}
|
|
1876
1888
|
if (post) return send(res, 405, 'Method not allowed', { allow: 'GET, HEAD' })
|
|
1877
|
-
if (
|
|
1878
|
-
if (serveStatic(req, res, url)) return
|
|
1879
|
-
// browsers still ask for /favicon.ico: the app's SVG icon answers it
|
|
1880
|
-
if (url.pathname === '/favicon.ico' && file('favicon.svg')) return send(res, 301, '', { location: withBase('/favicon.svg'), 'cache-control': 'public, max-age=86400' })
|
|
1889
|
+
if (await serveFixed(req, res, url)) return
|
|
1881
1890
|
if (isProbe(url.pathname)) return await answerProbe(req, res, url, rid)
|
|
1882
1891
|
notFound(req, res)
|
|
1883
1892
|
} catch (e) {
|
|
1884
1893
|
const busy = e instanceof HttpError && e.code === 'BRIDGE_BUSY'
|
|
1885
1894
|
if (busy) log('warn', 'backend bridge busy', { rid, path: req.$brainPath || String(req.url || '').split('?')[0], fix: e.fix })
|
|
1895
|
+
else if (e instanceof HttpError && OWN_CAUSES.has(e.message)) log('warn', 'backend unavailable', { rid, path: req.$brainPath || String(req.url || '').split('?')[0], status: e.status, cause: e.message, ...(CODE_SHAPE.test(e.transport || '') ? { transport: e.transport } : {}) })
|
|
1886
1896
|
else log('error', 'unhandled', { rid, path: req.url, ...trace(e) })
|
|
1887
1897
|
try {
|
|
1888
1898
|
if (busy && !res.headersSent) { res.setHeader('retry-after', String(e.retryAfter)); failPage(res, 503, rid, { code: e.code, message: 'Service Unavailable', fix: e.fix }) }
|