kernl 0.6.2 → 0.7.0

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.
Files changed (193) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/.turbo/turbo-check-types.log +1 -1
  3. package/CHANGELOG.md +44 -0
  4. package/dist/agent/__tests__/concurrency.test.js +1 -1
  5. package/dist/agent/__tests__/run.test.js +1 -1
  6. package/dist/agent/__tests__/systools.test.d.ts +2 -0
  7. package/dist/agent/__tests__/systools.test.d.ts.map +1 -0
  8. package/dist/agent/__tests__/systools.test.js +121 -0
  9. package/dist/{types/agent.d.ts → agent/types.d.ts} +19 -2
  10. package/dist/agent/types.d.ts.map +1 -0
  11. package/dist/agent.d.ts +48 -6
  12. package/dist/agent.d.ts.map +1 -1
  13. package/dist/agent.js +100 -13
  14. package/dist/api/models/thread.d.ts +1 -1
  15. package/dist/api/resources/agents/agents.d.ts +38 -0
  16. package/dist/api/resources/agents/agents.d.ts.map +1 -0
  17. package/dist/api/resources/agents/agents.js +44 -0
  18. package/dist/api/resources/agents/index.d.ts +2 -0
  19. package/dist/api/resources/agents/index.d.ts.map +1 -0
  20. package/dist/api/resources/agents/index.js +1 -0
  21. package/dist/api/resources/threads/threads.d.ts +1 -1
  22. package/dist/api/resources/threads/threads.d.ts.map +1 -1
  23. package/dist/api/resources/threads/threads.js +1 -1
  24. package/dist/api/resources/threads/types.d.ts +2 -2
  25. package/dist/api/resources/threads/types.d.ts.map +1 -1
  26. package/dist/context.d.ts +10 -4
  27. package/dist/context.d.ts.map +1 -1
  28. package/dist/context.js +5 -0
  29. package/dist/guardrail.d.ts +2 -2
  30. package/dist/index.d.ts +5 -3
  31. package/dist/index.d.ts.map +1 -1
  32. package/dist/index.js +4 -3
  33. package/dist/internal.d.ts +2 -2
  34. package/dist/internal.js +1 -1
  35. package/dist/kernl/index.d.ts +1 -1
  36. package/dist/kernl/index.d.ts.map +1 -1
  37. package/dist/kernl/index.js +0 -1
  38. package/dist/kernl/kernl.d.ts +11 -20
  39. package/dist/kernl/kernl.d.ts.map +1 -1
  40. package/dist/kernl/kernl.js +37 -34
  41. package/dist/kernl/types.d.ts +91 -0
  42. package/dist/kernl/types.d.ts.map +1 -0
  43. package/dist/lib/error.d.ts +2 -2
  44. package/dist/lifecycle.d.ts +2 -2
  45. package/dist/memory/codec.d.ts +32 -0
  46. package/dist/memory/codec.d.ts.map +1 -0
  47. package/dist/memory/codec.js +97 -0
  48. package/dist/memory/codecs/domain.d.ts +34 -0
  49. package/dist/memory/codecs/domain.d.ts.map +1 -0
  50. package/dist/memory/codecs/domain.js +99 -0
  51. package/dist/memory/codecs/identity.d.ts +12 -0
  52. package/dist/memory/codecs/identity.d.ts.map +1 -0
  53. package/dist/memory/codecs/identity.js +17 -0
  54. package/dist/memory/codecs/index.d.ts +31 -0
  55. package/dist/memory/codecs/index.d.ts.map +1 -0
  56. package/dist/memory/codecs/index.js +39 -0
  57. package/dist/memory/codecs/tpuf.d.ts +38 -0
  58. package/dist/memory/codecs/tpuf.d.ts.map +1 -0
  59. package/dist/memory/codecs/tpuf.js +90 -0
  60. package/dist/memory/encoder.d.ts +29 -0
  61. package/dist/memory/encoder.d.ts.map +1 -0
  62. package/dist/memory/encoder.js +45 -0
  63. package/dist/memory/handle.d.ts +89 -0
  64. package/dist/memory/handle.d.ts.map +1 -0
  65. package/dist/memory/handle.js +128 -0
  66. package/dist/memory/index.d.ts +12 -0
  67. package/dist/memory/index.d.ts.map +1 -0
  68. package/dist/memory/index.js +7 -0
  69. package/dist/memory/indexes.d.ts +91 -0
  70. package/dist/memory/indexes.d.ts.map +1 -0
  71. package/dist/memory/indexes.js +7 -0
  72. package/dist/memory/memory.d.ts +55 -0
  73. package/dist/memory/memory.d.ts.map +1 -0
  74. package/dist/memory/memory.js +113 -0
  75. package/dist/memory/schema.d.ts +41 -0
  76. package/dist/memory/schema.d.ts.map +1 -0
  77. package/dist/memory/schema.js +112 -0
  78. package/dist/memory/store.d.ts +36 -0
  79. package/dist/memory/store.d.ts.map +1 -0
  80. package/dist/memory/store.js +4 -0
  81. package/dist/memory/types.d.ts +250 -0
  82. package/dist/memory/types.d.ts.map +1 -0
  83. package/dist/memory/types.js +4 -0
  84. package/dist/storage/base.d.ts +6 -1
  85. package/dist/storage/base.d.ts.map +1 -1
  86. package/dist/storage/in-memory.d.ts +24 -2
  87. package/dist/storage/in-memory.d.ts.map +1 -1
  88. package/dist/storage/in-memory.js +131 -0
  89. package/dist/storage/thread.d.ts +1 -1
  90. package/dist/thread/__tests__/integration.test.js +1 -1
  91. package/dist/thread/__tests__/mock.d.ts +1 -1
  92. package/dist/thread/__tests__/namespace.test.js +1 -1
  93. package/dist/thread/__tests__/thread.test.js +1 -1
  94. package/dist/thread/thread.d.ts +2 -2
  95. package/dist/thread/thread.d.ts.map +1 -1
  96. package/dist/thread/thread.js +3 -1
  97. package/dist/{types/thread.d.ts → thread/types.d.ts} +2 -2
  98. package/dist/thread/types.d.ts.map +1 -0
  99. package/dist/thread/utils.d.ts +2 -2
  100. package/dist/thread/utils.d.ts.map +1 -1
  101. package/dist/tool/index.d.ts +1 -0
  102. package/dist/tool/index.d.ts.map +1 -1
  103. package/dist/tool/index.js +2 -0
  104. package/dist/tool/sys/index.d.ts +7 -0
  105. package/dist/tool/sys/index.d.ts.map +1 -0
  106. package/dist/tool/sys/index.js +6 -0
  107. package/dist/tool/sys/memory.d.ts +14 -0
  108. package/dist/tool/sys/memory.d.ts.map +1 -0
  109. package/dist/tool/sys/memory.js +103 -0
  110. package/dist/tool/tool.d.ts +1 -1
  111. package/dist/tool/tool.d.ts.map +1 -1
  112. package/dist/tool/tool.js +2 -2
  113. package/package.json +4 -2
  114. package/src/agent/__tests__/systools.test.ts +146 -0
  115. package/src/{types/agent.ts → agent/types.ts} +22 -1
  116. package/src/agent.ts +144 -36
  117. package/src/api/__tests__/threads.test.ts +2 -2
  118. package/src/api/models/thread.ts +1 -1
  119. package/src/api/resources/agents/agents.ts +56 -0
  120. package/src/api/resources/agents/index.ts +1 -0
  121. package/src/api/resources/threads/events.ts +1 -1
  122. package/src/api/resources/threads/threads.ts +2 -2
  123. package/src/api/resources/threads/types.ts +2 -2
  124. package/src/context.ts +14 -136
  125. package/src/guardrail.ts +2 -2
  126. package/src/index.ts +35 -6
  127. package/src/internal.ts +2 -2
  128. package/src/kernl/index.ts +8 -0
  129. package/src/{kernl.ts → kernl/kernl.ts} +50 -10
  130. package/src/kernl/types.ts +106 -0
  131. package/src/lib/error.ts +2 -2
  132. package/src/lifecycle.ts +2 -2
  133. package/src/memory/codecs/domain.ts +115 -0
  134. package/src/memory/codecs/identity.ts +28 -0
  135. package/src/memory/codecs/index.ts +61 -0
  136. package/src/memory/codecs/tpuf.ts +115 -0
  137. package/src/memory/encoder.ts +56 -0
  138. package/src/memory/handle.ts +189 -0
  139. package/src/memory/index.ts +49 -0
  140. package/src/memory/indexes.ts +108 -0
  141. package/src/memory/memory.ts +151 -0
  142. package/src/memory/schema.ts +142 -0
  143. package/src/memory/store.ts +47 -0
  144. package/src/memory/types.ts +282 -0
  145. package/src/storage/__tests__/in-memory.test.ts +1 -1
  146. package/src/storage/base.ts +7 -1
  147. package/src/storage/in-memory.ts +170 -2
  148. package/src/storage/thread.ts +1 -1
  149. package/src/thread/__tests__/integration.test.ts +1 -1
  150. package/src/thread/__tests__/mock.ts +1 -1
  151. package/src/thread/__tests__/thread.test.ts +1 -1
  152. package/src/thread/thread.ts +5 -3
  153. package/src/{types/thread.ts → thread/types.ts} +1 -1
  154. package/src/thread/utils.ts +2 -2
  155. package/src/tool/index.ts +3 -0
  156. package/src/tool/sys/index.ts +7 -0
  157. package/src/tool/sys/memory.ts +120 -0
  158. package/src/tool/tool.ts +8 -4
  159. package/tsconfig.tsbuildinfo +1 -0
  160. package/dist/api/__tests__/cursor-page.test.d.ts +0 -2
  161. package/dist/api/__tests__/cursor-page.test.d.ts.map +0 -1
  162. package/dist/api/__tests__/cursor-page.test.js +0 -414
  163. package/dist/api/__tests__/offset-page.test.d.ts +0 -2
  164. package/dist/api/__tests__/offset-page.test.d.ts.map +0 -1
  165. package/dist/api/__tests__/offset-page.test.js +0 -510
  166. package/dist/api/pagination/base.d.ts +0 -48
  167. package/dist/api/pagination/base.d.ts.map +0 -1
  168. package/dist/api/pagination/base.js +0 -45
  169. package/dist/api/pagination/cursor.d.ts +0 -44
  170. package/dist/api/pagination/cursor.d.ts.map +0 -1
  171. package/dist/api/pagination/cursor.js +0 -52
  172. package/dist/api/pagination/offset.d.ts +0 -42
  173. package/dist/api/pagination/offset.d.ts.map +0 -1
  174. package/dist/api/pagination/offset.js +0 -55
  175. package/dist/kernl/threads.d.ts +0 -110
  176. package/dist/kernl/threads.d.ts.map +0 -1
  177. package/dist/kernl/threads.js +0 -126
  178. package/dist/kernl.d.ts +0 -51
  179. package/dist/kernl.d.ts.map +0 -1
  180. package/dist/kernl.js +0 -91
  181. package/dist/types/agent.d.ts.map +0 -1
  182. package/dist/types/kernl.d.ts +0 -42
  183. package/dist/types/kernl.d.ts.map +0 -1
  184. package/dist/types/thread.d.ts.map +0 -1
  185. package/src/api/__tests__/cursor-page.test.ts +0 -512
  186. package/src/api/__tests__/offset-page.test.ts +0 -624
  187. package/src/api/pagination/base.ts +0 -79
  188. package/src/api/pagination/cursor.ts +0 -86
  189. package/src/api/pagination/offset.ts +0 -89
  190. package/src/types/kernl.ts +0 -51
  191. /package/dist/{types/agent.js → agent/types.js} +0 -0
  192. /package/dist/{types/kernl.js → kernl/types.js} +0 -0
  193. /package/dist/{types/thread.js → thread/types.js} +0 -0
