@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.
- package/LICENSE +201 -0
- package/README.md +333 -0
- package/dist/cli/deploy.d.ts +16 -0
- package/dist/cli/deploy.d.ts.map +1 -0
- package/dist/cli/deploy.js +324 -0
- package/dist/cli/deploy.js.map +1 -0
- package/dist/cli/index.d.ts +15 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +95 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/init.d.ts +9 -0
- package/dist/cli/init.d.ts.map +1 -0
- package/dist/cli/init.js +181 -0
- package/dist/cli/init.js.map +1 -0
- package/dist/cli/next-build.d.ts +24 -0
- package/dist/cli/next-build.d.ts.map +1 -0
- package/dist/cli/next-build.js +569 -0
- package/dist/cli/next-build.js.map +1 -0
- package/dist/cli/setup.d.ts +9 -0
- package/dist/cli/setup.d.ts.map +1 -0
- package/dist/cli/setup.js +157 -0
- package/dist/cli/setup.js.map +1 -0
- package/dist/cli/upload.d.ts +15 -0
- package/dist/cli/upload.d.ts.map +1 -0
- package/dist/cli/upload.js +436 -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 +142 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/index.js +475 -0
- package/dist/client/index.js.map +1 -0
- package/dist/client/next.d.ts +38 -0
- package/dist/client/next.d.ts.map +1 -0
- package/dist/client/next.js +175 -0
- package/dist/client/next.js.map +1 -0
- package/dist/client/nextAdapter.d.ts +4 -0
- package/dist/client/nextAdapter.d.ts.map +1 -0
- package/dist/client/nextAdapter.js +9 -0
- package/dist/client/nextAdapter.js.map +1 -0
- package/dist/component/_generated/api.d.ts +34 -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 +73 -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/lib.d.ts +88 -0
- package/dist/component/lib.d.ts.map +1 -0
- package/dist/component/lib.js +210 -0
- package/dist/component/lib.js.map +1 -0
- package/dist/component/schema.d.ts +27 -0
- package/dist/component/schema.d.ts.map +1 -0
- package/dist/component/schema.js +20 -0
- package/dist/component/schema.js.map +1 -0
- package/dist/react/index.d.ts +80 -0
- package/dist/react/index.d.ts.map +1 -0
- package/dist/react/index.js +138 -0
- package/dist/react/index.js.map +1 -0
- package/package.json +120 -0
- package/src/cli/deploy.ts +375 -0
- package/src/cli/index.ts +104 -0
- package/src/cli/init.ts +181 -0
- package/src/cli/next-build.ts +707 -0
- package/src/cli/setup.ts +190 -0
- package/src/cli/upload.ts +521 -0
- package/src/client/_generated/_ignore.ts +1 -0
- package/src/client/index.test.ts +67 -0
- package/src/client/index.ts +553 -0
- package/src/client/next.ts +223 -0
- package/src/client/nextAdapter.ts +17 -0
- package/src/client/setup.test.ts +26 -0
- package/src/component/_generated/api.ts +50 -0
- package/src/component/_generated/component.ts +104 -0
- package/src/component/_generated/dataModel.ts +60 -0
- package/src/component/_generated/server.ts +161 -0
- package/src/component/convex.config.ts +3 -0
- package/src/component/lib.test.ts +110 -0
- package/src/component/lib.ts +228 -0
- package/src/component/schema.ts +21 -0
- package/src/component/setup.test.ts +11 -0
- package/src/react/index.tsx +184 -0
- package/src/test.ts +18 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"convex.config.d.ts","sourceRoot":"","sources":["../../src/component/convex.config.ts"],"names":[],"mappings":";AAEA,wBAAgD"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"convex.config.js","sourceRoot":"","sources":["../../src/component/convex.config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAEhD,eAAe,eAAe,CAAC,eAAe,CAAC,CAAC"}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { v } from "convex/values";
|
|
2
|
+
/**
|
|
3
|
+
* Look up an asset by its URL path.
|
|
4
|
+
*/
|
|
5
|
+
export declare const getByPath: import("convex/server").RegisteredQuery<"public", {
|
|
6
|
+
path: string;
|
|
7
|
+
}, Promise<{
|
|
8
|
+
_id: import("convex/values").GenericId<"staticAssets">;
|
|
9
|
+
_creationTime: number;
|
|
10
|
+
blobId?: string | undefined;
|
|
11
|
+
storageId?: import("convex/values").GenericId<"_storage"> | undefined;
|
|
12
|
+
path: string;
|
|
13
|
+
contentType: string;
|
|
14
|
+
deploymentId: string;
|
|
15
|
+
} | null>>;
|
|
16
|
+
/**
|
|
17
|
+
* Generate a signed URL for uploading a file to Convex storage.
|
|
18
|
+
* Note: This is kept for backwards compatibility but the recommended approach
|
|
19
|
+
* is to use the app's storage directly via exposeUploadApi().
|
|
20
|
+
*/
|
|
21
|
+
export declare const generateUploadUrl: import("convex/server").RegisteredMutation<"public", {}, Promise<string>>;
|
|
22
|
+
/**
|
|
23
|
+
* Record an asset in the database after uploading to storage.
|
|
24
|
+
* If an asset already exists at this path, returns the old storageId for cleanup.
|
|
25
|
+
*
|
|
26
|
+
* Note: Storage files are stored in the app's storage, not the component's storage.
|
|
27
|
+
* The caller is responsible for deleting the returned storageId from app storage.
|
|
28
|
+
*/
|
|
29
|
+
export declare const recordAsset: import("convex/server").RegisteredMutation<"public", {
|
|
30
|
+
blobId?: string | undefined;
|
|
31
|
+
storageId?: import("convex/values").GenericId<"_storage"> | undefined;
|
|
32
|
+
path: string;
|
|
33
|
+
contentType: string;
|
|
34
|
+
deploymentId: string;
|
|
35
|
+
}, Promise<{
|
|
36
|
+
oldStorageId: import("convex/values").GenericId<"_storage"> | null;
|
|
37
|
+
oldBlobId: string | null;
|
|
38
|
+
}>>;
|
|
39
|
+
/**
|
|
40
|
+
* Garbage collect assets from old deployments.
|
|
41
|
+
* Returns the storageIds that need to be deleted from app storage.
|
|
42
|
+
*/
|
|
43
|
+
export declare const gcOldAssets: import("convex/server").RegisteredMutation<"public", {
|
|
44
|
+
currentDeploymentId: string;
|
|
45
|
+
}, Promise<{
|
|
46
|
+
storageIds: Array<ReturnType<typeof v.id<"_storage">>["type"]>;
|
|
47
|
+
blobIds: string[];
|
|
48
|
+
}>>;
|
|
49
|
+
/**
|
|
50
|
+
* List all assets (useful for debugging).
|
|
51
|
+
*/
|
|
52
|
+
export declare const listAssets: import("convex/server").RegisteredQuery<"public", {
|
|
53
|
+
limit?: number | undefined;
|
|
54
|
+
}, Promise<{
|
|
55
|
+
_id: import("convex/values").GenericId<"staticAssets">;
|
|
56
|
+
_creationTime: number;
|
|
57
|
+
blobId?: string | undefined;
|
|
58
|
+
storageId?: import("convex/values").GenericId<"_storage"> | undefined;
|
|
59
|
+
path: string;
|
|
60
|
+
contentType: string;
|
|
61
|
+
deploymentId: string;
|
|
62
|
+
}[]>>;
|
|
63
|
+
/**
|
|
64
|
+
* Delete all assets records (useful for cleanup).
|
|
65
|
+
* Returns storageIds that need to be deleted from app storage.
|
|
66
|
+
*/
|
|
67
|
+
export declare const deleteAllAssets: import("convex/server").RegisteredMutation<"internal", {}, Promise<{
|
|
68
|
+
storageIds: Array<ReturnType<typeof v.id<"_storage">>["type"]>;
|
|
69
|
+
blobIds: string[];
|
|
70
|
+
}>>;
|
|
71
|
+
/**
|
|
72
|
+
* Get the current deployment info.
|
|
73
|
+
* Clients subscribe to this to detect when a new deployment happens.
|
|
74
|
+
*/
|
|
75
|
+
export declare const getCurrentDeployment: import("convex/server").RegisteredQuery<"public", {}, Promise<{
|
|
76
|
+
_id: import("convex/values").GenericId<"deploymentInfo">;
|
|
77
|
+
_creationTime: number;
|
|
78
|
+
currentDeploymentId: string;
|
|
79
|
+
deployedAt: number;
|
|
80
|
+
} | null>>;
|
|
81
|
+
/**
|
|
82
|
+
* Update the current deployment ID.
|
|
83
|
+
* Called after a successful deployment to notify all connected clients.
|
|
84
|
+
*/
|
|
85
|
+
export declare const setCurrentDeployment: import("convex/server").RegisteredMutation<"public", {
|
|
86
|
+
deploymentId: string;
|
|
87
|
+
}, Promise<null>>;
|
|
88
|
+
//# sourceMappingURL=lib.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lib.d.ts","sourceRoot":"","sources":["../../src/component/lib.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,eAAe,CAAC;AAclC;;GAEG;AACH,eAAO,MAAM,SAAS;;;;;;;;;;UASpB,CAAC;AAEH;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,2EAM5B,CAAC;AAEH;;;;;;GAMG;AACH,eAAO,MAAM,WAAW;;;;;;;;;GAwCtB,CAAC;AAEH;;;GAGG;AACH,eAAO,MAAM,WAAW;;;gBA2BmB,KAAK,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;;GAI3F,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,UAAU;;;;;;;;;;KAWrB,CAAC;AAEH;;;GAGG;AACH,eAAO,MAAM,eAAe;gBAsBe,KAAK,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;;GAI3F,CAAC;AAaH;;;GAGG;AACH,eAAO,MAAM,oBAAoB;;;;;UAM/B,CAAC;AAEH;;;GAGG;AACH,eAAO,MAAM,oBAAoB;;iBAyB/B,CAAC"}
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import { v } from "convex/values";
|
|
2
|
+
import { mutation, query, internalMutation } from "./_generated/server.js";
|
|
3
|
+
// Validator for static asset documents (including system fields)
|
|
4
|
+
const staticAssetValidator = v.object({
|
|
5
|
+
_id: v.id("staticAssets"),
|
|
6
|
+
_creationTime: v.number(),
|
|
7
|
+
path: v.string(),
|
|
8
|
+
storageId: v.optional(v.id("_storage")),
|
|
9
|
+
blobId: v.optional(v.string()),
|
|
10
|
+
contentType: v.string(),
|
|
11
|
+
deploymentId: v.string(),
|
|
12
|
+
});
|
|
13
|
+
/**
|
|
14
|
+
* Look up an asset by its URL path.
|
|
15
|
+
*/
|
|
16
|
+
export const getByPath = query({
|
|
17
|
+
args: { path: v.string() },
|
|
18
|
+
returns: v.union(staticAssetValidator, v.null()),
|
|
19
|
+
handler: async (ctx, args) => {
|
|
20
|
+
return await ctx.db
|
|
21
|
+
.query("staticAssets")
|
|
22
|
+
.withIndex("by_path", (q) => q.eq("path", args.path))
|
|
23
|
+
.unique();
|
|
24
|
+
},
|
|
25
|
+
});
|
|
26
|
+
/**
|
|
27
|
+
* Generate a signed URL for uploading a file to Convex storage.
|
|
28
|
+
* Note: This is kept for backwards compatibility but the recommended approach
|
|
29
|
+
* is to use the app's storage directly via exposeUploadApi().
|
|
30
|
+
*/
|
|
31
|
+
export const generateUploadUrl = mutation({
|
|
32
|
+
args: {},
|
|
33
|
+
returns: v.string(),
|
|
34
|
+
handler: async (ctx) => {
|
|
35
|
+
return await ctx.storage.generateUploadUrl();
|
|
36
|
+
},
|
|
37
|
+
});
|
|
38
|
+
/**
|
|
39
|
+
* Record an asset in the database after uploading to storage.
|
|
40
|
+
* If an asset already exists at this path, returns the old storageId for cleanup.
|
|
41
|
+
*
|
|
42
|
+
* Note: Storage files are stored in the app's storage, not the component's storage.
|
|
43
|
+
* The caller is responsible for deleting the returned storageId from app storage.
|
|
44
|
+
*/
|
|
45
|
+
export const recordAsset = mutation({
|
|
46
|
+
args: {
|
|
47
|
+
path: v.string(),
|
|
48
|
+
storageId: v.optional(v.id("_storage")),
|
|
49
|
+
blobId: v.optional(v.string()),
|
|
50
|
+
contentType: v.string(),
|
|
51
|
+
deploymentId: v.string(),
|
|
52
|
+
},
|
|
53
|
+
returns: v.object({
|
|
54
|
+
oldStorageId: v.union(v.id("_storage"), v.null()),
|
|
55
|
+
oldBlobId: v.union(v.string(), v.null()),
|
|
56
|
+
}),
|
|
57
|
+
handler: async (ctx, args) => {
|
|
58
|
+
// Check if asset already exists at this path
|
|
59
|
+
const existing = await ctx.db
|
|
60
|
+
.query("staticAssets")
|
|
61
|
+
.withIndex("by_path", (q) => q.eq("path", args.path))
|
|
62
|
+
.unique();
|
|
63
|
+
let oldStorageId = null;
|
|
64
|
+
let oldBlobId = null;
|
|
65
|
+
if (existing) {
|
|
66
|
+
oldStorageId = existing.storageId ?? null;
|
|
67
|
+
oldBlobId = existing.blobId ?? null;
|
|
68
|
+
// Delete old record
|
|
69
|
+
await ctx.db.delete(existing._id);
|
|
70
|
+
}
|
|
71
|
+
// Insert new asset
|
|
72
|
+
await ctx.db.insert("staticAssets", {
|
|
73
|
+
path: args.path,
|
|
74
|
+
...(args.storageId ? { storageId: args.storageId } : {}),
|
|
75
|
+
...(args.blobId ? { blobId: args.blobId } : {}),
|
|
76
|
+
contentType: args.contentType,
|
|
77
|
+
deploymentId: args.deploymentId,
|
|
78
|
+
});
|
|
79
|
+
// Return old IDs so caller can clean up
|
|
80
|
+
return { oldStorageId, oldBlobId };
|
|
81
|
+
},
|
|
82
|
+
});
|
|
83
|
+
/**
|
|
84
|
+
* Garbage collect assets from old deployments.
|
|
85
|
+
* Returns the storageIds that need to be deleted from app storage.
|
|
86
|
+
*/
|
|
87
|
+
export const gcOldAssets = mutation({
|
|
88
|
+
args: {
|
|
89
|
+
currentDeploymentId: v.string(),
|
|
90
|
+
},
|
|
91
|
+
returns: v.object({
|
|
92
|
+
storageIds: v.array(v.id("_storage")),
|
|
93
|
+
blobIds: v.array(v.string()),
|
|
94
|
+
}),
|
|
95
|
+
handler: async (ctx, args) => {
|
|
96
|
+
const oldAssets = await ctx.db.query("staticAssets").collect();
|
|
97
|
+
const storageIds = [];
|
|
98
|
+
const blobIds = [];
|
|
99
|
+
for (const asset of oldAssets) {
|
|
100
|
+
if (asset.deploymentId !== args.currentDeploymentId) {
|
|
101
|
+
if (asset.storageId) {
|
|
102
|
+
storageIds.push(asset.storageId);
|
|
103
|
+
}
|
|
104
|
+
if (asset.blobId) {
|
|
105
|
+
blobIds.push(asset.blobId);
|
|
106
|
+
}
|
|
107
|
+
// Delete database record
|
|
108
|
+
await ctx.db.delete(asset._id);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return {
|
|
112
|
+
storageIds: storageIds,
|
|
113
|
+
blobIds,
|
|
114
|
+
};
|
|
115
|
+
},
|
|
116
|
+
});
|
|
117
|
+
/**
|
|
118
|
+
* List all assets (useful for debugging).
|
|
119
|
+
*/
|
|
120
|
+
export const listAssets = query({
|
|
121
|
+
args: {
|
|
122
|
+
limit: v.optional(v.number()),
|
|
123
|
+
},
|
|
124
|
+
returns: v.array(staticAssetValidator),
|
|
125
|
+
handler: async (ctx, args) => {
|
|
126
|
+
return await ctx.db
|
|
127
|
+
.query("staticAssets")
|
|
128
|
+
.order("asc")
|
|
129
|
+
.take(args.limit ?? 100);
|
|
130
|
+
},
|
|
131
|
+
});
|
|
132
|
+
/**
|
|
133
|
+
* Delete all assets records (useful for cleanup).
|
|
134
|
+
* Returns storageIds that need to be deleted from app storage.
|
|
135
|
+
*/
|
|
136
|
+
export const deleteAllAssets = internalMutation({
|
|
137
|
+
args: {},
|
|
138
|
+
returns: v.object({
|
|
139
|
+
storageIds: v.array(v.id("_storage")),
|
|
140
|
+
blobIds: v.array(v.string()),
|
|
141
|
+
}),
|
|
142
|
+
handler: async (ctx) => {
|
|
143
|
+
const assets = await ctx.db.query("staticAssets").collect();
|
|
144
|
+
const storageIds = [];
|
|
145
|
+
const blobIds = [];
|
|
146
|
+
for (const asset of assets) {
|
|
147
|
+
if (asset.storageId) {
|
|
148
|
+
storageIds.push(asset.storageId);
|
|
149
|
+
}
|
|
150
|
+
if (asset.blobId) {
|
|
151
|
+
blobIds.push(asset.blobId);
|
|
152
|
+
}
|
|
153
|
+
await ctx.db.delete(asset._id);
|
|
154
|
+
}
|
|
155
|
+
return {
|
|
156
|
+
storageIds: storageIds,
|
|
157
|
+
blobIds,
|
|
158
|
+
};
|
|
159
|
+
},
|
|
160
|
+
});
|
|
161
|
+
// ============================================================================
|
|
162
|
+
// Deployment Tracking - for live reload on deploy
|
|
163
|
+
// ============================================================================
|
|
164
|
+
const deploymentInfoValidator = v.object({
|
|
165
|
+
_id: v.id("deploymentInfo"),
|
|
166
|
+
_creationTime: v.number(),
|
|
167
|
+
currentDeploymentId: v.string(),
|
|
168
|
+
deployedAt: v.number(),
|
|
169
|
+
});
|
|
170
|
+
/**
|
|
171
|
+
* Get the current deployment info.
|
|
172
|
+
* Clients subscribe to this to detect when a new deployment happens.
|
|
173
|
+
*/
|
|
174
|
+
export const getCurrentDeployment = query({
|
|
175
|
+
args: {},
|
|
176
|
+
returns: v.union(deploymentInfoValidator, v.null()),
|
|
177
|
+
handler: async (ctx) => {
|
|
178
|
+
return await ctx.db.query("deploymentInfo").first();
|
|
179
|
+
},
|
|
180
|
+
});
|
|
181
|
+
/**
|
|
182
|
+
* Update the current deployment ID.
|
|
183
|
+
* Called after a successful deployment to notify all connected clients.
|
|
184
|
+
*/
|
|
185
|
+
export const setCurrentDeployment = mutation({
|
|
186
|
+
args: {
|
|
187
|
+
deploymentId: v.string(),
|
|
188
|
+
},
|
|
189
|
+
returns: v.null(),
|
|
190
|
+
handler: async (ctx, args) => {
|
|
191
|
+
// Get existing deployment info
|
|
192
|
+
const existing = await ctx.db.query("deploymentInfo").first();
|
|
193
|
+
if (existing) {
|
|
194
|
+
// Update existing record
|
|
195
|
+
await ctx.db.patch(existing._id, {
|
|
196
|
+
currentDeploymentId: args.deploymentId,
|
|
197
|
+
deployedAt: Date.now(),
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
else {
|
|
201
|
+
// Create new record
|
|
202
|
+
await ctx.db.insert("deploymentInfo", {
|
|
203
|
+
currentDeploymentId: args.deploymentId,
|
|
204
|
+
deployedAt: Date.now(),
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
return null;
|
|
208
|
+
},
|
|
209
|
+
});
|
|
210
|
+
//# sourceMappingURL=lib.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lib.js","sourceRoot":"","sources":["../../src/component/lib.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
declare const _default: import("convex/server").SchemaDefinition<{
|
|
2
|
+
staticAssets: import("convex/server").TableDefinition<import("convex/values").VObject<{
|
|
3
|
+
blobId?: string | undefined;
|
|
4
|
+
storageId?: import("convex/values").GenericId<"_storage"> | undefined;
|
|
5
|
+
path: string;
|
|
6
|
+
contentType: string;
|
|
7
|
+
deploymentId: string;
|
|
8
|
+
}, {
|
|
9
|
+
path: import("convex/values").VString<string, "required">;
|
|
10
|
+
storageId: import("convex/values").VId<import("convex/values").GenericId<"_storage"> | undefined, "optional">;
|
|
11
|
+
blobId: import("convex/values").VString<string | undefined, "optional">;
|
|
12
|
+
contentType: import("convex/values").VString<string, "required">;
|
|
13
|
+
deploymentId: import("convex/values").VString<string, "required">;
|
|
14
|
+
}, "required", "path" | "blobId" | "contentType" | "deploymentId" | "storageId">, {
|
|
15
|
+
by_path: ["path", "_creationTime"];
|
|
16
|
+
by_deploymentId: ["deploymentId", "_creationTime"];
|
|
17
|
+
}, {}, {}>;
|
|
18
|
+
deploymentInfo: import("convex/server").TableDefinition<import("convex/values").VObject<{
|
|
19
|
+
currentDeploymentId: string;
|
|
20
|
+
deployedAt: number;
|
|
21
|
+
}, {
|
|
22
|
+
currentDeploymentId: import("convex/values").VString<string, "required">;
|
|
23
|
+
deployedAt: import("convex/values").VFloat64<number, "required">;
|
|
24
|
+
}, "required", "currentDeploymentId" | "deployedAt">, {}, {}, {}>;
|
|
25
|
+
}, true>;
|
|
26
|
+
export default _default;
|
|
27
|
+
//# sourceMappingURL=schema.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../src/component/schema.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AAGA,wBAiBG"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { defineSchema, defineTable } from "convex/server";
|
|
2
|
+
import { v } from "convex/values";
|
|
3
|
+
export default defineSchema({
|
|
4
|
+
staticAssets: defineTable({
|
|
5
|
+
path: v.string(), // URL path, e.g., "/index.html", "/assets/main-abc123.js"
|
|
6
|
+
storageId: v.optional(v.id("_storage")), // Reference to Convex file storage (used for HTML + non-CDN assets)
|
|
7
|
+
blobId: v.optional(v.string()), // convex-fs blob ID (used for CDN-served assets)
|
|
8
|
+
contentType: v.string(), // MIME type, e.g., "text/html; charset=utf-8"
|
|
9
|
+
deploymentId: v.string(), // UUID for garbage collection
|
|
10
|
+
})
|
|
11
|
+
.index("by_path", ["path"])
|
|
12
|
+
.index("by_deploymentId", ["deploymentId"]),
|
|
13
|
+
// Singleton table to track the current deployment
|
|
14
|
+
// Clients subscribe to this to know when to reload
|
|
15
|
+
deploymentInfo: defineTable({
|
|
16
|
+
currentDeploymentId: v.string(),
|
|
17
|
+
deployedAt: v.number(), // timestamp
|
|
18
|
+
}),
|
|
19
|
+
});
|
|
20
|
+
//# sourceMappingURL=schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/component/schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC1D,OAAO,EAAE,CAAC,EAAE,MAAM,eAAe,CAAC;AAElC,eAAe,YAAY,CAAC;IAC1B,YAAY,EAAE,WAAW,CAAC;QACxB,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,0DAA0D;QAC5E,SAAS,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,oEAAoE;QAC7G,MAAM,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,EAAE,iDAAiD;QACjF,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,8CAA8C;QACvE,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,8BAA8B;KACzD,CAAC;SACC,KAAK,CAAC,SAAS,EAAE,CAAC,MAAM,CAAC,CAAC;SAC1B,KAAK,CAAC,iBAAiB,EAAE,CAAC,cAAc,CAAC,CAAC;IAE7C,kDAAkD;IAClD,mDAAmD;IACnD,cAAc,EAAE,WAAW,CAAC;QAC1B,mBAAmB,EAAE,CAAC,CAAC,MAAM,EAAE;QAC/B,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,YAAY;KACrC,CAAC;CACH,CAAC,CAAC"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { type CSSProperties, type JSX } from "react";
|
|
2
|
+
import type { FunctionReference } from "convex/server";
|
|
3
|
+
type DeploymentInfo = {
|
|
4
|
+
_id: string;
|
|
5
|
+
_creationTime: number;
|
|
6
|
+
currentDeploymentId: string;
|
|
7
|
+
deployedAt: number;
|
|
8
|
+
} | null;
|
|
9
|
+
/**
|
|
10
|
+
* Hook to detect when a new deployment is available.
|
|
11
|
+
* Shows a prompt to the user instead of auto-reloading.
|
|
12
|
+
*
|
|
13
|
+
* @param getCurrentDeployment - The query function reference from exposeDeploymentQuery
|
|
14
|
+
* @returns Object with update status and reload function
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```tsx
|
|
18
|
+
* import { useDeploymentUpdates } from "@convex-dev/static-hosting/react";
|
|
19
|
+
* import { api } from "../convex/_generated/api";
|
|
20
|
+
*
|
|
21
|
+
* function App() {
|
|
22
|
+
* const { updateAvailable, reload } = useDeploymentUpdates(
|
|
23
|
+
* api.staticHosting.getCurrentDeployment
|
|
24
|
+
* );
|
|
25
|
+
*
|
|
26
|
+
* return (
|
|
27
|
+
* <div>
|
|
28
|
+
* {updateAvailable && (
|
|
29
|
+
* <div className="update-banner">
|
|
30
|
+
* A new version is available!
|
|
31
|
+
* <button onClick={reload}>Reload</button>
|
|
32
|
+
* </div>
|
|
33
|
+
* )}
|
|
34
|
+
* {/* rest of your app *\/}
|
|
35
|
+
* </div>
|
|
36
|
+
* );
|
|
37
|
+
* }
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
export declare function useDeploymentUpdates(getCurrentDeployment: FunctionReference<"query", "public", Record<string, never>, DeploymentInfo>): {
|
|
41
|
+
/** True when a new deployment is available */
|
|
42
|
+
updateAvailable: boolean;
|
|
43
|
+
/** Reload the page to get the new version */
|
|
44
|
+
reload: () => void;
|
|
45
|
+
/** Dismiss the update notification (until next deploy) */
|
|
46
|
+
dismiss: () => void;
|
|
47
|
+
/** The current deployment info (or null if not yet loaded) */
|
|
48
|
+
deployment: DeploymentInfo | undefined;
|
|
49
|
+
};
|
|
50
|
+
/**
|
|
51
|
+
* A ready-to-use update banner component.
|
|
52
|
+
* Displays a notification when a new deployment is available.
|
|
53
|
+
*
|
|
54
|
+
* @example
|
|
55
|
+
* ```tsx
|
|
56
|
+
* import { UpdateBanner } from "@convex-dev/static-hosting/react";
|
|
57
|
+
* import { api } from "../convex/_generated/api";
|
|
58
|
+
*
|
|
59
|
+
* function App() {
|
|
60
|
+
* return (
|
|
61
|
+
* <div>
|
|
62
|
+
* <UpdateBanner
|
|
63
|
+
* getCurrentDeployment={api.staticHosting.getCurrentDeployment}
|
|
64
|
+
* />
|
|
65
|
+
* {/* rest of your app *\/}
|
|
66
|
+
* </div>
|
|
67
|
+
* );
|
|
68
|
+
* }
|
|
69
|
+
* ```
|
|
70
|
+
*/
|
|
71
|
+
export declare function UpdateBanner({ getCurrentDeployment, message, buttonText, dismissable, className, style, }: {
|
|
72
|
+
getCurrentDeployment: FunctionReference<"query", "public", Record<string, never>, DeploymentInfo>;
|
|
73
|
+
message?: string;
|
|
74
|
+
buttonText?: string;
|
|
75
|
+
dismissable?: boolean;
|
|
76
|
+
className?: string;
|
|
77
|
+
style?: CSSProperties;
|
|
78
|
+
}): JSX.Element | null;
|
|
79
|
+
export {};
|
|
80
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/react/index.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAqB,KAAK,aAAa,EAAE,KAAK,GAAG,EAAE,MAAM,OAAO,CAAC;AACxE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAEvD,KAAK,cAAc,GAAG;IACpB,GAAG,EAAE,MAAM,CAAC;IACZ,aAAa,EAAE,MAAM,CAAC;IACtB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,UAAU,EAAE,MAAM,CAAC;CACpB,GAAG,IAAI,CAAC;AAET;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,wBAAgB,oBAAoB,CAClC,oBAAoB,EAAE,iBAAiB,CAAC,OAAO,EAAE,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,cAAc,CAAC;IAoC/F,8CAA8C;;IAE9C,6CAA6C;;IAE7C,0DAA0D;;IAE1D,8DAA8D;;EAGjE;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,YAAY,CAAC,EAC3B,oBAAoB,EACpB,OAAuC,EACvC,UAAqB,EACrB,WAAkB,EAClB,SAAS,EACT,KAAK,GACN,EAAE;IACD,oBAAoB,EAAE,iBAAiB,CAAC,OAAO,EAAE,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,cAAc,CAAC,CAAC;IAClG,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,aAAa,CAAC;CACvB,GAAG,GAAG,CAAC,OAAO,GAAG,IAAI,CAwDrB"}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useQuery } from "convex/react";
|
|
4
|
+
import { useState, useMemo } from "react";
|
|
5
|
+
/**
|
|
6
|
+
* Hook to detect when a new deployment is available.
|
|
7
|
+
* Shows a prompt to the user instead of auto-reloading.
|
|
8
|
+
*
|
|
9
|
+
* @param getCurrentDeployment - The query function reference from exposeDeploymentQuery
|
|
10
|
+
* @returns Object with update status and reload function
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```tsx
|
|
14
|
+
* import { useDeploymentUpdates } from "@convex-dev/static-hosting/react";
|
|
15
|
+
* import { api } from "../convex/_generated/api";
|
|
16
|
+
*
|
|
17
|
+
* function App() {
|
|
18
|
+
* const { updateAvailable, reload } = useDeploymentUpdates(
|
|
19
|
+
* api.staticHosting.getCurrentDeployment
|
|
20
|
+
* );
|
|
21
|
+
*
|
|
22
|
+
* return (
|
|
23
|
+
* <div>
|
|
24
|
+
* {updateAvailable && (
|
|
25
|
+
* <div className="update-banner">
|
|
26
|
+
* A new version is available!
|
|
27
|
+
* <button onClick={reload}>Reload</button>
|
|
28
|
+
* </div>
|
|
29
|
+
* )}
|
|
30
|
+
* {/* rest of your app *\/}
|
|
31
|
+
* </div>
|
|
32
|
+
* );
|
|
33
|
+
* }
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
export function useDeploymentUpdates(getCurrentDeployment) {
|
|
37
|
+
const deployment = useQuery(getCurrentDeployment, {});
|
|
38
|
+
const [initialDeploymentId, setInitialDeploymentId] = useState(null);
|
|
39
|
+
const [dismissedDeploymentId, setDismissedDeploymentId] = useState(null);
|
|
40
|
+
// Capture the initial deployment ID on first load
|
|
41
|
+
// Using useState with functional update to avoid stale closure issues
|
|
42
|
+
if (deployment && initialDeploymentId === null) {
|
|
43
|
+
// This is safe - we're setting initial state based on first data load
|
|
44
|
+
// It only runs once when deployment first becomes available
|
|
45
|
+
setInitialDeploymentId(deployment.currentDeploymentId);
|
|
46
|
+
}
|
|
47
|
+
// Derive updateAvailable from current state
|
|
48
|
+
const updateAvailable = useMemo(() => {
|
|
49
|
+
if (!deployment || initialDeploymentId === null) {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
// Show update if deployment changed from initial AND user hasn't dismissed this one
|
|
53
|
+
const hasNewDeployment = deployment.currentDeploymentId !== initialDeploymentId;
|
|
54
|
+
const isDismissed = deployment.currentDeploymentId === dismissedDeploymentId;
|
|
55
|
+
return hasNewDeployment && !isDismissed;
|
|
56
|
+
}, [deployment, initialDeploymentId, dismissedDeploymentId]);
|
|
57
|
+
const reload = () => {
|
|
58
|
+
window.location.reload();
|
|
59
|
+
};
|
|
60
|
+
const dismiss = () => {
|
|
61
|
+
if (deployment) {
|
|
62
|
+
setDismissedDeploymentId(deployment.currentDeploymentId);
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
return {
|
|
66
|
+
/** True when a new deployment is available */
|
|
67
|
+
updateAvailable,
|
|
68
|
+
/** Reload the page to get the new version */
|
|
69
|
+
reload,
|
|
70
|
+
/** Dismiss the update notification (until next deploy) */
|
|
71
|
+
dismiss,
|
|
72
|
+
/** The current deployment info (or null if not yet loaded) */
|
|
73
|
+
deployment,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* A ready-to-use update banner component.
|
|
78
|
+
* Displays a notification when a new deployment is available.
|
|
79
|
+
*
|
|
80
|
+
* @example
|
|
81
|
+
* ```tsx
|
|
82
|
+
* import { UpdateBanner } from "@convex-dev/static-hosting/react";
|
|
83
|
+
* import { api } from "../convex/_generated/api";
|
|
84
|
+
*
|
|
85
|
+
* function App() {
|
|
86
|
+
* return (
|
|
87
|
+
* <div>
|
|
88
|
+
* <UpdateBanner
|
|
89
|
+
* getCurrentDeployment={api.staticHosting.getCurrentDeployment}
|
|
90
|
+
* />
|
|
91
|
+
* {/* rest of your app *\/}
|
|
92
|
+
* </div>
|
|
93
|
+
* );
|
|
94
|
+
* }
|
|
95
|
+
* ```
|
|
96
|
+
*/
|
|
97
|
+
export function UpdateBanner({ getCurrentDeployment, message = "A new version is available!", buttonText = "Reload", dismissable = true, className, style, }) {
|
|
98
|
+
const { updateAvailable, reload, dismiss } = useDeploymentUpdates(getCurrentDeployment);
|
|
99
|
+
if (!updateAvailable)
|
|
100
|
+
return null;
|
|
101
|
+
const defaultStyle = {
|
|
102
|
+
position: "fixed",
|
|
103
|
+
bottom: "1rem",
|
|
104
|
+
right: "1rem",
|
|
105
|
+
backgroundColor: "#1a1a2e",
|
|
106
|
+
color: "#fff",
|
|
107
|
+
padding: "1rem 1.5rem",
|
|
108
|
+
borderRadius: "8px",
|
|
109
|
+
boxShadow: "0 4px 12px rgba(0, 0, 0, 0.3)",
|
|
110
|
+
display: "flex",
|
|
111
|
+
alignItems: "center",
|
|
112
|
+
gap: "1rem",
|
|
113
|
+
zIndex: 9999,
|
|
114
|
+
fontFamily: "system-ui, -apple-system, sans-serif",
|
|
115
|
+
fontSize: "14px",
|
|
116
|
+
...style,
|
|
117
|
+
};
|
|
118
|
+
const buttonStyle = {
|
|
119
|
+
backgroundColor: "#4f46e5",
|
|
120
|
+
color: "#fff",
|
|
121
|
+
border: "none",
|
|
122
|
+
padding: "0.5rem 1rem",
|
|
123
|
+
borderRadius: "4px",
|
|
124
|
+
cursor: "pointer",
|
|
125
|
+
fontWeight: 500,
|
|
126
|
+
};
|
|
127
|
+
const dismissStyle = {
|
|
128
|
+
background: "none",
|
|
129
|
+
border: "none",
|
|
130
|
+
color: "#888",
|
|
131
|
+
cursor: "pointer",
|
|
132
|
+
padding: "0.25rem",
|
|
133
|
+
fontSize: "18px",
|
|
134
|
+
lineHeight: 1,
|
|
135
|
+
};
|
|
136
|
+
return (_jsxs("div", { className: className, style: defaultStyle, children: [_jsx("span", { children: message }), _jsx("button", { onClick: reload, style: buttonStyle, children: buttonText }), dismissable && (_jsx("button", { onClick: dismiss, style: dismissStyle, "aria-label": "Dismiss", children: "\u00D7" }))] }));
|
|
137
|
+
}
|
|
138
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/react/index.tsx"],"names":[],"mappings":"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"}
|