馃 distributed transcription service
thistle.dunkirk.sh
1/**
2 * Cursor encoding/decoding for pagination
3 * Cursors are base64url-encoded strings for opacity and URL safety
4 */
5
6/**
7 * Encode a cursor from components
8 */
9export function encodeCursor(parts: string[]): string {
10 const raw = parts.join("|");
11 // Use base64url encoding (no padding, URL-safe characters)
12 return Buffer.from(raw).toString("base64url");
13}
14
15/**
16 * Decode a cursor into components
17 */
18export function decodeCursor(cursor: string): string[] {
19 try {
20 const raw = Buffer.from(cursor, "base64url").toString("utf-8");
21 return raw.split("|");
22 } catch {
23 throw new Error("Invalid cursor format");
24 }
25}
26
27/**
28 * Encode a transcription/user cursor (timestamp-id)
29 */
30export function encodeSimpleCursor(timestamp: number, id: string): string {
31 return encodeCursor([timestamp.toString(), id]);
32}
33
34/**
35 * Decode a transcription/user cursor (timestamp-id)
36 */
37export function decodeSimpleCursor(cursor: string): {
38 timestamp: number;
39 id: string;
40} {
41 const parts = decodeCursor(cursor);
42 if (parts.length !== 2) {
43 throw new Error("Invalid cursor format");
44 }
45
46 const timestamp = Number.parseInt(parts[0] || "", 10);
47 const id = parts[1] || "";
48
49 if (Number.isNaN(timestamp) || !id) {
50 throw new Error("Invalid cursor format");
51 }
52
53 return { timestamp, id };
54}
55
56/**
57 * Encode a class cursor (year-semester-coursecode-id)
58 */
59export function encodeClassCursor(
60 year: number,
61 semester: string,
62 courseCode: string,
63 id: string,
64): string {
65 return encodeCursor([year.toString(), semester, courseCode, id]);
66}
67
68/**
69 * Decode a class cursor (year-semester-coursecode-id)
70 */
71export function decodeClassCursor(cursor: string): {
72 year: number;
73 semester: string;
74 courseCode: string;
75 id: string;
76} {
77 const parts = decodeCursor(cursor);
78 if (parts.length !== 4) {
79 throw new Error("Invalid cursor format");
80 }
81
82 const year = Number.parseInt(parts[0] || "", 10);
83 const semester = parts[1] || "";
84 const courseCode = parts[2] || "";
85 const id = parts[3] || "";
86
87 if (Number.isNaN(year) || !semester || !courseCode || !id) {
88 throw new Error("Invalid cursor format");
89 }
90
91 return { year, semester, courseCode, id };
92}