@stackframe/stack-shared 2.8.56 → 2.8.58
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/dist/apps/apps-config.d.mts +6 -0
- package/dist/apps/apps-config.d.ts +6 -0
- package/dist/apps/apps-config.js +6 -0
- package/dist/apps/apps-config.js.map +1 -1
- package/dist/config/schema-fuzzer.test.js +3 -0
- package/dist/config/schema-fuzzer.test.js.map +1 -1
- package/dist/config/schema.d.mts +162 -114
- package/dist/config/schema.d.ts +162 -114
- package/dist/config/schema.js +7 -0
- package/dist/config/schema.js.map +1 -1
- package/dist/esm/apps/apps-config.js +6 -0
- package/dist/esm/apps/apps-config.js.map +1 -1
- package/dist/esm/config/schema-fuzzer.test.js +3 -0
- package/dist/esm/config/schema-fuzzer.test.js.map +1 -1
- package/dist/esm/config/schema.js +7 -0
- package/dist/esm/config/schema.js.map +1 -1
- package/dist/esm/interface/admin-interface.js +49 -1
- package/dist/esm/interface/admin-interface.js.map +1 -1
- package/dist/esm/interface/client-interface.js +13 -4
- package/dist/esm/interface/client-interface.js.map +1 -1
- package/dist/esm/interface/crud/current-user.js +5 -2
- package/dist/esm/interface/crud/current-user.js.map +1 -1
- package/dist/esm/interface/crud/email-outbox.js +204 -0
- package/dist/esm/interface/crud/email-outbox.js.map +1 -0
- package/dist/esm/interface/crud/emails.js +0 -2
- package/dist/esm/interface/crud/emails.js.map +1 -1
- package/dist/esm/interface/crud/projects.js +3 -1
- package/dist/esm/interface/crud/projects.js.map +1 -1
- package/dist/esm/interface/crud/users.js +9 -2
- package/dist/esm/interface/crud/users.js.map +1 -1
- package/dist/esm/interface/server-interface.js +16 -0
- package/dist/esm/interface/server-interface.js.map +1 -1
- package/dist/esm/known-errors.js +45 -1
- package/dist/esm/known-errors.js.map +1 -1
- package/dist/esm/schema-fields.js +26 -2
- package/dist/esm/schema-fields.js.map +1 -1
- package/dist/esm/sessions.js +72 -8
- package/dist/esm/sessions.js.map +1 -1
- package/dist/esm/utils/env.js +13 -2
- package/dist/esm/utils/env.js.map +1 -1
- package/dist/esm/utils/esbuild.js +50 -21
- package/dist/esm/utils/esbuild.js.map +1 -1
- package/dist/esm/utils/globals.js +12 -0
- package/dist/esm/utils/globals.js.map +1 -1
- package/dist/esm/utils/paginated-lists.js +153 -23
- package/dist/esm/utils/paginated-lists.js.map +1 -1
- package/dist/esm/utils/paginated-lists.test.js +842 -0
- package/dist/esm/utils/paginated-lists.test.js.map +1 -0
- package/dist/esm/utils/proxies.js +28 -1
- package/dist/esm/utils/proxies.js.map +1 -1
- package/dist/esm/utils/react.js +7 -3
- package/dist/esm/utils/react.js.map +1 -1
- package/dist/esm/utils/results.js.map +1 -1
- package/dist/index.d.mts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/interface/admin-interface.d.mts +26 -3
- package/dist/interface/admin-interface.d.ts +26 -3
- package/dist/interface/admin-interface.js +49 -1
- package/dist/interface/admin-interface.js.map +1 -1
- package/dist/interface/client-interface.d.mts +5 -0
- package/dist/interface/client-interface.d.ts +5 -0
- package/dist/interface/client-interface.js +13 -4
- package/dist/interface/client-interface.js.map +1 -1
- package/dist/interface/crud/current-user.d.mts +23 -6
- package/dist/interface/crud/current-user.d.ts +23 -6
- package/dist/interface/crud/current-user.js +5 -2
- package/dist/interface/crud/current-user.js.map +1 -1
- package/dist/interface/crud/email-outbox.d.mts +1075 -0
- package/dist/interface/crud/email-outbox.d.ts +1075 -0
- package/dist/interface/crud/email-outbox.js +241 -0
- package/dist/interface/crud/email-outbox.js.map +1 -0
- package/dist/interface/crud/emails.d.mts +0 -34
- package/dist/interface/crud/emails.d.ts +0 -34
- package/dist/interface/crud/emails.js +0 -2
- package/dist/interface/crud/emails.js.map +1 -1
- package/dist/interface/crud/project-api-keys.d.mts +1 -1
- package/dist/interface/crud/project-api-keys.d.ts +1 -1
- package/dist/interface/crud/projects.d.mts +70 -66
- package/dist/interface/crud/projects.d.ts +70 -66
- package/dist/interface/crud/projects.js +3 -1
- package/dist/interface/crud/projects.js.map +1 -1
- package/dist/interface/crud/team-member-profiles.d.mts +28 -12
- package/dist/interface/crud/team-member-profiles.d.ts +28 -12
- package/dist/interface/crud/users.d.mts +38 -6
- package/dist/interface/crud/users.d.ts +38 -6
- package/dist/interface/crud/users.js +9 -2
- package/dist/interface/crud/users.js.map +1 -1
- package/dist/interface/server-interface.d.mts +29 -0
- package/dist/interface/server-interface.d.ts +29 -0
- package/dist/interface/server-interface.js +16 -0
- package/dist/interface/server-interface.js.map +1 -1
- package/dist/interface/webhooks.d.mts +18 -2
- package/dist/interface/webhooks.d.ts +18 -2
- package/dist/known-errors.d.mts +14 -1
- package/dist/known-errors.d.ts +14 -1
- package/dist/known-errors.js +45 -1
- package/dist/known-errors.js.map +1 -1
- package/dist/schema-fields.d.mts +34 -1
- package/dist/schema-fields.d.ts +34 -1
- package/dist/schema-fields.js +32 -2
- package/dist/schema-fields.js.map +1 -1
- package/dist/sessions.d.mts +35 -4
- package/dist/sessions.d.ts +35 -4
- package/dist/sessions.js +72 -8
- package/dist/sessions.js.map +1 -1
- package/dist/utils/env.d.mts +2 -1
- package/dist/utils/env.d.ts +2 -1
- package/dist/utils/env.js +13 -1
- package/dist/utils/env.js.map +1 -1
- package/dist/utils/esbuild.js +49 -20
- package/dist/utils/esbuild.js.map +1 -1
- package/dist/utils/globals.d.mts +6 -1
- package/dist/utils/globals.d.ts +6 -1
- package/dist/utils/globals.js +13 -0
- package/dist/utils/globals.js.map +1 -1
- package/dist/utils/paginated-lists.d.mts +269 -12
- package/dist/utils/paginated-lists.d.ts +269 -12
- package/dist/utils/paginated-lists.js +153 -23
- package/dist/utils/paginated-lists.js.map +1 -1
- package/dist/utils/paginated-lists.test.d.mts +2 -0
- package/dist/utils/paginated-lists.test.d.ts +2 -0
- package/dist/utils/paginated-lists.test.js +844 -0
- package/dist/utils/paginated-lists.test.js.map +1 -0
- package/dist/utils/proxies.d.mts +8 -1
- package/dist/utils/proxies.d.ts +8 -1
- package/dist/utils/proxies.js +30 -2
- package/dist/utils/proxies.js.map +1 -1
- package/dist/utils/react.d.mts +1 -1
- package/dist/utils/react.d.ts +1 -1
- package/dist/utils/react.js +7 -3
- package/dist/utils/react.js.map +1 -1
- package/dist/utils/results.d.mts +5 -5
- package/dist/utils/results.d.ts +5 -5
- package/dist/utils/results.js.map +1 -1
- package/package.json +2 -1
- package/CHANGELOG.md +0 -1354
- package/dist/esm/interface/crud/config.js +0 -40
- package/dist/esm/interface/crud/config.js.map +0 -1
- package/dist/interface/crud/config.d.mts +0 -49
- package/dist/interface/crud/config.d.ts +0 -49
- package/dist/interface/crud/config.js +0 -79
- package/dist/interface/crud/config.js.map +0 -1
|
@@ -0,0 +1,842 @@
|
|
|
1
|
+
// src/utils/paginated-lists.test.ts
|
|
2
|
+
import { describe, expect, it } from "vitest";
|
|
3
|
+
import { ArrayPaginatedList, PaginatedList } from "./paginated-lists.js";
|
|
4
|
+
import { stringCompare } from "./strings.js";
|
|
5
|
+
var items = (result) => result.items.map((i) => i.item);
|
|
6
|
+
describe("ArrayPaginatedList", () => {
|
|
7
|
+
describe("basic forward pagination (next)", () => {
|
|
8
|
+
it("should return all items when limit is greater than array length", async () => {
|
|
9
|
+
const list = new ArrayPaginatedList([1, 2, 3]);
|
|
10
|
+
const result = await list.next({
|
|
11
|
+
after: list.getFirstCursor(),
|
|
12
|
+
limit: 10,
|
|
13
|
+
filter: () => true,
|
|
14
|
+
orderBy: (a, b) => a - b,
|
|
15
|
+
limitPrecision: "exact"
|
|
16
|
+
});
|
|
17
|
+
expect(items(result)).toEqual([1, 2, 3]);
|
|
18
|
+
expect(result.isFirst).toBe(true);
|
|
19
|
+
expect(result.isLast).toBe(true);
|
|
20
|
+
});
|
|
21
|
+
it("should return exact limit when more items exist", async () => {
|
|
22
|
+
const list = new ArrayPaginatedList([1, 2, 3, 4, 5]);
|
|
23
|
+
const result = await list.next({
|
|
24
|
+
after: list.getFirstCursor(),
|
|
25
|
+
limit: 3,
|
|
26
|
+
filter: () => true,
|
|
27
|
+
orderBy: (a, b) => a - b,
|
|
28
|
+
limitPrecision: "exact"
|
|
29
|
+
});
|
|
30
|
+
expect(items(result)).toEqual([1, 2, 3]);
|
|
31
|
+
expect(result.isFirst).toBe(true);
|
|
32
|
+
expect(result.isLast).toBe(false);
|
|
33
|
+
});
|
|
34
|
+
it("should continue from cursor correctly", async () => {
|
|
35
|
+
const list = new ArrayPaginatedList([1, 2, 3, 4, 5]);
|
|
36
|
+
const first = await list.next({
|
|
37
|
+
after: list.getFirstCursor(),
|
|
38
|
+
limit: 2,
|
|
39
|
+
filter: () => true,
|
|
40
|
+
orderBy: (a, b) => a - b,
|
|
41
|
+
limitPrecision: "exact"
|
|
42
|
+
});
|
|
43
|
+
expect(items(first)).toEqual([1, 2]);
|
|
44
|
+
expect(first.cursor).toBe("before-2");
|
|
45
|
+
const second = await list.next({
|
|
46
|
+
after: first.cursor,
|
|
47
|
+
limit: 2,
|
|
48
|
+
filter: () => true,
|
|
49
|
+
orderBy: (a, b) => a - b,
|
|
50
|
+
limitPrecision: "exact"
|
|
51
|
+
});
|
|
52
|
+
expect(items(second)).toEqual([3, 4]);
|
|
53
|
+
expect(second.isFirst).toBe(false);
|
|
54
|
+
expect(second.isLast).toBe(false);
|
|
55
|
+
});
|
|
56
|
+
it("should handle empty array", async () => {
|
|
57
|
+
const list = new ArrayPaginatedList([]);
|
|
58
|
+
const result = await list.next({
|
|
59
|
+
after: list.getFirstCursor(),
|
|
60
|
+
limit: 10,
|
|
61
|
+
filter: () => true,
|
|
62
|
+
orderBy: (a, b) => a - b,
|
|
63
|
+
limitPrecision: "exact"
|
|
64
|
+
});
|
|
65
|
+
expect(items(result)).toEqual([]);
|
|
66
|
+
expect(result.isFirst).toBe(true);
|
|
67
|
+
expect(result.isLast).toBe(true);
|
|
68
|
+
});
|
|
69
|
+
it("should handle limit of 0", async () => {
|
|
70
|
+
const list = new ArrayPaginatedList([1, 2, 3]);
|
|
71
|
+
const result = await list.next({
|
|
72
|
+
after: list.getFirstCursor(),
|
|
73
|
+
limit: 0,
|
|
74
|
+
filter: () => true,
|
|
75
|
+
orderBy: (a, b) => a - b,
|
|
76
|
+
limitPrecision: "exact"
|
|
77
|
+
});
|
|
78
|
+
expect(items(result)).toEqual([]);
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
describe("backward pagination (prev)", () => {
|
|
82
|
+
it("should return items before cursor", async () => {
|
|
83
|
+
const list = new ArrayPaginatedList([1, 2, 3, 4, 5]);
|
|
84
|
+
const result = await list.prev({
|
|
85
|
+
before: list.getLastCursor(),
|
|
86
|
+
limit: 2,
|
|
87
|
+
filter: () => true,
|
|
88
|
+
orderBy: (a, b) => a - b,
|
|
89
|
+
limitPrecision: "exact"
|
|
90
|
+
});
|
|
91
|
+
expect(items(result)).toEqual([4, 5]);
|
|
92
|
+
expect(result.isFirst).toBe(false);
|
|
93
|
+
expect(result.isLast).toBe(true);
|
|
94
|
+
});
|
|
95
|
+
it("should paginate backwards correctly", async () => {
|
|
96
|
+
const list = new ArrayPaginatedList([1, 2, 3, 4, 5]);
|
|
97
|
+
const last = await list.prev({
|
|
98
|
+
before: list.getLastCursor(),
|
|
99
|
+
limit: 2,
|
|
100
|
+
filter: () => true,
|
|
101
|
+
orderBy: (a, b) => a - b,
|
|
102
|
+
limitPrecision: "exact"
|
|
103
|
+
});
|
|
104
|
+
expect(items(last)).toEqual([4, 5]);
|
|
105
|
+
const middle = await list.prev({
|
|
106
|
+
before: last.cursor,
|
|
107
|
+
limit: 2,
|
|
108
|
+
filter: () => true,
|
|
109
|
+
orderBy: (a, b) => a - b,
|
|
110
|
+
limitPrecision: "exact"
|
|
111
|
+
});
|
|
112
|
+
expect(items(middle)).toEqual([2, 3]);
|
|
113
|
+
const first = await list.prev({
|
|
114
|
+
before: middle.cursor,
|
|
115
|
+
limit: 2,
|
|
116
|
+
filter: () => true,
|
|
117
|
+
orderBy: (a, b) => a - b,
|
|
118
|
+
limitPrecision: "exact"
|
|
119
|
+
});
|
|
120
|
+
expect(items(first)).toEqual([1]);
|
|
121
|
+
expect(first.isFirst).toBe(true);
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
describe("cursor semantics (prevCursor and nextCursor)", () => {
|
|
125
|
+
it("should have prevCursor before the item and nextCursor after", async () => {
|
|
126
|
+
const list = new ArrayPaginatedList([10, 20, 30]);
|
|
127
|
+
const result = await list.next({
|
|
128
|
+
after: list.getFirstCursor(),
|
|
129
|
+
limit: 3,
|
|
130
|
+
filter: () => true,
|
|
131
|
+
orderBy: (a, b) => a - b,
|
|
132
|
+
limitPrecision: "exact"
|
|
133
|
+
});
|
|
134
|
+
expect(result.items[0].prevCursor).toBe("before-0");
|
|
135
|
+
expect(result.items[0].nextCursor).toBe("before-1");
|
|
136
|
+
expect(result.items[1].prevCursor).toBe("before-1");
|
|
137
|
+
expect(result.items[1].nextCursor).toBe("before-2");
|
|
138
|
+
expect(result.items[2].prevCursor).toBe("before-2");
|
|
139
|
+
expect(result.items[2].nextCursor).toBe("before-3");
|
|
140
|
+
});
|
|
141
|
+
it("should allow using nextCursor to continue forward pagination", async () => {
|
|
142
|
+
const list = new ArrayPaginatedList([1, 2, 3, 4, 5]);
|
|
143
|
+
const first = await list.next({
|
|
144
|
+
after: list.getFirstCursor(),
|
|
145
|
+
limit: 2,
|
|
146
|
+
filter: () => true,
|
|
147
|
+
orderBy: (a, b) => a - b,
|
|
148
|
+
limitPrecision: "exact"
|
|
149
|
+
});
|
|
150
|
+
const continueFrom = first.items[first.items.length - 1].nextCursor;
|
|
151
|
+
const second = await list.next({
|
|
152
|
+
after: continueFrom,
|
|
153
|
+
limit: 2,
|
|
154
|
+
filter: () => true,
|
|
155
|
+
orderBy: (a, b) => a - b,
|
|
156
|
+
limitPrecision: "exact"
|
|
157
|
+
});
|
|
158
|
+
expect(items(second)).toEqual([3, 4]);
|
|
159
|
+
});
|
|
160
|
+
it("should allow using prevCursor to go back", async () => {
|
|
161
|
+
const list = new ArrayPaginatedList([1, 2, 3, 4, 5]);
|
|
162
|
+
const middle = await list.next({
|
|
163
|
+
after: "before-2",
|
|
164
|
+
limit: 3,
|
|
165
|
+
filter: () => true,
|
|
166
|
+
orderBy: (a, b) => a - b,
|
|
167
|
+
limitPrecision: "exact"
|
|
168
|
+
});
|
|
169
|
+
expect(items(middle)).toEqual([3, 4, 5]);
|
|
170
|
+
expect(middle.items[0].prevCursor).toBe("before-2");
|
|
171
|
+
const goBack = await list.prev({
|
|
172
|
+
before: middle.items[0].prevCursor,
|
|
173
|
+
limit: 2,
|
|
174
|
+
filter: () => true,
|
|
175
|
+
orderBy: (a, b) => a - b,
|
|
176
|
+
limitPrecision: "exact"
|
|
177
|
+
});
|
|
178
|
+
expect(items(goBack)).toEqual([1, 2]);
|
|
179
|
+
});
|
|
180
|
+
});
|
|
181
|
+
describe("filtering", () => {
|
|
182
|
+
it("should filter items correctly", async () => {
|
|
183
|
+
const list = new ArrayPaginatedList([1, 2, 3, 4, 5, 6]);
|
|
184
|
+
const result = await list.next({
|
|
185
|
+
after: list.getFirstCursor(),
|
|
186
|
+
limit: 10,
|
|
187
|
+
filter: (n) => n % 2 === 0,
|
|
188
|
+
// only even numbers
|
|
189
|
+
orderBy: (a, b) => a - b,
|
|
190
|
+
limitPrecision: "exact"
|
|
191
|
+
});
|
|
192
|
+
expect(items(result)).toEqual([2, 4, 6]);
|
|
193
|
+
});
|
|
194
|
+
it("should respect limit with filtering", async () => {
|
|
195
|
+
const list = new ArrayPaginatedList([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
|
|
196
|
+
const result = await list.next({
|
|
197
|
+
after: list.getFirstCursor(),
|
|
198
|
+
limit: 2,
|
|
199
|
+
filter: (n) => n % 2 === 0,
|
|
200
|
+
orderBy: (a, b) => a - b,
|
|
201
|
+
limitPrecision: "exact"
|
|
202
|
+
});
|
|
203
|
+
expect(items(result)).toEqual([2, 4]);
|
|
204
|
+
});
|
|
205
|
+
it("should handle filter that matches nothing", async () => {
|
|
206
|
+
const list = new ArrayPaginatedList([1, 2, 3]);
|
|
207
|
+
const result = await list.next({
|
|
208
|
+
after: list.getFirstCursor(),
|
|
209
|
+
limit: 10,
|
|
210
|
+
filter: () => false,
|
|
211
|
+
orderBy: (a, b) => a - b,
|
|
212
|
+
limitPrecision: "exact"
|
|
213
|
+
});
|
|
214
|
+
expect(items(result)).toEqual([]);
|
|
215
|
+
});
|
|
216
|
+
});
|
|
217
|
+
describe("ordering", () => {
|
|
218
|
+
it("should sort items in ascending order", async () => {
|
|
219
|
+
const list = new ArrayPaginatedList([5, 2, 8, 1, 9]);
|
|
220
|
+
const result = await list.next({
|
|
221
|
+
after: list.getFirstCursor(),
|
|
222
|
+
limit: 10,
|
|
223
|
+
filter: () => true,
|
|
224
|
+
orderBy: (a, b) => a - b,
|
|
225
|
+
limitPrecision: "exact"
|
|
226
|
+
});
|
|
227
|
+
expect(items(result)).toEqual([1, 2, 5, 8, 9]);
|
|
228
|
+
});
|
|
229
|
+
it("should sort items in descending order", async () => {
|
|
230
|
+
const list = new ArrayPaginatedList([5, 2, 8, 1, 9]);
|
|
231
|
+
const result = await list.next({
|
|
232
|
+
after: list.getFirstCursor(),
|
|
233
|
+
limit: 10,
|
|
234
|
+
filter: () => true,
|
|
235
|
+
orderBy: (a, b) => b - a,
|
|
236
|
+
limitPrecision: "exact"
|
|
237
|
+
});
|
|
238
|
+
expect(items(result)).toEqual([9, 8, 5, 2, 1]);
|
|
239
|
+
});
|
|
240
|
+
it("should sort objects by property", async () => {
|
|
241
|
+
const list = new ArrayPaginatedList([
|
|
242
|
+
{ name: "Charlie", age: 30 },
|
|
243
|
+
{ name: "Alice", age: 25 },
|
|
244
|
+
{ name: "Bob", age: 35 }
|
|
245
|
+
]);
|
|
246
|
+
const byName = await list.next({
|
|
247
|
+
after: list.getFirstCursor(),
|
|
248
|
+
limit: 10,
|
|
249
|
+
filter: () => true,
|
|
250
|
+
orderBy: (a, b) => stringCompare(a.name, b.name),
|
|
251
|
+
limitPrecision: "exact"
|
|
252
|
+
});
|
|
253
|
+
expect(items(byName).map((p) => p.name)).toEqual(["Alice", "Bob", "Charlie"]);
|
|
254
|
+
const byAge = await list.next({
|
|
255
|
+
after: list.getFirstCursor(),
|
|
256
|
+
limit: 10,
|
|
257
|
+
filter: () => true,
|
|
258
|
+
orderBy: (a, b) => a.age - b.age,
|
|
259
|
+
limitPrecision: "exact"
|
|
260
|
+
});
|
|
261
|
+
expect(items(byAge).map((p) => p.name)).toEqual(["Alice", "Charlie", "Bob"]);
|
|
262
|
+
});
|
|
263
|
+
});
|
|
264
|
+
describe("limitPrecision", () => {
|
|
265
|
+
it("exact should return exactly the limit", async () => {
|
|
266
|
+
const list = new ArrayPaginatedList([1, 2, 3, 4, 5]);
|
|
267
|
+
const result = await list.next({
|
|
268
|
+
after: list.getFirstCursor(),
|
|
269
|
+
limit: 3,
|
|
270
|
+
filter: () => true,
|
|
271
|
+
orderBy: (a, b) => a - b,
|
|
272
|
+
limitPrecision: "exact"
|
|
273
|
+
});
|
|
274
|
+
expect(result.items.length).toBe(3);
|
|
275
|
+
});
|
|
276
|
+
it("at-least should return at least the limit (or all if less available)", async () => {
|
|
277
|
+
const list = new ArrayPaginatedList([1, 2, 3, 4, 5]);
|
|
278
|
+
const result = await list.next({
|
|
279
|
+
after: list.getFirstCursor(),
|
|
280
|
+
limit: 3,
|
|
281
|
+
filter: () => true,
|
|
282
|
+
orderBy: (a, b) => a - b,
|
|
283
|
+
limitPrecision: "at-least"
|
|
284
|
+
});
|
|
285
|
+
expect(result.items.length).toBeGreaterThanOrEqual(3);
|
|
286
|
+
});
|
|
287
|
+
it("at-most should return at most the limit", async () => {
|
|
288
|
+
const list = new ArrayPaginatedList([1, 2, 3, 4, 5]);
|
|
289
|
+
const result = await list.next({
|
|
290
|
+
after: list.getFirstCursor(),
|
|
291
|
+
limit: 3,
|
|
292
|
+
filter: () => true,
|
|
293
|
+
orderBy: (a, b) => a - b,
|
|
294
|
+
limitPrecision: "at-most"
|
|
295
|
+
});
|
|
296
|
+
expect(result.items.length).toBeLessThanOrEqual(3);
|
|
297
|
+
});
|
|
298
|
+
it("approximate should allow flexibility in either direction", async () => {
|
|
299
|
+
const list = new ArrayPaginatedList([1, 2, 3, 4, 5]);
|
|
300
|
+
const result = await list.next({
|
|
301
|
+
after: list.getFirstCursor(),
|
|
302
|
+
limit: 3,
|
|
303
|
+
filter: () => true,
|
|
304
|
+
orderBy: (a, b) => a - b,
|
|
305
|
+
limitPrecision: "approximate"
|
|
306
|
+
});
|
|
307
|
+
expect(result.items.length).toBeGreaterThan(0);
|
|
308
|
+
expect(items(result).every((n) => typeof n === "number")).toBe(true);
|
|
309
|
+
});
|
|
310
|
+
it("approximate should still make progress when limit > 0", async () => {
|
|
311
|
+
const list = new ArrayPaginatedList([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
|
|
312
|
+
const allItems = [];
|
|
313
|
+
let cursor = list.getFirstCursor();
|
|
314
|
+
let iterations = 0;
|
|
315
|
+
const maxIterations = 20;
|
|
316
|
+
while (iterations < maxIterations) {
|
|
317
|
+
const result = await list.next({
|
|
318
|
+
after: cursor,
|
|
319
|
+
limit: 2,
|
|
320
|
+
filter: () => true,
|
|
321
|
+
orderBy: (a, b) => a - b,
|
|
322
|
+
limitPrecision: "approximate"
|
|
323
|
+
});
|
|
324
|
+
if (result.items.length === 0) break;
|
|
325
|
+
allItems.push(...items(result));
|
|
326
|
+
expect(result.cursor).not.toBe(cursor);
|
|
327
|
+
cursor = result.cursor;
|
|
328
|
+
iterations++;
|
|
329
|
+
if (result.isLast) break;
|
|
330
|
+
}
|
|
331
|
+
expect(allItems.length).toBeGreaterThan(0);
|
|
332
|
+
});
|
|
333
|
+
});
|
|
334
|
+
describe("getFirstCursor and getLastCursor", () => {
|
|
335
|
+
it("should return correct cursor format", () => {
|
|
336
|
+
const list = new ArrayPaginatedList([1, 2, 3]);
|
|
337
|
+
expect(list.getFirstCursor()).toBe("before-0");
|
|
338
|
+
expect(list.getLastCursor()).toBe("before-3");
|
|
339
|
+
});
|
|
340
|
+
it("should work for empty array", () => {
|
|
341
|
+
const list = new ArrayPaginatedList([]);
|
|
342
|
+
expect(list.getFirstCursor()).toBe("before-0");
|
|
343
|
+
expect(list.getLastCursor()).toBe("before-0");
|
|
344
|
+
});
|
|
345
|
+
});
|
|
346
|
+
describe("compare", () => {
|
|
347
|
+
it("should compare items using the orderBy function", () => {
|
|
348
|
+
const list = new ArrayPaginatedList([1, 2, 3]);
|
|
349
|
+
const orderBy = (a, b) => a - b;
|
|
350
|
+
expect(list.compare(orderBy, 1, 2)).toBeLessThan(0);
|
|
351
|
+
expect(list.compare(orderBy, 2, 1)).toBeGreaterThan(0);
|
|
352
|
+
expect(list.compare(orderBy, 1, 1)).toBe(0);
|
|
353
|
+
});
|
|
354
|
+
});
|
|
355
|
+
});
|
|
356
|
+
describe("PaginatedList.empty", () => {
|
|
357
|
+
it("should return empty results", async () => {
|
|
358
|
+
const list = PaginatedList.empty();
|
|
359
|
+
const result = await list.next({
|
|
360
|
+
after: list.getFirstCursor(),
|
|
361
|
+
limit: 10,
|
|
362
|
+
filter: {},
|
|
363
|
+
orderBy: {},
|
|
364
|
+
limitPrecision: "exact"
|
|
365
|
+
});
|
|
366
|
+
expect(result.items).toEqual([]);
|
|
367
|
+
expect(result.isFirst).toBe(true);
|
|
368
|
+
expect(result.isLast).toBe(true);
|
|
369
|
+
});
|
|
370
|
+
it("should have first cursor", () => {
|
|
371
|
+
const list = PaginatedList.empty();
|
|
372
|
+
expect(list.getFirstCursor()).toBe("first");
|
|
373
|
+
expect(list.getLastCursor()).toBe("last");
|
|
374
|
+
});
|
|
375
|
+
});
|
|
376
|
+
describe("PaginatedList.map", () => {
|
|
377
|
+
it("should transform items", async () => {
|
|
378
|
+
const list = new ArrayPaginatedList([1, 2, 3]);
|
|
379
|
+
const doubled = list.map({
|
|
380
|
+
itemMapper: (n) => n * 2,
|
|
381
|
+
oldItemFromNewItem: (n) => n / 2,
|
|
382
|
+
oldFilterFromNewFilter: (f) => f,
|
|
383
|
+
oldOrderByFromNewOrderBy: (o) => o
|
|
384
|
+
});
|
|
385
|
+
const result = await doubled.next({
|
|
386
|
+
after: doubled.getFirstCursor(),
|
|
387
|
+
limit: 10,
|
|
388
|
+
filter: () => true,
|
|
389
|
+
orderBy: (a, b) => a - b,
|
|
390
|
+
limitPrecision: "exact"
|
|
391
|
+
});
|
|
392
|
+
expect(items(result)).toEqual([2, 4, 6]);
|
|
393
|
+
});
|
|
394
|
+
it("should preserve cursor semantics", async () => {
|
|
395
|
+
const list = new ArrayPaginatedList([1, 2, 3]);
|
|
396
|
+
const doubled = list.map({
|
|
397
|
+
itemMapper: (n) => n * 2,
|
|
398
|
+
oldItemFromNewItem: (n) => n / 2,
|
|
399
|
+
oldFilterFromNewFilter: (f) => f,
|
|
400
|
+
oldOrderByFromNewOrderBy: (o) => o
|
|
401
|
+
});
|
|
402
|
+
const first = await doubled.next({
|
|
403
|
+
after: doubled.getFirstCursor(),
|
|
404
|
+
limit: 1,
|
|
405
|
+
filter: () => true,
|
|
406
|
+
orderBy: (a, b) => a - b,
|
|
407
|
+
limitPrecision: "exact"
|
|
408
|
+
});
|
|
409
|
+
expect(items(first)).toEqual([2]);
|
|
410
|
+
const second = await doubled.next({
|
|
411
|
+
after: first.cursor,
|
|
412
|
+
limit: 1,
|
|
413
|
+
filter: () => true,
|
|
414
|
+
orderBy: (a, b) => a - b,
|
|
415
|
+
limitPrecision: "exact"
|
|
416
|
+
});
|
|
417
|
+
expect(items(second)).toEqual([4]);
|
|
418
|
+
});
|
|
419
|
+
});
|
|
420
|
+
describe("PaginatedList.filter", () => {
|
|
421
|
+
it("should filter items", async () => {
|
|
422
|
+
const list = new ArrayPaginatedList([1, 2, 3, 4, 5, 6]);
|
|
423
|
+
const evens = list.filter({
|
|
424
|
+
filter: (n) => n % 2 === 0,
|
|
425
|
+
oldFilterFromNewFilter: () => () => true,
|
|
426
|
+
estimateItemsToFetch: ({ limit }) => limit * 2
|
|
427
|
+
});
|
|
428
|
+
const result = await evens.next({
|
|
429
|
+
after: evens.getFirstCursor(),
|
|
430
|
+
limit: 10,
|
|
431
|
+
filter: void 0,
|
|
432
|
+
orderBy: (a, b) => a - b,
|
|
433
|
+
limitPrecision: "exact"
|
|
434
|
+
});
|
|
435
|
+
expect(items(result)).toEqual([2, 4, 6]);
|
|
436
|
+
});
|
|
437
|
+
});
|
|
438
|
+
describe("PaginatedList.addFilter", () => {
|
|
439
|
+
it("should add additional filter constraint", async () => {
|
|
440
|
+
const list = new ArrayPaginatedList([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
|
|
441
|
+
const evens = list.filter({
|
|
442
|
+
filter: (n) => n % 2 === 0,
|
|
443
|
+
oldFilterFromNewFilter: () => () => true,
|
|
444
|
+
estimateItemsToFetch: ({ limit }) => limit * 2
|
|
445
|
+
});
|
|
446
|
+
const evenAndGreaterThan5 = evens.addFilter({
|
|
447
|
+
filter: (n) => n > 5,
|
|
448
|
+
estimateItemsToFetch: ({ limit }) => limit * 2
|
|
449
|
+
});
|
|
450
|
+
const result = await evenAndGreaterThan5.next({
|
|
451
|
+
after: evenAndGreaterThan5.getFirstCursor(),
|
|
452
|
+
limit: 10,
|
|
453
|
+
filter: void 0,
|
|
454
|
+
orderBy: (a, b) => a - b,
|
|
455
|
+
limitPrecision: "exact"
|
|
456
|
+
});
|
|
457
|
+
expect(items(result)).toEqual([6, 8, 10]);
|
|
458
|
+
});
|
|
459
|
+
});
|
|
460
|
+
describe("PaginatedList.flatMap", () => {
|
|
461
|
+
it("should expand items", async () => {
|
|
462
|
+
const list = new ArrayPaginatedList([1, 2, 3]);
|
|
463
|
+
const expanded = list.flatMap({
|
|
464
|
+
itemMapper: (entry) => [
|
|
465
|
+
{ item: entry.item, prevCursor: entry.prevCursor, nextCursor: entry.nextCursor },
|
|
466
|
+
{ item: entry.item + 0.5, prevCursor: entry.prevCursor, nextCursor: entry.nextCursor }
|
|
467
|
+
],
|
|
468
|
+
compare: (_, a, b) => a - b,
|
|
469
|
+
newCursorFromOldCursor: (c) => c,
|
|
470
|
+
oldCursorFromNewCursor: (c) => c,
|
|
471
|
+
oldFilterFromNewFilter: (f) => f,
|
|
472
|
+
oldOrderByFromNewOrderBy: (o) => o,
|
|
473
|
+
estimateItemsToFetch: ({ limit }) => limit
|
|
474
|
+
});
|
|
475
|
+
const result = await expanded.next({
|
|
476
|
+
after: expanded.getFirstCursor(),
|
|
477
|
+
limit: 10,
|
|
478
|
+
filter: () => true,
|
|
479
|
+
orderBy: (a, b) => a - b,
|
|
480
|
+
limitPrecision: "exact"
|
|
481
|
+
});
|
|
482
|
+
expect(items(result)).toEqual([1, 1.5, 2, 2.5, 3, 3.5]);
|
|
483
|
+
});
|
|
484
|
+
it("should filter out items (return empty array)", async () => {
|
|
485
|
+
const list = new ArrayPaginatedList([1, 2, 3, 4, 5]);
|
|
486
|
+
const evensOnly = list.flatMap({
|
|
487
|
+
itemMapper: (entry) => entry.item % 2 === 0 ? [{ item: entry.item, prevCursor: entry.prevCursor, nextCursor: entry.nextCursor }] : [],
|
|
488
|
+
compare: (_, a, b) => a - b,
|
|
489
|
+
newCursorFromOldCursor: (c) => c,
|
|
490
|
+
oldCursorFromNewCursor: (c) => c,
|
|
491
|
+
oldFilterFromNewFilter: (f) => f,
|
|
492
|
+
oldOrderByFromNewOrderBy: (o) => o,
|
|
493
|
+
estimateItemsToFetch: ({ limit }) => limit * 2
|
|
494
|
+
});
|
|
495
|
+
const result = await evensOnly.next({
|
|
496
|
+
after: evensOnly.getFirstCursor(),
|
|
497
|
+
limit: 10,
|
|
498
|
+
filter: () => true,
|
|
499
|
+
orderBy: (a, b) => a - b,
|
|
500
|
+
limitPrecision: "exact"
|
|
501
|
+
});
|
|
502
|
+
expect(items(result)).toEqual([2, 4]);
|
|
503
|
+
});
|
|
504
|
+
});
|
|
505
|
+
describe("PaginatedList.merge", () => {
|
|
506
|
+
it("should merge two lists", async () => {
|
|
507
|
+
const list1 = new ArrayPaginatedList([1, 3, 5]);
|
|
508
|
+
const list2 = new ArrayPaginatedList([2, 4, 6]);
|
|
509
|
+
const merged = PaginatedList.merge(list1, list2);
|
|
510
|
+
const result = await merged.next({
|
|
511
|
+
after: merged.getFirstCursor(),
|
|
512
|
+
limit: 10,
|
|
513
|
+
filter: () => true,
|
|
514
|
+
orderBy: (a, b) => a - b,
|
|
515
|
+
limitPrecision: "exact"
|
|
516
|
+
});
|
|
517
|
+
expect(items(result)).toEqual([1, 2, 3, 4, 5, 6]);
|
|
518
|
+
});
|
|
519
|
+
it("should merge three lists", async () => {
|
|
520
|
+
const list1 = new ArrayPaginatedList([1, 4, 7]);
|
|
521
|
+
const list2 = new ArrayPaginatedList([2, 5, 8]);
|
|
522
|
+
const list3 = new ArrayPaginatedList([3, 6, 9]);
|
|
523
|
+
const merged = PaginatedList.merge(list1, list2, list3);
|
|
524
|
+
const result = await merged.next({
|
|
525
|
+
after: merged.getFirstCursor(),
|
|
526
|
+
limit: 10,
|
|
527
|
+
filter: () => true,
|
|
528
|
+
orderBy: (a, b) => a - b,
|
|
529
|
+
limitPrecision: "exact"
|
|
530
|
+
});
|
|
531
|
+
expect(items(result)).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9]);
|
|
532
|
+
});
|
|
533
|
+
it("should handle empty lists in merge", async () => {
|
|
534
|
+
const list1 = new ArrayPaginatedList([1, 2, 3]);
|
|
535
|
+
const list2 = new ArrayPaginatedList([]);
|
|
536
|
+
const merged = PaginatedList.merge(list1, list2);
|
|
537
|
+
const result = await merged.next({
|
|
538
|
+
after: merged.getFirstCursor(),
|
|
539
|
+
limit: 10,
|
|
540
|
+
filter: () => true,
|
|
541
|
+
orderBy: (a, b) => a - b,
|
|
542
|
+
limitPrecision: "exact"
|
|
543
|
+
});
|
|
544
|
+
expect(items(result)).toEqual([1, 2, 3]);
|
|
545
|
+
});
|
|
546
|
+
it("should paginate through merged list", async () => {
|
|
547
|
+
const list1 = new ArrayPaginatedList([1, 3, 5, 7, 9]);
|
|
548
|
+
const list2 = new ArrayPaginatedList([2, 4, 6, 8, 10]);
|
|
549
|
+
const merged = PaginatedList.merge(list1, list2);
|
|
550
|
+
const first = await merged.next({
|
|
551
|
+
after: merged.getFirstCursor(),
|
|
552
|
+
limit: 4,
|
|
553
|
+
filter: () => true,
|
|
554
|
+
orderBy: (a, b) => a - b,
|
|
555
|
+
limitPrecision: "exact"
|
|
556
|
+
});
|
|
557
|
+
expect(items(first)).toEqual([1, 2, 3, 4]);
|
|
558
|
+
const second = await merged.next({
|
|
559
|
+
after: first.cursor,
|
|
560
|
+
limit: 4,
|
|
561
|
+
filter: () => true,
|
|
562
|
+
orderBy: (a, b) => a - b,
|
|
563
|
+
limitPrecision: "exact"
|
|
564
|
+
});
|
|
565
|
+
expect(items(second)).toEqual([5, 6, 7, 8]);
|
|
566
|
+
});
|
|
567
|
+
it("should have JSON-encoded cursor", () => {
|
|
568
|
+
const list1 = new ArrayPaginatedList([1, 2]);
|
|
569
|
+
const list2 = new ArrayPaginatedList([3, 4]);
|
|
570
|
+
const merged = PaginatedList.merge(list1, list2);
|
|
571
|
+
const cursor = merged.getFirstCursor();
|
|
572
|
+
expect(() => JSON.parse(cursor)).not.toThrow();
|
|
573
|
+
expect(JSON.parse(cursor)).toEqual(["before-0", "before-0"]);
|
|
574
|
+
});
|
|
575
|
+
it("should paginate backward through merged list correctly", async () => {
|
|
576
|
+
const list1 = new ArrayPaginatedList([1, 3, 5]);
|
|
577
|
+
const list2 = new ArrayPaginatedList([2, 4, 6]);
|
|
578
|
+
const merged = PaginatedList.merge(list1, list2);
|
|
579
|
+
const last = await merged.prev({
|
|
580
|
+
before: merged.getLastCursor(),
|
|
581
|
+
limit: 3,
|
|
582
|
+
filter: () => true,
|
|
583
|
+
orderBy: (a, b) => a - b,
|
|
584
|
+
limitPrecision: "exact"
|
|
585
|
+
});
|
|
586
|
+
expect(items(last)).toEqual([4, 5, 6]);
|
|
587
|
+
expect(last.isLast).toBe(true);
|
|
588
|
+
expect(last.isFirst).toBe(false);
|
|
589
|
+
const middle = await merged.prev({
|
|
590
|
+
before: last.cursor,
|
|
591
|
+
limit: 3,
|
|
592
|
+
filter: () => true,
|
|
593
|
+
orderBy: (a, b) => a - b,
|
|
594
|
+
limitPrecision: "exact"
|
|
595
|
+
});
|
|
596
|
+
expect(items(middle)).toEqual([1, 2, 3]);
|
|
597
|
+
expect(middle.isFirst).toBe(true);
|
|
598
|
+
});
|
|
599
|
+
it("should paginate backward through entire merged list", async () => {
|
|
600
|
+
const list1 = new ArrayPaginatedList([1, 3, 5, 7, 9]);
|
|
601
|
+
const list2 = new ArrayPaginatedList([2, 4, 6, 8, 10]);
|
|
602
|
+
const merged = PaginatedList.merge(list1, list2);
|
|
603
|
+
const allItems = [];
|
|
604
|
+
let cursor = merged.getLastCursor();
|
|
605
|
+
let isFirst = false;
|
|
606
|
+
while (!isFirst) {
|
|
607
|
+
const result = await merged.prev({
|
|
608
|
+
before: cursor,
|
|
609
|
+
limit: 3,
|
|
610
|
+
filter: () => true,
|
|
611
|
+
orderBy: (a, b) => a - b,
|
|
612
|
+
limitPrecision: "exact"
|
|
613
|
+
});
|
|
614
|
+
allItems.unshift(...items(result));
|
|
615
|
+
cursor = result.cursor;
|
|
616
|
+
isFirst = result.isFirst;
|
|
617
|
+
}
|
|
618
|
+
expect(allItems).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
|
|
619
|
+
});
|
|
620
|
+
it("should have consistent forward and backward pagination results", async () => {
|
|
621
|
+
const list1 = new ArrayPaginatedList([1, 4, 7]);
|
|
622
|
+
const list2 = new ArrayPaginatedList([2, 5, 8]);
|
|
623
|
+
const list3 = new ArrayPaginatedList([3, 6, 9]);
|
|
624
|
+
const merged = PaginatedList.merge(list1, list2, list3);
|
|
625
|
+
const forwardItems = [];
|
|
626
|
+
let forwardCursor = merged.getFirstCursor();
|
|
627
|
+
let isLast = false;
|
|
628
|
+
while (!isLast) {
|
|
629
|
+
const result = await merged.next({
|
|
630
|
+
after: forwardCursor,
|
|
631
|
+
limit: 2,
|
|
632
|
+
filter: () => true,
|
|
633
|
+
orderBy: (a, b) => a - b,
|
|
634
|
+
limitPrecision: "exact"
|
|
635
|
+
});
|
|
636
|
+
forwardItems.push(...items(result));
|
|
637
|
+
forwardCursor = result.cursor;
|
|
638
|
+
isLast = result.isLast;
|
|
639
|
+
}
|
|
640
|
+
const backwardItems = [];
|
|
641
|
+
let backwardCursor = merged.getLastCursor();
|
|
642
|
+
let isFirst = false;
|
|
643
|
+
while (!isFirst) {
|
|
644
|
+
const result = await merged.prev({
|
|
645
|
+
before: backwardCursor,
|
|
646
|
+
limit: 2,
|
|
647
|
+
filter: () => true,
|
|
648
|
+
orderBy: (a, b) => a - b,
|
|
649
|
+
limitPrecision: "exact"
|
|
650
|
+
});
|
|
651
|
+
backwardItems.unshift(...items(result));
|
|
652
|
+
backwardCursor = result.cursor;
|
|
653
|
+
isFirst = result.isFirst;
|
|
654
|
+
}
|
|
655
|
+
expect(forwardItems).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9]);
|
|
656
|
+
expect(backwardItems).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9]);
|
|
657
|
+
});
|
|
658
|
+
});
|
|
659
|
+
describe("edge cases", () => {
|
|
660
|
+
it("should handle single item", async () => {
|
|
661
|
+
const list = new ArrayPaginatedList([42]);
|
|
662
|
+
const result = await list.next({
|
|
663
|
+
after: list.getFirstCursor(),
|
|
664
|
+
limit: 10,
|
|
665
|
+
filter: () => true,
|
|
666
|
+
orderBy: (a, b) => a - b,
|
|
667
|
+
limitPrecision: "exact"
|
|
668
|
+
});
|
|
669
|
+
expect(items(result)).toEqual([42]);
|
|
670
|
+
expect(result.isFirst).toBe(true);
|
|
671
|
+
expect(result.isLast).toBe(true);
|
|
672
|
+
});
|
|
673
|
+
it("should handle duplicate values", async () => {
|
|
674
|
+
const list = new ArrayPaginatedList([1, 1, 2, 2, 3, 3]);
|
|
675
|
+
const result = await list.next({
|
|
676
|
+
after: list.getFirstCursor(),
|
|
677
|
+
limit: 10,
|
|
678
|
+
filter: () => true,
|
|
679
|
+
orderBy: (a, b) => a - b,
|
|
680
|
+
limitPrecision: "exact"
|
|
681
|
+
});
|
|
682
|
+
expect(items(result)).toEqual([1, 1, 2, 2, 3, 3]);
|
|
683
|
+
});
|
|
684
|
+
it("should handle negative numbers", async () => {
|
|
685
|
+
const list = new ArrayPaginatedList([-3, -1, 0, 1, 3]);
|
|
686
|
+
const result = await list.next({
|
|
687
|
+
after: list.getFirstCursor(),
|
|
688
|
+
limit: 10,
|
|
689
|
+
filter: () => true,
|
|
690
|
+
orderBy: (a, b) => a - b,
|
|
691
|
+
limitPrecision: "exact"
|
|
692
|
+
});
|
|
693
|
+
expect(items(result)).toEqual([-3, -1, 0, 1, 3]);
|
|
694
|
+
});
|
|
695
|
+
it("should handle string items", async () => {
|
|
696
|
+
const list = new ArrayPaginatedList(["banana", "apple", "cherry"]);
|
|
697
|
+
const result = await list.next({
|
|
698
|
+
after: list.getFirstCursor(),
|
|
699
|
+
limit: 10,
|
|
700
|
+
filter: () => true,
|
|
701
|
+
orderBy: (a, b) => stringCompare(a, b),
|
|
702
|
+
limitPrecision: "exact"
|
|
703
|
+
});
|
|
704
|
+
expect(items(result)).toEqual(["apple", "banana", "cherry"]);
|
|
705
|
+
});
|
|
706
|
+
it("should handle object items", async () => {
|
|
707
|
+
const list = new ArrayPaginatedList([
|
|
708
|
+
{ id: 1, value: "a" },
|
|
709
|
+
{ id: 2, value: "b" }
|
|
710
|
+
]);
|
|
711
|
+
const result = await list.next({
|
|
712
|
+
after: list.getFirstCursor(),
|
|
713
|
+
limit: 10,
|
|
714
|
+
filter: () => true,
|
|
715
|
+
orderBy: (a, b) => a.id - b.id,
|
|
716
|
+
limitPrecision: "exact"
|
|
717
|
+
});
|
|
718
|
+
expect(items(result)).toEqual([
|
|
719
|
+
{ id: 1, value: "a" },
|
|
720
|
+
{ id: 2, value: "b" }
|
|
721
|
+
]);
|
|
722
|
+
});
|
|
723
|
+
});
|
|
724
|
+
describe("complete pagination walkthrough", () => {
|
|
725
|
+
it("should paginate forward through entire list", async () => {
|
|
726
|
+
const list = new ArrayPaginatedList([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
|
|
727
|
+
const allItems = [];
|
|
728
|
+
let cursor = list.getFirstCursor();
|
|
729
|
+
let isLast = false;
|
|
730
|
+
while (!isLast) {
|
|
731
|
+
const result = await list.next({
|
|
732
|
+
after: cursor,
|
|
733
|
+
limit: 3,
|
|
734
|
+
filter: () => true,
|
|
735
|
+
orderBy: (a, b) => a - b,
|
|
736
|
+
limitPrecision: "exact"
|
|
737
|
+
});
|
|
738
|
+
allItems.push(...items(result));
|
|
739
|
+
cursor = result.cursor;
|
|
740
|
+
isLast = result.isLast;
|
|
741
|
+
}
|
|
742
|
+
expect(allItems).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
|
|
743
|
+
});
|
|
744
|
+
it("should paginate backward through entire list", async () => {
|
|
745
|
+
const list = new ArrayPaginatedList([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
|
|
746
|
+
const allItems = [];
|
|
747
|
+
let cursor = list.getLastCursor();
|
|
748
|
+
let isFirst = false;
|
|
749
|
+
while (!isFirst) {
|
|
750
|
+
const result = await list.prev({
|
|
751
|
+
before: cursor,
|
|
752
|
+
limit: 3,
|
|
753
|
+
filter: () => true,
|
|
754
|
+
orderBy: (a, b) => a - b,
|
|
755
|
+
limitPrecision: "exact"
|
|
756
|
+
});
|
|
757
|
+
allItems.unshift(...items(result));
|
|
758
|
+
cursor = result.cursor;
|
|
759
|
+
isFirst = result.isFirst;
|
|
760
|
+
}
|
|
761
|
+
expect(allItems).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
|
|
762
|
+
});
|
|
763
|
+
});
|
|
764
|
+
describe("unsorted array pagination", () => {
|
|
765
|
+
it("should correctly paginate through an unsorted array with sorting", async () => {
|
|
766
|
+
const list = new ArrayPaginatedList([3, 1, 2]);
|
|
767
|
+
const first = await list.next({
|
|
768
|
+
after: list.getFirstCursor(),
|
|
769
|
+
limit: 2,
|
|
770
|
+
filter: () => true,
|
|
771
|
+
orderBy: (a, b) => a - b,
|
|
772
|
+
limitPrecision: "exact"
|
|
773
|
+
});
|
|
774
|
+
expect(items(first)).toEqual([1, 2]);
|
|
775
|
+
const second = await list.next({
|
|
776
|
+
after: first.cursor,
|
|
777
|
+
limit: 2,
|
|
778
|
+
filter: () => true,
|
|
779
|
+
orderBy: (a, b) => a - b,
|
|
780
|
+
limitPrecision: "exact"
|
|
781
|
+
});
|
|
782
|
+
expect(items(second)).toEqual([3]);
|
|
783
|
+
});
|
|
784
|
+
it("should maintain sorted order across pagination boundaries", async () => {
|
|
785
|
+
const list = new ArrayPaginatedList([5, 4, 3, 2, 1]);
|
|
786
|
+
const allItems = [];
|
|
787
|
+
let cursor = list.getFirstCursor();
|
|
788
|
+
let isLast = false;
|
|
789
|
+
while (!isLast) {
|
|
790
|
+
const result = await list.next({
|
|
791
|
+
after: cursor,
|
|
792
|
+
limit: 2,
|
|
793
|
+
filter: () => true,
|
|
794
|
+
orderBy: (a, b) => a - b,
|
|
795
|
+
limitPrecision: "exact"
|
|
796
|
+
});
|
|
797
|
+
allItems.push(...items(result));
|
|
798
|
+
cursor = result.cursor;
|
|
799
|
+
isLast = result.isLast;
|
|
800
|
+
}
|
|
801
|
+
expect(allItems).toEqual([1, 2, 3, 4, 5]);
|
|
802
|
+
});
|
|
803
|
+
it("should handle random order array correctly", async () => {
|
|
804
|
+
const list = new ArrayPaginatedList([7, 2, 9, 1, 5, 8, 3, 6, 4]);
|
|
805
|
+
const allItems = [];
|
|
806
|
+
let cursor = list.getFirstCursor();
|
|
807
|
+
let isLast = false;
|
|
808
|
+
while (!isLast) {
|
|
809
|
+
const result = await list.next({
|
|
810
|
+
after: cursor,
|
|
811
|
+
limit: 3,
|
|
812
|
+
filter: () => true,
|
|
813
|
+
orderBy: (a, b) => a - b,
|
|
814
|
+
limitPrecision: "exact"
|
|
815
|
+
});
|
|
816
|
+
allItems.push(...items(result));
|
|
817
|
+
cursor = result.cursor;
|
|
818
|
+
isLast = result.isLast;
|
|
819
|
+
}
|
|
820
|
+
expect(allItems).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9]);
|
|
821
|
+
});
|
|
822
|
+
it("should have consistent cursor semantics with sorted data", async () => {
|
|
823
|
+
const list = new ArrayPaginatedList([3, 1, 2]);
|
|
824
|
+
const first = await list.next({
|
|
825
|
+
after: list.getFirstCursor(),
|
|
826
|
+
limit: 1,
|
|
827
|
+
filter: () => true,
|
|
828
|
+
orderBy: (a, b) => a - b,
|
|
829
|
+
limitPrecision: "exact"
|
|
830
|
+
});
|
|
831
|
+
expect(items(first)).toEqual([1]);
|
|
832
|
+
const second = await list.next({
|
|
833
|
+
after: first.cursor,
|
|
834
|
+
limit: 1,
|
|
835
|
+
filter: () => true,
|
|
836
|
+
orderBy: (a, b) => a - b,
|
|
837
|
+
limitPrecision: "exact"
|
|
838
|
+
});
|
|
839
|
+
expect(items(second)).toEqual([2]);
|
|
840
|
+
});
|
|
841
|
+
});
|
|
842
|
+
//# sourceMappingURL=paginated-lists.test.js.map
|