@samebase/convex-hosting 0.14.0 → 0.15.0

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.
Files changed (35) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/README.md +69 -34
  3. package/dist/cli/convex.d.ts +6 -0
  4. package/dist/cli/convex.js +22 -1
  5. package/dist/cli/convex.js.map +1 -1
  6. package/dist/cli/files.js +6 -1
  7. package/dist/cli/files.js.map +1 -1
  8. package/dist/cli/main.js +138 -20
  9. package/dist/cli/main.js.map +1 -1
  10. package/dist/cli/manifest.d.ts +19 -0
  11. package/dist/cli/manifest.js +71 -0
  12. package/dist/cli/manifest.js.map +1 -0
  13. package/dist/cli/site-config.d.ts +32 -0
  14. package/dist/cli/site-config.js +15 -0
  15. package/dist/cli/site-config.js.map +1 -0
  16. package/dist/client/answer.d.ts +16 -0
  17. package/dist/client/answer.js +51 -0
  18. package/dist/client/answer.js.map +1 -0
  19. package/dist/client/index.d.ts +23 -3
  20. package/dist/client/index.js +57 -78
  21. package/dist/client/index.js.map +1 -1
  22. package/dist/client/manifest.d.ts +68 -0
  23. package/dist/client/manifest.js +48 -0
  24. package/dist/client/manifest.js.map +1 -0
  25. package/dist/client/version-module.d.ts +1 -1
  26. package/dist/component/_generated/component.d.ts +2 -18
  27. package/dist/component/lib.d.ts +15 -46
  28. package/dist/component/lib.js +35 -134
  29. package/dist/component/lib.js.map +1 -1
  30. package/dist/component/schema.d.ts +2 -2
  31. package/dist/component/schema.js +2 -2
  32. package/dist/workers-sdk/packages/workers-shared/asset-worker/src/assets-manifest.d.ts +14 -0
  33. package/dist/workers-sdk/packages/workers-shared/asset-worker/src/assets-manifest.js +84 -0
  34. package/dist/workers-sdk/packages/workers-shared/asset-worker/src/assets-manifest.js.map +1 -0
  35. package/package.json +2 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,37 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.15.0