@@ -0,0 +1,120 @@
1
+ /**
2
+ * Memory system toolkit.
3
+ *
4
+ * Provides tools for agents to store and retrieve memories.
5
+ * Enabled via `memory: true` in agent config.
6
+ */
7
+
8
+ import assert from "assert";
9
+ import { z } from "zod";
10
+
11
+ import { tool } from "../tool";
12
+ import { Toolkit } from "../toolkit";
13
+
14
+ // --- Tools ---
15
+
16
+ /**
17
+ * Search memories for relevant information using natural language.
18
+ */
19
+ const search = tool({
20
+ id: "memories.search",
21
+ description:
22
+ "Search your memories. " +
23
+ "Use this to recall facts, preferences, or context you've previously stored.",
24
+ parameters: z.object({
25
+ query: z.string().describe("Natural language search query"),
26
+ limit: z
27
+ .number()
28
+ .int()
29
+ .positive()
30
+ .optional()
31
+ .describe("Max results (default: 10)"),
32
+ }),
33
+ execute: async (ctx, { query, limit }) => {
34
+ assert(ctx.agent, "ctx.agent required for memory tools");
35
+
36
+ const mems = await ctx.agent.memories.search({
37
+ query,
38
+ limit: limit ?? 10,
39
+ });
40
+
41
+ return mems.map((h) => ({
42
+ id: h.document?.id,
43
+ text: h.document?.text,
44
+ score: h.score,
45
+ }));
46
+ },
47
+ });
48
+
49
+ /**
50
+ * Store a new memory to persist across conversations.
51
+ */
52
+ const create = tool({
53
+ id: "memories.create",
54
+ description:
55
+ "Store a new memory. Use this to remember important facts, user preferences, " +
56
+ "or context that should persist across conversations.",
57
+ parameters: z.object({
58
+ content: z.string().describe("Text content to remember"),
59
+ collection: z
60
+ .string()
61
+ .optional()
62
+ .describe("Category for organizing memories (default: 'facts')"),
63
+ }),
64
+ execute: async (ctx, { content, collection }) => {
65
+ assert(ctx.agent, "ctx.agent required for memory tools");
66
+
67
+ const mem = await ctx.agent.memories.create({
68
+ collection: collection ?? "facts",
69
+ content: { text: content },
70
+ });
71
+
72
+ return { id: mem.id, stored: true };
73
+ },
74
+ });
75
+
76
+ /**
77
+ * List stored memories, optionally filtered by collection.
78
+ */
79
+ const list = tool({
80
+ id: "memories.list",
81
+ description:
82
+ "List your stored memories. Use this to see what you've remembered, " +
83
+ "optionally filtered by collection.",
84
+ parameters: z.object({
85
+ collection: z.string().optional().describe("Filter by collection name"),
86
+ limit: z
87
+ .number()
88
+ .int()
89
+ .positive()
90
+ .optional()
91
+ .describe("Max results (default: 20)"),
92
+ }),
93
+ execute: async (ctx, { collection, limit }) => {
94
+ assert(ctx.agent, "ctx.agent required for memory tools");
95
+
96
+ const mems = await ctx.agent.memories.list({
97
+ collection,
98
+ limit: limit ?? 20,
99
+ });
100
+
101
+ return mems.map((r) => ({
102
+ id: r.id,
103
+ collection: r.collection,
104
+ text: r.content.text,
105
+ }));
106
+ },
107
+ });
108
+
109
+ // --- Toolkit ---
110
+
111
+ /**
112
+ * Memory system toolkit.
113
+ *
114
+ * Provides memories.search, memories.create, and memories.list tools.
115
+ */
116
+ export const memory = new Toolkit({
117
+ id: "sys.memory",
118
+ description: "Tools for storing and retrieving agent memories",
119
+ tools: [list, create, search],
120
+ });
package/src/tool/tool.ts CHANGED
@@ -3,12 +3,16 @@ import { Context, UnknownContext } from "@/context";
3
3
 
