Skip to content

Commit c8096f0

Browse files
committed
serde for undefined
1 parent 75a1b11 commit c8096f0

File tree

1 file changed

+29
-9
lines changed

1 file changed

+29
-9
lines changed

packages/convex-helpers/server/stream.ts

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import type { Value } from "convex/values";
2-
import { convexToJson, jsonToConvex } from "convex/values";
2+
import { convexToJson, compareValues, jsonToConvex } from "convex/values";
33
import type {
44
DataModelFromSchemaDefinition,
55
DocumentByInfo,
@@ -20,9 +20,8 @@ import type {
2020
SystemDataModel,
2121
TableNamesInDataModel,
2222
} from "convex/server";
23-
import { compareValues } from "./compare.js";
2423

25-
export type IndexKey = Value[];
24+
export type IndexKey = (Value | undefined)[];
2625

2726
//
2827
// Helper functions
@@ -328,7 +327,7 @@ abstract class QueryStream<T extends GenericStreamItem>
328327
};
329328
if (opts.cursor !== null) {
330329
newStartKey = {
331-
key: jsonToConvex(JSON.parse(opts.cursor)) as IndexKey,
330+
key: deserializeCursor(opts.cursor),
332331
inclusive: false,
333332
};
334333
}
@@ -341,7 +340,7 @@ abstract class QueryStream<T extends GenericStreamItem>
341340
let maxRows: number | undefined = opts.numItems;
342341
if (opts.endCursor) {
343342
newEndKey = {
344-
key: jsonToConvex(JSON.parse(opts.endCursor)) as IndexKey,
343+
key: deserializeCursor(opts.endCursor),
345344
inclusive: true,
346345
};
347346
// If there's an endCursor, continue until we get there even if it's more
@@ -370,7 +369,7 @@ abstract class QueryStream<T extends GenericStreamItem>
370369
(maxRowsToRead !== undefined && indexKeys.length >= maxRowsToRead)
371370
) {
372371
hasMore = true;
373-
continueCursor = JSON.stringify(convexToJson(indexKey as Value));
372+
continueCursor = serializeCursor(indexKey);
374373
break;
375374
}
376375
}
@@ -389,9 +388,7 @@ abstract class QueryStream<T extends GenericStreamItem>
389388
isDone: !hasMore,
390389
continueCursor,
391390
pageStatus,
392-
splitCursor: splitCursor
393-
? JSON.stringify(convexToJson(splitCursor as Value))
394-
: undefined,
391+
splitCursor: splitCursor ? serializeCursor(splitCursor) : undefined,
395392
};
396393
}
397394
async collect() {
@@ -1821,3 +1818,26 @@ function compareKeys(key1: Key, key2: Key): number {
18211818
// of key2.kind is valid...
18221819
throw new Error(`Unexpected key kind: ${key1.kind as any}`);
18231820
}
1821+
1822+
function serializeCursor(key: IndexKey): string {
1823+
return JSON.stringify(
1824+
convexToJson(
1825+
key.map((v): Value => (v === undefined ? { $undefined: true } : v)),
1826+
),
1827+
);
1828+
}
1829+
1830+
function deserializeCursor(cursor: string): IndexKey {
1831+
return (jsonToConvex(JSON.parse(cursor)) as Value[]).map((v) => {
1832+
if (typeof v === "object" && !Array.isArray(v) && v !== null) {
1833+
const entries = Object.entries(v);
1834+
if (entries.length === 1 && entries[0]![0] === "$undefined") {
1835+
// This is a special case for the undefined value.
1836+
// It's not a valid value in the index, but it's a valid value in the
1837+
// cursor.
1838+
return undefined;
1839+
}
1840+
}
1841+
return v;
1842+
});
1843+
}

0 commit comments

Comments
 (0)