spark-ssr 0.3.0 → 0.3.1
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/src/server.js +20 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "spark-ssr",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "Zero-config SSR for spark-html on Bun. The HTML template infers everything: filesystem routing, layouts, <spark-ssr> declarative data (SQL, URLs, globs, modules), auto CRUD with validation, guards, no-JS forms, schema + seeds, live updates, SEO. No build step.",
|
|
5
5
|
"homepage": "https://wilkinnovo.github.io/spark-html",
|
|
6
6
|
"type": "module",
|
package/src/server.js
CHANGED
|
@@ -300,6 +300,19 @@ export async function serve(options = {}) {
|
|
|
300
300
|
const RELOAD_CLIENT = '<script>(()=>{const e=new EventSource("/__spark/reload");let d=0;'
|
|
301
301
|
+ 'e.onmessage=()=>location.reload();e.onerror=()=>{d=1};e.onopen=()=>{if(d)location.reload()}})()</script>';
|
|
302
302
|
|
|
303
|
+
// Heartbeats keep every SSE socket outside Bun's idleTimeout (the default
|
|
304
|
+
// would kill them at 10 s — and a killed reload socket reconnects, which
|
|
305
|
+
// the client reads as "the server came back": a spurious reload). The ping
|
|
306
|
+
// also flushes dead clients (enqueue throws → drop).
|
|
307
|
+
const heartbeat = setInterval(() => {
|
|
308
|
+
for (const set of [sseClients, liveClients]) {
|
|
309
|
+
for (const c of set) {
|
|
310
|
+
try { c.enqueue(sseEnc.encode(': ping\n\n')); } catch { set.delete(c); }
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
}, 25000);
|
|
314
|
+
heartbeat.unref?.();
|
|
315
|
+
|
|
303
316
|
// ── live data channel (§9) — a production feature, unlike dev reload ──
|
|
304
317
|
// Any write through the server pings /__spark/live with the table name;
|
|
305
318
|
// hydrated pages refetch through their own session (scoping intact) and
|
|
@@ -1007,7 +1020,9 @@ export async function serve(options = {}) {
|
|
|
1007
1020
|
}
|
|
1008
1021
|
|
|
1009
1022
|
async function buildScope(pd, req) {
|
|
1010
|
-
|
|
1023
|
+
// `path` is ambient like `session` — the layout's nav highlights the
|
|
1024
|
+
// current page with it. Query/params may shadow it deliberately.
|
|
1025
|
+
const scope = { path: req.path, ...req.query, ...req.params, session: req.session };
|
|
1011
1026
|
if (pd.code) Object.assign(scope, await runPageScript(pd.code, req));
|
|
1012
1027
|
for (const p of pd.plan) {
|
|
1013
1028
|
if (scope[p.var] !== undefined) continue; // the page <script> won
|
|
@@ -1216,6 +1231,9 @@ code{color:#fdba74}em{color:#a8a29e}</style></head><body>
|
|
|
1216
1231
|
// ── the server ──
|
|
1217
1232
|
const server = Bun.serve({
|
|
1218
1233
|
port: options.port ?? 3000,
|
|
1234
|
+
// SSE channels idle between events (heartbeat every 25 s keeps them
|
|
1235
|
+
// alive); slow queries and big uploads get headroom too.
|
|
1236
|
+
idleTimeout: 60,
|
|
1219
1237
|
async fetch(request, srv) {
|
|
1220
1238
|
const url = new URL(request.url);
|
|
1221
1239
|
let pathname;
|
|
@@ -1417,6 +1435,7 @@ code{color:#fdba74}em{color:#a8a29e}</style></head><body>
|
|
|
1417
1435
|
db,
|
|
1418
1436
|
stop(force) {
|
|
1419
1437
|
if (watchTimer) clearInterval(watchTimer);
|
|
1438
|
+
clearInterval(heartbeat);
|
|
1420
1439
|
for (const c of sseClients) { try { c.close(); } catch { /* gone */ } }
|
|
1421
1440
|
sseClients.clear();
|
|
1422
1441
|
for (const c of liveClients) { try { c.close(); } catch { /* gone */ } }
|