lakebed 0.0.11 → 0.0.12
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 +5 -5
- package/src/cli.js +97 -16
- package/src/runtime.js +16 -0
- package/src/version.js +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lakebed",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.12",
|
|
4
4
|
"description": "Agent-native CLI and runtime for building and deploying Lakebed capsules.",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"type": "module",
|
|
@@ -52,6 +52,9 @@
|
|
|
52
52
|
"publishConfig": {
|
|
53
53
|
"access": "public"
|
|
54
54
|
},
|
|
55
|
+
"scripts": {
|
|
56
|
+
"check": "node --check src/cli.js && node --check src/runtime.js && node --check src/server.js && node --check src/client.js && node --check src/source-store.js && node --check src/source-runtime.js && node --check src/source-runtime-worker.js && node --check src/source-runtime-loader.mjs && node --check src/anonymous.js && node --check src/anonymous-server.js && node --check src/auth.js && node --check src/version.js"
|
|
57
|
+
},
|
|
55
58
|
"dependencies": {
|
|
56
59
|
"esbuild": "^0.27.1",
|
|
57
60
|
"pg": "^8.16.3",
|
|
@@ -60,8 +63,5 @@
|
|
|
60
63
|
},
|
|
61
64
|
"devDependencies": {
|
|
62
65
|
"@types/ws": "^8.18.1"
|
|
63
|
-
},
|
|
64
|
-
"scripts": {
|
|
65
|
-
"check": "node --check src/cli.js && node --check src/runtime.js && node --check src/server.js && node --check src/client.js && node --check src/source-store.js && node --check src/source-runtime.js && node --check src/source-runtime-worker.js && node --check src/source-runtime-loader.mjs && node --check src/anonymous.js && node --check src/anonymous-server.js && node --check src/auth.js && node --check src/version.js"
|
|
66
66
|
}
|
|
67
|
-
}
|
|
67
|
+
}
|
package/src/cli.js
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
import { execFile } from "node:child_process";
|
|
3
3
|
import { createServer } from "node:http";
|
|
4
4
|
import { existsSync, realpathSync } from "node:fs";
|
|
5
|
-
import { mkdir, readFile, rm, writeFile } from "node:fs/promises";
|
|
6
|
-
import { basename, dirname, isAbsolute, join, resolve } from "node:path";
|
|
5
|
+
import { mkdir, readdir, readFile, rm, stat, writeFile } from "node:fs/promises";
|
|
6
|
+
import { basename, dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
7
7
|
import { createInterface } from "node:readline/promises";
|
|
8
8
|
import { promisify } from "node:util";
|
|
9
9
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
@@ -371,6 +371,32 @@ function sendJson(ws, message) {
|
|
|
371
371
|
ws.send(JSON.stringify(message));
|
|
372
372
|
}
|
|
373
373
|
|
|
374
|
+
async function capsuleFileFingerprint(rootDir, dir = rootDir, entries = []) {
|
|
375
|
+
const dirEntries = await readdir(dir, { withFileTypes: true });
|
|
376
|
+
|
|
377
|
+
for (const entry of dirEntries) {
|
|
378
|
+
if (entry.name === "node_modules" || entry.name === ".lakebed" || entry.name === ".DS_Store") {
|
|
379
|
+
continue;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
const absolutePath = join(dir, entry.name);
|
|
383
|
+
if (entry.isDirectory()) {
|
|
384
|
+
await capsuleFileFingerprint(rootDir, absolutePath, entries);
|
|
385
|
+
continue;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
if (!entry.isFile()) {
|
|
389
|
+
continue;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
const info = await stat(absolutePath);
|
|
393
|
+
const path = relative(rootDir, absolutePath).split(sep).join("/");
|
|
394
|
+
entries.push(`${path}:${info.size}:${info.mtimeMs}`);
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
return entries.sort().join("\n");
|
|
398
|
+
}
|
|
399
|
+
|
|
374
400
|
function createContext({ stateCell, auth, logs, env }) {
|
|
375
401
|
return {
|
|
376
402
|
auth,
|
|
@@ -417,25 +443,53 @@ export async function startDevServer({
|
|
|
417
443
|
shooBaseUrl = shooBaseUrlFromEnv()
|
|
418
444
|
} = {}) {
|
|
419
445
|
const resolvedCapsuleDir = resolveCapsuleDir(capsuleDir);
|
|
420
|
-
|
|
446
|
+
let currentBuild = await buildCapsule({ capsuleDir: resolvedCapsuleDir, sourceStore, capsuleId });
|
|
421
447
|
const defaultAuth = await readAuth();
|
|
422
|
-
const stateCell = new StateCell(
|
|
448
|
+
const stateCell = new StateCell(currentBuild.app.schema);
|
|
423
449
|
const logs = new LogBuffer();
|
|
424
450
|
const subscriptions = new Map();
|
|
451
|
+
let fileFingerprint = sourceStore ? "" : await capsuleFileFingerprint(resolvedCapsuleDir);
|
|
452
|
+
let rebuildTimer = null;
|
|
453
|
+
let rebuildPromise = Promise.resolve();
|
|
454
|
+
|
|
455
|
+
async function rebuild() {
|
|
456
|
+
try {
|
|
457
|
+
const nextBuild = await buildCapsule({ capsuleDir: resolvedCapsuleDir, sourceStore, capsuleId });
|
|
458
|
+
currentBuild = nextBuild;
|
|
459
|
+
stateCell.updateSchema(nextBuild.app.schema);
|
|
460
|
+
logs.append("info", "dev server rebuilt capsule", { capsuleDir: resolvedCapsuleDir });
|
|
461
|
+
for (const client of wss.clients) {
|
|
462
|
+
sendJson(client, { op: "refresh" });
|
|
463
|
+
}
|
|
464
|
+
} catch (error) {
|
|
465
|
+
logs.append("error", "dev server rebuild failed", { error: error instanceof Error ? error.message : String(error) });
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
function scheduleRebuild() {
|
|
470
|
+
if (rebuildTimer) {
|
|
471
|
+
clearTimeout(rebuildTimer);
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
rebuildTimer = setTimeout(() => {
|
|
475
|
+
rebuildTimer = null;
|
|
476
|
+
rebuildPromise = rebuildPromise.then(rebuild, rebuild);
|
|
477
|
+
}, 100);
|
|
478
|
+
}
|
|
425
479
|
|
|
426
480
|
const server = createServer(async (req, res) => {
|
|
427
481
|
try {
|
|
428
482
|
const requestUrl = new URL(req.url ?? "/", `http://${req.headers.host ?? "localhost"}`);
|
|
429
483
|
|
|
430
484
|
if (requestUrl.pathname === "/" || requestUrl.pathname === "/index.html" || requestUrl.pathname === "/auth/callback") {
|
|
431
|
-
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
|
432
|
-
res.end(html(
|
|
485
|
+
res.writeHead(200, { "Cache-Control": "no-store", "Content-Type": "text/html; charset=utf-8" });
|
|
486
|
+
res.end(html(currentBuild.app.name ?? "Lakebed Capsule", { shooBaseUrl }));
|
|
433
487
|
return;
|
|
434
488
|
}
|
|
435
489
|
|
|
436
490
|
if (requestUrl.pathname === "/client.js") {
|
|
437
|
-
res.writeHead(200, { "Content-Type": "application/javascript; charset=utf-8" });
|
|
438
|
-
res.end(await readFile(
|
|
491
|
+
res.writeHead(200, { "Cache-Control": "no-store", "Content-Type": "application/javascript; charset=utf-8" });
|
|
492
|
+
res.end(await readFile(currentBuild.clientOut, "utf8"));
|
|
439
493
|
return;
|
|
440
494
|
}
|
|
441
495
|
|
|
@@ -472,11 +526,11 @@ export async function startDevServer({
|
|
|
472
526
|
for (const name of subscription.queries) {
|
|
473
527
|
try {
|
|
474
528
|
const data = await runQuery({
|
|
475
|
-
app:
|
|
529
|
+
app: currentBuild.app,
|
|
476
530
|
stateCell,
|
|
477
531
|
auth: subscription.auth,
|
|
478
532
|
logs,
|
|
479
|
-
env:
|
|
533
|
+
env: currentBuild.env,
|
|
480
534
|
name
|
|
481
535
|
});
|
|
482
536
|
sendJson(ws, { op: "query.result", name, data });
|
|
@@ -508,11 +562,11 @@ export async function startDevServer({
|
|
|
508
562
|
if (message.op === "query.subscribe") {
|
|
509
563
|
subscription.queries.add(message.name);
|
|
510
564
|
const data = await runQuery({
|
|
511
|
-
app:
|
|
565
|
+
app: currentBuild.app,
|
|
512
566
|
stateCell,
|
|
513
567
|
auth: subscription.auth,
|
|
514
568
|
logs,
|
|
515
|
-
env:
|
|
569
|
+
env: currentBuild.env,
|
|
516
570
|
name: message.name
|
|
517
571
|
});
|
|
518
572
|
sendJson(ws, { id: message.id, op: "query.result", ok: true, name: message.name, data });
|
|
@@ -521,11 +575,11 @@ export async function startDevServer({
|
|
|
521
575
|
|
|
522
576
|
if (message.op === "mutation.run") {
|
|
523
577
|
const { result } = await runMutation({
|
|
524
|
-
app:
|
|
578
|
+
app: currentBuild.app,
|
|
525
579
|
stateCell,
|
|
526
580
|
auth: subscription.auth,
|
|
527
581
|
logs,
|
|
528
|
-
env:
|
|
582
|
+
env: currentBuild.env,
|
|
529
583
|
name: message.name,
|
|
530
584
|
args: message.args ?? []
|
|
531
585
|
});
|
|
@@ -585,15 +639,42 @@ export async function startDevServer({
|
|
|
585
639
|
console.log(`Auth: ${defaultAuth.userId}`);
|
|
586
640
|
}
|
|
587
641
|
|
|
642
|
+
const watchInterval = sourceStore
|
|
643
|
+
? null
|
|
644
|
+
: setInterval(async () => {
|
|
645
|
+
try {
|
|
646
|
+
const nextFingerprint = await capsuleFileFingerprint(resolvedCapsuleDir);
|
|
647
|
+
if (nextFingerprint === fileFingerprint) {
|
|
648
|
+
return;
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
fileFingerprint = nextFingerprint;
|
|
652
|
+
scheduleRebuild();
|
|
653
|
+
} catch (error) {
|
|
654
|
+
logs.append("error", "dev server file watch failed", { error: error instanceof Error ? error.message : String(error) });
|
|
655
|
+
}
|
|
656
|
+
}, 300);
|
|
657
|
+
|
|
588
658
|
return {
|
|
589
|
-
app
|
|
590
|
-
|
|
659
|
+
get app() {
|
|
660
|
+
return currentBuild.app;
|
|
661
|
+
},
|
|
662
|
+
get buildDir() {
|
|
663
|
+
return currentBuild.buildDir;
|
|
664
|
+
},
|
|
591
665
|
capsuleDir: resolvedCapsuleDir,
|
|
592
666
|
logs,
|
|
593
667
|
port,
|
|
594
668
|
stateCell,
|
|
595
669
|
url: `http://localhost:${port}`,
|
|
596
670
|
async close() {
|
|
671
|
+
if (watchInterval) {
|
|
672
|
+
clearInterval(watchInterval);
|
|
673
|
+
}
|
|
674
|
+
if (rebuildTimer) {
|
|
675
|
+
clearTimeout(rebuildTimer);
|
|
676
|
+
}
|
|
677
|
+
await rebuildPromise.catch(() => {});
|
|
597
678
|
for (const client of wss.clients) {
|
|
598
679
|
client.close();
|
|
599
680
|
}
|
package/src/runtime.js
CHANGED
|
@@ -187,6 +187,22 @@ export class StateCell {
|
|
|
187
187
|
}
|
|
188
188
|
}
|
|
189
189
|
|
|
190
|
+
updateSchema(schema) {
|
|
191
|
+
this.schema = schema;
|
|
192
|
+
|
|
193
|
+
for (const tableName of Object.keys(schema ?? {})) {
|
|
194
|
+
if (!this.tables.has(tableName)) {
|
|
195
|
+
this.tables.set(tableName, new Map());
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
for (const tableName of this.tables.keys()) {
|
|
200
|
+
if (!schema?.[tableName]) {
|
|
201
|
+
this.tables.delete(tableName);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
190
206
|
createDb() {
|
|
191
207
|
const db = {};
|
|
192
208
|
for (const tableName of this.tables.keys()) {
|
package/src/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const LAKEBED_VERSION = "0.0.
|
|
1
|
+
export const LAKEBED_VERSION = "0.0.12";
|