@convex-dev/table-history 0.1.3
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 +231 -0
- package/dist/esm/client/index.d.ts +116 -0
- package/dist/esm/client/index.d.ts.map +1 -0
- package/dist/esm/client/index.js +93 -0
- package/dist/esm/client/index.js.map +1 -0
- package/dist/esm/component/_generated/api.d.ts +34 -0
- package/dist/esm/component/_generated/api.d.ts.map +1 -0
- package/dist/esm/component/_generated/api.js +31 -0
- package/dist/esm/component/_generated/api.js.map +1 -0
- package/dist/esm/component/_generated/component.d.ts +105 -0
- package/dist/esm/component/_generated/component.d.ts.map +1 -0
- package/dist/esm/component/_generated/component.js +11 -0
- package/dist/esm/component/_generated/component.js.map +1 -0
- package/dist/esm/component/_generated/dataModel.d.ts +46 -0
- package/dist/esm/component/_generated/dataModel.d.ts.map +1 -0
- package/dist/esm/component/_generated/dataModel.js +11 -0
- package/dist/esm/component/_generated/dataModel.js.map +1 -0
- package/dist/esm/component/_generated/server.d.ts +121 -0
- package/dist/esm/component/_generated/server.d.ts.map +1 -0
- package/dist/esm/component/_generated/server.js +78 -0
- package/dist/esm/component/_generated/server.js.map +1 -0
- package/dist/esm/component/convex.config.d.ts +3 -0
- package/dist/esm/component/convex.config.d.ts.map +1 -0
- package/dist/esm/component/convex.config.js +3 -0
- package/dist/esm/component/convex.config.js.map +1 -0
- package/dist/esm/component/lib.d.ts +137 -0
- package/dist/esm/component/lib.d.ts.map +1 -0
- package/dist/esm/component/lib.js +316 -0
- package/dist/esm/component/lib.js.map +1 -0
- package/dist/esm/component/schema.d.ts +25 -0
- package/dist/esm/component/schema.d.ts.map +1 -0
- package/dist/esm/component/schema.js +17 -0
- package/dist/esm/component/schema.js.map +1 -0
- package/dist/esm/react/index.d.ts +2 -0
- package/dist/esm/react/index.d.ts.map +1 -0
- package/dist/esm/react/index.js +8 -0
- package/dist/esm/react/index.js.map +1 -0
- package/package.json +84 -0
- package/src/client/index.ts +174 -0
- package/src/component/_generated/api.ts +50 -0
- package/src/component/_generated/component.ts +136 -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 +285 -0
- package/src/component/lib.ts +337 -0
- package/src/component/schema.ts +17 -0
- package/src/react/index.ts +8 -0
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
import { v } from "convex/values";
|
|
2
|
+
import { internalMutation, mutation, query } from "./_generated/server";
|
|
3
|
+
import { paginator } from "convex-helpers/server/pagination";
|
|
4
|
+
import schema from "./schema.js";
|
|
5
|
+
import { paginationOptsValidator } from "convex/server";
|
|
6
|
+
import { internal } from "./_generated/api";
|
|
7
|
+
export const serializabilityValidator = v.union(
|
|
8
|
+
/// "table" serializability means all writes to the table are serialized,
|
|
9
|
+
/// so the timestamps are in causal order. This gives the strictest guarantees
|
|
10
|
+
/// but can cause OCC conflicts if the table updates frequently.
|
|
11
|
+
/// Writes to different tables are not serialized.
|
|
12
|
+
v.literal("table"),
|
|
13
|
+
/// "document" serializability means all writes to the same document are serialized,
|
|
14
|
+
/// but writes to different documents may have out-of-order timestamps.
|
|
15
|
+
v.literal("document"),
|
|
16
|
+
/// "wallclock" serializability means the timestamp is set to the current time
|
|
17
|
+
/// according to the server's clock. This provides no guarantees, but it's
|
|
18
|
+
/// usually in causal order and causes no OCC conflicts.
|
|
19
|
+
/// Wallclock serializability is the default.
|
|
20
|
+
v.literal("wallclock"));
|
|
21
|
+
export const historyEntryValidator = v.object({
|
|
22
|
+
id: v.string(),
|
|
23
|
+
doc: v.any(),
|
|
24
|
+
ts: v.number(),
|
|
25
|
+
isDeleted: v.boolean(),
|
|
26
|
+
attribution: v.any(),
|
|
27
|
+
});
|
|
28
|
+
async function newTimestamp(ctx, serializability, id) {
|
|
29
|
+
switch (serializability) {
|
|
30
|
+
case "table": {
|
|
31
|
+
const latest = await ctx.db.query("history").withIndex("ts").order("desc").first();
|
|
32
|
+
if (latest) {
|
|
33
|
+
return Math.max(latest.ts + 1, Date.now());
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
return Date.now();
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
case "document": {
|
|
40
|
+
const latest = await ctx.db.query("history").withIndex("id", (q) => q.eq("id", id)).order("desc").first();
|
|
41
|
+
if (latest) {
|
|
42
|
+
return Math.max(latest.ts + 1, Date.now());
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
return Date.now();
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
case "wallclock": {
|
|
49
|
+
return Date.now();
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
export const update = mutation({
|
|
54
|
+
args: {
|
|
55
|
+
id: v.string(),
|
|
56
|
+
doc: v.union(v.any(), v.null()),
|
|
57
|
+
serializability: serializabilityValidator,
|
|
58
|
+
attribution: v.any(),
|
|
59
|
+
},
|
|
60
|
+
returns: v.number(),
|
|
61
|
+
handler: async (ctx, args) => {
|
|
62
|
+
let ts = await newTimestamp(ctx, args.serializability, args.id);
|
|
63
|
+
const existing = await ctx.db.query("history").withIndex("id", (q) => q.eq("id", args.id).eq("ts", ts)).first();
|
|
64
|
+
if (existing) {
|
|
65
|
+
ts = existing.ts + 1;
|
|
66
|
+
}
|
|
67
|
+
await ctx.db.insert("history", {
|
|
68
|
+
id: args.id,
|
|
69
|
+
doc: args.doc,
|
|
70
|
+
ts,
|
|
71
|
+
isDeleted: args.doc === null,
|
|
72
|
+
attribution: args.attribution,
|
|
73
|
+
});
|
|
74
|
+
return ts;
|
|
75
|
+
},
|
|
76
|
+
});
|
|
77
|
+
function paginationResultValidator(itemValidator) {
|
|
78
|
+
return v.object({
|
|
79
|
+
continueCursor: v.string(),
|
|
80
|
+
isDone: v.boolean(),
|
|
81
|
+
page: v.array(itemValidator),
|
|
82
|
+
pageStatus: v.optional(v.union(v.null(), v.literal("SplitRequired"), v.literal("SplitRecommended"))),
|
|
83
|
+
splitCursor: v.optional(v.union(v.null(), v.string())),
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
export const listHistory = query({
|
|
87
|
+
args: {
|
|
88
|
+
maxTs: v.number(),
|
|
89
|
+
paginationOpts: paginationOptsValidator,
|
|
90
|
+
},
|
|
91
|
+
returns: paginationResultValidator(historyEntryValidator),
|
|
92
|
+
handler: async (ctx, args) => {
|
|
93
|
+
const results = await paginator(ctx.db, schema)
|
|
94
|
+
.query("history")
|
|
95
|
+
.withIndex("ts", (q) => q.lte("ts", args.maxTs))
|
|
96
|
+
.order("desc")
|
|
97
|
+
.paginate(args.paginationOpts);
|
|
98
|
+
return {
|
|
99
|
+
...results,
|
|
100
|
+
page: results.page.map(extractHistoryEntry),
|
|
101
|
+
};
|
|
102
|
+
},
|
|
103
|
+
});
|
|
104
|
+
function extractHistoryEntry(h) {
|
|
105
|
+
return {
|
|
106
|
+
id: h.id,
|
|
107
|
+
doc: h.doc,
|
|
108
|
+
ts: h.ts,
|
|
109
|
+
isDeleted: h.isDeleted,
|
|
110
|
+
attribution: h.attribution,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
export const listDocumentHistory = query({
|
|
114
|
+
args: {
|
|
115
|
+
id: v.string(),
|
|
116
|
+
maxTs: v.number(),
|
|
117
|
+
paginationOpts: paginationOptsValidator,
|
|
118
|
+
},
|
|
119
|
+
returns: paginationResultValidator(historyEntryValidator),
|
|
120
|
+
handler: async (ctx, args) => {
|
|
121
|
+
const results = await paginator(ctx.db, schema)
|
|
122
|
+
.query("history")
|
|
123
|
+
.withIndex("id", (q) => q.eq("id", args.id).lte("ts", args.maxTs))
|
|
124
|
+
.order("desc")
|
|
125
|
+
.paginate(args.paginationOpts);
|
|
126
|
+
return {
|
|
127
|
+
...results,
|
|
128
|
+
page: results.page.map(extractHistoryEntry),
|
|
129
|
+
};
|
|
130
|
+
},
|
|
131
|
+
});
|
|
132
|
+
// Sentinel value for end of cursor.
|
|
133
|
+
const END_CURSOR = "END_CURSOR";
|
|
134
|
+
export const listSnapshot = query({
|
|
135
|
+
args: {
|
|
136
|
+
snapshotTs: v.number(),
|
|
137
|
+
currentTs: v.number(),
|
|
138
|
+
paginationOpts: paginationOptsValidator,
|
|
139
|
+
},
|
|
140
|
+
returns: paginationResultValidator(historyEntryValidator),
|
|
141
|
+
handler: async (ctx, args) => {
|
|
142
|
+
const pageSize = args.paginationOpts.numItems;
|
|
143
|
+
const page = [];
|
|
144
|
+
if (args.paginationOpts.cursor === END_CURSOR) {
|
|
145
|
+
return {
|
|
146
|
+
continueCursor: END_CURSOR,
|
|
147
|
+
isDone: true,
|
|
148
|
+
page: [],
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
if (pageSize <= 0) {
|
|
152
|
+
throw new Error("pageSize must be positive");
|
|
153
|
+
}
|
|
154
|
+
if (args.currentTs < args.snapshotTs) {
|
|
155
|
+
throw new Error("currentTs must be >= snapshotTs");
|
|
156
|
+
}
|
|
157
|
+
const vacuumed = await ctx.db.query("vacuumed").first();
|
|
158
|
+
if (vacuumed && vacuumed.minTsToKeep > args.snapshotTs) {
|
|
159
|
+
throw new Error("invalid snapshotTs, snapshot has been vacuumed");
|
|
160
|
+
}
|
|
161
|
+
const targetEndCursor = args.paginationOpts.endCursor ?? null;
|
|
162
|
+
let prevId = args.paginationOpts.cursor;
|
|
163
|
+
const allIdsSeen = [];
|
|
164
|
+
const allIdsBeforeCurrentTs = [];
|
|
165
|
+
while (allIdsBeforeCurrentTs.length < pageSize || targetEndCursor !== null) {
|
|
166
|
+
const itemWithNextId = await ctx.db.query("history").withIndex("id", (q) => prevId !== null ? q.lt("id", prevId) : q).order("desc").first();
|
|
167
|
+
if (itemWithNextId === null) {
|
|
168
|
+
return {
|
|
169
|
+
continueCursor: END_CURSOR,
|
|
170
|
+
isDone: true,
|
|
171
|
+
page,
|
|
172
|
+
...maybeSplit(allIdsSeen, pageSize),
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
allIdsSeen.push(itemWithNextId.id);
|
|
176
|
+
prevId = itemWithNextId.id;
|
|
177
|
+
if (targetEndCursor !== null && targetEndCursor !== END_CURSOR && itemWithNextId.id < targetEndCursor) {
|
|
178
|
+
// We've reached the end of the page.
|
|
179
|
+
return {
|
|
180
|
+
continueCursor: targetEndCursor,
|
|
181
|
+
isDone: targetEndCursor === END_CURSOR,
|
|
182
|
+
page,
|
|
183
|
+
...maybeSplit(allIdsSeen, pageSize),
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
let revision = itemWithNextId;
|
|
187
|
+
if (itemWithNextId.ts > args.snapshotTs) {
|
|
188
|
+
// Find the revision as it existed at args.ts
|
|
189
|
+
const itemAtSnapshotTs = await ctx.db.query("history").withIndex("id", (q) => q.eq("id", itemWithNextId.id).lte("ts", args.snapshotTs)).order("desc").first();
|
|
190
|
+
if (itemAtSnapshotTs === null) {
|
|
191
|
+
// The item doesn't exist in the snapshotTs snapshot.
|
|
192
|
+
// Check if it exists as of currentTs
|
|
193
|
+
const itemAtCurrentTs = await ctx.db.query("history").withIndex("id", (q) => q.eq("id", itemWithNextId.id).lte("ts", args.currentTs)).order("desc").first();
|
|
194
|
+
if (itemAtCurrentTs === null) {
|
|
195
|
+
// It was created after currentTs, so we should treat it like it doesn't exist.
|
|
196
|
+
// prevId has advanced, but it never gets returned.
|
|
197
|
+
continue;
|
|
198
|
+
}
|
|
199
|
+
else {
|
|
200
|
+
// It was created between snapshotTs and currentTs, so it counts toward the limit and can be in the cursor, but it's not in the page.
|
|
201
|
+
revision = null;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
else {
|
|
205
|
+
revision = itemAtSnapshotTs;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
if (revision && revision.isDeleted) {
|
|
209
|
+
// If it's deleted, we don't want to include it in the page, but it counts toward the limit and can be in the cursor.
|
|
210
|
+
revision = null;
|
|
211
|
+
}
|
|
212
|
+
allIdsBeforeCurrentTs.push(itemWithNextId.id);
|
|
213
|
+
if (revision) {
|
|
214
|
+
page.push(extractHistoryEntry(revision));
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
const output = {
|
|
218
|
+
continueCursor: allIdsBeforeCurrentTs[allIdsBeforeCurrentTs.length - 1],
|
|
219
|
+
isDone: false,
|
|
220
|
+
page,
|
|
221
|
+
...maybeSplit(allIdsSeen, pageSize),
|
|
222
|
+
};
|
|
223
|
+
return output;
|
|
224
|
+
},
|
|
225
|
+
});
|
|
226
|
+
function maybeSplit(allIdsSeen, pageSize) {
|
|
227
|
+
if (allIdsSeen.length >= pageSize * 2) {
|
|
228
|
+
return {
|
|
229
|
+
splitCursor: allIdsSeen[pageSize - 1],
|
|
230
|
+
pageStatus: "SplitRecommended",
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
return {};
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Deletes history of state that was gone (overwritten or deleted) before
|
|
237
|
+
* minTsToKeep.
|
|
238
|
+
*
|
|
239
|
+
* After `vacuumHistory` is called, `listSnapshot` with `ts` before `minTsToKeep` will not
|
|
240
|
+
* necessarily be correct.
|
|
241
|
+
*
|
|
242
|
+
* This mutation does not delete history atomically. It may take a while with
|
|
243
|
+
* async operations.
|
|
244
|
+
*
|
|
245
|
+
* NOTE: `usePaginatedQuery` on `listSnapshot` may yield pages that have gaps or
|
|
246
|
+
* overlap if a reactive query is subscribed when `vacuumHistory` runs.
|
|
247
|
+
*/
|
|
248
|
+
export const vacuumHistory = mutation({
|
|
249
|
+
args: {
|
|
250
|
+
minTsToKeep: v.number(),
|
|
251
|
+
},
|
|
252
|
+
handler: async (ctx, args) => {
|
|
253
|
+
// Ensure that no one relies on vacuuming running immediately by waiting
|
|
254
|
+
// 100ms.
|
|
255
|
+
// This also avoids race conditions where `args.minTsToKeep` is so recent
|
|
256
|
+
// that new entries with earlier timestamps are still being added to the
|
|
257
|
+
// history table.
|
|
258
|
+
await ctx.scheduler.runAfter(100, internal.lib.vacuumHistoryRecursive, {
|
|
259
|
+
minTsToKeep: args.minTsToKeep,
|
|
260
|
+
paginationOpts: {
|
|
261
|
+
numItems: 100,
|
|
262
|
+
cursor: null,
|
|
263
|
+
},
|
|
264
|
+
});
|
|
265
|
+
},
|
|
266
|
+
});
|
|
267
|
+
export const vacuumHistoryRecursive = internalMutation({
|
|
268
|
+
args: {
|
|
269
|
+
minTsToKeep: v.number(),
|
|
270
|
+
paginationOpts: paginationOptsValidator,
|
|
271
|
+
},
|
|
272
|
+
handler: async (ctx, args) => {
|
|
273
|
+
const vacuumed = await ctx.db.query("vacuumed").first();
|
|
274
|
+
const startTs = vacuumed?.minTsToKeep ?? 0;
|
|
275
|
+
if (startTs >= args.minTsToKeep) {
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
const toDelete = await paginator(ctx.db, schema)
|
|
279
|
+
.query("history")
|
|
280
|
+
.withIndex("ts", (q) => q.gt("ts", startTs).lte("ts", args.minTsToKeep))
|
|
281
|
+
.order("asc")
|
|
282
|
+
.paginate(args.paginationOpts);
|
|
283
|
+
let maxTs = startTs;
|
|
284
|
+
for (const h of toDelete.page) {
|
|
285
|
+
const prevRev = await ctx.db.query("history").withIndex("id", (q) => q.eq("id", h.id).lt("ts", h.ts)).order("desc").first();
|
|
286
|
+
if (prevRev !== null) {
|
|
287
|
+
await ctx.db.delete(prevRev._id);
|
|
288
|
+
}
|
|
289
|
+
if (h.isDeleted) {
|
|
290
|
+
await ctx.db.delete(h._id);
|
|
291
|
+
}
|
|
292
|
+
maxTs = Math.max(maxTs, h.ts);
|
|
293
|
+
}
|
|
294
|
+
if (vacuumed === null) {
|
|
295
|
+
await ctx.db.insert("vacuumed", {
|
|
296
|
+
minTsToKeep: maxTs,
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
else {
|
|
300
|
+
await ctx.db.patch(vacuumed._id, {
|
|
301
|
+
minTsToKeep: maxTs,
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
if (toDelete.isDone) {
|
|
305
|
+
return;
|
|
306
|
+
}
|
|
307
|
+
await ctx.scheduler.runAfter(0, internal.lib.vacuumHistoryRecursive, {
|
|
308
|
+
minTsToKeep: args.minTsToKeep,
|
|
309
|
+
paginationOpts: {
|
|
310
|
+
numItems: args.paginationOpts.numItems,
|
|
311
|
+
cursor: toDelete.continueCursor,
|
|
312
|
+
}
|
|
313
|
+
});
|
|
314
|
+
},
|
|
315
|
+
});
|
|
316
|
+
//# 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,25 @@
|
|
|
1
|
+
declare const _default: import("convex/server").SchemaDefinition<{
|
|
2
|
+
history: import("convex/server").TableDefinition<import("convex/values").VObject<{
|
|
3
|
+
id: string;
|
|
4
|
+
ts: number;
|
|
5
|
+
doc: any;
|
|
6
|
+
isDeleted: boolean;
|
|
7
|
+
attribution: any;
|
|
8
|
+
}, {
|
|
9
|
+
id: import("convex/values").VString<string, "required">;
|
|
10
|
+
ts: import("convex/values").VFloat64<number, "required">;
|
|
11
|
+
doc: import("convex/values").VUnion<any, [import("convex/values").VAny<any, "required", string>, import("convex/values").VNull<null, "required">], "required", string>;
|
|
12
|
+
isDeleted: import("convex/values").VBoolean<boolean, "required">;
|
|
13
|
+
attribution: import("convex/values").VAny<any, "required", string>;
|
|
14
|
+
}, "required", "id" | "ts" | "doc" | "isDeleted" | "attribution" | `doc.${string}` | `attribution.${string}`>, {
|
|
15
|
+
ts: ["ts", "_creationTime"];
|
|
16
|
+
id: ["id", "ts", "_creationTime"];
|
|
17
|
+
}, {}, {}>;
|
|
18
|
+
vacuumed: import("convex/server").TableDefinition<import("convex/values").VObject<{
|
|
19
|
+
minTsToKeep: number;
|
|
20
|
+
}, {
|
|
21
|
+
minTsToKeep: import("convex/values").VFloat64<number, "required">;
|
|
22
|
+
}, "required", "minTsToKeep">, {}, {}, {}>;
|
|
23
|
+
}, true>;
|
|
24
|
+
export default _default;
|
|
25
|
+
//# sourceMappingURL=schema.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../../src/component/schema.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAGA,wBAaG"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { defineSchema, defineTable } from "convex/server";
|
|
2
|
+
import { v } from "convex/values";
|
|
3
|
+
export default defineSchema({
|
|
4
|
+
history: defineTable({
|
|
5
|
+
id: v.string(),
|
|
6
|
+
ts: v.number(),
|
|
7
|
+
doc: v.union(v.any(), v.null()),
|
|
8
|
+
isDeleted: v.boolean(),
|
|
9
|
+
attribution: v.any(),
|
|
10
|
+
})
|
|
11
|
+
.index("ts", ["ts"])
|
|
12
|
+
.index("id", ["id", "ts"]),
|
|
13
|
+
vacuumed: defineTable({
|
|
14
|
+
minTsToKeep: v.number(),
|
|
15
|
+
}),
|
|
16
|
+
});
|
|
17
|
+
//# 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,OAAO,EAAE,WAAW,CAAC;QACnB,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE;QACd,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE;QACd,GAAG,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;QAC/B,SAAS,EAAE,CAAC,CAAC,OAAO,EAAE;QACtB,WAAW,EAAE,CAAC,CAAC,GAAG,EAAE;KACrB,CAAC;SACC,KAAK,CAAC,IAAI,EAAE,CAAC,IAAI,CAAC,CAAC;SACnB,KAAK,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IAC5B,QAAQ,EAAE,WAAW,CAAC;QACpB,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE;KACxB,CAAC;CACH,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/react/index.ts"],"names":[],"mappings":"AAKA,wBAAgB,QAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAErD"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/react/index.ts"],"names":[],"mappings":"AAAA,qCAAqC;AACrC,IAAI,OAAO,MAAM,KAAK,WAAW,EAAE,CAAC;IAClC,MAAM,IAAI,KAAK,CAAC,yDAAyD,CAAC,CAAC;AAC7E,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,CAAS,EAAE,CAAS;IAC3C,OAAO,CAAC,GAAG,CAAC,CAAC;AACf,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@convex-dev/table-history",
|
|
3
|
+
"description": "A table history component for Convex.",
|
|
4
|
+
"repository": "github:get-convex/table-history",
|
|
5
|
+
"homepage": "https://github.com/get-convex/table-history#readme",
|
|
6
|
+
"bugs": {
|
|
7
|
+
"email": "support@convex.dev",
|
|
8
|
+
"url": "https://github.com/get-convex/table-history/issues"
|
|
9
|
+
},
|
|
10
|
+
"version": "0.1.3",
|
|
11
|
+
"license": "Apache-2.0",
|
|
12
|
+
"keywords": [
|
|
13
|
+
"convex",
|
|
14
|
+
"component"
|
|
15
|
+
],
|
|
16
|
+
"type": "module",
|
|
17
|
+
"scripts": {
|
|
18
|
+
"build": "npm run build:esm",
|
|
19
|
+
"build:esm": "tsc --project ./esm.json",
|
|
20
|
+
"dev": "convex dev",
|
|
21
|
+
"dev:frontend": "cd example && vite",
|
|
22
|
+
"typecheck": "tsc --noEmit",
|
|
23
|
+
"prepare": "npm run build",
|
|
24
|
+
"test": "vitest run",
|
|
25
|
+
"test:debug": "vitest --inspect-brk --no-file-parallelism",
|
|
26
|
+
"test:coverage": "vitest run --coverage --coverage.reporter=text"
|
|
27
|
+
},
|
|
28
|
+
"files": [
|
|
29
|
+
"dist",
|
|
30
|
+
"src"
|
|
31
|
+
],
|
|
32
|
+
"exports": {
|
|
33
|
+
"./package.json": "./package.json",
|
|
34
|
+
".": {
|
|
35
|
+
"types": "./dist/esm/client/index.d.ts",
|
|
36
|
+
"default": "./dist/esm/client/index.js"
|
|
37
|
+
},
|
|
38
|
+
"./react": {
|
|
39
|
+
"types": "./dist/esm/react/index.d.ts",
|
|
40
|
+
"default": "./dist/esm/react/index.js"
|
|
41
|
+
},
|
|
42
|
+
"./_generated/component.js": {
|
|
43
|
+
"types": "./dist/esm/component/_generated/component.d.ts",
|
|
44
|
+
"default": "./dist/esm/component/_generated/component.js"
|
|
45
|
+
},
|
|
46
|
+
"./convex.config": {
|
|
47
|
+
"types": "./dist/esm/component/convex.config.d.ts",
|
|
48
|
+
"default": "./dist/esm/component/convex.config.js"
|
|
49
|
+
},
|
|
50
|
+
"./convex.config.js": {
|
|
51
|
+
"types": "./dist/esm/component/convex.config.d.ts",
|
|
52
|
+
"default": "./dist/esm/component/convex.config.js"
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
"dependencies": {
|
|
56
|
+
"convex-helpers": "^0.1.100"
|
|
57
|
+
},
|
|
58
|
+
"peerDependencies": {
|
|
59
|
+
"convex": ">= 1.18.0"
|
|
60
|
+
},
|
|
61
|
+
"devDependencies": {
|
|
62
|
+
"@eslint/eslintrc": "^3.1.0",
|
|
63
|
+
"@eslint/js": "^9.9.1",
|
|
64
|
+
"@types/node": "^20.19.37",
|
|
65
|
+
"@types/react": "^18.3.3",
|
|
66
|
+
"@types/react-dom": "^18.3.0",
|
|
67
|
+
"@vitejs/plugin-react": "^4.3.1",
|
|
68
|
+
"convex": "1.34.1",
|
|
69
|
+
"convex-test": "^0.0.33",
|
|
70
|
+
"eslint": "^9.9.1",
|
|
71
|
+
"eslint-plugin-react-hooks": "^5.1.0-rc.0",
|
|
72
|
+
"eslint-plugin-react-refresh": "^0.4.9",
|
|
73
|
+
"globals": "^15.9.0",
|
|
74
|
+
"prettier": "3.2.5",
|
|
75
|
+
"react": "^18.3.1",
|
|
76
|
+
"react-dom": "^18.3.1",
|
|
77
|
+
"typescript": "5.8.3",
|
|
78
|
+
"typescript-eslint": "^8.4.0",
|
|
79
|
+
"vite": "^5.4.1",
|
|
80
|
+
"vitest": "^2.1.4"
|
|
81
|
+
},
|
|
82
|
+
"types": "./dist/esm/client/index.d.ts",
|
|
83
|
+
"module": "./dist/esm/client/index.js"
|
|
84
|
+
}
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DocumentByName,
|
|
3
|
+
GenericDataModel,
|
|
4
|
+
GenericMutationCtx,
|
|
5
|
+
GenericQueryCtx,
|
|
6
|
+
PaginationOptions,
|
|
7
|
+
TableNamesInDataModel,
|
|
8
|
+
} from "convex/server";
|
|
9
|
+
import { GenericId } from "convex/values";
|
|
10
|
+
import type { Serializability } from "../component/lib.js";
|
|
11
|
+
import type { ComponentApi } from "../component/_generated/component.js";
|
|
12
|
+
|
|
13
|
+
export class TableHistory<
|
|
14
|
+
DataModel extends GenericDataModel,
|
|
15
|
+
TableName extends TableNamesInDataModel<DataModel>,
|
|
16
|
+
> {
|
|
17
|
+
public options: {
|
|
18
|
+
serializability: Serializability;
|
|
19
|
+
};
|
|
20
|
+
constructor(
|
|
21
|
+
public component: ComponentApi,
|
|
22
|
+
options?: {
|
|
23
|
+
serializability?: Serializability;
|
|
24
|
+
}
|
|
25
|
+
) {
|
|
26
|
+
this.options = {
|
|
27
|
+
serializability: options?.serializability ?? "wallclock",
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Write a new history entry.
|
|
33
|
+
*
|
|
34
|
+
* @argument attribution an arbitrary object that will be stored with the
|
|
35
|
+
* history entry. Attribution can include actor/user identity, reason for
|
|
36
|
+
* change, etc.
|
|
37
|
+
*/
|
|
38
|
+
async update(
|
|
39
|
+
ctx: RunMutationCtx,
|
|
40
|
+
id: GenericId<TableName>,
|
|
41
|
+
doc: DocumentByName<DataModel, TableName> | null,
|
|
42
|
+
attribution: unknown = null
|
|
43
|
+
) {
|
|
44
|
+
return ctx.runMutation(this.component.lib.update, {
|
|
45
|
+
id,
|
|
46
|
+
doc,
|
|
47
|
+
serializability: this.options.serializability,
|
|
48
|
+
attribution,
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Paginate the history of the table, from newest to oldest.
|
|
54
|
+
*
|
|
55
|
+
* @argument maxTs To keep pages contiguous, set `maxTs` to a fixed timestamp
|
|
56
|
+
* (milliseconds since epoch, like Date.now()) and keep
|
|
57
|
+
* it the same for subsequent pages.
|
|
58
|
+
*/
|
|
59
|
+
async listHistory(
|
|
60
|
+
ctx: RunQueryCtx,
|
|
61
|
+
maxTs: number,
|
|
62
|
+
paginationOpts: PaginationOptions
|
|
63
|
+
) {
|
|
64
|
+
return ctx.runQuery(this.component.lib.listHistory, {
|
|
65
|
+
maxTs,
|
|
66
|
+
paginationOpts,
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Paginate the history of a single document, from newest to oldest.
|
|
71
|
+
*
|
|
72
|
+
* @argument maxTs To keep pages contiguous, set `maxTs` to a fixed timestamp
|
|
73
|
+
* (milliseconds since epoch, like Date.now()) and keep
|
|
74
|
+
* it the same for subsequent pages.
|
|
75
|
+
*/
|
|
76
|
+
async listDocumentHistory(
|
|
77
|
+
ctx: RunQueryCtx,
|
|
78
|
+
id: GenericId<TableName>,
|
|
79
|
+
maxTs: number,
|
|
80
|
+
paginationOpts: PaginationOptions
|
|
81
|
+
) {
|
|
82
|
+
return ctx.runQuery(this.component.lib.listDocumentHistory, {
|
|
83
|
+
id,
|
|
84
|
+
maxTs,
|
|
85
|
+
paginationOpts,
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Paginate a snapshot of the table at a fixed timestamp.
|
|
90
|
+
*
|
|
91
|
+
* @argument ts the snapshot at which you want to list the table (milliseconds since epoch)
|
|
92
|
+
* @argument currentTs a fixed recent timestamp (milliseconds since epoch, like Date.now())
|
|
93
|
+
* which should be the same for subsequent pages.
|
|
94
|
+
*/
|
|
95
|
+
async listSnapshot(
|
|
96
|
+
ctx: RunQueryCtx,
|
|
97
|
+
snapshotTs: number,
|
|
98
|
+
currentTs: number,
|
|
99
|
+
paginationOpts: PaginationOptions
|
|
100
|
+
) {
|
|
101
|
+
return ctx.runQuery(this.component.lib.listSnapshot, {
|
|
102
|
+
snapshotTs,
|
|
103
|
+
currentTs,
|
|
104
|
+
paginationOpts,
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Delete old history entries.
|
|
110
|
+
*
|
|
111
|
+
* @argument minTsToKeep the timestamp (milliseconds since epoch) of the oldest
|
|
112
|
+
* snapshot of history that should be kept.
|
|
113
|
+
*/
|
|
114
|
+
async vacuumHistory(ctx: RunMutationCtx, minTsToKeep: number) {
|
|
115
|
+
return ctx.runMutation(this.component.lib.vacuumHistory, { minTsToKeep });
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* For use with `Triggers` from "convex-helpers/server/triggers".
|
|
120
|
+
*/
|
|
121
|
+
trigger<Ctx extends RunMutationCtx>(): Trigger<Ctx, DataModel, TableName> {
|
|
122
|
+
return async (ctx, change) => {
|
|
123
|
+
let attribution: unknown = null;
|
|
124
|
+
if (
|
|
125
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
126
|
+
(ctx as any).auth &&
|
|
127
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
128
|
+
typeof (ctx as any).auth.getUserIdentity === "function"
|
|
129
|
+
) {
|
|
130
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
131
|
+
attribution = await (ctx as any).auth.getUserIdentity();
|
|
132
|
+
}
|
|
133
|
+
await this.update(ctx, change.id, change.newDoc, attribution);
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/* Type utils follow */
|
|
139
|
+
|
|
140
|
+
export type Trigger<
|
|
141
|
+
Ctx,
|
|
142
|
+
DataModel extends GenericDataModel,
|
|
143
|
+
TableName extends TableNamesInDataModel<DataModel>,
|
|
144
|
+
> = (ctx: Ctx, change: Change<DataModel, TableName>) => Promise<void>;
|
|
145
|
+
|
|
146
|
+
export type Change<
|
|
147
|
+
DataModel extends GenericDataModel,
|
|
148
|
+
TableName extends TableNamesInDataModel<DataModel>,
|
|
149
|
+
> = {
|
|
150
|
+
id: GenericId<TableName>;
|
|
151
|
+
} & (
|
|
152
|
+
| {
|
|
153
|
+
operation: "insert";
|
|
154
|
+
oldDoc: null;
|
|
155
|
+
newDoc: DocumentByName<DataModel, TableName>;
|
|
156
|
+
}
|
|
157
|
+
| {
|
|
158
|
+
operation: "update";
|
|
159
|
+
oldDoc: DocumentByName<DataModel, TableName>;
|
|
160
|
+
newDoc: DocumentByName<DataModel, TableName>;
|
|
161
|
+
}
|
|
162
|
+
| {
|
|
163
|
+
operation: "delete";
|
|
164
|
+
oldDoc: DocumentByName<DataModel, TableName>;
|
|
165
|
+
newDoc: null;
|
|
166
|
+
}
|
|
167
|
+
);
|
|
168
|
+
|
|
169
|
+
type RunQueryCtx = {
|
|
170
|
+
runQuery: GenericQueryCtx<GenericDataModel>["runQuery"];
|
|
171
|
+
};
|
|
172
|
+
type RunMutationCtx = {
|
|
173
|
+
runMutation: GenericMutationCtx<GenericDataModel>["runMutation"];
|
|
174
|
+
};
|