4
+
5
+ - A request reads no database. The deploy writes the version's assets into
6
+ `.convex-hosting/version.js` (each path's content hash, content type, and stored file, in the
7
+ layout of Cloudflare's asset manifest), and the HTTP action answers from them. A page, a miss, a
8
+ redirect, a 304, and a session refresh cost one function call, the HTTP action, where they cost
9
+ two; an answer with a body costs two, the action and the download of the file, where it cost
10
+ three. The component's `route` query is gone.
11
+ - Breaking: `routeAll` takes one object, with every input named, and the assets in place of the
12
+ version id:
13
+
14
+ ```ts
15
+ import { assets } from "../.convex-hosting/version.js";
16
+
17
+ routeAll({ http, component: components.hosting, assets, worker: server });
18
+ ```
19
+
20
+ A files-only site leaves `worker` out. `assets` and `worker` are Cloudflare's names: the static
21
+ assets of a Worker, and the Worker script. The file exports only `assets`: the query that reloads
22
+ open tabs reads `assets.versionId` in place of `versionId`. A test that mocks the version module
23
+ adds `assets: { format: 1, versionId: "test", markToken: "test", files: null }`.
24
+
25
+ - After the push, `deploy` sends one request with the version's mark token, and the code that
26
+ answers marks its version live; before, the first request did. `markLive` answers what it did and
27
+ never throws. It takes the time the request arrived and refuses a mark when another version went
28
+ live after it: a mark that was still running when another deploy's push and mark landed deleted
29
+ the entries of the version that serves.
30
+ - A first install pushes twice: once to install the component, and once after the upload, to serve
31
+ the site.
32
+ - A version file uploaded to another deployment, such as production's pushed to development by a
33
+ running `npx convex dev`, answers 404 naming that deployment.
34
+
3
35
  ## 0.14.0
4
36
 
5
37
  - The README, which this package ships, starts with what the component is, who it is for, and the
package/README.md CHANGED
@@ -6,15 +6,18 @@ A [Convex component](https://www.convex.dev/components/samebase/convex-hosting)
6
6
  app's built files and server-rendered pages from the deployment that runs its backend, at
7
7
  `https://<deployment>.convex.site`.
8
8
 
9
- Use it when the backend is on Convex and the frontend should be served from the same deployment,
10
- including a [TanStack Start](https://tanstack.com/start) app with server-side rendering.
9
+ Use it when the backend is on Convex and the frontend should be served from the same deployment: a
10
+ [TanStack Start](https://tanstack.com/start) app whose pages render on the server, or a files-only
11
+ site.
11
12
 
12
13
  ## Quick start
13
14
 
14
- Requires Node 22 or later and `convex` 1.46 or later.
15
+ Requires Node 22 or later and `convex` 1.46 or later. These steps set up a
16
+ [TanStack Start](https://tanstack.com/start) app whose pages render on the server; a site without a
17
+ server skips the three server steps, see [Files only](#files-only).
15
18
 
16
19
  ```sh
17
- npm install @samebase/convex-hosting
20
+ npm install @samebase/convex-hosting @samebase/convex-hosting-tanstack-start
18
21
  ```
19
22
 
20
23
  `convex/convex.config.ts` mounts the component:
@@ -29,18 +32,38 @@ app.use(hosting);
29
32
  export default app;
30
33
  ```
31
34
 
32
- `convex/http.ts` registers the site, every method at every path, as every request reaches a
33
- Cloudflare Worker:
35
+ `vite.config.ts` adds the adapter's plugin before TanStack Start's. It builds `src/server.ts` as the
36
+ server entry and sets the build up for Convex's runtime:
37
+
38
+ ```ts
39
+ import { convexSsr } from "@samebase/convex-hosting-tanstack-start/vite";
40
+
41
+ export default defineConfig({
42
+ plugins: [convexSsr(), tanstackStart(), react()],
43
+ });
44
+ ```
45
+
46
+ `src/server.ts` is that entry. The adapter's handler renders every request that no file answers:
47
+
48
+ ```ts
49
+ export { default } from "@samebase/convex-hosting-tanstack-start/server";
50
+ ```
51
+
52
+ `convex/http.ts` registers the site, every method at every path: a file for a path that has one, the
53
+ server build for every other request, as a Cloudflare Worker with static assets does:
34
54
 
35
55
  ```ts
36
56
  import { httpRouter } from "convex/server";
37
57
  import { routeAll } from "@samebase/convex-hosting";
38
58
  import { components } from "./_generated/api.js";
39
- import { versionId } from "../.convex-hosting/version.js";
59
+ import { assets } from "../.convex-hosting/version.js";
60
+ // Type only, nothing is bundled: the server build imported below.
61
+ import type {} from "@samebase/convex-hosting-tanstack-start/server-build";
62
+ import server from "../dist/server/server.js";
40
63
 
41
64
  const http = httpRouter();
42
65
 
43
- routeAll(http, components.hosting, versionId);
66
+ routeAll({ http, component: components.hosting, assets, worker: server });
44
67
 
45
68
  export default http;
46
69
  ```
@@ -48,38 +71,48 @@ export default http;
48
71
  Deploy:
49
72
 
50
73
  ```sh
51
- npx convex-hosting deploy --build "vite build" --dist dist
74
+ npx convex-hosting deploy --build "vite build" --dist dist/client
52
75
  ```
53
76
 
54
- It builds, uploads `dist`, pushes the functions, and prints
55
- `Deployed version <id> to https://<deployment>.convex.site.` A site with no build omits `--build`; a
56
- single-page app adds `--not-found-handling single-page-application`. Deploy production with this
57
- command, never with a plain `npx convex deploy`: a plain push names the version of this checkout's
58
- last deploy, often another deployment's, and the site answers 404 until the next
59
- `convex-hosting deploy`.
77
+ It builds, uploads `dist/client`, pushes the functions with the server build, and prints
78
+ `Deployed version <id> to https://<deployment>.convex.site.` Deploy production with this command,
79
+ never with a plain `npx convex deploy`: a plain push names the version of this checkout's last
80
+ deploy, often another deployment's, and the site answers 404 until the next `convex-hosting deploy`.
60
81
 
61
82
  On a fresh clone, the first push of `npx convex dev` fails: `convex/http.ts` imports
62
- `.convex-hosting/version.js`, which only a deploy writes. `npx convex dev` keeps running after that
63
- error, so the first run is two terminals, for a cloud, local, or anonymous deployment alike:
83
+ `.convex-hosting/version.js` and `dist/server/server.js`, which only a deploy writes.
84
+ `npx convex dev` keeps running after that error, so the first run is two terminals, for a cloud,
85
+ local, or anonymous deployment alike:
64
86
 
65
87
  ```sh
66
88
  npx convex dev # creates your dev deployment and .env.local; keep it running
67
89
  ```
68
90
 
69
91
  ```sh
70
- npx convex-hosting deploy --dev --build "vite build" --dist dist
92
+ npx convex-hosting deploy --dev --build "vite build" --dist dist/client
71
93
  ```
72
94
 
73
- The deploy writes the version file and pushes; the watcher in the first terminal shows its error
74
- until the next change under `convex/`, and works as usual from then on.
95
+ The deploy writes the version file, builds, and pushes; the watcher in the first terminal shows its
96
+ error until the next change under `convex/`, and works as usual from then on.
97
+
98
+ The pages get the request's data, such as a session, through a `src/server.ts` that wraps the
99
+ adapter's handler and passes a `context`; the
100
+ [adapter's README](https://github.com/samebase/convex-hosting/tree/main/packages/convex-hosting-tanstack-start)
101
+ has that, the loaders, and the settings. Convex Auth sessions in httpOnly cookies come from
102
+ [`@samebase/convex-auth-tanstack-start`](https://github.com/samebase/convex-hosting/tree/main/packages/convex-auth-tanstack-start),
103
+ which runs on any host; the
104
+ [tanstack-ssr example](https://github.com/samebase/convex-hosting/tree/main/examples/tanstack-ssr)
105
+ wires both.
75
106
 
76
- ## Server-rendered pages
107
+ ## Files only
77
108
 
78
- An app that renders pages passes its server entry as the fourth argument of `routeAll`. The entry
79
- takes the place of a Worker script: an object with `fetch(request)` that answers what no file
80
- answers. TanStack Start has
81
- [an adapter](https://github.com/samebase/convex-hosting/tree/main/packages/convex-hosting-tanstack-start),
82
- with streamed pages, server functions, server routes, request middleware, and cookie sessions.
109
+ A site without a server leaves `worker` out,
110
+ `routeAll({ http, component: components.hosting, assets })`, needs no adapter package, and deploys
111
+ its build directory: `--dist dist` after a `vite build`, or `--dist public` with no `--build` at
112
+ all. A single-page app adds `--not-found-handling single-page-application`. The
113
+ [tanstack-spa](https://github.com/samebase/convex-hosting/tree/main/examples/tanstack-spa) and
114
+ [hello-world-assets-only](https://github.com/samebase/convex-hosting/tree/main/examples/hello-world-assets-only)
115
+ examples are such sites.
83
116
 
84
117
  ## Examples
85
118
 
@@ -128,7 +161,7 @@ inside a Convex HTTP action, so `_headers`, `_redirects`, `.assetsignore`, `html
128
161
  `not_found_handling` mean what they mean on Cloudflare.
129
162
 
130
163
  This repository's CI runs Cloudflare's own tests against it: its
131
- [196 cases](https://github.com/samebase/convex-hosting/blob/main/packages/convex-hosting/src/component/conformance.test.ts)
164
+ [196 cases](https://github.com/samebase/convex-hosting/blob/main/packages/convex-hosting/src/client/answer.test.ts)
132
165
  for `html_handling` and path encoding, and its
133
166
  [integration test](https://github.com/samebase/convex-hosting/tree/main/packages/convex-hosting/src/workers-sdk/fixtures/workers-with-assets-only)
134
167
  for a files-only site, with its imports pointed at this package, on a local Convex deployment.
@@ -152,13 +185,15 @@ lists what differs and how to move an app.
152
185
  On Cloudflare, `wrangler deploy` uploads the files and the Worker code as one version, so the switch
153
186
  is atomic. Here two systems hold the two halves: the component's storage holds the files, and
154
187
  `npx convex deploy` pushes the code. `deploy` binds them the same way. It uploads the files as a new
155
- version, writes that version's id to `.convex-hosting/version.js`, and then pushes the code, which
156
- imports the file and asks the component for exactly that version. The push is the switch: no pointer
157
- moves in a second step, so there is no moment when new code serves old files, and no page refers to
158
- a file that is not uploaded yet. The first request the new code serves marks its version live and
159
- deletes the versions that were live before it. A version whose push never came is deleted at a later
160
- switch, once it is a day old; nothing is deleted by the clock alone. The one rule: run one deploy at
161
- a time per deployment. The
188
+ version, writes that version's id and its files (each path's stored file, in the layout of
189
+ Cloudflare's asset manifest) to `.convex-hosting/version.js`, and then pushes the code, which
190
+ imports the file. The push is the switch: no pointer moves in a second step, so there is no moment
191
+ when new code serves old files, and no page refers to a file that is not uploaded yet. A request
192
+ reads no database: it costs one function call, the HTTP action, and a file adds the download of its
193
+ bytes, which Convex counts as a call too. After the push, `deploy` asks the served code to mark its
194
+ version live, which deletes the versions that were live before it. A version whose push never came
195
+ is deleted at a later switch, once it is a day old; nothing is deleted by the clock alone. The one
196
+ rule: run one deploy at a time per deployment. The
162
197
  [design](https://github.com/samebase/convex-hosting/blob/main/docs/design.md#orderings-and-timing)
163
198
  works through every interleaving.
164
199
 
@@ -66,6 +66,12 @@ export declare function probeComponent(cli: Cli, target: Target): Promise<string
66
66
  * and it is the custom domain when the dashboard's "Override Environment Variables" sets one.
67
67
  */
68
68
  export declare function siteUrl(cli: Cli, flags: string[]): Promise<string>;
69
+ /**
70
+ * Evaluates `code` as an inline query in the component and validates its result. An inline query
71
+ * runs whatever release of this package the component's functions are, so the upload of a new
72
+ * release reads with it before the push has installed the new functions.
73
+ */
74
+ export declare function runComponentQuery<T>(cli: Cli, target: Target, code: string, result: z.ZodType<T>): Promise<T>;
69
75
  /**
70
76
  * Runs a function of the component with `npx convex run` and validates its result. The Convex CLI
71
77
  * prints the result as JSON from another process, so `result` checks it at runtime; its type makes
@@ -14,7 +14,9 @@ export const nodeCli = {
14
14
  cwd: process.cwd(),
15
15
  env: process.env,
16
16
  convex: (args) => new Promise((resolve) => {
17
- execFile(process.execPath, [convexBin(process.cwd()), ...args], { encoding: "utf8" }, (error, stdout, stderr) => {
17
+ execFile(process.execPath, [convexBin(process.cwd()), ...args],
18
+ // A page of storage URLs can pass Node's default of 1 MiB.
19
+ { encoding: "utf8", maxBuffer: 64 * 1024 * 1024 }, (error, stdout, stderr) => {
18
20
  const exitCode = error === null ? 0 : typeof error.code === "number" ? error.code : 1;
19
21
  resolve({ exitCode, stdout, stderr });
20
22
  });
@@ -103,6 +105,25 @@ export async function siteUrl(cli, flags) {
103
105
  }
104
106
  return z.string().parse(JSON.parse(stdout));
105
107
  }
108
+ /**
109
+ * Evaluates `code` as an inline query in the component and validates its result. An inline query
110
+ * runs whatever release of this package the component's functions are, so the upload of a new
111
+ * release reads with it before the push has installed the new functions.
112
+ */
113
+ export async function runComponentQuery(cli, target, code, result) {
114
+ const { exitCode, stdout, stderr } = await cli.convex([
115
+ "run",
116
+ "--component",
117
+ target.component,
118
+ "--inline-query",
119
+ code,
120
+ ...target.flags,
121
+ ]);
122
+ if (exitCode !== 0) {
123
+ throw new Error(stderr.trim() || stdout.trim() || `npx convex run --inline-query exited with ${exitCode}.`);
124
+ }
125
+ return result.parse(JSON.parse(stdout));
126
+ }
106
127
  /**
107
128
  * Runs a function of the component with `npx convex run` and validates its result. The Convex CLI
108
129
  * prints the result as JSON from another process, so `result` checks it at runtime; its type makes
@@ -1 +1 @@
1
- {"version":3,"file":"convex.js","sourceRoot":"","sources":["../../src/cli/convex.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"convex.js","sourceRoot":"","sources":["../../src/cli/convex.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAqB,MAAM,oBAAoB,CAAC;AACxE,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AACrC,OAAO,EACL,eAAe,GAKhB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAsBxB,8FAA8F;AAC9F,SAAS,SAAS,CAAC,GAAW;IAC5B,MAAM,cAAc,GAAG,aAAa,CAAC,IAAI,CAAC,GAAG,EAAE,cAAc,CAAC,CAAC,CAAC;IAChE,OAAO,IAAI,CAAC,OAAO,CAAC,cAAc,CAAC,OAAO,CAAC,qBAAqB,CAAC,CAAC,EAAE,KAAK,EAAE,SAAS,CAAC,CAAC;AACxF,CAAC;AAED,MAAM,CAAC,MAAM,OAAO,GAAQ;IAC1B,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE;IAClB,GAAG,EAAE,OAAO,CAAC,GAAG;IAChB,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE,CACf,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QACtB,QAAQ,CACN,OAAO,CAAC,QAAQ,EAChB,CAAC,SAAS,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,EAAE,GAAG,IAAI,CAAC;QACnC,2DAA2D;QAC3D,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,EAAE,GAAG,IAAI,GAAG,IAAI,EAAE,EACjD,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE;YACxB,MAAM,QAAQ,GAAG,KAAK,KAAK,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,KAAK,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;YACtF,OAAO,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;QACxC,CAAC,CACF,CAAC;IACJ,CAAC,CAAC;IACJ,eAAe,EAAE,CAAC,IAAI,EAAE,EAAE,CACxB,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,SAAS,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,EAAE,GAAG,IAAI,CAAC,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,CAAC;IAC9F,KAAK,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,CAAC;IAC/E,KAAK,EAAE,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,EAAE,IAAI,CAAC;IAC1C,GAAG,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC;CACzC,CAAC;AAEF,oDAAoD;AACpD,SAAS,QAAQ,CAAC,KAAmB;IACnC,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC;IACtE,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,aAAa,CAAC,GAAQ;IACpC,MAAM,QAAQ,GAAG,CAAC,IAAY,EAAE,EAAE;QAChC,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;QACjC,OAAO,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IACtE,CAAC,CAAC;IACF,OAAO,EAAE,GAAG,QAAQ,CAAC,MAAM,CAAC,EAAE,GAAG,QAAQ,CAAC,YAAY,CAAC,EAAE,GAAG,GAAG,CAAC,GAAG,EAAE,CAAC;AACxE,CAAC;AAQD;;;;;;GAMG;AACH,MAAM,UAAU,kBAAkB,CAAC,GAAQ,EAAE,WAAwB;IACnE,IAAI,aAAa,CAAC,GAAG,CAAC,CAAC,sBAAsB;QAAE,OAAO,EAAE,CAAC;IACzD,QAAQ,WAAW,CAAC,IAAI,EAAE,CAAC;QACzB,KAAK,YAAY;YACf,OAAO,CAAC,QAAQ,CAAC,CAAC;QACpB,KAAK,SAAS;YACZ,OAAO,CAAC,gBAAgB,EAAE,WAAW,CAAC,IAAI,CAAC,CAAC;QAC9C,KAAK,KAAK;YACR,OAAO,EAAE,CAAC;QACZ;YACE,OAAO,WAA2B,CAAC;IACvC,CAAC;AACH,CAAC;AAKD;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,GAAQ,EAAE,MAAc;IAC3D,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,GAAG,CAAC,MAAM,CAAC;QACpD,KAAK;QACL,aAAa;QACb,MAAM,CAAC,SAAS;QAChB,gBAAgB;QAChB,MAAM;QACN,GAAG,MAAM,CAAC,KAAK;KAChB,CAAC,CAAC;IACH,IAAI,QAAQ,KAAK,CAAC;QAAE,OAAO,SAAS,CAAC;IACrC,MAAM,MAAM,GAAG,GAAG,MAAM,KAAK,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC;IAC7C,IAAI,MAAM,CAAC,QAAQ,CAAC,mBAAmB,MAAM,CAAC,SAAS,kBAAkB,CAAC;QAAE,OAAO,MAAM,CAAC;IAC1F,MAAM,IAAI,KAAK,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,MAAM,CAAC,IAAI,EAAE,IAAI,8BAA8B,QAAQ,GAAG,CAAC,CAAC;AAC/F,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,OAAO,CAAC,GAAQ,EAAE,KAAe;IACrD,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,GAAG,CAAC,MAAM,CAAC;QACpD,KAAK;QACL,gBAAgB;QAChB,6BAA6B;QAC7B,GAAG,KAAK;KACT,CAAC,CAAC;IACH,IAAI,QAAQ,KAAK,CAAC,EAAE,CAAC;QACnB,MAAM,IAAI,KAAK,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,MAAM,CAAC,IAAI,EAAE,IAAI,8BAA8B,QAAQ,GAAG,CAAC,CAAC;IAC/F,CAAC;IACD,OAAO,CAAC,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC;AAC9C,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,GAAQ,EACR,MAAc,EACd,IAAY,EACZ,MAAoB;IAEpB,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,GAAG,CAAC,MAAM,CAAC;QACpD,KAAK;QACL,aAAa;QACb,MAAM,CAAC,SAAS;QAChB,gBAAgB;QAChB,IAAI;QACJ,GAAG,MAAM,CAAC,KAAK;KAChB,CAAC,CAAC;IACH,IAAI,QAAQ,KAAK,CAAC,EAAE,CAAC;QACnB,MAAM,IAAI,KAAK,CACb,MAAM,CAAC,IAAI,EAAE,IAAI,MAAM,CAAC,IAAI,EAAE,IAAI,6CAA6C,QAAQ,GAAG,CAC3F,CAAC;IACJ,CAAC;IACD,OAAO,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC;AAC1C,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,GAAQ,EACR,MAAc,EACd,GAAQ,EACR,IAAuB,EACvB,MAA0C;IAE1C,MAAM,YAAY,GAAG,eAAe,CAAC,GAAG,CAAC,CAAC;IAC1C,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,GAAG,CAAC,MAAM,CAAC;QACpD,KAAK;QACL,aAAa;QACb,MAAM,CAAC,SAAS;QAChB,YAAY;QACZ,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC;QACpB,GAAG,MAAM,CAAC,KAAK;KAChB,CAAC,CAAC;IACH,IAAI,QAAQ,KAAK,CAAC,EAAE,CAAC;QACnB,MAAM,IAAI,KAAK,CACb,MAAM,CAAC,IAAI,EAAE,IAAI,MAAM,CAAC,IAAI,EAAE,IAAI,kBAAkB,YAAY,gBAAgB,QAAQ,GAAG,CAC5F,CAAC;IACJ,CAAC;IACD,OAAO,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC;AACxE,CAAC"}
package/dist/cli/files.js CHANGED
@@ -4,7 +4,7 @@ import { join, relative } from "node:path";
4
4
  import { constructHeaders, constructRedirects, } from "../workers-sdk/packages/workers-shared/utils/configuration/constructConfiguration.js";
5
5
  import { parseHeaders } from "../workers-sdk/packages/workers-shared/utils/configuration/parseHeaders.js";
6
6
  import { parseRedirects } from "../workers-sdk/packages/workers-shared/utils/configuration/parseRedirects.js";
7
- import { HEADERS_FILENAME, MAX_ASSET_SIZE, REDIRECTS_FILENAME, } from "../workers-sdk/packages/workers-shared/utils/constants.js";
7
+ import { HEADERS_FILENAME, MAX_ASSET_COUNT, MAX_ASSET_SIZE, REDIRECTS_FILENAME, } from "../workers-sdk/packages/workers-shared/utils/constants.js";
8
8
  import { createAssetsIgnoreFunction, getContentType, maybeGetFile, normalizeFilePath, } from "../workers-sdk/packages/workers-shared/utils/helpers.js";
9
9
  /**
10
10
  * Convex limits a document to 1 MiB. The version document holds the config next to two short
@@ -45,6 +45,11 @@ export async function readDist(dist, handling, log) {
45
45
  walk(file);
46
46
  }
47
47
  else if (stats.isFile() && !assetsIgnoreFunction(ignorePath)) {
48
+ // Cloudflare's limit per Worker version on its Paid plan, 100,000 files.
49
+ if (entries.length === MAX_ASSET_COUNT) {
50
+ throw new Error(`${dist} has more than ${MAX_ASSET_COUNT.toLocaleString("en-US")} files, Cloudflare's ` +
51
+ "limit per Worker version. List the files that are not served in .assetsignore.");
52
+ }
48
53
  const bytes = readFileSync(file);
49
54
  // Cloudflare's limit per asset, 25 MiB. The HTTP action that serves a file cannot answer
50
55
  // more than Convex's 100 MiB response limit anyway.
@@ -1 +1 @@
1
- {"version":3,"file":"files.js","sourceRoot":"","sources":["../../src/cli/files.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,WAAW,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACrF,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAE3C,OAAO,EACL,gBAAgB,EAChB,kBAAkB,GACnB,MAAM,sFAAsF,CAAC;AAC9F,OAAO,EAAE,YAAY,EAAE,MAAM,4EAA4E,CAAC;AAC1G,OAAO,EAAE,cAAc,EAAE,MAAM,8EAA8E,CAAC;AAC9G,OAAO,EACL,gBAAgB,EAChB,cAAc,EACd,kBAAkB,GACnB,MAAM,2DAA2D,CAAC;AACnE,OAAO,EACL,0BAA0B,EAC1B,cAAc,EACd,YAAY,EACZ,iBAAiB,GAClB,MAAM,yDAAyD,CAAC;AAEjE;;;GAGG;AACH,MAAM,gBAAgB,GAAG,GAAG,GAAG,IAAI,CAAC;AAYpC;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,QAAQ,CAC5B,IAAY,EACZ,QAAyB,EACzB,GAA8B;IAE9B,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,qBAAqB,IAAI,kBAAkB,CAAC,CAAC;IACpF,MAAM,EAAE,oBAAoB,EAAE,GAAG,MAAM,0BAA0B,CAAC,IAAI,CAAC,CAAC;IACxE,MAAM,OAAO,GAAgB,EAAE,CAAC;IAEhC,MAAM,IAAI,GAAG,CAAC,SAAiB,EAAE,EAAE;QACjC,KAAK,MAAM,IAAI,IAAI,WAAW,CAAC,SAAS,CAAC,EAAE,CAAC;YAC1C,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;YACnC,0FAA0F;YAC1F,6FAA6F;YAC7F,yEAAyE;YACzE,MAAM,KAAK,GAAG,QAAQ,CAAC,IAAI,EAAE,EAAE,cAAc,EAAE,KAAK,EAAE,CAAC,CAAC;YACxD,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;gBACxB,GAAG,CAAC,WAAW,IAAI,+BAA+B,CAAC,CAAC;gBACpD,SAAS;YACX,CAAC;YACD,yFAAyF;YACzF,MAAM,UAAU,GAAG,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,UAAU,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;YAC9D,IAAI,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC;gBACxB,sFAAsF;gBACtF,IAAI,oBAAoB,CAAC,GAAG,UAAU,GAAG,CAAC;oBAAE,SAAS;gBACrD,IAAI,SAAS,CAAC,IAAI,CAAC,CAAC,cAAc,EAAE,EAAE,CAAC;oBACrC,GAAG,CAAC,WAAW,IAAI,qDAAqD,CAAC,CAAC;oBAC1E,SAAS;gBACX,CAAC;gBACD,IAAI,CAAC,IAAI,CAAC,CAAC;YACb,CAAC;iBAAM,IAAI,KAAK,CAAC,MAAM,EAAE,IAAI,CAAC,oBAAoB,CAAC,UAAU,CAAC,EAAE,CAAC;gBAC/D,MAAM,KAAK,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;gBACjC,yFAAyF;gBACzF,oDAAoD;gBACpD,IAAI,KAAK,CAAC,UAAU,GAAG,cAAc,EAAE,CAAC;oBACtC,MAAM,IAAI,KAAK,CACb,GAAG,IAAI,OAAO,CAAC,KAAK,CAAC,UAAU,GAAG,IAAI,GAAG,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,4BAA4B;wBACnF,gDAAgD,CACnD,CAAC;gBACJ,CAAC;gBACD,OAAO,CAAC,IAAI,CAAC;oBACX,IAAI,EAAE,iBAAiB,CAAC,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;oBAC7C,MAAM,EAAE,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;oBACxD,WAAW,EAAE,cAAc,CAAC,IAAI,CAAC,IAAI,SAAS;oBAC9C,IAAI;iBACL,CAAC,CAAC;YACL,CAAC;QACH,CAAC;IACH,CAAC,CAAC;IACF,IAAI,CAAC,IAAI,CAAC,CAAC;IAEX,MAAM,MAAM,GAAG;QACb,KAAK,EAAE,GAAG,EAAE,GAAE,CAAC;QACf,GAAG;QACH,IAAI,EAAE,GAAG;QACT,IAAI,EAAE,GAAG;QACT,KAAK,EAAE,CAAC,KAAY,EAAE,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC;KAC5C,CAAC;IACF,MAAM,aAAa,GAAG,IAAI,CAAC,IAAI,EAAE,kBAAkB,CAAC,CAAC;IACrD,MAAM,SAAS,GAAG,YAAY,CAAC,aAAa,CAAC,CAAC;IAC9C,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,EAAE,gBAAgB,CAAC,CAAC;IACjD,MAAM,OAAO,GAAG,YAAY,CAAC,WAAW,CAAC,CAAC;IAC1C,MAAM,MAAM,GAAW;QACrB,aAAa,EAAE,QAAQ,CAAC,aAAa;QACrC,kBAAkB,EAAE,QAAQ,CAAC,kBAAkB;QAC/C,GAAG,CAAC,SAAS,KAAK,SAAS;YACzB,CAAC,CAAC,EAAE;YACJ,CAAC,CAAC,kBAAkB,CAAC;gBACjB,SAAS,EAAE,cAAc,CAAC,SAAS,EAAE,EAAE,YAAY,EAAE,QAAQ,CAAC,aAAa,EAAE,CAAC;gBAC9E,aAAa;gBACb,MAAM;aACP,CAAC,CAAC;QACP,GAAG,CAAC,OAAO,KAAK,SAAS;YACvB,CAAC,CAAC,EAAE;YACJ,CAAC,CAAC,gBAAgB,CAAC,EAAE,OAAO,EAAE,YAAY,CAAC,OAAO,CAAC,EAAE,WAAW,EAAE,MAAM,EAAE,CAAC,CAAC;KAC/E,CAAC;IAEF,MAAM,WAAW,GAAG,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC;IAC9D,IAAI,WAAW,GAAG,gBAAgB,EAAE,CAAC;QACnC,MAAM,OAAO,GACX,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC;YACzD,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC;YACrD,CAAC,CAAC,aAAa;YACf,CAAC,CAAC,WAAW,CAAC;QAClB,MAAM,IAAI,KAAK,CACb,gBAAgB,OAAO,8BAA8B,IAAI,CAAC,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,QAAQ;YACxF,8EAA8E,CACjF,CAAC;IACJ,CAAC;IACD,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC;AAC7B,CAAC"}
1
+ {"version":3,"file":"files.js","sourceRoot":"","sources":["../../src/cli/files.ts"],"names":[],"mappings":"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"}
package/dist/cli/main.js CHANGED
@@ -1,13 +1,16 @@
1
1
  import { randomUUID } from "node:crypto";
2
- import { appendFileSync, existsSync, mkdirSync, readFileSync, rmSync, writeFileSync, } from "node:fs";
2
+ import { appendFileSync, existsSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync, } from "node:fs";
3
3
  import { join, resolve } from "node:path";
4
4
  import { fileURLToPath } from "node:url";
5
5
  import { parseArgs } from "node:util";
6
6
  import { z } from "zod";
7
+ import { ASSETS_FORMAT, MARK_HEADER, MARK_PATH, } from "../client/manifest.js";
7
8
  import { internal } from "../component/_generated/api.js";
8
9
  import { AssetConfigSchema } from "../workers-sdk/packages/workers-shared/utils/types.js";
9
- import { deploymentSelector, probeComponent, readConvexEnv, runComponent, siteUrl, } from "./convex.js";
10
+ import { deploymentSelector, probeComponent, readConvexEnv, runComponent, runComponentQuery, siteUrl, } from "./convex.js";
10
11
  import { readDist } from "./files.js";
12
+ import { buildAssetFiles } from "./manifest.js";
13
+ import { siteConfig } from "./site-config.js";
11
14
  const USAGE = `Usage:
12
15
  convex-hosting deploy --dist <dir> [options]
13
16
  Runs the build, if any, stores the site files, and pushes the functions. The push switches
@@ -35,6 +38,11 @@ export const VERSION_FILE = join(STATE_DIR, "version.js");
35
38
  * `deploy` then uploads after the push.
36
39
  */
37
40
  const FIRST_INSTALL_MARKER = join(STATE_DIR, "first-install");
41
+ /**
42
+ * Entries per inline query that reads the storage URLs: 2,000 entries and their `_storage`
43
+ * documents are 4,000 of the 32,000 documents a query may read, and about 300 KB of result.
44
+ */
45
+ const URL_PAGE = 2000;
38
46
  /** Files that the upload sends to storage at once. */
39
47
  const UPLOAD_CONCURRENCY = 4;
40
48
  /**
@@ -132,9 +140,9 @@ function quote(arg) {
132
140
  return process.platform === "win32" ? `"${arg}"` : `'${arg.replaceAll("'", `'\\''`)}'`;
133
141
  }
134
142
  /**
135
- * Builds, uploads, writes the version file, and pushes. The push is the switch: the first request
136
- * that the new code serves marks its version live. Then it reports the site URL, for a person and,
137
- * through `$GITHUB_OUTPUT`, for the next step of a GitHub Actions job.
143
+ * Builds, uploads, writes the version file, and pushes. The push is the switch. Then it asks the
144
+ * served code to mark its version live, and reports the site URL, for a person and, through
145
+ * `$GITHUB_OUTPUT`, for the next step of a GitHub Actions job.
138
146
  */
139
147
  async function deploy(cli, options, build) {
140
148
  // A deploy key wins over every selector in the Convex CLI, so a production key in the environment
@@ -187,6 +195,7 @@ async function deploy(cli, options, build) {
187
195
  const message = error instanceof Error ? error.message : String(error);
188
196
  throw new Error(`Deployed version ${versionId}, but reading the site URL failed: ${message}`);
189
197
  }
198
+ await markServedVersion(cli, url, readVersionFile(cli));
190
199
  cli.log(`Deployed version ${versionId} to ${url}.`);
191
200
  if (cli.env.GITHUB_OUTPUT)
192
201
  appendFileSync(cli.env.GITHUB_OUTPUT, `url=${url}\n`);
@@ -197,7 +206,7 @@ async function deploy(cli, options, build) {
197
206
  * reads `VITE_CONVEX_URL`). Then the upload runs here, and `npx convex run --push` pushes, with an inline
198
207
  * query at the app's root, which exists before the push installs the component. It works whether
199
208
  * or not `npx convex dev` runs; `npx convex dev --once` refuses while it runs a local deployment's
200
- * backend.
209
+ * backend. A first install pushes, uploads, and pushes again.
201
210
  */
202
211
  async function deployDev(cli, options, target, versionId, build) {
203
212
  if (build !== undefined) {
@@ -208,13 +217,21 @@ async function deployDev(cli, options, target, versionId, build) {
208
217
  const missing = await upload(cli, options, target, versionId);
209
218
  if (missing !== undefined)
210
219
  cli.log(notInstalledYet(options.component));
220
+ await pushDev(cli);
221
+ if (missing !== undefined) {
222
+ await uploadAfterPush(cli, options, target, versionId);
223
+ await pushDev(cli);
224
+ }
225
+ }
226
+ async function pushDev(cli) {
211
227
  const exitCode = await cli.convexInherited(["run", "--push", "--inline-query", "null"]);
212
228
  if (exitCode !== 0)
213
229
  throw new Error(`npx convex run --push failed with exit code ${exitCode}.`);
214
- if (missing !== undefined)
215
- await uploadAfterPush(cli, options, target, versionId);
216
230
  }
217
- /** Production or a preview: the build, if any, and the upload run inside `npx convex deploy --cmd`. */
231
+ /**
232
+ * Production or a preview: the build, if any, and the upload run inside `npx convex deploy --cmd`.
233
+ * A first install uploads after that push and pushes again with `npx convex deploy`.
234
+ */
218
235
  async function deployWithConvexDeploy(cli, options, target, versionId, build) {
219
236
  const marker = join(cli.cwd, FIRST_INSTALL_MARKER);
220
237
  rmSync(marker, { force: true });
@@ -250,8 +267,12 @@ async function deployWithConvexDeploy(cli, options, target, versionId, build) {
250
267
  rmSync(marker, { force: true });
251
268
  if (exitCode !== 0)
252
269
  throw new Error(`npx convex deploy failed with exit code ${exitCode}.`);
253
- if (firstInstall)
270
+ if (firstInstall) {
254
271
  await uploadAfterPush(cli, options, target, versionId);
272
+ const again = await cli.convexInherited(["deploy", ...deployFlags]);
273
+ if (again !== 0)
274
+ throw new Error(`npx convex deploy failed with exit code ${again}.`);
275
+ }
255
276
  }
256
277
  /** The upload inside `npx convex deploy --cmd`. On a first install, `deploy` uploads after the push. */
257
278
  async function uploadInsideDeploy(cli, options, versionId) {
@@ -265,7 +286,7 @@ async function uploadInsideDeploy(cli, options, versionId) {
265
286
  }
266
287
  }
267
288
  function notInstalledYet(component) {
268
- return `The component "${component}" is not installed yet. The push installs it, and the upload runs after the push.`;
289
+ return `The component "${component}" is not installed yet. The push installs it, the upload runs after the push, and a second push serves the site.`;
269
290
  }
270
291
  /** The upload of a first install, after the push. The probe's output shows a mistyped `--component`. */
271
292
  async function uploadAfterPush(cli, options, target, versionId) {
@@ -276,17 +297,19 @@ async function uploadAfterPush(cli, options, target, versionId) {
276
297
  }
277
298
  }
278
299
  /**
279
- * Uploads `--dist` as version `versionId` and writes the version file. When the component is not
280
- * installed yet, it writes only the version file, so that the push that follows installs the
281
- * component, and returns the probe's output.
300
+ * Uploads `--dist` as version `versionId` and writes the version file with the site's files. When
301
+ * the component is not installed yet, it writes a version file without files, which serves
302
+ * nothing, so that the push that follows installs the component, and returns the probe's output.
282
303
  */
283
304
  async function upload(cli, options, target, versionId) {
284
305
  const dist = resolve(cli.cwd, options.dist);
285
306
  const { config, entries } = await readDist(dist, options, cli.log);
286
307
  const generateUploadUrls = (count) => runComponent(cli, target, internal.lib.generateUploadUrls, { count }, z.array(z.string()).length(count));
308
+ // The CLI sends it after the push; see `MARK_HEADER`.
309
+ const markToken = randomUUID();
287
310
  const missing = await probeComponent(cli, target);
288
311
  if (missing !== undefined) {
289
- writeVersionFile(cli, versionId);
312
+ writeVersionFile(cli, { format: ASSETS_FORMAT, versionId, markToken, files: null });
290
313
  return missing;
291
314
  }
292
315
  cli.log(`Uploading version ${versionId}: ${entries.length} files.`);
@@ -350,21 +373,116 @@ async function upload(cli, options, target, versionId) {
350
373
  await record(uploaded);
351
374
  }
352
375
  await runComponent(cli, target, internal.lib.finishVersion, { versionId }, z.null());
353
- writeVersionFile(cli, versionId);
376
+ const files = await assetFiles(cli, target, versionId, config, entries);
377
+ writeVersionFile(cli, { format: ASSETS_FORMAT, versionId, markToken, files });
354
378
  cli.log(`Uploaded version ${versionId} and wrote ${VERSION_FILE}.`);
355
379
  return undefined;
356
380
  }
357
- function writeVersionFile(cli, versionId) {
358
- writeStateFile(cli, VERSION_FILE, `export const versionId = ${JSON.stringify(versionId)};\n`);
381
+ /**
382
+ * The files of the site that serving reads: the storage URL of each entry, which only the
383
+ * component can make, read with inline queries in the component, which work whatever release of
384
+ * this package its functions are, and the deployment's `CONVEX_CLOUD_URL`, their origin.
385
+ */
386
+ async function assetFiles(cli, target, versionId, config, entries) {
387
+ const urls = new Map();
388
+ let cloudUrl = "";
389
+ let after = "";
390
+ for (;;) {
391
+ const page = await runComponentQuery(cli, target, `const rows = await ctx.db.query("entries").withIndex("by_version_path", (q) => ` +
392
+ `q.eq("versionId", ${JSON.stringify(versionId)}).gt("path", ${JSON.stringify(after)}))` +
393
+ `.take(${URL_PAGE}); return { cloudUrl: process.env.CONVEX_CLOUD_URL, files: await ` +
394
+ `Promise.all(rows.map(async (row) => [row.path, await ctx.storage.getUrl(row.storageId)])) };`, z.object({
395
+ cloudUrl: z.string(),
396
+ files: z.array(z.tuple([z.string(), z.string().nullable()])),
397
+ }));
398
+ cloudUrl = page.cloudUrl;
399
+ for (const [path, url] of page.files) {
400
+ if (url === null) {
401
+ throw new Error(`The stored file of ${path} is gone, deleted by the age rule; run the deploy again.`);
402
+ }
403
+ urls.set(path, url);
404
+ }
405
+ if (page.files.length < URL_PAGE)
406
+ break;
407
+ after = page.files[page.files.length - 1][0];
408
+ }
409
+ return buildAssetFiles(cloudUrl, siteConfig(config), entries.map(({ path, sha256, contentType }) => {
410
+ const url = urls.get(path);
411
+ if (url === undefined)
412
+ throw new Error(`Version ${versionId} has no entry for ${path}.`);
413
+ return { path, sha256, contentType, url };
414
+ }));
359
415
  }
360
- /** Writes a file of the state folder, and the folder's `.gitignore` when it is absent. */
416
+ /** The line of the version file that holds the assets. */
417
+ const ASSETS_LINE = "export const assets = ";
418
+ /**
419
+ * Writes the version file. The assets carry their type as JSDoc: Convex's default
420
+ * `convex/tsconfig.json` sets `allowJs`, so tsc reads this file instead of the package's
421
+ * declaration, and would infer `string` where `Assets` has a union of literals.
422
+ */
423
+ function writeVersionFile(cli, assets) {
424
+ writeStateFile(cli, VERSION_FILE, `/** @type {import("@samebase/convex-hosting").Assets} */\n` +
425
+ `${ASSETS_LINE}${JSON.stringify(assets)};\n`);
426
+ }
427
+ /** The id and the mark token of the assets in the version file that the upload wrote. */
428
+ function readVersionFile(cli) {
429
+ const line = readFileSync(join(cli.cwd, VERSION_FILE), "utf8")
430
+ .split("\n")
431
+ .find((text) => text.startsWith(ASSETS_LINE));
432
+ if (line === undefined)
433
+ throw new Error(`${VERSION_FILE} has no assets.`);
434
+ return z
435
+ .object({ versionId: z.string(), markToken: z.string() })
436
+ .parse(JSON.parse(line.slice(ASSETS_LINE.length, -1)));
437
+ }
438
+ /**
439
+ * Asks the code that the deployment serves to mark its version live, with the site's mark token,
440
+ * which only the code of this deploy has. A failure deletes nothing: the versions live before stay
441
+ * until a later deploy marks its own, so it is reported, and the deploy succeeds.
442
+ */
443
+ async function markServedVersion(cli, url, site) {
444
+ const later = `Version ${site.versionId} serves, and the next deploy deletes the files of the versions before it.`;
445
+ let answer;
446
+ try {
447
+ // A redirect would carry the token to another origin, so it counts as other code answering.
448
+ const response = await cli.fetch(url + MARK_PATH, {
449
+ method: "POST",
450
+ headers: { [MARK_HEADER]: site.markToken },
451
+ redirect: "manual",
452
+ });
453
+ // Other code answers the path with a page or a 404, which is no mark.
454
+ answer = response.ok ? await response.json().catch(() => null) : null;
455
+ }
456
+ catch (error) {
457
+ const message = error instanceof Error ? error.message : String(error);
458
+ cli.log(`Asking the site to mark version ${site.versionId} live failed: ${message}. ${later}`);
459
+ return;
460
+ }
461
+ const parsed = z.object({ versionId: z.string(), result: z.string() }).safeParse(answer);
462
+ if (!parsed.success || parsed.data.versionId !== site.versionId) {
463
+ cli.log(`The site did not answer as version ${site.versionId}, so its code is not this deploy's.`);
464
+ return;
465
+ }
466
+ const { result } = parsed.data;
467
+ if (result === "superseded") {
468
+ cli.log(`Another version went live over ${site.versionId}, so a deploy overlapped this one; ${later}`);
469
+ }
470
+ else if (result !== "marked" && result !== "live") {
471
+ cli.log(`Version ${site.versionId} could not be marked live: it is ${result}.`);
472
+ }
473
+ }
474
+ /**
475
+ * Writes a file of the state folder, and the folder's `.gitignore` when it is absent. It writes a
476
+ * temporary file and renames it, so a running `npx convex dev` never reads half a file.
477
+ */
361
478
  function writeStateFile(cli, path, body) {
362
479
  const gitignore = join(cli.cwd, STATE_DIR, ".gitignore");
363
480
  if (!existsSync(gitignore)) {
364
481
  mkdirSync(join(cli.cwd, STATE_DIR), { recursive: true });
365
482
  writeFileSync(gitignore, "*\n");
366
483
  }
367
- writeFileSync(join(cli.cwd, path), body);
484
+ writeFileSync(join(cli.cwd, `${path}.tmp`), body);
485
+ renameSync(join(cli.cwd, `${path}.tmp`), join(cli.cwd, path));
368
486
  }
369
487
  /** Stores one file through an upload URL and returns its storage id; `what` names it in errors. */
370
488
  async function store(cli, url, body, contentType, what) {