@convex-dev/static-hosting 0.1.2-beta.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 (98) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +333 -0
  3. package/dist/cli/deploy.d.ts +16 -0
  4. package/dist/cli/deploy.d.ts.map +1 -0
  5. package/dist/cli/deploy.js +324 -0
  6. package/dist/cli/deploy.js.map +1 -0
  7. package/dist/cli/index.d.ts +15 -0
  8. package/dist/cli/index.d.ts.map +1 -0
  9. package/dist/cli/index.js +95 -0
  10. package/dist/cli/index.js.map +1 -0
  11. package/dist/cli/init.d.ts +9 -0
  12. package/dist/cli/init.d.ts.map +1 -0
  13. package/dist/cli/init.js +181 -0
  14. package/dist/cli/init.js.map +1 -0
  15. package/dist/cli/next-build.d.ts +24 -0
  16. package/dist/cli/next-build.d.ts.map +1 -0
  17. package/dist/cli/next-build.js +569 -0
  18. package/dist/cli/next-build.js.map +1 -0
  19. package/dist/cli/setup.d.ts +9 -0
  20. package/dist/cli/setup.d.ts.map +1 -0
  21. package/dist/cli/setup.js +157 -0
  22. package/dist/cli/setup.js.map +1 -0
  23. package/dist/cli/upload.d.ts +15 -0
  24. package/dist/cli/upload.d.ts.map +1 -0
  25. package/dist/cli/upload.js +436 -0
  26. package/dist/cli/upload.js.map +1 -0
  27. package/dist/client/_generated/_ignore.d.ts +1 -0
  28. package/dist/client/_generated/_ignore.d.ts.map +1 -0
  29. package/dist/client/_generated/_ignore.js +3 -0
  30. package/dist/client/_generated/_ignore.js.map +1 -0
  31. package/dist/client/index.d.ts +142 -0
  32. package/dist/client/index.d.ts.map +1 -0
  33. package/dist/client/index.js +475 -0
  34. package/dist/client/index.js.map +1 -0
  35. package/dist/client/next.d.ts +38 -0
  36. package/dist/client/next.d.ts.map +1 -0
  37. package/dist/client/next.js +175 -0
  38. package/dist/client/next.js.map +1 -0
  39. package/dist/client/nextAdapter.d.ts +4 -0
  40. package/dist/client/nextAdapter.d.ts.map +1 -0
  41. package/dist/client/nextAdapter.js +9 -0
  42. package/dist/client/nextAdapter.js.map +1 -0
  43. package/dist/component/_generated/api.d.ts +34 -0
  44. package/dist/component/_generated/api.d.ts.map +1 -0
  45. package/dist/component/_generated/api.js +31 -0
  46. package/dist/component/_generated/api.js.map +1 -0
  47. package/dist/component/_generated/component.d.ts +73 -0
  48. package/dist/component/_generated/component.d.ts.map +1 -0
  49. package/dist/component/_generated/component.js +11 -0
  50. package/dist/component/_generated/component.js.map +1 -0
  51. package/dist/component/_generated/dataModel.d.ts +46 -0
  52. package/dist/component/_generated/dataModel.d.ts.map +1 -0
  53. package/dist/component/_generated/dataModel.js +11 -0
  54. package/dist/component/_generated/dataModel.js.map +1 -0
  55. package/dist/component/_generated/server.d.ts +121 -0
  56. package/dist/component/_generated/server.d.ts.map +1 -0
  57. package/dist/component/_generated/server.js +78 -0
  58. package/dist/component/_generated/server.js.map +1 -0
  59. package/dist/component/convex.config.d.ts +3 -0
  60. package/dist/component/convex.config.d.ts.map +1 -0
  61. package/dist/component/convex.config.js +3 -0
  62. package/dist/component/convex.config.js.map +1 -0
  63. package/dist/component/lib.d.ts +88 -0
  64. package/dist/component/lib.d.ts.map +1 -0
  65. package/dist/component/lib.js +210 -0
  66. package/dist/component/lib.js.map +1 -0
  67. package/dist/component/schema.d.ts +27 -0
  68. package/dist/component/schema.d.ts.map +1 -0
  69. package/dist/component/schema.js +20 -0
  70. package/dist/component/schema.js.map +1 -0
  71. package/dist/react/index.d.ts +80 -0
  72. package/dist/react/index.d.ts.map +1 -0
  73. package/dist/react/index.js +138 -0
  74. package/dist/react/index.js.map +1 -0
  75. package/package.json +120 -0
  76. package/src/cli/deploy.ts +375 -0
  77. package/src/cli/index.ts +104 -0
  78. package/src/cli/init.ts +181 -0
  79. package/src/cli/next-build.ts +707 -0
  80. package/src/cli/setup.ts +190 -0
  81. package/src/cli/upload.ts +521 -0
  82. package/src/client/_generated/_ignore.ts +1 -0
  83. package/src/client/index.test.ts +67 -0
  84. package/src/client/index.ts +553 -0
  85. package/src/client/next.ts +223 -0
  86. package/src/client/nextAdapter.ts +17 -0
  87. package/src/client/setup.test.ts +26 -0
  88. package/src/component/_generated/api.ts +50 -0
  89. package/src/component/_generated/component.ts +104 -0
  90. package/src/component/_generated/dataModel.ts +60 -0
  91. package/src/component/_generated/server.ts +161 -0
  92. package/src/component/convex.config.ts +3 -0
  93. package/src/component/lib.test.ts +110 -0
  94. package/src/component/lib.ts +228 -0
  95. package/src/component/schema.ts +21 -0
  96. package/src/component/setup.test.ts +11 -0
  97. package/src/react/index.tsx +184 -0
  98. package/src/test.ts +18 -0