4
4
  import { ModelBehaviorError } from "@/lib/error";
5
5
  import { logger } from "@/lib/logger";
6
+
7
+ import {
8
+ FAILED,
9
+ COMPLETED,
10
+ INTERRUPTIBLE,
11
+ type LanguageModelTool,
12
+ } from "@kernl-sdk/protocol";
6
13
  import { json } from "@kernl-sdk/shared/lib";
7
- import { FAILED, COMPLETED, INTERRUPTIBLE } from "@kernl-sdk/protocol";
8
- import type { LanguageModelTool } from "@kernl-sdk/protocol";
9
14
 
10
15
  import type {
11
- ToolType,
12
16
  ToolConfig,
13
17
  ToolApprovalFunction,
14
18
  ToolEnabledFunction,
@@ -199,7 +203,7 @@ export class FunctionTool<
199
203
  description: this.description,
200
204
  parameters: z.toJSONSchema(this.parameters ?? z.object({}), {
201
205
  target: "draft-7",
202
- }) as any, // Use empty object if no parameters (matches AI SDK)
206
+ }) as any, // use empty object if no parameters (matches AI SDK)
203
207
  };
204
208
  }
205
209
  }
@@ -0,0 +1 @@
1
+ {"root":["./src/agent.ts","./src/context.ts","./src/guardrail.ts","./src/index.ts","./src/internal.ts","./src/kernl.ts","./src/lifecycle.ts","./src/task.ts","./src/agent/index.ts","./src/agent/__tests__/concurrency.test.ts","./src/agent/__tests__/run.test.ts","./src/api/__tests__/threads.test.ts","./src/api/models/index.ts","./src/api/models/thread.ts","./src/api/resources/threads/events.ts","./src/api/resources/threads/index.ts","./src/api/resources/threads/threads.ts","./src/api/resources/threads/types.ts","./src/api/resources/threads/utils.ts","./src/lib/env.ts","./src/lib/error.ts","./src/lib/logger.ts","./src/lib/serde/json.ts","./src/mcp/base.ts","./src/mcp/http.ts","./src/mcp/sse.ts","./src/mcp/stdio.ts","./src/mcp/types.ts","./src/mcp/utils.ts","./src/mcp/__tests__/base.test.ts","./src/mcp/__tests__/integration.test.ts","./src/mcp/__tests__/stdio.test.ts","./src/mcp/__tests__/utils.test.ts","./src/mcp/__tests__/fixtures/server.ts","./src/mcp/__tests__/fixtures/utils.ts","./src/memory/byte.ts","./src/memory/index.ts","./src/memory/indexes.ts","./src/memory/memory.ts","./src/memory/store.ts","./src/memory/types.ts","./src/storage/base.ts","./src/storage/in-memory.ts","./src/storage/index.ts","./src/storage/thread.ts","./src/storage/__tests__/in-memory.test.ts","./src/thread/index.ts","./src/thread/thread.ts","./src/thread/utils.ts","./src/thread/__tests__/integration.test.ts","./src/thread/__tests__/mock.ts","./src/thread/__tests__/namespace.test.ts","./src/thread/__tests__/thread-persistence.test.ts","./src/thread/__tests__/thread.test.ts","./src/thread/__tests__/fixtures/mock-model.ts","./src/tool/index.ts","./src/tool/tool.ts","./src/tool/toolkit.ts","./src/tool/types.ts","./src/tool/__tests__/fixtures.ts","./src/tool/__tests__/tool.test.ts","./src/tool/__tests__/toolkit.test.ts","./src/trace/processor.ts","./src/trace/traces.ts","./src/trace/utils.ts","./src/types/agent.ts","./src/types/kernl.ts","./src/types/thread.ts"],"version":"5.9.2"}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=cursor-page.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"cursor-page.test.d.ts","sourceRoot":"","sources":["../../../src/api/__tests__/cursor-page.test.ts"],"names":[],"mappings":""}
@@ -1,414 +0,0 @@
1
- import { describe, it, expect, vi } from "vitest";
2
- import { CursorPage, } from "../pagination/cursor.js";
3
- describe("CursorPage", () => {
4
- describe("construction", () => {
5
- it("should initialize with response data", () => {
6
- const response = {
7
- data: ["a", "b", "c"],
8
- next: "cursor_123",
9
- last: false,
10
- };
11
- const loader = vi.fn();
12
- const page = new CursorPage({
13
- params: {},
14
- response,
15
- loader,
16
- });
17
- expect(page.data).toEqual(["a", "b", "c"]);
18
- expect(page.items).toEqual(["a", "b", "c"]);
19
- expect(page.last).toBe(false);
20
- });
21
- it("should handle empty data array", () => {
22
- const response = {
23
- data: [],
24
- next: null,
25
- last: true,
26
- };
27
- const loader = vi.fn();
28
- const page = new CursorPage({
29
- params: {},
30
- response,
31
- loader,
32
- });
33
- expect(page.data).toEqual([]);
34
- expect(page.items).toEqual([]);
35
- expect(page.last).toBe(true);
36
- });
37
- it("should handle null data gracefully", () => {
38
- const response = {
39
- data: null, // simulate backend returning null
40
- next: null,
41
- last: true,
42
- };
43
- const loader = vi.fn();
44
- const page = new CursorPage({
45
- params: {},
46
- response,
47
- loader,
48
- });
49
- expect(page.data).toEqual([]);
50
- expect(page.items).toEqual([]);
51
- });
52
- });
53
- describe("items getter", () => {
54
- it("should return exact reference to data array", () => {
55
- const data = ["a", "b", "c"];
56
- const response = {
57
- data,
58
- next: null,
59
- last: false,
60
- };
61
- const page = new CursorPage({
62
- params: {},
63
- response,
64
- loader: vi.fn(),
65
- });
66
- expect(page.items).toBe(page.data);
67
- });
68
- });
69
- describe("last getter", () => {
70
- it("should return true when response.last is true", () => {
71
- const page = new CursorPage({
72
- params: {},
73
- response: { data: ["a"], next: "cursor", last: true },
74
- loader: vi.fn(),
75
- });
76
- expect(page.last).toBe(true);
77
- });
78
- it("should return true when next cursor is null", () => {
79
- const page = new CursorPage({
80
- params: {},
81
- response: { data: ["a"], next: null, last: false },
82
- loader: vi.fn(),
83
- });
84
- expect(page.last).toBe(true);
85
- });
86
- it("should return true when data is empty", () => {
87
- const page = new CursorPage({
88
- params: {},
89
- response: { data: [], next: "cursor", last: false },
90
- loader: vi.fn(),
91
- });
92
- expect(page.last).toBe(true);
93
- });
94
- it("should return false when has next cursor and data", () => {
95
- const page = new CursorPage({
96
- params: {},
97
- response: { data: ["a", "b"], next: "cursor_123", last: false },
98
- loader: vi.fn(),
99
- });
100
- expect(page.last).toBe(false);
101
- });
102
- it("should prioritize response.last over next cursor", () => {
103
- const page = new CursorPage({
104
- params: {},
105
- response: { data: ["a"], next: "cursor", last: true },
106
- loader: vi.fn(),
107
- });
108
- expect(page.last).toBe(true);
109
- });
110
- });
111
- describe("next()", () => {
112
- it("should return null when last is true", async () => {
113
- const page = new CursorPage({
114
- params: {},
115
- response: { data: ["a"], next: null, last: true },
116
- loader: vi.fn(),
117
- });
118
- const nextPage = await page.next();
119
- expect(nextPage).toBe(null);
120
- });
121
- it("should return null when no next cursor", async () => {
122
- const page = new CursorPage({
123
- params: {},
124
- response: { data: ["a"], next: null, last: false },
125
- loader: vi.fn(),
126
- });
127
- const nextPage = await page.next();
128
- expect(nextPage).toBe(null);
129
- });
130
- it("should fetch next page with cursor in params", async () => {
131
- const loader = vi.fn().mockResolvedValue({
132
- data: ["d", "e", "f"],
133
- next: "cursor_456",
134
- last: false,
135
- });
136
- const page = new CursorPage({
137
- params: { limit: 10 },
138
- response: { data: ["a", "b", "c"], next: "cursor_123", last: false },
139
- loader,
140
- });
141
- const nextPage = await page.next();
142
- expect(loader).toHaveBeenCalledTimes(1);
143
- expect(loader).toHaveBeenCalledWith({
144
- limit: 10,
145
- cursor: "cursor_123",
146
- });
147
- expect(nextPage).not.toBe(null);
148
- expect(nextPage.data).toEqual(["d", "e", "f"]);
149
- expect(nextPage.items).toEqual(["d", "e", "f"]);
150
- });
151
- it("should preserve params across pagination", async () => {
152
- const loader = vi.fn().mockResolvedValue({
153
- data: ["x"],
154
- next: null,
155
- last: true,
156
- });
157
- const page = new CursorPage({
158
- params: { limit: 5, cursor: "initial" },
159
- response: { data: ["a"], next: "cursor_next", last: false },
160
- loader,
161
- });
162
- await page.next();
163
- expect(loader).toHaveBeenCalledWith({
164
- limit: 5,
165
- cursor: "cursor_next", // cursor gets updated
166
- });
167
- });
168
- it("should return instance of CursorPage", async () => {
169
- const loader = vi.fn().mockResolvedValue({
170
- data: ["d"],
171
- next: null,
172
- last: true,
173
- });
174
- const page = new CursorPage({
175
- params: {},
176
- response: { data: ["a"], next: "cursor", last: false },
177
- loader,
178
- });
179
- const nextPage = await page.next();
180
- expect(nextPage).toBeInstanceOf(CursorPage);
181
- });
182
- });
183
- describe("pages() generator", () => {
184
- it("should yield only current page when last", async () => {
185
- const page = new CursorPage({
186
- params: {},
187
- response: { data: ["a"], next: null, last: true },
188
- loader: vi.fn(),
189
- });
190
- const pages = [];
191
- for await (const p of page.pages()) {
192
- pages.push(p);
193
- }
194
- expect(pages).toHaveLength(1);
195
- expect(pages[0]).toBe(page);
196
- });
197
- it("should yield multiple pages until last", async () => {
198
- const loader = vi
199
- .fn()
200
- .mockResolvedValueOnce({
201
- data: ["d", "e"],
202
- next: "cursor_2",
203
- last: false,
204
- })
205
- .mockResolvedValueOnce({
206
- data: ["f"],
207
- next: null,
208
- last: true,
209
- });
210
- const page = new CursorPage({
211
- params: {},
212
- response: { data: ["a", "b", "c"], next: "cursor_1", last: false },
213
- loader,
214
- });
215
- const pages = [];
216
- for await (const p of page.pages()) {
217
- pages.push(p);
218
- }
219
- expect(pages).toHaveLength(3);
220
- expect(pages[0].data).toEqual(["a", "b", "c"]);
221
- expect(pages[1].data).toEqual(["d", "e"]);
222
- expect(pages[2].data).toEqual(["f"]);
223
- expect(loader).toHaveBeenCalledTimes(2);
224
- });
225
- it("should stop when next() returns null", async () => {
226
- const loader = vi.fn().mockResolvedValue({
227
- data: [],
228
- next: null,
229
- last: true,
230
- });
231
- const page = new CursorPage({
232
- params: {},
233
- response: { data: ["a"], next: "cursor", last: false },
234
- loader,
235
- });
236
- const pages = [];
237
- for await (const p of page.pages()) {
238
- pages.push(p);
239
- }
240
- expect(pages).toHaveLength(2); // original + one fetched
241
- });
242
- });
243
- describe("Symbol.asyncIterator", () => {
244
- it("should iterate over items in single page", async () => {
245
- const page = new CursorPage({
246
- params: {},
247
- response: { data: ["a", "b", "c"], next: null, last: true },
248
- loader: vi.fn(),
249
- });
250
- const items = [];
251
- for await (const item of page) {
252
- items.push(item);
253
- }
254
- expect(items).toEqual(["a", "b", "c"]);
255
- });
256
- it("should iterate over items across multiple pages", async () => {
257
- const loader = vi.fn().mockResolvedValueOnce({
258
- data: ["d", "e"],
259
- next: null,
260
- last: true,
261
- });
262
- const page = new CursorPage({
263
- params: {},
264
- response: { data: ["a", "b", "c"], next: "cursor_1", last: false },
265
- loader,
266
- });
267
- const items = [];
268
- for await (const item of page) {
269
- items.push(item);
270
- }
271
- expect(items).toEqual(["a", "b", "c", "d", "e"]);
272
- });
273
- it("should handle empty pages gracefully", async () => {
274
- const page = new CursorPage({
275
- params: {},
276
- response: { data: [], next: null, last: true },
277
- loader: vi.fn(),
278
- });
279
- const items = [];
280
- for await (const item of page) {
281
- items.push(item);
282
- }
283
- expect(items).toEqual([]);
284
- });
285
- it("should work with complex objects", async () => {
286
- const users = [
287
- { id: "1", name: "Alice" },
288
- { id: "2", name: "Bob" },
289
- ];
290
- const page = new CursorPage({
291
- params: {},
292
- response: { data: users, next: null, last: true },
293
- loader: vi.fn(),
294
- });
295
- const collected = [];
296
- for await (const user of page) {
297
- collected.push(user);
298
- }
299
- expect(collected).toEqual(users);
300
- expect(collected[0]).toEqual({ id: "1", name: "Alice" });
301
- });
302
- });
303
- describe("collect()", () => {
304
- it("should collect all items from single page", async () => {
305
- const page = new CursorPage({
306
- params: {},
307
- response: { data: ["a", "b", "c"], next: null, last: true },
308
- loader: vi.fn(),
309
- });
310
- const items = await page.collect();
311
- expect(items).toEqual(["a", "b", "c"]);
312
- });
313
- it("should collect all items from multiple pages", async () => {
314
- const loader = vi
315
- .fn()
316
- .mockResolvedValueOnce({
317
- data: ["d", "e", "f"],
318
- next: "cursor_2",
319
- last: false,
320
- })
321
- .mockResolvedValueOnce({
322
- data: ["g", "h"],
323
- next: null,
324
- last: true,
325
- });
326
- const page = new CursorPage({
327
- params: {},
328
- response: { data: ["a", "b", "c"], next: "cursor_1", last: false },
329
- loader,
330
- });
331
- const items = await page.collect();
332
- expect(items).toEqual(["a", "b", "c", "d", "e", "f", "g", "h"]);
333
- expect(loader).toHaveBeenCalledTimes(2);
334
- });
335
- it("should return empty array for empty page", async () => {
336
- const page = new CursorPage({
337
- params: {},
338
- response: { data: [], next: null, last: true },
339
- loader: vi.fn(),
340
- });
341
- const items = await page.collect();
342
- expect(items).toEqual([]);
343
- });
344
- it("should preserve item order across pages", async () => {
345
- const loader = vi
346
- .fn()
347
- .mockResolvedValueOnce({ data: [4, 5, 6], next: null, last: true });
348
- const page = new CursorPage({
349
- params: {},
350
- response: { data: [1, 2, 3], next: "cursor", last: false },
351
- loader,
352
- });
353
- const items = await page.collect();
354
- expect(items).toEqual([1, 2, 3, 4, 5, 6]);
355
- });
356
- });
357
- describe("edge cases", () => {
358
- it("should handle consecutive next() calls correctly", async () => {
359
- const loader = vi.fn().mockResolvedValue({
360
- data: ["next"],
361
- next: null,
362
- last: true,
363
- });
364
- const page = new CursorPage({
365
- params: {},
366
- response: { data: ["a"], next: "cursor", last: false },
367
- loader,
368
- });
369
- const next1 = await page.next();
370
- const next2 = await page.next();
371
- expect(next1).not.toBe(null);
372
- expect(next2).not.toBe(null);
373
- expect(loader).toHaveBeenCalledTimes(2);
374
- });
375
- it("should handle loader throwing error", async () => {
376
- const loader = vi.fn().mockRejectedValue(new Error("Network error"));
377
- const page = new CursorPage({
378
- params: {},
379
- response: { data: ["a"], next: "cursor", last: false },
380
- loader,
381
- });
382
- await expect(page.next()).rejects.toThrow("Network error");
383
- });
384
- it("should not call loader when last is true", async () => {
385
- const loader = vi.fn();
386
- const page = new CursorPage({
387
- params: {},
388
- response: { data: ["a"], next: null, last: true },
389
- loader,
390
- });
391
- await page.next();
392
- expect(loader).not.toHaveBeenCalled();
393
- });
394
- it("should handle params with custom properties", async () => {
395
- const loader = vi.fn().mockResolvedValue({
396
- data: ["b"],
397
- next: null,
398
- last: true,
399
- });
400
- const page = new CursorPage({
401
- params: { agentId: "agent-1", filter: "active", limit: 10 },
402
- response: { data: ["a"], next: "cursor", last: false },
403
- loader,
404
- });
405
- await page.next();
406
- expect(loader).toHaveBeenCalledWith({
407
- agentId: "agent-1",
408
- filter: "active",
409
- limit: 10,
410
- cursor: "cursor",
411
- });
412
- });
413
- });
414
- });
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=offset-page.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"offset-page.test.d.ts","sourceRoot":"","sources":["../../../src/api/__tests__/offset-page.test.ts"],"names":[],"mappings":""}