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 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.4",
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 = "0.0.3" }) {
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 = "0.0.3" }) {
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: "0.0.3",
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?.claimed) {
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
- throw error;
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
- console.log(`${mode === "updated" ? "Updated" : "Created"} anonymous preview.\n`);
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";