@samebase/convex-hosting 0.4.1 → 0.5.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.5.0
4
+
5
+ - `serveSite(ctx, component, request, versionId, render?)` serves the whole site from one route: the
6
+ file that matches, else the page that `render` returns, else 404. A page and a 404 answer
7
+ `Cache-Control: no-store`, so `convex/http.ts` no longer sets it. `serveAsset` stays, as its first
8
+ step.
9
+
3
10
  ## 0.4.1
4
11
 
5
12
  - `convex-hosting init` writes `.convex-hosting/version.js` with no site version when it is absent,
package/README.md CHANGED
@@ -35,7 +35,7 @@ Requires `convex` 1.46 or later.
35
35
 
36
36
  ```ts
37
37
  import { httpRouter } from "convex/server";
38
- import { serveAsset } from "@samebase/convex-hosting";
38
+ import { serveSite } from "@samebase/convex-hosting";
39
39
  import { components } from "./_generated/api.js";
40
40
  import { httpAction } from "./_generated/server.js";
41
41
  import { versionId } from "../.convex-hosting/version.js";
@@ -45,13 +45,7 @@ Requires `convex` 1.46 or later.
45
45
  http.route({
46
46
  pathPrefix: "/",
47
47
  method: "GET",
48
- handler: httpAction(async (ctx, request) => {
49
- const asset = await serveAsset(ctx, components.hosting, request, versionId);
50
- if (asset) return asset;
51
- // A static site: 404. A server-rendered site: render the page here, also with `no-store`.
52
- // The deployment's Cloudflare front caches answers on static-looking paths.
53
- return new Response(null, { status: 404, headers: { "Cache-Control": "no-store" } });
54
- }),
48
+ handler: httpAction((ctx, request) => serveSite(ctx, components.hosting, request, versionId)),
55
49
  });
56
50
 
57
51
  export default http;
@@ -90,7 +84,6 @@ declares the type of `.convex-hosting/version.js`, so the app typechecks without
90
84
  after `init` that is none: the site answers 404 until the next `convex-hosting deploy`.
91
85
  - Do not commit `.convex-hosting/`. It ignores itself.
92
86
  - Do not change directory in `--build`. The build and the upload run in the project directory.
93
- - Answer a miss in `convex/http.ts` with `Cache-Control: no-store`, as in step 3.
94
87
 
95
88
  ## Deploy
96
89
 
@@ -191,9 +184,15 @@ files for a year, add a `_headers` rule:
191
184
 
192
185
  ## Routing
193
186
 
194
- `serveAsset` returns the asset's response, or `null` when no asset matches, so your route continues.
195
- When the version does not exist or is still uploading, it returns a 404 response that names the
196
- version. At the first request for a new version, it marks the version live.
187
+ `serveSite` answers with the file that matches, else with the page that `render` returns (see
188
+ [Server-rendered pages](#server-rendered-pages)), else 404. A page and a 404 answer
189
+ `Cache-Control: no-store`. The deployment's Cloudflare front keeps that for a path without an
190
+ extension and replaces it with a four-hour browser `max-age` for a path with one, so a browser that
191
+ asks for a script before its upload ended keeps the 404; the deploy order prevents that case,
192
+ because no page refers to a file that is not uploaded yet. When the version does not exist or is
193
+ still uploading, it answers 404 naming the version. At the first request for a new version, it marks
194
+ the version live. `serveAsset`, its first step, returns the file's response or `null`, for a route
195
+ that does something else on a miss.
197
196
 
198
197
  `not_found_handling` applies to navigation requests (`Sec-Fetch-Mode: navigate`) only, as on
199
198
  Cloudflare when a Worker runs: other requests that match no file reach your code. Browsers send that
@@ -242,9 +241,12 @@ export function useReloadOnNewVersion() {
242
241
 
243
242
  ## Server-rendered pages
244
243
 
244
+ `serveSite` takes a fifth argument, `render`, a function from the request to the page's response,
245
+ and calls it when no file matches.
245
246
  [`@samebase/convex-hosting-tanstack-start`](https://github.com/samebase/convex-hosting/tree/main/packages/convex-hosting-tanstack-start)
246
- renders TanStack Start pages in the same route, after `serveAsset`. See the
247
- [TanStack Start example](https://github.com/samebase/convex-hosting/tree/main/examples/tanstack-ssr).
247
+ renders TanStack Start pages in it. See the
248
+ [TanStack Start example](https://github.com/samebase/convex-hosting/tree/main/examples/tanstack-ssr),
249
+ live at [gallant-llama-40.convex.site](https://gallant-llama-40.convex.site).
248
250
 
249
251
  ## License
250
252
 
@@ -1,12 +1,20 @@
1
1
  import type { GenericActionCtx, GenericDataModel } from "convex/server";
2
2
  import type { ComponentApi } from "../component/_generated/component.js";
3
+ /**
4
+ * Answers `request` with the file of version `versionId`, else with the page that `render`
5
+ * returns, else 404. A page and a miss answer `Cache-Control: no-store`. The deployment's
6
+ * Cloudflare front keeps that for a path without an extension and replaces it with a four-hour
7
+ * browser `max-age` for a path with one, so a browser that asks for a script before its upload
8
+ * ended keeps the 404; the deploy order prevents that case, because no page refers to a file that
9
+ * is not uploaded yet.
10
+ */
11
+ export declare function serveSite(ctx: Pick<GenericActionCtx<GenericDataModel>, "runQuery" | "runMutation">, component: ComponentApi, request: Request, versionId: string, render?: () => Promise<Response>): Promise<Response>;
3
12
  /**
4
13
  * Answers `request` with the asset of version `versionId` that Cloudflare's asset handler picks.
5
14
  *
6
- * Returns `null` when no asset matches, so the app continues (a static site answers 404, a
7
- * server-rendered site renders the page). A version that does not exist, is still uploading, or
8
- * is being deleted answers 404, so a half-installed site does not render pages without their
9
- * files. The first request for a `ready` version marks it live, which deletes the versions that
15
+ * Returns `null` when no asset matches, so the caller continues, as `serveSite` does. A version
16
+ * that does not exist, is still uploading, or is being deleted answers 404, so a half-installed
17
+ * site does not render pages without their files. The first request for a `ready` version marks it live, which deletes the versions that
10
18
  * were live before it.
11
19
  */
12
20
  export declare function serveAsset(ctx: Pick<GenericActionCtx<GenericDataModel>, "runQuery" | "runMutation">, component: ComponentApi, request: Request, versionId: string): Promise<Response | null>;
@@ -1,10 +1,28 @@
1
+ /**
2
+ * Answers `request` with the file of version `versionId`, else with the page that `render`
3
+ * returns, else 404. A page and a miss answer `Cache-Control: no-store`. The deployment's
4
+ * Cloudflare front keeps that for a path without an extension and replaces it with a four-hour
5
+ * browser `max-age` for a path with one, so a browser that asks for a script before its upload
6
+ * ended keeps the 404; the deploy order prevents that case, because no page refers to a file that
7
+ * is not uploaded yet.
8
+ */
9
+ export async function serveSite(ctx, component, request, versionId, render) {
10
+ const asset = await serveAsset(ctx, component, request, versionId);
11
+ if (asset !== null)
12
+ return asset;
13
+ if (render === undefined)
14
+ return new Response(null, { status: 404, headers: NO_STORE });
15
+ const page = await render();
16
+ const headers = new Headers(page.headers);
17
+ headers.set("Cache-Control", "no-store");
18
+ return new Response(page.body, { status: page.status, headers });
19
+ }
1
20
  /**
2
21
  * Answers `request` with the asset of version `versionId` that Cloudflare's asset handler picks.
3
22
  *
4
- * Returns `null` when no asset matches, so the app continues (a static site answers 404, a
5
- * server-rendered site renders the page). A version that does not exist, is still uploading, or
6
- * is being deleted answers 404, so a half-installed site does not render pages without their
7
- * files. The first request for a `ready` version marks it live, which deletes the versions that
23
+ * Returns `null` when no asset matches, so the caller continues, as `serveSite` does. A version
24
+ * that does not exist, is still uploading, or is being deleted answers 404, so a half-installed
25
+ * site does not render pages without their files. The first request for a `ready` version marks it live, which deletes the versions that
8
26
  * were live before it.
9
27
  */
10
28
  export async function serveAsset(ctx, component, request, versionId) {
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@samebase/convex-hosting",
3
- "version": "0.4.1",
3
+ "version": "0.5.0",
4
4
  "description": "A Convex component that hosts a web app: static files, with optional server rendering.",
5
5
  "keywords": [
6
6
  "component",