lakebed 0.0.4 → 0.0.5
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/README.md +1 -1
- package/package.json +7 -6
- package/src/anonymous.js +4 -2
- package/src/cli.js +84 -6
- package/src/version.js +1 -0
package/README.md
CHANGED
|
@@ -144,7 +144,7 @@ LAKEBED_GITHUB_CLIENT_SECRET=...
|
|
|
144
144
|
LAKEBED_SESSION_SECRET=...
|
|
145
145
|
```
|
|
146
146
|
|
|
147
|
-
Claimed deploys are listed at `/deploys` on the deploy API origin. They keep the same resource limits as anonymous deploys. Anonymous deploys cannot use outbound `fetch`; after a deploy is claimed, `lakebed deploy` can update it with a source-backed server artifact that supports async handlers and server-side fetch.
|
|
147
|
+
Claimed deploys are listed at `/deploys` on the deploy API origin. They keep the same resource limits as anonymous deploys. Anonymous deploys cannot use outbound `fetch`; after a deploy is claimed, `lakebed deploy` can update it with a source-backed server artifact that supports async handlers and server-side fetch. If the first deploy already needs server-side `fetch`, `lakebed deploy` creates a claim-required preview, saves its claim metadata, and prints the claim URL. Open that URL, then run `lakebed deploy` again to publish the real source-backed app.
|
|
148
148
|
|
|
149
149
|
## Admin Dashboard
|
|
150
150
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lakebed",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.5",
|
|
4
4
|
"description": "Agent-native CLI and runtime for building and deploying Lakebed capsules.",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"type": "module",
|
|
@@ -29,7 +29,8 @@
|
|
|
29
29
|
"src/runtime.js",
|
|
30
30
|
"src/server.d.ts",
|
|
31
31
|
"src/server.js",
|
|
32
|
-
"src/source-store.js"
|
|
32
|
+
"src/source-store.js",
|
|
33
|
+
"src/version.js"
|
|
33
34
|
],
|
|
34
35
|
"exports": {
|
|
35
36
|
"./package.json": "./package.json",
|
|
@@ -48,6 +49,9 @@
|
|
|
48
49
|
"publishConfig": {
|
|
49
50
|
"access": "public"
|
|
50
51
|
},
|
|
52
|
+
"scripts": {
|
|
53
|
+
"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/anonymous.js && node --check src/anonymous-server.js && node --check src/auth.js && node --check src/version.js"
|
|
54
|
+
},
|
|
51
55
|
"dependencies": {
|
|
52
56
|
"esbuild": "^0.27.1",
|
|
53
57
|
"pg": "^8.16.3",
|
|
@@ -56,8 +60,5 @@
|
|
|
56
60
|
},
|
|
57
61
|
"devDependencies": {
|
|
58
62
|
"@types/ws": "^8.18.1"
|
|
59
|
-
},
|
|
60
|
-
"scripts": {
|
|
61
|
-
"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/anonymous.js && node --check src/anonymous-server.js && node --check src/auth.js"
|
|
62
63
|
}
|
|
63
|
-
}
|
|
64
|
+
}
|
package/src/anonymous.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { createHash, randomBytes, randomUUID } from "node:crypto";
|
|
2
2
|
import { readFile } from "node:fs/promises";
|
|
3
|
+
import { LAKEBED_VERSION } from "./version.js";
|
|
3
4
|
|
|
4
5
|
export const ANONYMOUS_ARTIFACT_FORMAT = "lakebed.capsule.artifact.v1";
|
|
5
6
|
export const ANONYMOUS_ARTIFACT_MEDIA_TYPE = "application/vnd.lakebed.artifact+json";
|
|
7
|
+
export { LAKEBED_VERSION };
|
|
6
8
|
|
|
7
9
|
export const DEFAULT_ANONYMOUS_LIMITS = {
|
|
8
10
|
artifactBytes: 1024 * 1024,
|
|
@@ -494,7 +496,7 @@ function compileServerToIr(app, schema) {
|
|
|
494
496
|
return { diagnostics, mutations, queries };
|
|
495
497
|
}
|
|
496
498
|
|
|
497
|
-
export async function createAnonymousArtifact({ app, clientOut, sourceStore, version =
|
|
499
|
+
export async function createAnonymousArtifact({ app, clientOut, sourceStore, version = LAKEBED_VERSION }) {
|
|
498
500
|
const sourceFiles = await readSourceFiles(sourceStore);
|
|
499
501
|
const diagnostics = forbiddenSourceDiagnostics(sourceFiles);
|
|
500
502
|
const { diagnostics: schemaDiagnostics, schema } = serializeSchema(app.schema);
|
|
@@ -556,7 +558,7 @@ export async function createAnonymousArtifact({ app, clientOut, sourceStore, ver
|
|
|
556
558
|
};
|
|
557
559
|
}
|
|
558
560
|
|
|
559
|
-
export async function createClaimedArtifact({ app, clientOut, serverOut, sourceStore, version =
|
|
561
|
+
export async function createClaimedArtifact({ app, clientOut, serverOut, sourceStore, version = LAKEBED_VERSION }) {
|
|
560
562
|
if (!serverOut) {
|
|
561
563
|
throw new AnonymousCompilerError([diagnostic("server/index.ts", "Claimed deploys require a bundled server module.")]);
|
|
562
564
|
}
|
package/src/cli.js
CHANGED
|
@@ -9,6 +9,7 @@ import { WebSocketServer } from "ws";
|
|
|
9
9
|
import {
|
|
10
10
|
ANONYMOUS_ARTIFACT_MEDIA_TYPE,
|
|
11
11
|
AnonymousCompilerError,
|
|
12
|
+
LAKEBED_VERSION,
|
|
12
13
|
createAnonymousArtifact,
|
|
13
14
|
createClaimedArtifact,
|
|
14
15
|
parseTtlSeconds,
|
|
@@ -17,7 +18,7 @@ import {
|
|
|
17
18
|
import { startAnonymousServer } from "./anonymous-server.js";
|
|
18
19
|
import { authFromUrl as resolveAuthFromUrl, createGuestAuth, requestOrigin, shooBaseUrlFromEnv } from "./auth.js";
|
|
19
20
|
import { LogBuffer, StateCell } from "./runtime.js";
|
|
20
|
-
import { createMemorySourceStoreFromDirectory, sourcePathDirname, sourcePathJoin } from "./source-store.js";
|
|
21
|
+
import { MemorySourceStore, createMemorySourceStoreFromDirectory, sourcePathDirname, sourcePathJoin } from "./source-store.js";
|
|
21
22
|
|
|
22
23
|
const root = process.cwd();
|
|
23
24
|
const packageDir = resolve(dirname(fileURLToPath(import.meta.url)), "..");
|
|
@@ -601,6 +602,71 @@ async function buildAnonymousEnvelope(capsuleArg) {
|
|
|
601
602
|
};
|
|
602
603
|
}
|
|
603
604
|
|
|
605
|
+
const claimRequiredDiagnosticMessages = new Set([
|
|
606
|
+
"Outbound fetch is disabled for anonymous deploys.",
|
|
607
|
+
"Async server handlers are not part of the anonymous IR yet. Use synchronous Lakebed database operations."
|
|
608
|
+
]);
|
|
609
|
+
|
|
610
|
+
function canDeployAfterClaim(error) {
|
|
611
|
+
return (
|
|
612
|
+
error instanceof AnonymousCompilerError &&
|
|
613
|
+
error.diagnostics.length > 0 &&
|
|
614
|
+
error.diagnostics.every((entry) => claimRequiredDiagnosticMessages.has(entry.message))
|
|
615
|
+
);
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
async function buildClaimRequiredEnvelope({ capsuleDir }) {
|
|
619
|
+
const sourceStore = new MemorySourceStore();
|
|
620
|
+
await sourceStore.writeFile(
|
|
621
|
+
"server/index.ts",
|
|
622
|
+
`import { capsule } from "lakebed/server";
|
|
623
|
+
|
|
624
|
+
export default capsule({
|
|
625
|
+
name: "Claim Required",
|
|
626
|
+
schema: {},
|
|
627
|
+
queries: {},
|
|
628
|
+
mutations: {}
|
|
629
|
+
});
|
|
630
|
+
`
|
|
631
|
+
);
|
|
632
|
+
await sourceStore.writeFile(
|
|
633
|
+
"client/index.tsx",
|
|
634
|
+
`export function App() {
|
|
635
|
+
return (
|
|
636
|
+
<main className="min-h-screen bg-neutral-950 px-6 py-12 text-neutral-100">
|
|
637
|
+
<section className="mx-auto max-w-2xl rounded-lg border border-neutral-800 bg-neutral-900 p-8 shadow-2xl">
|
|
638
|
+
<p className="text-sm font-semibold uppercase tracking-wide text-cyan-300">Lakebed deploy</p>
|
|
639
|
+
<h1 className="mt-3 text-3xl font-semibold">Claim required</h1>
|
|
640
|
+
<p className="mt-4 text-neutral-300">
|
|
641
|
+
This capsule uses server-side fetch. Claim this deploy, then run lakebed deploy again to publish the app.
|
|
642
|
+
</p>
|
|
643
|
+
</section>
|
|
644
|
+
</main>
|
|
645
|
+
);
|
|
646
|
+
}
|
|
647
|
+
`
|
|
648
|
+
);
|
|
649
|
+
const built = await buildCapsule({
|
|
650
|
+
capsuleDir,
|
|
651
|
+
capsuleId: `claim-required-${Date.now()}`,
|
|
652
|
+
sourceStore
|
|
653
|
+
});
|
|
654
|
+
const artifact = await createAnonymousArtifact({
|
|
655
|
+
app: built.app,
|
|
656
|
+
clientOut: built.clientOut,
|
|
657
|
+
sourceStore
|
|
658
|
+
});
|
|
659
|
+
|
|
660
|
+
return {
|
|
661
|
+
artifact: artifact.artifact,
|
|
662
|
+
artifactHash: artifact.artifactHash,
|
|
663
|
+
clientBundle: artifact.clientBundle,
|
|
664
|
+
clientBundleHash: artifact.clientBundleHash,
|
|
665
|
+
claimRequired: true,
|
|
666
|
+
mediaType: ANONYMOUS_ARTIFACT_MEDIA_TYPE
|
|
667
|
+
};
|
|
668
|
+
}
|
|
669
|
+
|
|
604
670
|
async function buildClaimedEnvelope(capsuleArg) {
|
|
605
671
|
const capsuleDir = resolveCapsuleDir(capsuleArg);
|
|
606
672
|
const sourceStore = await createMemorySourceStoreFromDirectory(capsuleDir);
|
|
@@ -681,7 +747,7 @@ function deployRequestBody(envelope, ttl) {
|
|
|
681
747
|
return JSON.stringify({
|
|
682
748
|
artifact: envelope.artifact,
|
|
683
749
|
clientBundle: envelope.clientBundle,
|
|
684
|
-
clientVersion:
|
|
750
|
+
clientVersion: LAKEBED_VERSION,
|
|
685
751
|
requestedTtlSeconds: ttl
|
|
686
752
|
});
|
|
687
753
|
}
|
|
@@ -752,12 +818,16 @@ async function deployCommand(args) {
|
|
|
752
818
|
try {
|
|
753
819
|
envelope = currentDeploy?.claimed ? await buildClaimedEnvelope(capsuleDir) : await buildAnonymousEnvelope(capsuleDir);
|
|
754
820
|
} catch (error) {
|
|
755
|
-
if (error instanceof AnonymousCompilerError && canUpdate && !currentDeploy
|
|
821
|
+
if (error instanceof AnonymousCompilerError && canUpdate && currentDeploy && !currentDeploy.claimed) {
|
|
756
822
|
throw new Error(
|
|
757
823
|
`${error.message}\n\nThis deploy is still anonymous. Claim it first, then run lakebed deploy again to use server-side fetch.`
|
|
758
824
|
);
|
|
759
825
|
}
|
|
760
|
-
|
|
826
|
+
if ((!canUpdate || !currentDeploy) && canDeployAfterClaim(error)) {
|
|
827
|
+
envelope = await buildClaimRequiredEnvelope({ capsuleDir });
|
|
828
|
+
} else {
|
|
829
|
+
throw error;
|
|
830
|
+
}
|
|
761
831
|
}
|
|
762
832
|
const body = deployRequestBody(envelope, ttl);
|
|
763
833
|
let mode = "created";
|
|
@@ -801,11 +871,15 @@ async function deployCommand(args) {
|
|
|
801
871
|
}
|
|
802
872
|
|
|
803
873
|
if (hasFlag(args, "--json")) {
|
|
804
|
-
console.log(JSON.stringify(deployed, null, 2));
|
|
874
|
+
console.log(JSON.stringify(envelope.claimRequired ? { ...deployed, claimRequired: true } : deployed, null, 2));
|
|
805
875
|
return;
|
|
806
876
|
}
|
|
807
877
|
|
|
808
|
-
|
|
878
|
+
if (envelope.claimRequired && mode !== "updated") {
|
|
879
|
+
console.log("Created claim-required preview.\n");
|
|
880
|
+
} else {
|
|
881
|
+
console.log(`${mode === "updated" ? "Updated" : "Created"} anonymous preview.\n`);
|
|
882
|
+
}
|
|
809
883
|
console.log(`App: ${deployed.url}`);
|
|
810
884
|
console.log(`Expires: ${deployed.expiresAt}`);
|
|
811
885
|
if (deployed.claimUrl) {
|
|
@@ -818,6 +892,10 @@ async function deployCommand(args) {
|
|
|
818
892
|
console.log(` requests: ${deployed.limits.requestsPerDay} / day`);
|
|
819
893
|
console.log(` mutations: ${deployed.limits.mutationsPerDay} / day`);
|
|
820
894
|
console.log(` outbound fetch: ${envelope.artifact.deployTarget === "claimed-source" ? "enabled" : "disabled"}`);
|
|
895
|
+
if (envelope.claimRequired) {
|
|
896
|
+
console.log("\nThis app needs a claimed deploy before server-side fetch can run.");
|
|
897
|
+
console.log("Open the claim URL, then run lakebed deploy again.");
|
|
898
|
+
}
|
|
821
899
|
}
|
|
822
900
|
|
|
823
901
|
async function claimCommand(args) {
|
package/src/version.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const LAKEBED_VERSION = "0.0.5";
|