kernl 0.6.2 → 0.6.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/.turbo/turbo-build.log +1 -1
- package/.turbo/turbo-check-types.log +1 -1
- package/CHANGELOG.md +30 -0
- package/dist/agent/__tests__/concurrency.test.js +1 -1
- package/dist/agent/__tests__/run.test.js +1 -1
- package/dist/{types/agent.d.ts → agent/types.d.ts} +2 -2
- package/dist/agent/types.d.ts.map +1 -0
- package/dist/agent.d.ts +36 -4
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +58 -0
- package/dist/api/models/thread.d.ts +1 -1
- package/dist/api/resources/threads/threads.d.ts +1 -1
- package/dist/api/resources/threads/threads.d.ts.map +1 -1
- package/dist/api/resources/threads/threads.js +1 -1
- package/dist/api/resources/threads/types.d.ts +2 -2
- package/dist/api/resources/threads/types.d.ts.map +1 -1
- package/dist/context.d.ts +4 -4
- package/dist/context.d.ts.map +1 -1
- package/dist/guardrail.d.ts +2 -2
- package/dist/index.d.ts +5 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -3
- package/dist/internal.d.ts +2 -2
- package/dist/internal.js +1 -1
- package/dist/kernl/index.d.ts +1 -1
- package/dist/kernl/index.d.ts.map +1 -1
- package/dist/kernl/index.js +0 -1
- package/dist/kernl/kernl.d.ts +7 -18
- package/dist/kernl/kernl.d.ts.map +1 -1
- package/dist/kernl/kernl.js +29 -29
- package/dist/kernl/types.d.ts +91 -0
- package/dist/kernl/types.d.ts.map +1 -0
- package/dist/lib/error.d.ts +2 -2
- package/dist/lifecycle.d.ts +2 -2
- package/dist/memory/codec.d.ts +32 -0
- package/dist/memory/codec.d.ts.map +1 -0
- package/dist/memory/codec.js +97 -0
- package/dist/memory/codecs/domain.d.ts +34 -0
- package/dist/memory/codecs/domain.d.ts.map +1 -0
- package/dist/memory/codecs/domain.js +99 -0
- package/dist/memory/codecs/identity.d.ts +12 -0
- package/dist/memory/codecs/identity.d.ts.map +1 -0
- package/dist/memory/codecs/identity.js +17 -0
- package/dist/memory/codecs/index.d.ts +31 -0
- package/dist/memory/codecs/index.d.ts.map +1 -0
- package/dist/memory/codecs/index.js +39 -0
- package/dist/memory/codecs/tpuf.d.ts +38 -0
- package/dist/memory/codecs/tpuf.d.ts.map +1 -0
- package/dist/memory/codecs/tpuf.js +90 -0
- package/dist/memory/encoder.d.ts +29 -0
- package/dist/memory/encoder.d.ts.map +1 -0
- package/dist/memory/encoder.js +45 -0
- package/dist/memory/handle.d.ts +89 -0
- package/dist/memory/handle.d.ts.map +1 -0
- package/dist/memory/handle.js +128 -0
- package/dist/memory/index.d.ts +12 -0
- package/dist/memory/index.d.ts.map +1 -0
- package/dist/memory/index.js +7 -0
- package/dist/memory/indexes.d.ts +91 -0
- package/dist/memory/indexes.d.ts.map +1 -0
- package/dist/memory/indexes.js +7 -0
- package/dist/memory/memory.d.ts +51 -0
- package/dist/memory/memory.d.ts.map +1 -0
- package/dist/memory/memory.js +107 -0
- package/dist/memory/schema.d.ts +41 -0
- package/dist/memory/schema.d.ts.map +1 -0
- package/dist/memory/schema.js +112 -0
- package/dist/memory/store.d.ts +36 -0
- package/dist/memory/store.d.ts.map +1 -0
- package/dist/memory/store.js +4 -0
- package/dist/memory/types.d.ts +250 -0
- package/dist/memory/types.d.ts.map +1 -0
- package/dist/memory/types.js +4 -0
- package/dist/storage/base.d.ts +6 -1
- package/dist/storage/base.d.ts.map +1 -1
- package/dist/storage/in-memory.d.ts +24 -2
- package/dist/storage/in-memory.d.ts.map +1 -1
- package/dist/storage/in-memory.js +131 -0
- package/dist/storage/thread.d.ts +1 -1
- package/dist/thread/__tests__/integration.test.js +1 -1
- package/dist/thread/__tests__/mock.d.ts +1 -1
- package/dist/thread/__tests__/namespace.test.js +1 -1
- package/dist/thread/__tests__/thread.test.js +1 -1
- package/dist/thread/thread.d.ts +2 -2
- package/dist/thread/thread.d.ts.map +1 -1
- package/dist/{types/thread.d.ts → thread/types.d.ts} +2 -2
- package/dist/thread/types.d.ts.map +1 -0
- package/dist/thread/utils.d.ts +2 -2
- package/dist/thread/utils.d.ts.map +1 -1
- package/package.json +4 -2
- package/src/{types/agent.ts → agent/types.ts} +1 -1
- package/src/agent.ts +78 -2
- package/src/api/__tests__/threads.test.ts +2 -2
- package/src/api/models/thread.ts +1 -1
- package/src/api/resources/threads/events.ts +1 -1
- package/src/api/resources/threads/threads.ts +2 -2
- package/src/api/resources/threads/types.ts +2 -2
- package/src/context.ts +6 -136
- package/src/guardrail.ts +2 -2
- package/src/index.ts +35 -6
- package/src/internal.ts +2 -2
- package/src/kernl/index.ts +8 -0
- package/src/{kernl.ts → kernl/kernl.ts} +40 -3
- package/src/kernl/types.ts +106 -0
- package/src/lib/error.ts +2 -2
- package/src/lifecycle.ts +2 -2
- package/src/memory/codecs/domain.ts +115 -0
- package/src/memory/codecs/identity.ts +28 -0
- package/src/memory/codecs/index.ts +61 -0
- package/src/memory/codecs/tpuf.ts +115 -0
- package/src/memory/encoder.ts +56 -0
- package/src/memory/handle.ts +189 -0
- package/src/memory/index.ts +49 -0
- package/src/memory/indexes.ts +108 -0
- package/src/memory/memory.ts +143 -0
- package/src/memory/schema.ts +142 -0
- package/src/memory/store.ts +47 -0
- package/src/memory/types.ts +282 -0
- package/src/storage/__tests__/in-memory.test.ts +1 -1
- package/src/storage/base.ts +7 -1
- package/src/storage/in-memory.ts +170 -2
- package/src/storage/thread.ts +1 -1
- package/src/thread/__tests__/integration.test.ts +1 -1
- package/src/thread/__tests__/mock.ts +1 -1
- package/src/thread/__tests__/thread.test.ts +1 -1
- package/src/thread/thread.ts +2 -2
- package/src/{types/thread.ts → thread/types.ts} +1 -1
- package/src/thread/utils.ts +2 -2
- package/tsconfig.tsbuildinfo +1 -0
- package/dist/api/__tests__/cursor-page.test.d.ts +0 -2
- package/dist/api/__tests__/cursor-page.test.d.ts.map +0 -1
- package/dist/api/__tests__/cursor-page.test.js +0 -414
- package/dist/api/__tests__/offset-page.test.d.ts +0 -2
- package/dist/api/__tests__/offset-page.test.d.ts.map +0 -1
- package/dist/api/__tests__/offset-page.test.js +0 -510
- package/dist/api/pagination/base.d.ts +0 -48
- package/dist/api/pagination/base.d.ts.map +0 -1
- package/dist/api/pagination/base.js +0 -45
- package/dist/api/pagination/cursor.d.ts +0 -44
- package/dist/api/pagination/cursor.d.ts.map +0 -1
- package/dist/api/pagination/cursor.js +0 -52
- package/dist/api/pagination/offset.d.ts +0 -42
- package/dist/api/pagination/offset.d.ts.map +0 -1
- package/dist/api/pagination/offset.js +0 -55
- package/dist/kernl/threads.d.ts +0 -110
- package/dist/kernl/threads.d.ts.map +0 -1
- package/dist/kernl/threads.js +0 -126
- package/dist/kernl.d.ts +0 -51
- package/dist/kernl.d.ts.map +0 -1
- package/dist/kernl.js +0 -91
- package/dist/types/agent.d.ts.map +0 -1
- package/dist/types/kernl.d.ts +0 -42
- package/dist/types/kernl.d.ts.map +0 -1
- package/dist/types/thread.d.ts.map +0 -1
- package/src/api/__tests__/cursor-page.test.ts +0 -512
- package/src/api/__tests__/offset-page.test.ts +0 -624
- package/src/api/pagination/base.ts +0 -79
- package/src/api/pagination/cursor.ts +0 -86
- package/src/api/pagination/offset.ts +0 -89
- package/src/types/kernl.ts +0 -51
- /package/dist/{types/agent.js → agent/types.js} +0 -0
- /package/dist/{types/kernl.js → kernl/types.js} +0 -0
- /package/dist/{types/thread.js → thread/types.js} +0 -0
|
@@ -1,510 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, vi } from "vitest";
|
|
2
|
-
import { OffsetPage, } from "../pagination/offset.js";
|
|
3
|
-
describe("OffsetPage", () => {
|
|
4
|
-
describe("construction", () => {
|
|
5
|
-
it("should initialize with response data", () => {
|
|
6
|
-
const response = {
|
|
7
|
-
data: ["a", "b", "c"],
|
|
8
|
-
offset: 0,
|
|
9
|
-
limit: 10,
|
|
10
|
-
total: 100,
|
|
11
|
-
};
|
|
12
|
-
const loader = vi.fn();
|
|
13
|
-
const page = new OffsetPage({
|
|
14
|
-
params: { offset: 0, limit: 10 },
|
|
15
|
-
response,
|
|
16
|
-
loader,
|
|
17
|
-
});
|
|
18
|
-
expect(page.data).toEqual(["a", "b", "c"]);
|
|
19
|
-
expect(page.items).toEqual(["a", "b", "c"]);
|
|
20
|
-
expect(page.offset).toBe(0);
|
|
21
|
-
expect(page.limit).toBe(10);
|
|
22
|
-
expect(page.total).toBe(100);
|
|
23
|
-
});
|
|
24
|
-
it("should handle empty data array", () => {
|
|
25
|
-
const response = {
|
|
26
|
-
data: [],
|
|
27
|
-
offset: 0,
|
|
28
|
-
limit: 10,
|
|
29
|
-
};
|
|
30
|
-
const loader = vi.fn();
|
|
31
|
-
const page = new OffsetPage({
|
|
32
|
-
params: {},
|
|
33
|
-
response,
|
|
34
|
-
loader,
|
|
35
|
-
});
|
|
36
|
-
expect(page.data).toEqual([]);
|
|
37
|
-
expect(page.items).toEqual([]);
|
|
38
|
-
expect(page.last).toBe(true);
|
|
39
|
-
});
|
|
40
|
-
it("should handle null data gracefully", () => {
|
|
41
|
-
const response = {
|
|
42
|
-
data: null,
|
|
43
|
-
offset: 0,
|
|
44
|
-
limit: 10,
|
|
45
|
-
};
|
|
46
|
-
const loader = vi.fn();
|
|
47
|
-
const page = new OffsetPage({
|
|
48
|
-
params: {},
|
|
49
|
-
response,
|
|
50
|
-
loader,
|
|
51
|
-
});
|
|
52
|
-
expect(page.data).toEqual([]);
|
|
53
|
-
expect(page.items).toEqual([]);
|
|
54
|
-
});
|
|
55
|
-
it("should handle response without total", () => {
|
|
56
|
-
const response = {
|
|
57
|
-
data: ["a", "b"],
|
|
58
|
-
offset: 0,
|
|
59
|
-
limit: 10,
|
|
60
|
-
};
|
|
61
|
-
const page = new OffsetPage({
|
|
62
|
-
params: {},
|
|
63
|
-
response,
|
|
64
|
-
loader: vi.fn(),
|
|
65
|
-
});
|
|
66
|
-
expect(page.total).toBeUndefined();
|
|
67
|
-
});
|
|
68
|
-
});
|
|
69
|
-
describe("items getter", () => {
|
|
70
|
-
it("should return exact reference to data array", () => {
|
|
71
|
-
const data = ["a", "b", "c"];
|
|
72
|
-
const response = {
|
|
73
|
-
data,
|
|
74
|
-
offset: 0,
|
|
75
|
-
limit: 10,
|
|
76
|
-
};
|
|
77
|
-
const page = new OffsetPage({
|
|
78
|
-
params: {},
|
|
79
|
-
response,
|
|
80
|
-
loader: vi.fn(),
|
|
81
|
-
});
|
|
82
|
-
expect(page.items).toBe(page.data);
|
|
83
|
-
});
|
|
84
|
-
});
|
|
85
|
-
describe("last getter", () => {
|
|
86
|
-
it("should return true when data is empty", () => {
|
|
87
|
-
const page = new OffsetPage({
|
|
88
|
-
params: {},
|
|
89
|
-
response: { data: [], offset: 0, limit: 10 },
|
|
90
|
-
loader: vi.fn(),
|
|
91
|
-
});
|
|
92
|
-
expect(page.last).toBe(true);
|
|
93
|
-
});
|
|
94
|
-
it("should return true when offset + limit >= total", () => {
|
|
95
|
-
const page = new OffsetPage({
|
|
96
|
-
params: {},
|
|
97
|
-
response: { data: ["a"], offset: 90, limit: 10, total: 100 },
|
|
98
|
-
loader: vi.fn(),
|
|
99
|
-
});
|
|
100
|
-
expect(page.last).toBe(true);
|
|
101
|
-
});
|
|
102
|
-
it("should return true when offset + limit equals total exactly", () => {
|
|
103
|
-
const page = new OffsetPage({
|
|
104
|
-
params: {},
|
|
105
|
-
response: { data: ["a", "b"], offset: 98, limit: 2, total: 100 },
|
|
106
|
-
loader: vi.fn(),
|
|
107
|
-
});
|
|
108
|
-
expect(page.last).toBe(true);
|
|
109
|
-
});
|
|
110
|
-
it("should return false when offset + limit < total", () => {
|
|
111
|
-
const page = new OffsetPage({
|
|
112
|
-
params: {},
|
|
113
|
-
response: { data: ["a", "b"], offset: 0, limit: 10, total: 100 },
|
|
114
|
-
loader: vi.fn(),
|
|
115
|
-
});
|
|
116
|
-
expect(page.last).toBe(false);
|
|
117
|
-
});
|
|
118
|
-
it("should return false when total is undefined and data exists", () => {
|
|
119
|
-
const page = new OffsetPage({
|
|
120
|
-
params: {},
|
|
121
|
-
response: { data: ["a", "b"], offset: 0, limit: 10 },
|
|
122
|
-
loader: vi.fn(),
|
|
123
|
-
});
|
|
124
|
-
expect(page.last).toBe(false);
|
|
125
|
-
});
|
|
126
|
-
it("should handle offset and limit of 0", () => {
|
|
127
|
-
const page = new OffsetPage({
|
|
128
|
-
params: {},
|
|
129
|
-
response: { data: ["a"], offset: 0, limit: 0 },
|
|
130
|
-
loader: vi.fn(),
|
|
131
|
-
});
|
|
132
|
-
// With limit 0, next would be 0, so it can't progress
|
|
133
|
-
expect(page.last).toBe(false);
|
|
134
|
-
});
|
|
135
|
-
it("should handle undefined offset and limit", () => {
|
|
136
|
-
const page = new OffsetPage({
|
|
137
|
-
params: {},
|
|
138
|
-
response: {
|
|
139
|
-
data: ["a"],
|
|
140
|
-
offset: undefined,
|
|
141
|
-
limit: undefined,
|
|
142
|
-
},
|
|
143
|
-
loader: vi.fn(),
|
|
144
|
-
});
|
|
145
|
-
expect(page.last).toBe(false);
|
|
146
|
-
});
|
|
147
|
-
});
|
|
148
|
-
describe("next()", () => {
|
|
149
|
-
it("should return null when last is true", async () => {
|
|
150
|
-
const page = new OffsetPage({
|
|
151
|
-
params: {},
|
|
152
|
-
response: { data: [], offset: 0, limit: 10 },
|
|
153
|
-
loader: vi.fn(),
|
|
154
|
-
});
|
|
155
|
-
const nextPage = await page.next();
|
|
156
|
-
expect(nextPage).toBe(null);
|
|
157
|
-
});
|
|
158
|
-
it("should fetch next page with incremented offset", async () => {
|
|
159
|
-
const loader = vi.fn().mockResolvedValue({
|
|
160
|
-
data: ["d", "e", "f"],
|
|
161
|
-
offset: 10,
|
|
162
|
-
limit: 10,
|
|
163
|
-
total: 100,
|
|
164
|
-
});
|
|
165
|
-
const page = new OffsetPage({
|
|
166
|
-
params: { offset: 0, limit: 10 },
|
|
167
|
-
response: { data: ["a", "b", "c"], offset: 0, limit: 10, total: 100 },
|
|
168
|
-
loader,
|
|
169
|
-
});
|
|
170
|
-
const nextPage = await page.next();
|
|
171
|
-
expect(loader).toHaveBeenCalledTimes(1);
|
|
172
|
-
expect(loader).toHaveBeenCalledWith({
|
|
173
|
-
offset: 10,
|
|
174
|
-
limit: 10,
|
|
175
|
-
});
|
|
176
|
-
expect(nextPage).not.toBe(null);
|
|
177
|
-
expect(nextPage.data).toEqual(["d", "e", "f"]);
|
|
178
|
-
expect(nextPage.offset).toBe(10);
|
|
179
|
-
});
|
|
180
|
-
it("should calculate next offset correctly", async () => {
|
|
181
|
-
const loader = vi.fn().mockResolvedValue({
|
|
182
|
-
data: ["x"],
|
|
183
|
-
offset: 25,
|
|
184
|
-
limit: 5,
|
|
185
|
-
total: 100,
|
|
186
|
-
});
|
|
187
|
-
const page = new OffsetPage({
|
|
188
|
-
params: { offset: 20, limit: 5 },
|
|
189
|
-
response: { data: ["a"], offset: 20, limit: 5, total: 100 },
|
|
190
|
-
loader,
|
|
191
|
-
});
|
|
192
|
-
await page.next();
|
|
193
|
-
expect(loader).toHaveBeenCalledWith({
|
|
194
|
-
offset: 25,
|
|
195
|
-
limit: 5,
|
|
196
|
-
});
|
|
197
|
-
});
|
|
198
|
-
it("should preserve params across pagination", async () => {
|
|
199
|
-
const loader = vi.fn().mockResolvedValue({
|
|
200
|
-
data: ["x"],
|
|
201
|
-
offset: 10,
|
|
202
|
-
limit: 10,
|
|
203
|
-
});
|
|
204
|
-
const page = new OffsetPage({
|
|
205
|
-
params: { offset: 0, limit: 10 },
|
|
206
|
-
response: { data: ["a"], offset: 0, limit: 10 },
|
|
207
|
-
loader,
|
|
208
|
-
});
|
|
209
|
-
await page.next();
|
|
210
|
-
expect(loader).toHaveBeenCalledWith({
|
|
211
|
-
offset: 10,
|
|
212
|
-
limit: 10,
|
|
213
|
-
});
|
|
214
|
-
});
|
|
215
|
-
it("should return instance of OffsetPage", async () => {
|
|
216
|
-
const loader = vi.fn().mockResolvedValue({
|
|
217
|
-
data: ["d"],
|
|
218
|
-
offset: 10,
|
|
219
|
-
limit: 10,
|
|
220
|
-
});
|
|
221
|
-
const page = new OffsetPage({
|
|
222
|
-
params: { offset: 0, limit: 10 },
|
|
223
|
-
response: { data: ["a"], offset: 0, limit: 10 },
|
|
224
|
-
loader,
|
|
225
|
-
});
|
|
226
|
-
const nextPage = await page.next();
|
|
227
|
-
expect(nextPage).toBeInstanceOf(OffsetPage);
|
|
228
|
-
});
|
|
229
|
-
it("should handle undefined offset and limit", async () => {
|
|
230
|
-
const loader = vi.fn().mockResolvedValue({
|
|
231
|
-
data: ["b"],
|
|
232
|
-
offset: 0,
|
|
233
|
-
limit: 0,
|
|
234
|
-
});
|
|
235
|
-
const page = new OffsetPage({
|
|
236
|
-
params: {},
|
|
237
|
-
response: {
|
|
238
|
-
data: ["a"],
|
|
239
|
-
offset: undefined,
|
|
240
|
-
limit: undefined,
|
|
241
|
-
},
|
|
242
|
-
loader,
|
|
243
|
-
});
|
|
244
|
-
await page.next();
|
|
245
|
-
// offset ?? 0 + limit ?? 0 = 0
|
|
246
|
-
expect(loader).toHaveBeenCalledWith({
|
|
247
|
-
offset: 0,
|
|
248
|
-
});
|
|
249
|
-
});
|
|
250
|
-
});
|
|
251
|
-
describe("pages() generator", () => {
|
|
252
|
-
it("should yield only current page when last", async () => {
|
|
253
|
-
const page = new OffsetPage({
|
|
254
|
-
params: {},
|
|
255
|
-
response: { data: [], offset: 0, limit: 10 },
|
|
256
|
-
loader: vi.fn(),
|
|
257
|
-
});
|
|
258
|
-
const pages = [];
|
|
259
|
-
for await (const p of page.pages()) {
|
|
260
|
-
pages.push(p);
|
|
261
|
-
}
|
|
262
|
-
expect(pages).toHaveLength(1);
|
|
263
|
-
expect(pages[0]).toBe(page);
|
|
264
|
-
});
|
|
265
|
-
it("should yield multiple pages until last", async () => {
|
|
266
|
-
const loader = vi
|
|
267
|
-
.fn()
|
|
268
|
-
.mockResolvedValueOnce({
|
|
269
|
-
data: ["d", "e"],
|
|
270
|
-
offset: 3,
|
|
271
|
-
limit: 3,
|
|
272
|
-
total: 8,
|
|
273
|
-
})
|
|
274
|
-
.mockResolvedValueOnce({
|
|
275
|
-
data: ["f", "g"],
|
|
276
|
-
offset: 6,
|
|
277
|
-
limit: 3,
|
|
278
|
-
total: 8,
|
|
279
|
-
});
|
|
280
|
-
const page = new OffsetPage({
|
|
281
|
-
params: { offset: 0, limit: 3 },
|
|
282
|
-
response: { data: ["a", "b", "c"], offset: 0, limit: 3, total: 8 },
|
|
283
|
-
loader,
|
|
284
|
-
});
|
|
285
|
-
const pages = [];
|
|
286
|
-
for await (const p of page.pages()) {
|
|
287
|
-
pages.push(p);
|
|
288
|
-
}
|
|
289
|
-
expect(pages).toHaveLength(3);
|
|
290
|
-
expect(pages[0].data).toEqual(["a", "b", "c"]);
|
|
291
|
-
expect(pages[1].data).toEqual(["d", "e"]);
|
|
292
|
-
expect(pages[2].data).toEqual(["f", "g"]);
|
|
293
|
-
expect(loader).toHaveBeenCalledTimes(2);
|
|
294
|
-
});
|
|
295
|
-
it("should stop when next() returns null", async () => {
|
|
296
|
-
const loader = vi.fn().mockResolvedValue({
|
|
297
|
-
data: [],
|
|
298
|
-
offset: 10,
|
|
299
|
-
limit: 10,
|
|
300
|
-
});
|
|
301
|
-
const page = new OffsetPage({
|
|
302
|
-
params: { offset: 0, limit: 10 },
|
|
303
|
-
response: { data: ["a"], offset: 0, limit: 10 },
|
|
304
|
-
loader,
|
|
305
|
-
});
|
|
306
|
-
const pages = [];
|
|
307
|
-
for await (const p of page.pages()) {
|
|
308
|
-
pages.push(p);
|
|
309
|
-
}
|
|
310
|
-
expect(pages).toHaveLength(2); // original + one fetched (which is empty/last)
|
|
311
|
-
});
|
|
312
|
-
});
|
|
313
|
-
describe("Symbol.asyncIterator", () => {
|
|
314
|
-
it("should iterate over items in single page", async () => {
|
|
315
|
-
const page = new OffsetPage({
|
|
316
|
-
params: {},
|
|
317
|
-
response: { data: ["a", "b", "c"], offset: 0, limit: 10, total: 3 },
|
|
318
|
-
loader: vi.fn(),
|
|
319
|
-
});
|
|
320
|
-
const items = [];
|
|
321
|
-
for await (const item of page) {
|
|
322
|
-
items.push(item);
|
|
323
|
-
}
|
|
324
|
-
expect(items).toEqual(["a", "b", "c"]);
|
|
325
|
-
});
|
|
326
|
-
it("should iterate over items across multiple pages", async () => {
|
|
327
|
-
const loader = vi.fn().mockResolvedValueOnce({
|
|
328
|
-
data: ["d", "e"],
|
|
329
|
-
offset: 3,
|
|
330
|
-
limit: 3,
|
|
331
|
-
total: 5,
|
|
332
|
-
});
|
|
333
|
-
const page = new OffsetPage({
|
|
334
|
-
params: { offset: 0, limit: 3 },
|
|
335
|
-
response: { data: ["a", "b", "c"], offset: 0, limit: 3, total: 5 },
|
|
336
|
-
loader,
|
|
337
|
-
});
|
|
338
|
-
const items = [];
|
|
339
|
-
for await (const item of page) {
|
|
340
|
-
items.push(item);
|
|
341
|
-
}
|
|
342
|
-
expect(items).toEqual(["a", "b", "c", "d", "e"]);
|
|
343
|
-
});
|
|
344
|
-
it("should handle empty pages gracefully", async () => {
|
|
345
|
-
const page = new OffsetPage({
|
|
346
|
-
params: {},
|
|
347
|
-
response: { data: [], offset: 0, limit: 10 },
|
|
348
|
-
loader: vi.fn(),
|
|
349
|
-
});
|
|
350
|
-
const items = [];
|
|
351
|
-
for await (const item of page) {
|
|
352
|
-
items.push(item);
|
|
353
|
-
}
|
|
354
|
-
expect(items).toEqual([]);
|
|
355
|
-
});
|
|
356
|
-
it("should work with complex objects", async () => {
|
|
357
|
-
const users = [
|
|
358
|
-
{ id: 1, name: "Alice" },
|
|
359
|
-
{ id: 2, name: "Bob" },
|
|
360
|
-
];
|
|
361
|
-
const page = new OffsetPage({
|
|
362
|
-
params: {},
|
|
363
|
-
response: { data: users, offset: 0, limit: 10, total: 2 },
|
|
364
|
-
loader: vi.fn(),
|
|
365
|
-
});
|
|
366
|
-
const collected = [];
|
|
367
|
-
for await (const user of page) {
|
|
368
|
-
collected.push(user);
|
|
369
|
-
}
|
|
370
|
-
expect(collected).toEqual(users);
|
|
371
|
-
expect(collected[0]).toEqual({ id: 1, name: "Alice" });
|
|
372
|
-
});
|
|
373
|
-
});
|
|
374
|
-
describe("collect()", () => {
|
|
375
|
-
it("should collect all items from single page", async () => {
|
|
376
|
-
const page = new OffsetPage({
|
|
377
|
-
params: {},
|
|
378
|
-
response: { data: ["a", "b", "c"], offset: 0, limit: 10, total: 3 },
|
|
379
|
-
loader: vi.fn(),
|
|
380
|
-
});
|
|
381
|
-
const items = await page.collect();
|
|
382
|
-
expect(items).toEqual(["a", "b", "c"]);
|
|
383
|
-
});
|
|
384
|
-
it("should collect all items from multiple pages", async () => {
|
|
385
|
-
const loader = vi
|
|
386
|
-
.fn()
|
|
387
|
-
.mockResolvedValueOnce({
|
|
388
|
-
data: ["d", "e", "f"],
|
|
389
|
-
offset: 3,
|
|
390
|
-
limit: 3,
|
|
391
|
-
total: 8,
|
|
392
|
-
})
|
|
393
|
-
.mockResolvedValueOnce({
|
|
394
|
-
data: ["g", "h"],
|
|
395
|
-
offset: 6,
|
|
396
|
-
limit: 3,
|
|
397
|
-
total: 8,
|
|
398
|
-
});
|
|
399
|
-
const page = new OffsetPage({
|
|
400
|
-
params: { offset: 0, limit: 3 },
|
|
401
|
-
response: { data: ["a", "b", "c"], offset: 0, limit: 3, total: 8 },
|
|
402
|
-
loader,
|
|
403
|
-
});
|
|
404
|
-
const items = await page.collect();
|
|
405
|
-
expect(items).toEqual(["a", "b", "c", "d", "e", "f", "g", "h"]);
|
|
406
|
-
expect(loader).toHaveBeenCalledTimes(2);
|
|
407
|
-
});
|
|
408
|
-
it("should return empty array for empty page", async () => {
|
|
409
|
-
const page = new OffsetPage({
|
|
410
|
-
params: {},
|
|
411
|
-
response: { data: [], offset: 0, limit: 10 },
|
|
412
|
-
loader: vi.fn(),
|
|
413
|
-
});
|
|
414
|
-
const items = await page.collect();
|
|
415
|
-
expect(items).toEqual([]);
|
|
416
|
-
});
|
|
417
|
-
it("should preserve item order across pages", async () => {
|
|
418
|
-
const loader = vi
|
|
419
|
-
.fn()
|
|
420
|
-
.mockResolvedValueOnce({
|
|
421
|
-
data: [4, 5, 6],
|
|
422
|
-
offset: 3,
|
|
423
|
-
limit: 3,
|
|
424
|
-
total: 6,
|
|
425
|
-
});
|
|
426
|
-
const page = new OffsetPage({
|
|
427
|
-
params: { offset: 0, limit: 3 },
|
|
428
|
-
response: { data: [1, 2, 3], offset: 0, limit: 3, total: 6 },
|
|
429
|
-
loader,
|
|
430
|
-
});
|
|
431
|
-
const items = await page.collect();
|
|
432
|
-
expect(items).toEqual([1, 2, 3, 4, 5, 6]);
|
|
433
|
-
});
|
|
434
|
-
});
|
|
435
|
-
describe("edge cases", () => {
|
|
436
|
-
it("should handle consecutive next() calls correctly", async () => {
|
|
437
|
-
const loader = vi.fn().mockResolvedValue({
|
|
438
|
-
data: ["next"],
|
|
439
|
-
offset: 10,
|
|
440
|
-
limit: 10,
|
|
441
|
-
});
|
|
442
|
-
const page = new OffsetPage({
|
|
443
|
-
params: { offset: 0, limit: 10 },
|
|
444
|
-
response: { data: ["a"], offset: 0, limit: 10 },
|
|
445
|
-
loader,
|
|
446
|
-
});
|
|
447
|
-
const next1 = await page.next();
|
|
448
|
-
const next2 = await page.next();
|
|
449
|
-
expect(next1).not.toBe(null);
|
|
450
|
-
expect(next2).not.toBe(null);
|
|
451
|
-
expect(loader).toHaveBeenCalledTimes(2);
|
|
452
|
-
});
|
|
453
|
-
it("should handle loader throwing error", async () => {
|
|
454
|
-
const loader = vi.fn().mockRejectedValue(new Error("Database error"));
|
|
455
|
-
const page = new OffsetPage({
|
|
456
|
-
params: { offset: 0, limit: 10 },
|
|
457
|
-
response: { data: ["a"], offset: 0, limit: 10 },
|
|
458
|
-
loader,
|
|
459
|
-
});
|
|
460
|
-
await expect(page.next()).rejects.toThrow("Database error");
|
|
461
|
-
});
|
|
462
|
-
it("should not call loader when last is true", async () => {
|
|
463
|
-
const loader = vi.fn();
|
|
464
|
-
const page = new OffsetPage({
|
|
465
|
-
params: {},
|
|
466
|
-
response: { data: [], offset: 0, limit: 10 },
|
|
467
|
-
loader,
|
|
468
|
-
});
|
|
469
|
-
await page.next();
|
|
470
|
-
expect(loader).not.toHaveBeenCalled();
|
|
471
|
-
});
|
|
472
|
-
it("should handle params with custom properties", async () => {
|
|
473
|
-
const loader = vi.fn().mockResolvedValue({
|
|
474
|
-
data: ["b"],
|
|
475
|
-
offset: 10,
|
|
476
|
-
limit: 10,
|
|
477
|
-
});
|
|
478
|
-
const page = new OffsetPage({
|
|
479
|
-
params: { agentId: "agent-1", filter: "active", offset: 0, limit: 10 },
|
|
480
|
-
response: { data: ["a"], offset: 0, limit: 10 },
|
|
481
|
-
loader,
|
|
482
|
-
});
|
|
483
|
-
await page.next();
|
|
484
|
-
expect(loader).toHaveBeenCalledWith({
|
|
485
|
-
agentId: "agent-1",
|
|
486
|
-
filter: "active",
|
|
487
|
-
offset: 10,
|
|
488
|
-
limit: 10,
|
|
489
|
-
});
|
|
490
|
-
});
|
|
491
|
-
it("should handle large offsets correctly", async () => {
|
|
492
|
-
const loader = vi.fn().mockResolvedValue({
|
|
493
|
-
data: ["x"],
|
|
494
|
-
offset: 1000010,
|
|
495
|
-
limit: 10,
|
|
496
|
-
total: 2000000,
|
|
497
|
-
});
|
|
498
|
-
const page = new OffsetPage({
|
|
499
|
-
params: { offset: 1000000, limit: 10 },
|
|
500
|
-
response: { data: ["a"], offset: 1000000, limit: 10, total: 2000000 },
|
|
501
|
-
loader,
|
|
502
|
-
});
|
|
503
|
-
await page.next();
|
|
504
|
-
expect(loader).toHaveBeenCalledWith({
|
|
505
|
-
offset: 1000010,
|
|
506
|
-
limit: 10,
|
|
507
|
-
});
|
|
508
|
-
});
|
|
509
|
-
});
|
|
510
|
-
});
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
export interface PageParamsBase {
|
|
2
|
-
/**
|
|
3
|
-
* Maximum number of items to return in a single page.
|
|
4
|
-
*/
|
|
5
|
-
limit?: number;
|
|
6
|
-
}
|
|
7
|
-
/**
|
|
8
|
-
* Generic page abstraction shared by all pagination modes.
|
|
9
|
-
*
|
|
10
|
-
* Loader: function that, given params, returns a page response.
|
|
11
|
-
*/
|
|
12
|
-
export declare abstract class AbstractPage<T, TParams extends PageParamsBase, TResponse> implements AsyncIterable<T> {
|
|
13
|
-
protected readonly params: TParams;
|
|
14
|
-
protected readonly response: TResponse;
|
|
15
|
-
protected readonly loader: (params: TParams) => Promise<TResponse>;
|
|
16
|
-
constructor(args: {
|
|
17
|
-
params: TParams;
|
|
18
|
-
response: TResponse;
|
|
19
|
-
loader: (params: TParams) => Promise<TResponse>;
|
|
20
|
-
});
|
|
21
|
-
/**
|
|
22
|
-
* All items contained in this page.
|
|
23
|
-
*/
|
|
24
|
-
abstract get items(): T[];
|
|
25
|
-
/**
|
|
26
|
-
* True if this is the last page in the sequence.
|
|
27
|
-
*
|
|
28
|
-
* When `last` is true, `next()` will return null.
|
|
29
|
-
*/
|
|
30
|
-
abstract get last(): boolean;
|
|
31
|
-
/**
|
|
32
|
-
* Fetch the next page, or null if there is no next page.
|
|
33
|
-
*/
|
|
34
|
-
abstract next(): Promise<this | null>;
|
|
35
|
-
/**
|
|
36
|
-
* Iterate over this page and all subsequent pages.
|
|
37
|
-
*/
|
|
38
|
-
pages(): AsyncGenerator<this>;
|
|
39
|
-
/**
|
|
40
|
-
* Iterate over all items across this page and all subsequent pages.
|
|
41
|
-
*/
|
|
42
|
-
[Symbol.asyncIterator](): AsyncGenerator<T>;
|
|
43
|
-
/**
|
|
44
|
-
* Collect all items from this page and all subsequent pages into an array.
|
|
45
|
-
*/
|
|
46
|
-
collect(): Promise<T[]>;
|
|
47
|
-
}
|
|
48
|
-
//# sourceMappingURL=base.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"base.d.ts","sourceRoot":"","sources":["../../../src/api/pagination/base.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,cAAc;IAC7B;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;GAIG;AACH,8BAAsB,YAAY,CAAC,CAAC,EAAE,OAAO,SAAS,cAAc,EAAE,SAAS,CAC7E,YAAW,aAAa,CAAC,CAAC,CAAC;IAE3B,SAAS,CAAC,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACnC,SAAS,CAAC,QAAQ,CAAC,QAAQ,EAAE,SAAS,CAAC;IACvC,SAAS,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,OAAO,CAAC,SAAS,CAAC,CAAC;gBAEvD,IAAI,EAAE;QAChB,MAAM,EAAE,OAAO,CAAC;QAChB,QAAQ,EAAE,SAAS,CAAC;QACpB,MAAM,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,OAAO,CAAC,SAAS,CAAC,CAAC;KACjD;IAMD;;OAEG;IACH,QAAQ,KAAK,KAAK,IAAI,CAAC,EAAE,CAAC;IAE1B;;;;OAIG;IACH,QAAQ,KAAK,IAAI,IAAI,OAAO,CAAC;IAE7B;;OAEG;IACH,QAAQ,CAAC,IAAI,IAAI,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC;IAErC;;OAEG;IACI,KAAK,IAAI,cAAc,CAAC,IAAI,CAAC;IAQpC;;OAEG;IACI,CAAC,MAAM,CAAC,aAAa,CAAC,IAAI,cAAc,CAAC,CAAC,CAAC;IAQlD;;OAEG;IACG,OAAO,IAAI,OAAO,CAAC,CAAC,EAAE,CAAC;CAO9B"}
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Generic page abstraction shared by all pagination modes.
|
|
3
|
-
*
|
|
4
|
-
* Loader: function that, given params, returns a page response.
|
|
5
|
-
*/
|
|
6
|
-
export class AbstractPage {
|
|
7
|
-
params;
|
|
8
|
-
response;
|
|
9
|
-
loader;
|
|
10
|
-
constructor(args) {
|
|
11
|
-
this.params = args.params;
|
|
12
|
-
this.response = args.response;
|
|
13
|
-
this.loader = args.loader;
|
|
14
|
-
}
|
|
15
|
-
/**
|
|
16
|
-
* Iterate over this page and all subsequent pages.
|
|
17
|
-
*/
|
|
18
|
-
async *pages() {
|
|
19
|
-
let page = this;
|
|
20
|
-
while (page) {
|
|
21
|
-
yield page;
|
|
22
|
-
page = await page.next();
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
/**
|
|
26
|
-
* Iterate over all items across this page and all subsequent pages.
|
|
27
|
-
*/
|
|
28
|
-
async *[Symbol.asyncIterator]() {
|
|
29
|
-
for await (const page of this.pages()) {
|
|
30
|
-
for (const item of page.items) {
|
|
31
|
-
yield item;
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
/**
|
|
36
|
-
* Collect all items from this page and all subsequent pages into an array.
|
|
37
|
-
*/
|
|
38
|
-
async collect() {
|
|
39
|
-
const items = [];
|
|
40
|
-
for await (const item of this) {
|
|
41
|
-
items.push(item);
|
|
42
|
-
}
|
|
43
|
-
return items;
|
|
44
|
-
}
|
|
45
|
-
}
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import { AbstractPage, type PageParamsBase } from "./base.js";
|
|
2
|
-
export interface CursorPageParams extends PageParamsBase {
|
|
3
|
-
/**
|
|
4
|
-
* Pagination cursor returned from a previous page.
|
|
5
|
-
* If omitted, starts from the beginning of the collection.
|
|
6
|
-
*/
|
|
7
|
-
cursor?: string;
|
|
8
|
-
}
|
|
9
|
-
export interface CursorPageResponse<T> {
|
|
10
|
-
data: T[];
|
|
11
|
-
/**
|
|
12
|
-
* Cursor for the next page, or null if there is no next page.
|
|
13
|
-
*/
|
|
14
|
-
next: string | null;
|
|
15
|
-
/**
|
|
16
|
-
* True if this is the last page (no further pages).
|
|
17
|
-
*/
|
|
18
|
-
last: boolean;
|
|
19
|
-
}
|
|
20
|
-
export declare class CursorPage<T, TParams extends CursorPageParams = CursorPageParams> extends AbstractPage<T, TParams, CursorPageResponse<T>> {
|
|
21
|
-
data: T[];
|
|
22
|
-
private readonly _next;
|
|
23
|
-
private readonly _last;
|
|
24
|
-
constructor(args: {
|
|
25
|
-
params: TParams;
|
|
26
|
-
response: CursorPageResponse<T>;
|
|
27
|
-
loader: (params: TParams) => Promise<CursorPageResponse<T>>;
|
|
28
|
-
});
|
|
29
|
-
/**
|
|
30
|
-
* All items contained in this page.
|
|
31
|
-
*/
|
|
32
|
-
get items(): T[];
|
|
33
|
-
/**
|
|
34
|
-
* True if this is the last page in the sequence.
|
|
35
|
-
*
|
|
36
|
-
* When `last` is true, `next()` will return null.
|
|
37
|
-
*/
|
|
38
|
-
get last(): boolean;
|
|
39
|
-
/**
|
|
40
|
-
* Fetch the next page, or null if there is no next page.
|
|
41
|
-
*/
|
|
42
|
-
next(): Promise<this | null>;
|
|
43
|
-
}
|
|
44
|
-
//# sourceMappingURL=cursor.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"cursor.d.ts","sourceRoot":"","sources":["../../../src/api/pagination/cursor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,KAAK,cAAc,EAAE,MAAM,QAAQ,CAAC;AAE3D,MAAM,WAAW,gBAAiB,SAAQ,cAAc;IACtD;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,kBAAkB,CAAC,CAAC;IACnC,IAAI,EAAE,CAAC,EAAE,CAAC;IACV;;OAEG;IACH,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB;;OAEG;IACH,IAAI,EAAE,OAAO,CAAC;CACf;AAED,qBAAa,UAAU,CACrB,CAAC,EACD,OAAO,SAAS,gBAAgB,GAAG,gBAAgB,CACnD,SAAQ,YAAY,CAAC,CAAC,EAAE,OAAO,EAAE,kBAAkB,CAAC,CAAC,CAAC,CAAC;IACvD,IAAI,EAAE,CAAC,EAAE,CAAC;IACV,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAgB;IACtC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAU;gBAEpB,IAAI,EAAE;QAChB,MAAM,EAAE,OAAO,CAAC;QAChB,QAAQ,EAAE,kBAAkB,CAAC,CAAC,CAAC,CAAC;QAChC,MAAM,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,OAAO,CAAC,kBAAkB,CAAC,CAAC,CAAC,CAAC,CAAC;KAC7D;IAOD;;OAEG;IACH,IAAI,KAAK,IAAI,CAAC,EAAE,CAEf;IAED;;;;OAIG;IACH,IAAI,IAAI,IAAI,OAAO,CAIlB;IAED;;OAEG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC;CAuBnC"}
|