@samebase/convex-hosting 0.0.0-stage → 0.3.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 +127 -0
- package/INTEGRATION.md +399 -0
- package/LICENSE +201 -0
- package/NOTICE +7 -0
- package/README.md +470 -2
- package/dist/cli/args.d.ts +28 -0
- package/dist/cli/args.d.ts.map +1 -0
- package/dist/cli/args.js +148 -0
- package/dist/cli/args.js.map +1 -0
- package/dist/cli/argumentChunks.d.ts +3 -0
- package/dist/cli/argumentChunks.d.ts.map +1 -0
- package/dist/cli/argumentChunks.js +29 -0
- package/dist/cli/argumentChunks.js.map +1 -0
- package/dist/cli/commands.d.ts +8 -0
- package/dist/cli/commands.d.ts.map +1 -0
- package/dist/cli/commands.js +56 -0
- package/dist/cli/commands.js.map +1 -0
- package/dist/cli/componentName.d.ts +32 -0
- package/dist/cli/componentName.d.ts.map +1 -0
- package/dist/cli/componentName.js +43 -0
- package/dist/cli/componentName.js.map +1 -0
- package/dist/cli/deploy.d.ts +16 -0
- package/dist/cli/deploy.d.ts.map +1 -0
- package/dist/cli/deploy.js +283 -0
- package/dist/cli/deploy.js.map +1 -0
- package/dist/cli/deployEnvironment.d.ts +10 -0
- package/dist/cli/deployEnvironment.d.ts.map +1 -0
- package/dist/cli/deployEnvironment.js +12 -0
- package/dist/cli/deployEnvironment.js.map +1 -0
- package/dist/cli/index.d.ts +12 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +60 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/upload.d.ts +22 -0
- package/dist/cli/upload.d.ts.map +1 -0
- package/dist/cli/upload.js +668 -0
- package/dist/cli/upload.js.map +1 -0
- package/dist/client/_generated/_ignore.d.ts +1 -0
- package/dist/client/_generated/_ignore.d.ts.map +1 -0
- package/dist/client/_generated/_ignore.js +3 -0
- package/dist/client/_generated/_ignore.js.map +1 -0
- package/dist/client/index.d.ts +119 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/index.js +272 -0
- package/dist/client/index.js.map +1 -0
- package/dist/component/_generated/api.d.ts +38 -0
- package/dist/component/_generated/api.d.ts.map +1 -0
- package/dist/component/_generated/api.js +31 -0
- package/dist/component/_generated/api.js.map +1 -0
- package/dist/component/_generated/component.d.ts +43 -0
- package/dist/component/_generated/component.d.ts.map +1 -0
- package/dist/component/_generated/component.js +11 -0
- package/dist/component/_generated/component.js.map +1 -0
- package/dist/component/_generated/dataModel.d.ts +46 -0
- package/dist/component/_generated/dataModel.d.ts.map +1 -0
- package/dist/component/_generated/dataModel.js +11 -0
- package/dist/component/_generated/dataModel.js.map +1 -0
- package/dist/component/_generated/server.d.ts +121 -0
- package/dist/component/_generated/server.d.ts.map +1 -0
- package/dist/component/_generated/server.js +78 -0
- package/dist/component/_generated/server.js.map +1 -0
- package/dist/component/convex.config.d.ts +3 -0
- package/dist/component/convex.config.d.ts.map +1 -0
- package/dist/component/convex.config.js +3 -0
- package/dist/component/convex.config.js.map +1 -0
- package/dist/component/http.d.ts +4 -0
- package/dist/component/http.d.ts.map +1 -0
- package/dist/component/http.js +125 -0
- package/dist/component/http.js.map +1 -0
- package/dist/component/lib.d.ts +164 -0
- package/dist/component/lib.d.ts.map +1 -0
- package/dist/component/lib.js +768 -0
- package/dist/component/lib.js.map +1 -0
- package/dist/component/schema.d.ts +84 -0
- package/dist/component/schema.d.ts.map +1 -0
- package/dist/component/schema.js +69 -0
- package/dist/component/schema.js.map +1 -0
- package/dist/component/serving.d.ts +17 -0
- package/dist/component/serving.d.ts.map +1 -0
- package/dist/component/serving.js +150 -0
- package/dist/component/serving.js.map +1 -0
- package/dist/react/index.d.ts +59 -0
- package/dist/react/index.d.ts.map +1 -0
- package/dist/react/index.js +137 -0
- package/dist/react/index.js.map +1 -0
- package/package.json +78 -3
- package/src/cli/args.test.ts +79 -0
- package/src/cli/args.ts +153 -0
- package/src/cli/argumentChunks.test.ts +27 -0
- package/src/cli/argumentChunks.ts +40 -0
- package/src/cli/commands.ts +68 -0
- package/src/cli/componentName.test.ts +41 -0
- package/src/cli/componentName.ts +46 -0
- package/src/cli/deploy.ts +336 -0
- package/src/cli/deployEnvironment.test.ts +27 -0
- package/src/cli/deployEnvironment.ts +17 -0
- package/src/cli/index.ts +65 -0
- package/src/cli/upload.ts +885 -0
- package/src/client/_generated/_ignore.ts +1 -0
- package/src/client/index.test.ts +510 -0
- package/src/client/index.ts +355 -0
- package/src/client/setup.test.ts +22 -0
- package/src/component/_generated/api.ts +54 -0
- package/src/component/_generated/component.ts +55 -0
- package/src/component/_generated/dataModel.ts +60 -0
- package/src/component/_generated/server.ts +156 -0
- package/src/component/convex.config.ts +3 -0
- package/src/component/http.test.ts +223 -0
- package/src/component/http.ts +144 -0
- package/src/component/lib.test.ts +1007 -0
- package/src/component/lib.ts +862 -0
- package/src/component/schema.ts +75 -0
- package/src/component/serving.ts +158 -0
- package/src/component/setup.test.ts +11 -0
- package/src/react/index.tsx +197 -0
- package/src/test.ts +23 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.3.0
|
|
4
|
+
|
|
5
|
+
First release as `@samebase/convex-hosting`, Samebase's fork of `@convex-dev/static-hosting`. See
|
|
6
|
+
"Switching from @convex-dev/static-hosting" in the README.
|
|
7
|
+
|
|
8
|
+
- The package, the CLI command (`convex-hosting`), the component (`hosting`), the default deployment
|
|
9
|
+
query module (`convex/hosting.ts`), and the `CONVEX_HOSTING_BASE_PATH` build variable share one
|
|
10
|
+
name. The CLI still finds an instance named `staticHosting`.
|
|
11
|
+
- `serveStaticAsset` serves one uploaded file from your own HTTP route, for custom routing and
|
|
12
|
+
server rendering.
|
|
13
|
+
- `upload --preview-name` uploads to a named preview deployment.
|
|
14
|
+
- Replaced files stay available for at least seven days, so pages that are already open can still
|
|
15
|
+
load their scripts.
|
|
16
|
+
- The `setup` and `init` commands are removed. The README describes the setup steps.
|
|
17
|
+
|
|
18
|
+
Versions up to 0.2.1 were released as `@convex-dev/static-hosting`.
|
|
19
|
+
|
|
20
|
+
## 0.2.1
|
|
21
|
+
|
|
22
|
+
- Smoother migration story, with more automatic fallbacks to the old component name.
|
|
23
|
+
|
|
24
|
+
## 0.2.0
|
|
25
|
+
|
|
26
|
+
Component-owned HTTP and storage.
|
|
27
|
+
|
|
28
|
+
**Breaking — redeploy your static assets after upgrading.**
|
|
29
|
+
|
|
30
|
+
See the [0.1.x to 0.2.x migration guide](./MIGRATION.md) before upgrading an existing deployment.
|
|
31
|
+
|
|
32
|
+
- The component now hosts its own HTTP endpoints and owns the file storage that serves them. Wire it
|
|
33
|
+
up with `app.use(staticHosting, { httpPrefix: "/" })` and delete related code in`convex/http.ts` +
|
|
34
|
+
the upload-API re-exports from `convex/staticHosting.ts`.
|
|
35
|
+
- Removed `exposeUploadApi` from the client API. Uploads and file lifecycle are now handled by
|
|
36
|
+
private component functions.
|
|
37
|
+
- `registerStaticRoutes` remains as a compatibility mode for apps that need to keep existing auth,
|
|
38
|
+
webhook, or API routes at the root. It serves the new component-owned files from the app's
|
|
39
|
+
existing `convex/http.ts` catch-all. During a same-name v1→v2 migration it also serves inherited
|
|
40
|
+
v1 files straight from the app's own storage, so the live site stays up with no re-upload gap.
|
|
41
|
+
Mounting the component's own handler is still the faster default.
|
|
42
|
+
- `exposeDeploymentQuery` and `getConvexUrl` remain if you use the UpdateBanner.
|
|
43
|
+
- The component is now named `staticHosting` (previously `selfHosting`). The CLI invokes it directly
|
|
44
|
+
via `npx convex run --component staticHosting lib:...`. If you mount the component under a
|
|
45
|
+
different name, pass `--component <your-name>`. For a smooth upgrade, pass {name: "selfHosting} to
|
|
46
|
+
`app.use(staticHosting, { name: "selfHosting" })`
|
|
47
|
+
- `useDeploymentUpdates` / `UpdateBanner` use `useQuery_experimental` and default to
|
|
48
|
+
`api.staticHosting.getCurrentDeployment`. If you don't surface deployment updates, you no longer
|
|
49
|
+
need to expose anything.
|
|
50
|
+
- Assets uploaded under 0.1.x lived in the app's storage — the component-owned HTTP handler can't
|
|
51
|
+
resolve those references in 0.2.x. Run `npx @convex-dev/static-hosting deploy` to repopulate. (In
|
|
52
|
+
app-owned root routing the app-side handler keeps serving those v1 files from app storage until
|
|
53
|
+
the first upload replaces them, so that mode has no asset-less window.) The first 0.2.x upload
|
|
54
|
+
safely discards those legacy storage references instead of trying to delete them from component
|
|
55
|
+
storage. The old blobs remain in app storage until you explicitly clean them up after the rollback
|
|
56
|
+
window. Older 0.1.x deploys may also have left historical blobs that the current asset manifest no
|
|
57
|
+
longer lists, so follow the migration guide's app-storage audit before cleanup.
|
|
58
|
+
- Default setup prefixes your own HTTP routes with `defineApp({ httpPrefix: "/api" })` so the static
|
|
59
|
+
site can own the root without the catch-all route shadowing them. Use `registerStaticRoutes` when
|
|
60
|
+
those existing root route URLs cannot move.
|
|
61
|
+
- SPA fallback is now configurable per deployment. Deploy with `--no-spa` (`deploy` or `upload`) to
|
|
62
|
+
make extension-less misses return 404 instead of `index.html`. The setting is stored on the
|
|
63
|
+
deployment record, so it travels with the code you ship.
|
|
64
|
+
- Hashed-asset caching recognizes Vite's base64url hashes (including `-`) and numeric extensions
|
|
65
|
+
such as `.woff2`. HTML always uses revalidation, even if its filename looks content-hashed.
|
|
66
|
+
- The `cdnBaseUrl` override remains available in `registerStaticRoutes` compatibility mode.
|
|
67
|
+
Component-owned HTTP routes redirect CDN blobs to the deployment's own `{origin}/fs/blobs`.
|
|
68
|
+
- `--cdn` is now documented as a legacy option. Its setup guide targeted an old unauthenticated
|
|
69
|
+
convex-fs API and does not work with current ConvexFS. New integrations should use Convex storage
|
|
70
|
+
until authenticated CDN uploads are designed.
|
|
71
|
+
- Storage and legacy CDN assets, deployment settings, and old-file cleanup are published in one
|
|
72
|
+
atomic mutation after all uploads finish. New HTML cannot race ahead of its referenced asset
|
|
73
|
+
records, and a failed publish leaves the previous deployment live. Replaced CDN blob IDs are
|
|
74
|
+
retained for cleanup.
|
|
75
|
+
- Upload errors now report the failing file and HTTP status. Oversized manifests fail with a clear
|
|
76
|
+
size error, and newly uploaded component files are removed after a failed upload or publish.
|
|
77
|
+
- Static handlers decode percent-encoded paths, accept weak or listed ETags, and return a
|
|
78
|
+
non-cacheable HTTP 503 setup page until the first upload succeeds.
|
|
79
|
+
- Removed the install-time console banner.
|
|
80
|
+
|
|
81
|
+
## 0.1.5
|
|
82
|
+
|
|
83
|
+
- Forward-compatibility for 0.2.x rollbacks. The `deploymentInfo` schema and the
|
|
84
|
+
`getCurrentDeployment` return validator now allow the optional `spaFallback` (boolean) and
|
|
85
|
+
`pendingBlobCleanupCount` (number) fields that 0.2.x writes to the deployment record. 0.1.5
|
|
86
|
+
ignores them, but allowing them means a deployment upgraded to 0.2.x and later rolled back to
|
|
87
|
+
0.1.x keeps validating. No behavior change for 0.1.x users; upgrade to 0.1.5 before trying 0.2.x
|
|
88
|
+
if you want a clean rollback path. (0.2.x also creates additional tables such as `stagedAssets`
|
|
89
|
+
and `cleanupState`; extra tables don't fail 0.1.x schema validation, so they need no entry here.)
|
|
90
|
+
|
|
91
|
+
## 0.1.4
|
|
92
|
+
|
|
93
|
+
- Added support for Windows
|
|
94
|
+
|
|
95
|
+
## 0.1.3
|
|
96
|
+
|
|
97
|
+
- Fix missing README in published package
|
|
98
|
+
|
|
99
|
+
## 0.1.2
|
|
100
|
+
|
|
101
|
+
- Rename package from `@convex-dev/self-hosting` to `@convex-dev/static-hosting`
|
|
102
|
+
|
|
103
|
+
## 0.1.1-alpha.0
|
|
104
|
+
|
|
105
|
+
- Add optional convex-fs CDN mode for static asset serving (`--cdn` flag)
|
|
106
|
+
- Non-HTML assets can be served from CDN edge network via convex-fs
|
|
107
|
+
- New `cdnBaseUrl` option on `registerStaticRoutes`
|
|
108
|
+
- Schema: `storageId` now optional, new `blobId` field for CDN assets
|
|
109
|
+
- `gcOldAssets` returns `{ deleted, blobIds }` (breaking change from number return)
|
|
110
|
+
|
|
111
|
+
## 0.1.2-alpha.1
|
|
112
|
+
|
|
113
|
+
removed cloudflare for now.
|
|
114
|
+
|
|
115
|
+
## 0.1.1
|
|
116
|
+
|
|
117
|
+
- Setup wizard now configures custom domains for Cloudflare Workers
|
|
118
|
+
- Added note about running `convex dev` for HTTP actions error
|
|
119
|
+
- Added documentation for non-Vite bundlers (Expo, Next.js)
|
|
120
|
+
|
|
121
|
+
## 0.1.0
|
|
122
|
+
|
|
123
|
+
- Migrated from Cloudflare Pages to Workers Static Assets
|
|
124
|
+
|
|
125
|
+
## 0.0.0
|
|
126
|
+
|
|
127
|
+
- Initial release.
|
package/INTEGRATION.md
ADDED
|
@@ -0,0 +1,399 @@
|
|
|
1
|
+
# Integration Guide: @samebase/convex-hosting
|
|
2
|
+
|
|
3
|
+
This guide walks you through hosting a static React/Vite app on Convex using the
|
|
4
|
+
`@samebase/convex-hosting` component. Your frontend ends up at `https://<deployment>.convex.site`,
|
|
5
|
+
served alongside your Convex backend with SPA routing and smart caching.
|
|
6
|
+
|
|
7
|
+
## What this component gives you
|
|
8
|
+
|
|
9
|
+
- A drop-in HTTP handler that serves your static files from Convex storage
|
|
10
|
+
- One CLI command (`deploy`) that builds, deploys the backend, and uploads files
|
|
11
|
+
- SPA fallback to `index.html` for client-side routing
|
|
12
|
+
- Long-term cache headers on hashed assets and ETag-based revalidation on HTML
|
|
13
|
+
- Optional live-reload notifications when a new deploy ships
|
|
14
|
+
- Authenticated uploads via the Convex CLI (no public upload endpoint)
|
|
15
|
+
|
|
16
|
+
## Quick Start
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npm install @samebase/convex-hosting
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Register the component and add a `deploy` script as described in [Manual Setup](#manual-setup).
|
|
23
|
+
Then:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
npm run deploy
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Manual Setup
|
|
30
|
+
|
|
31
|
+
### `convex/convex.config.ts`
|
|
32
|
+
|
|
33
|
+
The default setup mounts the component's HTTP handler at the root and moves app-owned routes under
|
|
34
|
+
`/api`.
|
|
35
|
+
|
|
36
|
+
```typescript
|
|
37
|
+
import { defineApp } from "convex/server";
|
|
38
|
+
import hosting from "@samebase/convex-hosting/convex.config";
|
|
39
|
+
|
|
40
|
+
// Serve your own HTTP endpoints (convex/http.ts) under /api so the static
|
|
41
|
+
// site can own the root.
|
|
42
|
+
const app = defineApp({ httpPrefix: "/api" });
|
|
43
|
+
app.use(hosting, { httpPrefix: "/" });
|
|
44
|
+
|
|
45
|
+
export default app;
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
This is the fastest serving mode because the component reads its storage directly. It is appropriate
|
|
49
|
+
for new apps and apps whose HTTP routes can live under `/api`.
|
|
50
|
+
|
|
51
|
+
If existing webhook, auth, or API routes must stay at the root, use
|
|
52
|
+
[App-owned root routing](#app-owned-root-routing). To host the static site under a sub-path, see
|
|
53
|
+
[Mounting under a sub-path](#mounting-under-a-sub-path).
|
|
54
|
+
|
|
55
|
+
> Run `npx convex dev` after editing `convex.config.ts` so codegen picks up the component.
|
|
56
|
+
|
|
57
|
+
### App-owned root routing
|
|
58
|
+
|
|
59
|
+
Keep the app in control of `convex/http.ts` when changing existing route URLs would break clients or
|
|
60
|
+
third-party callbacks.
|
|
61
|
+
|
|
62
|
+
`convex/convex.config.ts`:
|
|
63
|
+
|
|
64
|
+
```typescript
|
|
65
|
+
import { defineApp } from "convex/server";
|
|
66
|
+
import hosting from "@samebase/convex-hosting/convex.config";
|
|
67
|
+
|
|
68
|
+
const app = defineApp();
|
|
69
|
+
app.use(hosting); // no httpPrefix
|
|
70
|
+
|
|
71
|
+
export default app;
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
`convex/http.ts`:
|
|
75
|
+
|
|
76
|
+
```typescript
|
|
77
|
+
import { httpRouter } from "convex/server";
|
|
78
|
+
import { registerStaticRoutes } from "@samebase/convex-hosting";
|
|
79
|
+
import { components } from "./_generated/api";
|
|
80
|
+
|
|
81
|
+
const http = httpRouter();
|
|
82
|
+
|
|
83
|
+
// Keep existing exact routes at their current URLs.
|
|
84
|
+
// auth.addHttpRoutes(http);
|
|
85
|
+
registerStaticRoutes(http, components.hosting);
|
|
86
|
+
|
|
87
|
+
export default http;
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Exact routes win over the static catch-all. Uploads, deployment metadata, SPA configuration, and
|
|
91
|
+
file storage remain inside the component, so you can remove the old `exposeUploadApi` wrappers.
|
|
92
|
+
During a same-name 0.1.x→0.2.x migration this mode also keeps serving your existing v1 files from
|
|
93
|
+
app storage until the first 0.2.x upload, so the live site never drops to the setup page. This
|
|
94
|
+
compatibility mode adds an internal query and storage fetch on uncached requests. Use the
|
|
95
|
+
component-owned handler when preserving root-level app routes is not necessary.
|
|
96
|
+
|
|
97
|
+
### `package.json`
|
|
98
|
+
|
|
99
|
+
```json
|
|
100
|
+
{
|
|
101
|
+
"scripts": {
|
|
102
|
+
"deploy": "npx @samebase/convex-hosting deploy"
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## Deployment
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
# First time
|
|
111
|
+
npx convex login
|
|
112
|
+
|
|
113
|
+
# Build + backend deploy + upload static files
|
|
114
|
+
npx @samebase/convex-hosting deploy
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Two-step alternative:
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
npx convex deploy
|
|
121
|
+
npx @samebase/convex-hosting upload --build --prod
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
Your app is live at `https://<deployment>.convex.site`.
|
|
125
|
+
|
|
126
|
+
## Development workflow
|
|
127
|
+
|
|
128
|
+
Do not use uploaded static files as the main development loop. Run the normal frontend dev server
|
|
129
|
+
alongside Convex:
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
npx convex dev
|
|
133
|
+
npm run dev
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
Vite keeps HMR, source maps, and transient UI state intact. Agent-written code does not change that
|
|
137
|
+
tradeoff. HMR may matter less while code is being produced, but it still matters when a person
|
|
138
|
+
verifies visuals and debugs interactions.
|
|
139
|
+
|
|
140
|
+
Use the hosted development deployment as a smoke-test target before release:
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
npx @samebase/convex-hosting upload --build
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
That catches differences in HTTP routing, cache headers, SPA fallback, and base paths. Production
|
|
147
|
+
remains the one-command `deploy` flow.
|
|
148
|
+
|
|
149
|
+
## Live reload banner (optional)
|
|
150
|
+
|
|
151
|
+
Show users a banner when a new version is deployed:
|
|
152
|
+
|
|
153
|
+
### `convex/hosting.ts`
|
|
154
|
+
|
|
155
|
+
```typescript
|
|
156
|
+
import { exposeDeploymentQuery } from "@samebase/convex-hosting";
|
|
157
|
+
import { components } from "./_generated/api";
|
|
158
|
+
|
|
159
|
+
export const { getCurrentDeployment } = exposeDeploymentQuery(components.hosting);
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
### Frontend
|
|
163
|
+
|
|
164
|
+
```tsx
|
|
165
|
+
import { UpdateBanner } from "@samebase/convex-hosting/react";
|
|
166
|
+
|
|
167
|
+
function App() {
|
|
168
|
+
return (
|
|
169
|
+
<>
|
|
170
|
+
<UpdateBanner message="New version available!" buttonText="Reload" />
|
|
171
|
+
{/* rest of app */}
|
|
172
|
+
</>
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
`UpdateBanner` resolves `api.hosting.getCurrentDeployment` by default. To re-export the query under
|
|
178
|
+
a different module, pass the reference explicitly:
|
|
179
|
+
|
|
180
|
+
```tsx
|
|
181
|
+
import { api } from "../convex/_generated/api";
|
|
182
|
+
<UpdateBanner getCurrentDeployment={api.myModule.getCurrentDeployment} />;
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
For custom UI, use the hook:
|
|
186
|
+
|
|
187
|
+
```tsx
|
|
188
|
+
import { useDeploymentUpdates } from "@samebase/convex-hosting/react";
|
|
189
|
+
|
|
190
|
+
const { updateAvailable, reload, dismiss } = useDeploymentUpdates();
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
## Legacy CDN mode
|
|
194
|
+
|
|
195
|
+
Do not use `--cdn` for a new integration. It targets an older, unauthenticated `convex-fs` HTTP API.
|
|
196
|
+
Current ConvexFS releases require app-owned routes and upload authentication, which this CLI does
|
|
197
|
+
not yet provide.
|
|
198
|
+
|
|
199
|
+
Existing deployments that already expose the legacy routes can keep using the flag during migration,
|
|
200
|
+
but they must use app-owned root compatibility mode. `/fs/upload` and `/fs/blobs/*` are app routes,
|
|
201
|
+
so component-owned root mode would break both upload and serving. Everyone else should use the
|
|
202
|
+
default Convex storage mode. Supporting current ConvexFS properly needs a separate authenticated
|
|
203
|
+
upload and garbage-collection design, not a copy-paste configuration snippet.
|
|
204
|
+
|
|
205
|
+
The CLI uploads every file before atomically publishing the mixed storage/CDN manifest and
|
|
206
|
+
deployment settings while queuing exact old-file IDs for bounded follow-up cleanup. New HTML does
|
|
207
|
+
not point at asset records that are still missing, and a failed publish leaves the previous
|
|
208
|
+
deployment live. Failure cleanup removes new files only after it can prove publication did not win
|
|
209
|
+
the transaction race; otherwise later maintenance recovers them. Failed legacy CDN uploads can only
|
|
210
|
+
be cleaned automatically when `--cdn-delete-function` is configured. Old CDN IDs are kept in a
|
|
211
|
+
private cleanup queue until that function succeeds, so a lost publish response does not make them
|
|
212
|
+
unrecoverable.
|
|
213
|
+
|
|
214
|
+
## Non-Vite bundlers
|
|
215
|
+
|
|
216
|
+
The `deploy` build step and `upload --build` set `VITE_CONVEX_URL`. To forward it to another env var
|
|
217
|
+
(Expo, Next.js, etc.), wrap your build script:
|
|
218
|
+
|
|
219
|
+
```text
|
|
220
|
+
Expo build script:
|
|
221
|
+
EXPO_PUBLIC_CONVEX_URL=${VITE_CONVEX_URL:-$EXPO_PUBLIC_CONVEX_URL} npx expo export --platform web
|
|
222
|
+
|
|
223
|
+
Next.js build script:
|
|
224
|
+
NEXT_PUBLIC_CONVEX_URL=${VITE_CONVEX_URL:-$NEXT_PUBLIC_CONVEX_URL} next build
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
## Security
|
|
228
|
+
|
|
229
|
+
Upload functions are **internal** to the component. They can only be called via:
|
|
230
|
+
|
|
231
|
+
- `npx convex run` (requires Convex CLI authentication)
|
|
232
|
+
- Other Convex functions (server-side only)
|
|
233
|
+
|
|
234
|
+
This means unauthorized users cannot upload files, even if they know your Convex URL.
|
|
235
|
+
|
|
236
|
+
## CLI Reference
|
|
237
|
+
|
|
238
|
+
```bash
|
|
239
|
+
npx @samebase/convex-hosting deploy [options]
|
|
240
|
+
-d, --dist <path> Path to dist directory (default: ./dist)
|
|
241
|
+
-c, --component <name> Component instance name (default: hosting)
|
|
242
|
+
--skip-build Skip the build step
|
|
243
|
+
--skip-convex Skip Convex backend deployment
|
|
244
|
+
--build-command <cmd> Build command to run (default: 'npm run build')
|
|
245
|
+
--no-spa Disable SPA fallback (404 instead of /index.html)
|
|
246
|
+
--spa Enable SPA fallback (default)
|
|
247
|
+
--cdn Upload non-HTML assets to convex-fs CDN
|
|
248
|
+
--cdn-delete-function App function path that deletes CDN blobs
|
|
249
|
+
|
|
250
|
+
npx @samebase/convex-hosting upload [options]
|
|
251
|
+
-d, --dist <path> Path to dist directory (default: ./dist)
|
|
252
|
+
-c, --component <name> Component instance name (default: hosting)
|
|
253
|
+
--prod Deploy to production deployment
|
|
254
|
+
--preview-name <name> Upload to the named preview deployment
|
|
255
|
+
-b, --build Run 'npm run build' with VITE_CONVEX_URL set
|
|
256
|
+
--build-command <cmd> Override the build command; implies --build
|
|
257
|
+
--no-spa Disable SPA fallback (404 instead of /index.html)
|
|
258
|
+
--spa Enable SPA fallback (default)
|
|
259
|
+
--cdn Upload non-HTML assets to convex-fs CDN
|
|
260
|
+
--cdn-delete-function App function path that deletes CDN blobs
|
|
261
|
+
-j, --concurrency <n> Parallel upload workers (default: 5)
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
The complete manifest is staged in small, portable command-line chunks after all uploads finish. One
|
|
265
|
+
final mutation atomically publishes the manifest and deployment settings. Replaced files stay in
|
|
266
|
+
component storage for at least seven days. A request for a missing file with an extension, except
|
|
267
|
+
HTML, gets the newest replaced copy, so pages that are already open can still load their scripts.
|
|
268
|
+
Files remain available until a later upload removes expired files in bounded cleanup transactions.
|
|
269
|
+
Legacy CDN blobs are not retained. A failed attempt leaves the previous deployment live and removes
|
|
270
|
+
only new files that are not referenced by the live manifest. The supported maximum is 1,800 files
|
|
271
|
+
and 2 MiB of serialized manifest metadata, which keeps the atomic switch below Convex transaction
|
|
272
|
+
limits. Upload URLs are generated in bounded batches. Later uploads recover component files and
|
|
273
|
+
staging records left unreferenced for more than 24 hours after an interrupted CLI.
|
|
274
|
+
|
|
275
|
+
Convex HTTP routers currently expose GET but not HEAD routes. Uptime monitors must use a lightweight
|
|
276
|
+
GET request.
|
|
277
|
+
|
|
278
|
+
## Mounting under a sub-path
|
|
279
|
+
|
|
280
|
+
To mount under `/app/` (for example, if you have other HTTP routes at the root):
|
|
281
|
+
|
|
282
|
+
```typescript
|
|
283
|
+
app.use(hosting, { httpPrefix: "/app/" });
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
The bundler also needs to know the base path so the emitted HTML references the right URLs. The CLI
|
|
287
|
+
sets a `CONVEX_HOSTING_BASE_PATH` env var matching the component's mount when it runs your build, so
|
|
288
|
+
`vite.config.ts` can read it:
|
|
289
|
+
|
|
290
|
+
```typescript
|
|
291
|
+
import { defineConfig } from "vite";
|
|
292
|
+
|
|
293
|
+
export default defineConfig({
|
|
294
|
+
base: process.env.CONVEX_HOSTING_BASE_PATH ?? "/",
|
|
295
|
+
});
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
Root-mounted apps don't need this; the default is `/`. Webpack/Next.js equivalents: `publicPath` and
|
|
299
|
+
`assetPrefix`.
|
|
300
|
+
|
|
301
|
+
## SPA routing
|
|
302
|
+
|
|
303
|
+
Requests for an extension-less path that doesn't match an uploaded file fall back to `index.html`,
|
|
304
|
+
so client-side routes survive a reload. Paths with an extension (e.g. `/missing.js`) always 404 when
|
|
305
|
+
not found. To turn the fallback off for a multi-page app (unknown paths become real 404s), deploy
|
|
306
|
+
with `--no-spa`:
|
|
307
|
+
|
|
308
|
+
```bash
|
|
309
|
+
npx @samebase/convex-hosting deploy --no-spa
|
|
310
|
+
# or: npx @samebase/convex-hosting upload --no-spa
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
The flag is stored on the deployment record (the `deploymentInfo` table), so the serving behavior
|
|
314
|
+
travels with the code you ship rather than living in a separate env var. Re-deploy without
|
|
315
|
+
`--no-spa` or with `--spa` to turn it back on.
|
|
316
|
+
|
|
317
|
+
## Switching from @convex-dev/static-hosting
|
|
318
|
+
|
|
319
|
+
Follow
|
|
320
|
+
[Switching from @convex-dev/static-hosting](./README.md#switching-from-convex-devstatic-hosting) in
|
|
321
|
+
the README. Mount the component with `{ name: "staticHosting" }` to keep the uploaded files and
|
|
322
|
+
URLs. An app on 0.1.x first follows upstream's
|
|
323
|
+
[0.1.x to 0.2.x migration guide](https://github.com/get-convex/static-hosting/blob/main/MIGRATION.md).
|
|
324
|
+
|
|
325
|
+
## Troubleshooting
|
|
326
|
+
|
|
327
|
+
### 404s on every path
|
|
328
|
+
|
|
329
|
+
Run `npx convex dev` (or `npx convex deploy`) after adding the component. In component-owned mode,
|
|
330
|
+
verify its `httpPrefix`. In app-owned mode, verify that `registerStaticRoutes` is called from
|
|
331
|
+
`convex/http.ts` and the component itself has no `httpPrefix`.
|
|
332
|
+
|
|
333
|
+
### Wrong `VITE_CONVEX_URL` in the built bundle
|
|
334
|
+
|
|
335
|
+
```bash
|
|
336
|
+
# Right: CLI sets VITE_CONVEX_URL for the target deployment
|
|
337
|
+
npx @samebase/convex-hosting deploy
|
|
338
|
+
|
|
339
|
+
# Wrong: uses VITE_CONVEX_URL from .env.local
|
|
340
|
+
npm run build && npx @samebase/convex-hosting upload --prod
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
### "Cannot find module convex.config"
|
|
344
|
+
|
|
345
|
+
Make sure you've installed the package and it's listed in `package.json`:
|
|
346
|
+
|
|
347
|
+
```bash
|
|
348
|
+
npm install @samebase/convex-hosting
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
### Component name mismatch
|
|
352
|
+
|
|
353
|
+
If you've renamed the component instance with `app.use(hosting, { name: "custom" })`, pass it to the
|
|
354
|
+
CLI and replace every generated `components.hosting` reference with `components.custom`:
|
|
355
|
+
|
|
356
|
+
```bash
|
|
357
|
+
npx @samebase/convex-hosting upload --component custom
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
## API Reference
|
|
361
|
+
|
|
362
|
+
### `registerStaticRoutes(http, component, options?)`
|
|
363
|
+
|
|
364
|
+
Registers a static catch-all in the app's `convex/http.ts`. Use it when exact app routes must remain
|
|
365
|
+
at the same root. `pathPrefix`, `spaFallback`, and `cdnBaseUrl` are supported. The component must be
|
|
366
|
+
installed without an `httpPrefix` so only the app owns that URL space.
|
|
367
|
+
|
|
368
|
+
### `serveStaticAsset(ctx, component, request, options?)`
|
|
369
|
+
|
|
370
|
+
Serves an uploaded file from an app-owned HTTP action, or returns `null` when no file matches so the
|
|
371
|
+
action can continue with its own routing (server rendering, prerendered pages, redirects). Responses
|
|
372
|
+
match `registerStaticRoutes`. The lookup is exact: `/` is not mapped to `/index.html`, and
|
|
373
|
+
`spaFallback` defaults to `false`. `path` (the decoded file path, defaulting to the decoded request
|
|
374
|
+
path, starting with `/` and without your route's prefix), `spaFallback`, and `cdnBaseUrl` are
|
|
375
|
+
supported. A request path with malformed percent-encoding returns `null`. A found file whose storage
|
|
376
|
+
can't be read gets a 500 response. The component must be installed without an `httpPrefix`.
|
|
377
|
+
|
|
378
|
+
### `decodeRequestPath(pathname)`
|
|
379
|
+
|
|
380
|
+
Decodes a URL pathname the way the static handlers do. Returns `null` for malformed
|
|
381
|
+
percent-encoding.
|
|
382
|
+
|
|
383
|
+
### `exposeDeploymentQuery(component)`
|
|
384
|
+
|
|
385
|
+
Returns `{ getCurrentDeployment }`: a public query that wraps the component's deployment singleton.
|
|
386
|
+
Add this only if you use `<UpdateBanner />` or `useDeploymentUpdates`.
|
|
387
|
+
|
|
388
|
+
### `getConvexUrl()`
|
|
389
|
+
|
|
390
|
+
Browser-only. Returns `https://<deployment>.convex.cloud` when the page is served from
|
|
391
|
+
`<deployment>.convex.site`.
|
|
392
|
+
|
|
393
|
+
## Additional Resources
|
|
394
|
+
|
|
395
|
+
- [README](./README.md)
|
|
396
|
+
- [Upstream 0.1.x to 0.2.x migration guide](https://github.com/get-convex/static-hosting/blob/main/MIGRATION.md)
|
|
397
|
+
- [`examples/spa`](https://github.com/samebase/convex-hosting/tree/main/examples/spa): Working
|
|
398
|
+
example app
|
|
399
|
+
- [Component source](https://github.com/samebase/convex-hosting/tree/main/packages/convex-hosting/src/component)
|