@@ -0,0 +1,142 @@
1
+ import type { HttpRouter } from "convex/server";
2
+ import type { ComponentApi } from "../component/_generated/component.js";
3
+ /**
4
+ * Get MIME type for a file path based on its extension.
5
+ */
6
+ export declare function getMimeType(path: string): string;
7
+ export declare function registerStaticRoutes(http: HttpRouter, component: ComponentApi, { pathPrefix, spaFallback, cdnBaseUrl, }?: {
8
+ pathPrefix?: string;
9
+ spaFallback?: boolean;
10
+ /** Base URL for CDN blob redirects (e.g., `(req) => \`${new URL(req.url).origin}/fs/blobs\``).
11
+ * When set, assets with a blobId (non-HTML) will return a 302 redirect to `{cdnBaseUrl}/{blobId}`. */
12
+ cdnBaseUrl?: string | ((request: Request) => string);
13
+ }): void;
14
+ /**
15
+ * Expose the upload API as INTERNAL functions for secure deployments.
16
+ * These functions can only be called via `npx convex run` or from other Convex functions.
17
+ *
18
+ * @param component - The component API reference
19
+ *
20
+ * @example
21
+ * ```typescript
22
+ * // In your convex/staticHosting.ts
23
+ * import { exposeUploadApi } from "@convex-dev/static-hosting";
24
+ * import { components } from "./_generated/api";
25
+ *
26
+ * export const { generateUploadUrl, recordAsset, gcOldAssets, listAssets } =
27
+ * exposeUploadApi(components.staticHosting);
28
+ * ```
29
+ *
30
+ * Then deploy using:
31
+ * ```bash
32
+ * npm run deploy:static
33
+ * ```
34
+ */
35
+ export declare function exposeUploadApi(component: ComponentApi): {
36
+ /**
37
+ * Generate a signed URL for uploading a file.
38
+ * Files are stored in the app's storage (not the component's).
39
+ */
40
+ generateUploadUrl: import("convex/server").RegisteredMutation<"internal", {}, Promise<string>>;
41
+ /**
42
+ * Record an uploaded asset in the database.
43
+ * Automatically cleans up old storage files when replacing.
44
+ * Pass storageId for Convex storage assets, or blobId for CDN assets.
45
+ */
46
+ recordAsset: import("convex/server").RegisteredMutation<"internal", {
47
+ blobId?: string | undefined;
48
+ storageId?: string | undefined;
49
+ path: string;
50
+ contentType: string;
51
+ deploymentId: string;
52
+ }, Promise<string | null>>;
53
+ /**
54
+ * Garbage collect old assets and notify clients of the new deployment.
55
+ * Returns the count of deleted assets.
56
+ * Also triggers connected clients to reload via the deployment subscription.
57
+ */
58
+ gcOldAssets: import("convex/server").RegisteredMutation<"internal", {
59
+ currentDeploymentId: string;
60
+ }, Promise<{
61
+ deleted: number;
62
+ blobIds: string[];
63
+ }>>;
64
+ /**
65
+ * Generate multiple signed upload URLs in one call.
66
+ * Much faster than calling generateUploadUrl N times.
67
+ */
68
+ generateUploadUrls: import("convex/server").RegisteredMutation<"internal", {
69
+ count: number;
70
+ }, Promise<string[]>>;
71
+ /**
72
+ * Record multiple uploaded assets in one call.
73
+ */
74
+ recordAssets: import("convex/server").RegisteredMutation<"internal", {
75
+ assets: {
76
+ path: string;
77
+ contentType: string;
78
+ deploymentId: string;
79
+ storageId: string;
80
+ }[];
81
+ }, Promise<void>>;
82
+ /**
83
+ * List all static assets (for debugging).
84
+ */
85
+ listAssets: import("convex/server").RegisteredQuery<"internal", {
86
+ limit?: number | undefined;
87
+ }, Promise<{
88
+ _creationTime: number;
89
+ _id: string;
90
+ blobId?: string;
91
+ contentType: string;
92
+ deploymentId: string;
93
+ path: string;
94
+ storageId?: string;
95
+ }[]>>;
96
+ };
97
+ /**
98
+ * Expose a query that clients can subscribe to for live reload on deploy.
99
+ * When a new deployment happens, subscribed clients will be notified.
100
+ *
101
+ * @param component - The component API reference
102
+ *
103
+ * @example
104
+ * ```typescript
105
+ * // In your convex/staticHosting.ts
106
+ * import { exposeUploadApi, exposeDeploymentQuery } from "@convex-dev/static-hosting";
107
+ * import { components } from "./_generated/api";
108
+ *
109
+ * export const { generateUploadUrl, recordAsset, gcOldAssets, listAssets } =
110
+ * exposeUploadApi(components.staticHosting);
111
+ *
112
+ * export const { getCurrentDeployment } = exposeDeploymentQuery(components.staticHosting);
113
+ * ```
114
+ */
115
+ export declare function exposeDeploymentQuery(component: ComponentApi): {
116
+ /**
117
+ * Get the current deployment info.
118
+ * Subscribe to this query to detect when a new deployment happens.
119
+ */
120
+ getCurrentDeployment: import("convex/server").RegisteredQuery<"public", {}, Promise<{
121
+ _creationTime: number;
122
+ _id: string;
123
+ currentDeploymentId: string;
124
+ deployedAt: number;
125
+ } | null>>;
126
+ };
127
+ /**
128
+ * Derive the Convex cloud URL from a .convex.site hostname.
129
+ * Useful for client-side code that needs to connect to the Convex backend
130
+ * when hosted on Convex static hosting.
131
+ *
132
+ * @example
133
+ * ```typescript
134
+ * // In your React app's main.tsx
135
+ * import { getConvexUrl } from "@convex-dev/static-hosting";
136
+ *
137
+ * const convexUrl = import.meta.env.VITE_CONVEX_URL ?? getConvexUrl();
138
+ * const convex = new ConvexReactClient(convexUrl);
139
+ * ```
140
+ */
141
+ export declare function getConvexUrl(): string;
142
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAEhD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sCAAsC,CAAC;AAwGzE;;GAEG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAGhD;AAwDD,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,UAAU,EAChB,SAAS,EAAE,YAAY,EACvB,EACE,UAAgB,EAChB,WAAkB,EAClB,UAAU,GACX,GAAE;IACD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB;0GACsG;IACtG,UAAU,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,OAAO,EAAE,OAAO,KAAK,MAAM,CAAC,CAAC;CACjD,QA6IP;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,eAAe,CAAC,SAAS,EAAE,YAAY;IAEnD;;;OAGG;;IAQH;;;;OAIG;;;;;;;;IAgCH;;;;OAIG;;;;;;;IA8BH;;;OAGG;;;;IAYH;;OAEG;;;;;;;;;IAwBH;;OAEG;;;;;;;;;;;;EAYN;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,qBAAqB,CAAC,SAAS,EAAE,YAAY;IAEzD;;;OAGG;;;;;;;EAQN;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,YAAY,IAAI,MAAM,CAarC"}
@@ -0,0 +1,475 @@
1
+ import { httpActionGeneric, internalMutationGeneric, internalQueryGeneric, queryGeneric, } from "convex/server";
2
+ import { v } from "convex/values";
3
+ // MIME type mapping for common file types
4
+ const MIME_TYPES = {
5
+ ".html": "text/html; charset=utf-8",
6
+ ".js": "application/javascript; charset=utf-8",
7
+ ".mjs": "application/javascript; charset=utf-8",
8
+ ".css": "text/css; charset=utf-8",
9
+ ".json": "application/json; charset=utf-8",
10
+ ".png": "image/png",
11
+ ".jpg": "image/jpeg",
12
+ ".jpeg": "image/jpeg",
13
+ ".gif": "image/gif",
14
+ ".svg": "image/svg+xml",
15
+ ".ico": "image/x-icon",
16
+ ".webp": "image/webp",
17
+ ".woff": "font/woff",
18
+ ".woff2": "font/woff2",
19
+ ".ttf": "font/ttf",
20
+ ".txt": "text/plain; charset=utf-8",
21
+ ".map": "application/json",
22
+ ".webmanifest": "application/manifest+json",
23
+ ".xml": "application/xml",
24
+ };
25
+ /**
26
+ * Generate HTML page shown when no assets have been deployed yet.
27
+ */
28
+ function getSetupHtml() {
29
+ return `<!DOCTYPE html>
30
+ <html lang="en">
31
+ <head>
32
+ <meta charset="UTF-8">
33
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
34
+ <title>Convex Static Hosting</title>
35
+ <style>
36
+ * { box-sizing: border-box; }
37
+ body {
38
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
39
+ max-width: 640px;
40
+ margin: 0 auto;
41
+ padding: 40px 20px;
42
+ background: #fafafa;
43
+ color: #333;
44
+ line-height: 1.6;
45
+ }
46
+ h1 { color: #111; margin-bottom: 8px; }
47
+ .subtitle { color: #666; margin-bottom: 32px; }
48
+ code {
49
+ background: #e8e8e8;
50
+ padding: 2px 6px;
51
+ border-radius: 4px;
52
+ font-size: 14px;
53
+ }
54
+ pre {
55
+ background: #1a1a1a;
56
+ color: #f0f0f0;
57
+ padding: 16px;
58
+ border-radius: 8px;
59
+ overflow-x: auto;
60
+ font-size: 14px;
61
+ }
62
+ .step { margin-bottom: 24px; }
63
+ .step-num {
64
+ display: inline-block;
65
+ background: #333;
66
+ color: white;
67
+ width: 24px;
68
+ height: 24px;
69
+ border-radius: 50%;
70
+ text-align: center;
71
+ font-size: 14px;
72
+ line-height: 24px;
73
+ margin-right: 8px;
74
+ }
75
+ a { color: #0070f3; }
76
+ </style>
77
+ </head>
78
+ <body>
79
+ <h1>Almost there!</h1>
80
+ <p class="subtitle">Your Convex backend is running, but no static files have been deployed yet.</p>
81
+
82
+ <div class="step">
83
+ <span class="step-num">1</span>
84
+ <strong>Build your frontend</strong>
85
+ <pre>npm run build</pre>
86
+ </div>
87
+
88
+ <div class="step">
89
+ <span class="step-num">2</span>
90
+ <strong>Deploy your static files</strong>
91
+ <pre>npx @convex-dev/static-hosting deploy</pre>
92
+ </div>
93
+
94
+ <p>Or deploy everything in one command:</p>
95
+ <pre>npm run deploy</pre>
96
+
97
+ <p style="margin-top: 32px; color: #666; font-size: 14px;">
98
+ Learn more at <a href="https://github.com/get-convex/static-hosting">github.com/get-convex/static-hosting</a>
99
+ </p>
100
+ </body>
101
+ </html>`;
102
+ }
103
+ /**
104
+ * Get MIME type for a file path based on its extension.
105
+ */
106
+ export function getMimeType(path) {
107
+ const ext = path.substring(path.lastIndexOf(".")).toLowerCase();
108
+ return MIME_TYPES[ext] || "application/octet-stream";
109
+ }
110
+ /**
111
+ * Check if a path has a file extension.
112
+ */
113
+ function hasFileExtension(path) {
114
+ const lastSegment = path.split("/").pop() || "";
115
+ return lastSegment.includes(".") && !lastSegment.startsWith(".");
116
+ }
117
+ /**
118
+ * Check if asset is a hashed asset (for cache control).
119
+ * Vite produces: index-lj_vq_aF.js, style-B71cUw87.css
120
+ */
121
+ function isHashedAsset(path) {
122
+ return /[-.][\dA-Za-z_]{6,12}\.[a-z]+$/.test(path);
123
+ }
124
+ /**
125
+ * Register HTTP routes for serving static files.
126
+ * This creates a catch-all route that serves files from Convex storage
127
+ * with SPA fallback support.
128
+ *
129
+ * @param http - The HTTP router to register routes on
130
+ * @param component - The component API reference
131
+ * @param options - Configuration options
132
+ * @param options.pathPrefix - URL prefix for static files (default: "/")
133
+ * @param options.spaFallback - Enable SPA fallback to index.html (default: true)
134
+ *
135
+ * @example
136
+ * ```typescript
137
+ * // In your convex/http.ts
138
+ * import { httpRouter } from "convex/server";
139
+ * import { registerStaticRoutes } from "@convex-dev/static-hosting";
140
+ * import { components } from "./_generated/api";
141
+ *
142
+ * const http = httpRouter();
143
+ *
144
+ * // Serve static files at root
145
+ * registerStaticRoutes(http, components.staticHosting);
146
+ *
147
+ * // Or serve at a specific path prefix
148
+ * registerStaticRoutes(http, components.staticHosting, {
149
+ * pathPrefix: "/app",
150
+ * });
151
+ *
152
+ * export default http;
153
+ * ```
154
+ */
155
+ /**
156
+ * Check if a content type is HTML.
157
+ */
158
+ function isHtmlContentType(contentType) {
159
+ return contentType.startsWith("text/html");
160
+ }
161
+ export function registerStaticRoutes(http, component, { pathPrefix = "/", spaFallback = true, cdnBaseUrl, } = {}) {
162
+ // Normalize pathPrefix - ensure it starts with / and doesn't end with /
163
+ const normalizedPrefix = pathPrefix === "/" ? "" : pathPrefix.replace(/\/$/, "");
164
+ const serveStaticFile = httpActionGeneric(async (ctx, request) => {
165
+ const url = new URL(request.url);
166
+ let path = url.pathname;
167
+ // Remove prefix if present
168
+ if (normalizedPrefix && path.startsWith(normalizedPrefix)) {
169
+ path = path.slice(normalizedPrefix.length) || "/";
170
+ }
171
+ // Normalize: serve index.html for root
172
+ if (path === "" || path === "/") {
173
+ path = "/index.html";
174
+ }
175
+ let asset = await ctx.runQuery(component.lib.getByPath, { path });
176
+ // SPA fallback: if not found and no file extension, serve index.html
177
+ if (!asset && spaFallback && !hasFileExtension(path)) {
178
+ asset = await ctx.runQuery(component.lib.getByPath, {
179
+ path: "/index.html",
180
+ });
181
+ }
182
+ // 404 if still not found
183
+ if (!asset) {
184
+ // If looking for index.html and it's not there, show setup instructions
185
+ if (path === "/index.html") {
186
+ return new Response(getSetupHtml(), {
187
+ status: 200,
188
+ headers: { "Content-Type": "text/html; charset=utf-8" },
189
+ });
190
+ }
191
+ return new Response("Not Found", {
192
+ status: 404,
193
+ headers: { "Content-Type": "text/plain" },
194
+ });
195
+ }
196
+ // CDN redirect: if asset has blobId, is not HTML, and cdnBaseUrl is configured
197
+ if (asset.blobId && cdnBaseUrl && !isHtmlContentType(asset.contentType)) {
198
+ const baseUrl = typeof cdnBaseUrl === "function" ? cdnBaseUrl(request) : cdnBaseUrl;
199
+ const redirectUrl = `${baseUrl.replace(/\/$/, "")}/${asset.blobId}`;
200
+ // Cache control for redirect: hashed assets can cache the redirect itself
201
+ const cacheControl = isHashedAsset(path)
202
+ ? "public, max-age=31536000, immutable"
203
+ : "public, max-age=0, must-revalidate";
204
+ return new Response(null, {
205
+ status: 302,
206
+ headers: {
207
+ Location: redirectUrl,
208
+ "Cache-Control": cacheControl,
209
+ },
210
+ });
211
+ }
212
+ // Serve from Convex storage
213
+ if (!asset.storageId) {
214
+ return new Response("Asset not available", {
215
+ status: 500,
216
+ headers: { "Content-Type": "text/plain" },
217
+ });
218
+ }
219
+ // Use storageId as ETag (unique per file content)
220
+ const etag = `"${asset.storageId}"`;
221
+ // Check for conditional request (If-None-Match)
222
+ const ifNoneMatch = request.headers.get("If-None-Match");
223
+ if (ifNoneMatch === etag) {
224
+ // Client has current version - return 304 Not Modified
225
+ return new Response(null, {
226
+ status: 304,
227
+ headers: {
228
+ ETag: etag,
229
+ "Cache-Control": isHashedAsset(path)
230
+ ? "public, max-age=31536000, immutable"
231
+ : "public, max-age=0, must-revalidate",
232
+ },
233
+ });
234
+ }
235
+ // Get file from storage
236
+ const blob = await ctx.storage.get(asset.storageId);
237
+ if (!blob) {
238
+ return new Response("Storage error", {
239
+ status: 500,
240
+ headers: { "Content-Type": "text/plain" },
241
+ });
242
+ }
243
+ // Cache control: hashed assets can be cached forever
244
+ const cacheControl = isHashedAsset(path)
245
+ ? "public, max-age=31536000, immutable"
246
+ : "public, max-age=0, must-revalidate";
247
+ return new Response(blob, {
248
+ status: 200,
249
+ headers: {
250
+ "Content-Type": asset.contentType,
251
+ "Cache-Control": cacheControl,
252
+ ETag: etag,
253
+ "X-Content-Type-Options": "nosniff",
254
+ },
255
+ });
256
+ });
257
+ // Use pathPrefix routing
258
+ http.route({
259
+ pathPrefix: pathPrefix === "/" ? "/" : `${normalizedPrefix}/`,
260
+ method: "GET",
261
+ handler: serveStaticFile,
262
+ });
263
+ // Also handle exact prefix without trailing slash
264
+ if (normalizedPrefix) {
265
+ http.route({
266
+ path: normalizedPrefix,
267
+ method: "GET",
268
+ handler: serveStaticFile,
269
+ });
270
+ }
271
+ }
272
+ /**
273
+ * Expose the upload API as INTERNAL functions for secure deployments.
274
+ * These functions can only be called via `npx convex run` or from other Convex functions.
275
+ *
276
+ * @param component - The component API reference
277
+ *
278
+ * @example
279
+ * ```typescript
280
+ * // In your convex/staticHosting.ts
281
+ * import { exposeUploadApi } from "@convex-dev/static-hosting";
282
+ * import { components } from "./_generated/api";
283
+ *
284
+ * export const { generateUploadUrl, recordAsset, gcOldAssets, listAssets } =
285
+ * exposeUploadApi(components.staticHosting);
286
+ * ```
287
+ *
288
+ * Then deploy using:
289
+ * ```bash
290
+ * npm run deploy:static
291
+ * ```
292
+ */
293
+ export function exposeUploadApi(component) {
294
+ return {
295
+ /**
296
+ * Generate a signed URL for uploading a file.
297
+ * Files are stored in the app's storage (not the component's).
298
+ */
299
+ generateUploadUrl: internalMutationGeneric({
300
+ args: {},
301
+ handler: async (ctx) => {
302
+ return await ctx.storage.generateUploadUrl();
303
+ },
304
+ }),
305
+ /**
306
+ * Record an uploaded asset in the database.
307
+ * Automatically cleans up old storage files when replacing.
308
+ * Pass storageId for Convex storage assets, or blobId for CDN assets.
309
+ */
310
+ recordAsset: internalMutationGeneric({
311
+ args: {
312
+ path: v.string(),
313
+ storageId: v.optional(v.string()),
314
+ blobId: v.optional(v.string()),
315
+ contentType: v.string(),
316
+ deploymentId: v.string(),
317
+ },
318
+ handler: async (ctx, args) => {
319
+ const { oldStorageId, oldBlobId } = await ctx.runMutation(component.lib.recordAsset, {
320
+ path: args.path,
321
+ ...(args.storageId ? { storageId: args.storageId } : {}),
322
+ ...(args.blobId ? { blobId: args.blobId } : {}),
323
+ contentType: args.contentType,
324
+ deploymentId: args.deploymentId,
325
+ });
326
+ if (oldStorageId) {
327
+ try {
328
+ await ctx.storage.delete(oldStorageId);
329
+ }
330
+ catch {
331
+ // Ignore - old file may have been in different storage
332
+ }
333
+ }
334
+ // Return oldBlobId so caller can clean up CDN blobs if needed
335
+ return oldBlobId ?? null;
336
+ },
337
+ }),
338
+ /**
339
+ * Garbage collect old assets and notify clients of the new deployment.
340
+ * Returns the count of deleted assets.
341
+ * Also triggers connected clients to reload via the deployment subscription.
342
+ */
343
+ gcOldAssets: internalMutationGeneric({
344
+ args: {
345
+ currentDeploymentId: v.string(),
346
+ },
347
+ handler: async (ctx, args) => {
348
+ const { storageIds, blobIds } = await ctx.runMutation(component.lib.gcOldAssets, {
349
+ currentDeploymentId: args.currentDeploymentId,
350
+ });
351
+ for (const storageId of storageIds) {
352
+ try {
353
+ await ctx.storage.delete(storageId);
354
+ }
355
+ catch {
356
+ // Ignore - old file may have been in different storage
357
+ }
358
+ }
359
+ // Update deployment info to trigger client reloads
360
+ await ctx.runMutation(component.lib.setCurrentDeployment, {
361
+ deploymentId: args.currentDeploymentId,
362
+ });
363
+ // Return both counts and blobIds for CDN cleanup
364
+ return { deleted: storageIds.length, blobIds };
365
+ },
366
+ }),
367
+ /**
368
+ * Generate multiple signed upload URLs in one call.
369
+ * Much faster than calling generateUploadUrl N times.
370
+ */
371
+ generateUploadUrls: internalMutationGeneric({
372
+ args: { count: v.number() },
373
+ handler: async (ctx, { count }) => {
374
+ const urls = [];
375
+ for (let i = 0; i < count; i++) {
376
+ urls.push(await ctx.storage.generateUploadUrl());
377
+ }
378
+ return urls;
379
+ },
380
+ }),
381
+ /**
382
+ * Record multiple uploaded assets in one call.
383
+ */
384
+ recordAssets: internalMutationGeneric({
385
+ args: {
386
+ assets: v.array(v.object({
387
+ path: v.string(),
388
+ storageId: v.string(),
389
+ contentType: v.string(),
390
+ deploymentId: v.string(),
391
+ })),
392
+ },
393
+ handler: async (ctx, { assets }) => {
394
+ for (const asset of assets) {
395
+ await ctx.runMutation(component.lib.recordAsset, {
396
+ path: asset.path,
397
+ storageId: asset.storageId,
398
+ contentType: asset.contentType,
399
+ deploymentId: asset.deploymentId,
400
+ });
401
+ }
402
+ },
403
+ }),
404
+ /**
405
+ * List all static assets (for debugging).
406
+ */
407
+ listAssets: internalQueryGeneric({
408
+ args: {
409
+ limit: v.optional(v.number()),
410
+ },
411
+ handler: async (ctx, args) => {
412
+ return await ctx.runQuery(component.lib.listAssets, {
413
+ limit: args.limit,
414
+ });
415
+ },
416
+ }),
417
+ };
418
+ }
419
+ /**
420
+ * Expose a query that clients can subscribe to for live reload on deploy.
421
+ * When a new deployment happens, subscribed clients will be notified.
422
+ *
423
+ * @param component - The component API reference
424
+ *
425
+ * @example
426
+ * ```typescript
427
+ * // In your convex/staticHosting.ts
428
+ * import { exposeUploadApi, exposeDeploymentQuery } from "@convex-dev/static-hosting";
429
+ * import { components } from "./_generated/api";
430
+ *
431
+ * export const { generateUploadUrl, recordAsset, gcOldAssets, listAssets } =
432
+ * exposeUploadApi(components.staticHosting);
433
+ *
434
+ * export const { getCurrentDeployment } = exposeDeploymentQuery(components.staticHosting);
435
+ * ```
436
+ */
437
+ export function exposeDeploymentQuery(component) {
438
+ return {
439
+ /**
440
+ * Get the current deployment info.
441
+ * Subscribe to this query to detect when a new deployment happens.
442
+ */
443
+ getCurrentDeployment: queryGeneric({
444
+ args: {},
445
+ handler: async (ctx) => {
446
+ return await ctx.runQuery(component.lib.getCurrentDeployment, {});
447
+ },
448
+ }),
449
+ };
450
+ }
451
+ /**
452
+ * Derive the Convex cloud URL from a .convex.site hostname.
453
+ * Useful for client-side code that needs to connect to the Convex backend
454
+ * when hosted on Convex static hosting.
455
+ *
456
+ * @example
457
+ * ```typescript
458
+ * // In your React app's main.tsx
459
+ * import { getConvexUrl } from "@convex-dev/static-hosting";
460
+ *
461
+ * const convexUrl = import.meta.env.VITE_CONVEX_URL ?? getConvexUrl();
462
+ * const convex = new ConvexReactClient(convexUrl);
463
+ * ```
464
+ */
465
+ export function getConvexUrl() {
466
+ if (typeof window === "undefined") {
467
+ throw new Error("getConvexUrl() can only be called in a browser context");
468
+ }
469
+ // If hosted on Convex (.convex.site), derive API URL (.convex.cloud)
470
+ if (window.location.hostname.endsWith(".convex.site")) {
471
+ return `https://${window.location.hostname.replace(".convex.site", ".convex.cloud")}`;
472
+ }
473
+ throw new Error("Unable to derive Convex URL. Please set VITE_CONVEX_URL environment variable.");
474
+ }
475
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,38 @@
1
+ import type { FunctionReference, HttpRouter } from "convex/server";
2
+ import type { ComponentApi } from "../component/_generated/component.js";
3
+ /**
4
+ * Register HTTP routes for serving a Next.js app via Convex HTTP actions.
5
+ *
6
+ * Static assets (`/_next/static/*`) are served directly from Convex storage
7
+ * (V8 runtime, instant). All other requests are forwarded to a Node.js action
8
+ * that runs NextServer.
9
+ *
10
+ * @param http - The HTTP router to register routes on
11
+ * @param component - The component API reference (for storage lookups)
12
+ * @param actionRef - Reference to the generated `handle` action
13
+ * @param options - Configuration options
14
+ * @param options.pathPrefix - URL prefix (default: "/")
15
+ *
16
+ * @example
17
+ * ```typescript
18
+ * // convex/http.ts
19
+ * import { httpRouter } from "convex/server";
20
+ * import { registerNextRoutes } from "@convex-dev/static-hosting/next";
21
+ * import { components, internal } from "./_generated/api";
22
+ *
23
+ * const http = httpRouter();
24
+ * registerNextRoutes(http, components.staticHosting, internal._generatedNextServer.handle);
25
+ * export default http;
26
+ * ```
27
+ */
28
+ export declare function registerNextRoutes(http: HttpRouter, component: ComponentApi, actionRef: FunctionReference<"action", "internal", Record<string, unknown>, unknown>, { pathPrefix, warmup, }?: {
29
+ pathPrefix?: string;
30
+ /** Inject a warmup script into static HTML pages to pre-boot the Node.js
31
+ * NextServer in the background. Defaults to true. */
32
+ warmup?: boolean;
33
+ }): void;
34
+ /**
35
+ * Get the MIME type for a file path.
36
+ */
37
+ export declare function getNextMimeType(filePath: string): string;
38
+ //# sourceMappingURL=next.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"next.d.ts","sourceRoot":"","sources":["../../src/client/next.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AACnE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sCAAsC,CAAC;AAqBzE;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,UAAU,EAChB,SAAS,EAAE,YAAY,EACvB,SAAS,EAAE,iBAAiB,CAAC,QAAQ,EAAE,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,CAAC,EACpF,EACE,UAAgB,EAChB,MAAa,GACd,GAAE;IACD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;0DACsD;IACtD,MAAM,CAAC,EAAE,OAAO,CAAC;CACb,QA0JP;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAGxD"}