@kernl-sdk/libsql 0.1.38 → 0.1.39

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 (137) hide show
  1. package/.turbo/turbo-build.log +5 -4
  2. package/CHANGELOG.md +8 -0
  3. package/README.md +225 -0
  4. package/dist/__tests__/constraints.test.d.ts +2 -0
  5. package/dist/__tests__/constraints.test.d.ts.map +1 -0
  6. package/dist/__tests__/constraints.test.js +97 -0
  7. package/dist/__tests__/helpers.d.ts +36 -0
  8. package/dist/__tests__/helpers.d.ts.map +1 -0
  9. package/dist/__tests__/helpers.js +80 -0
  10. package/dist/__tests__/memory.create-get.test.d.ts +2 -0
  11. package/dist/__tests__/memory.create-get.test.d.ts.map +1 -0
  12. package/dist/__tests__/memory.create-get.test.js +8 -0
  13. package/dist/__tests__/memory.delete.test.d.ts +2 -0
  14. package/dist/__tests__/memory.delete.test.d.ts.map +1 -0
  15. package/dist/__tests__/memory.delete.test.js +6 -0
  16. package/dist/__tests__/memory.list.test.d.ts +2 -0
  17. package/dist/__tests__/memory.list.test.d.ts.map +1 -0
  18. package/dist/__tests__/memory.list.test.js +8 -0
  19. package/dist/__tests__/memory.update.test.d.ts +2 -0
  20. package/dist/__tests__/memory.update.test.d.ts.map +1 -0
  21. package/dist/__tests__/memory.update.test.js +8 -0
  22. package/dist/__tests__/migrations.test.d.ts +2 -0
  23. package/dist/__tests__/migrations.test.d.ts.map +1 -0
  24. package/dist/__tests__/migrations.test.js +68 -0
  25. package/dist/__tests__/row-codecs.test.d.ts +2 -0
  26. package/dist/__tests__/row-codecs.test.d.ts.map +1 -0
  27. package/dist/__tests__/row-codecs.test.js +175 -0
  28. package/dist/__tests__/sql-utils.test.d.ts +2 -0
  29. package/dist/__tests__/sql-utils.test.d.ts.map +1 -0
  30. package/dist/__tests__/sql-utils.test.js +45 -0
  31. package/dist/__tests__/storage.init.test.d.ts +2 -0
  32. package/dist/__tests__/storage.init.test.d.ts.map +1 -0
  33. package/dist/__tests__/storage.init.test.js +63 -0
  34. package/dist/__tests__/thread.lifecycle.test.d.ts +2 -0
  35. package/dist/__tests__/thread.lifecycle.test.d.ts.map +1 -0
  36. package/dist/__tests__/thread.lifecycle.test.js +172 -0
  37. package/dist/__tests__/transaction.test.d.ts +2 -0
  38. package/dist/__tests__/transaction.test.d.ts.map +1 -0
  39. package/dist/__tests__/transaction.test.js +16 -0
  40. package/dist/__tests__/utils.test.d.ts +2 -0
  41. package/dist/__tests__/utils.test.d.ts.map +1 -0
  42. package/dist/__tests__/utils.test.js +31 -0
  43. package/dist/client.d.ts +46 -0
  44. package/dist/client.d.ts.map +1 -0
  45. package/dist/client.js +46 -0
  46. package/dist/index.d.ts +5 -0
  47. package/dist/index.d.ts.map +1 -1
  48. package/dist/index.js +5 -1
  49. package/dist/memory/__tests__/create-get.test.d.ts +2 -0
  50. package/dist/memory/__tests__/create-get.test.d.ts.map +1 -0
  51. package/dist/memory/__tests__/create-get.test.js +126 -0
  52. package/dist/memory/__tests__/delete.test.d.ts +2 -0
  53. package/dist/memory/__tests__/delete.test.d.ts.map +1 -0
  54. package/dist/memory/__tests__/delete.test.js +96 -0
  55. package/dist/memory/__tests__/list.test.d.ts +2 -0
  56. package/dist/memory/__tests__/list.test.d.ts.map +1 -0
  57. package/dist/memory/__tests__/list.test.js +168 -0
  58. package/dist/memory/__tests__/sql.test.d.ts +2 -0
  59. package/dist/memory/__tests__/sql.test.d.ts.map +1 -0
  60. package/dist/memory/__tests__/sql.test.js +159 -0
  61. package/dist/memory/__tests__/update.test.d.ts +2 -0
  62. package/dist/memory/__tests__/update.test.d.ts.map +1 -0
  63. package/dist/memory/__tests__/update.test.js +113 -0
  64. package/dist/memory/row.d.ts +11 -0
  65. package/dist/memory/row.d.ts.map +1 -0
  66. package/dist/memory/row.js +29 -0
  67. package/dist/memory/sql.d.ts +34 -0
  68. package/dist/memory/sql.d.ts.map +1 -0
  69. package/dist/memory/sql.js +109 -0
  70. package/dist/memory/store.d.ts +41 -0
  71. package/dist/memory/store.d.ts.map +1 -0
  72. package/dist/memory/store.js +132 -0
  73. package/dist/migrations.d.ts +32 -0
  74. package/dist/migrations.d.ts.map +1 -0
  75. package/dist/migrations.js +157 -0
  76. package/dist/sql.d.ts +28 -0
  77. package/dist/sql.d.ts.map +1 -0
  78. package/dist/sql.js +22 -0
  79. package/dist/storage.d.ts +75 -0
  80. package/dist/storage.d.ts.map +1 -0
  81. package/dist/storage.js +123 -0
  82. package/dist/thread/__tests__/append.test.d.ts +2 -0
  83. package/dist/thread/__tests__/append.test.d.ts.map +1 -0
  84. package/dist/thread/__tests__/append.test.js +141 -0
  85. package/dist/thread/__tests__/history.test.d.ts +2 -0
  86. package/dist/thread/__tests__/history.test.d.ts.map +1 -0
  87. package/dist/thread/__tests__/history.test.js +146 -0
  88. package/dist/thread/__tests__/sql.test.d.ts +2 -0
  89. package/dist/thread/__tests__/sql.test.d.ts.map +1 -0
  90. package/dist/thread/__tests__/sql.test.js +129 -0
  91. package/dist/thread/__tests__/store.test.d.ts +2 -0
  92. package/dist/thread/__tests__/store.test.d.ts.map +1 -0
  93. package/dist/thread/__tests__/store.test.js +170 -0
  94. package/dist/thread/row.d.ts +19 -0
  95. package/dist/thread/row.d.ts.map +1 -0
  96. package/dist/thread/row.js +65 -0
  97. package/dist/thread/sql.d.ts +33 -0
  98. package/dist/thread/sql.d.ts.map +1 -0
  99. package/dist/thread/sql.js +112 -0
  100. package/dist/thread/store.d.ts +67 -0
  101. package/dist/thread/store.d.ts.map +1 -0
  102. package/dist/thread/store.js +282 -0
  103. package/dist/utils.d.ts +10 -0
  104. package/dist/utils.d.ts.map +1 -0
  105. package/dist/utils.js +21 -0
  106. package/package.json +15 -11
  107. package/src/__tests__/constraints.test.ts +123 -0
  108. package/src/__tests__/helpers.ts +98 -0
  109. package/src/__tests__/migrations.test.ts +114 -0
  110. package/src/__tests__/row-codecs.test.ts +201 -0
  111. package/src/__tests__/sql-utils.test.ts +52 -0
  112. package/src/__tests__/storage.init.test.ts +92 -0
  113. package/src/__tests__/thread.lifecycle.test.ts +234 -0
  114. package/src/__tests__/transaction.test.ts +25 -0
  115. package/src/__tests__/utils.test.ts +38 -0
  116. package/src/client.ts +71 -0
  117. package/src/index.ts +10 -0
  118. package/src/memory/__tests__/create-get.test.ts +161 -0
  119. package/src/memory/__tests__/delete.test.ts +124 -0
  120. package/src/memory/__tests__/list.test.ts +198 -0
  121. package/src/memory/__tests__/sql.test.ts +186 -0
  122. package/src/memory/__tests__/update.test.ts +148 -0
  123. package/src/memory/row.ts +36 -0
  124. package/src/memory/sql.ts +142 -0
  125. package/src/memory/store.ts +173 -0
  126. package/src/migrations.ts +206 -0
  127. package/src/sql.ts +35 -0
  128. package/src/storage.ts +170 -0
  129. package/src/thread/__tests__/append.test.ts +201 -0
  130. package/src/thread/__tests__/history.test.ts +198 -0
  131. package/src/thread/__tests__/sql.test.ts +154 -0
  132. package/src/thread/__tests__/store.test.ts +219 -0
  133. package/src/thread/row.ts +77 -0
  134. package/src/thread/sql.ts +153 -0
  135. package/src/thread/store.ts +381 -0
  136. package/src/utils.ts +20 -0
  137. package/LICENSE +0 -201
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-get.test.d.ts","sourceRoot":"","sources":["../../../src/memory/__tests__/create-get.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,126 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from "vitest";
2
+ import { create_client, create_storage, testid } from "../../__tests__/helpers.js";
3
+ describe("LibSQLMemoryStore create/get", () => {
4
+ let client;
5
+ let storage;
6
+ beforeEach(async () => {
7
+ client = create_client();
8
+ storage = create_storage(client);
9
+ await storage.memories.list(); // init
10
+ });
11
+ afterEach(() => {
12
+ client.close();
13
+ });
14
+ it("creates and reads text memory", async () => {
15
+ const id = testid("mem");
16
+ const created = await storage.memories.create({
17
+ id,
18
+ scope: { namespace: "default", entityId: "user-1", agentId: "agent-1" },
19
+ kind: "semantic",
20
+ collection: "facts",
21
+ content: { text: "User prefers dark mode" },
22
+ });
23
+ expect(created.id).toBe(id);
24
+ expect(created.content).toEqual({ text: "User prefers dark mode" });
25
+ expect(created.kind).toBe("semantic");
26
+ const found = await storage.memories.get(id);
27
+ expect(found).not.toBeNull();
28
+ expect(found?.content).toEqual({ text: "User prefers dark mode" });
29
+ expect(found?.scope.namespace).toBe("default");
30
+ expect(found?.scope.entityId).toBe("user-1");
31
+ });
32
+ it("creates and reads object memory", async () => {
33
+ const id = testid("mem");
34
+ const objectContent = {
35
+ object: {
36
+ preferences: {
37
+ theme: "dark",
38
+ language: "en",
39
+ notifications: true,
40
+ },
41
+ },
42
+ };
43
+ const created = await storage.memories.create({
44
+ id,
45
+ scope: { namespace: "default" },
46
+ kind: "semantic",
47
+ content: objectContent,
48
+ });
49
+ expect(created.content).toEqual(objectContent);
50
+ const found = await storage.memories.get(id);
51
+ expect(found?.content).toEqual(objectContent);
52
+ });
53
+ it("creates working memory (wmem=true)", async () => {
54
+ const id = testid("mem");
55
+ const created = await storage.memories.create({
56
+ id,
57
+ scope: { namespace: "default" },
58
+ kind: "episodic",
59
+ content: { text: "Current task context" },
60
+ wmem: true,
61
+ });
62
+ expect(created.wmem).toBe(true);
63
+ const found = await storage.memories.get(id);
64
+ expect(found?.wmem).toBe(true);
65
+ });
66
+ it("creates short-term memory with expiration", async () => {
67
+ const id = testid("mem");
68
+ const expiresAt = Date.now() + 3600000; // 1 hour from now
69
+ const created = await storage.memories.create({
70
+ id,
71
+ scope: { namespace: "default" },
72
+ kind: "episodic",
73
+ content: { text: "Temporary note" },
74
+ smem: { expiresAt },
75
+ });
76
+ expect(created.smem.expiresAt).toBe(expiresAt);
77
+ const found = await storage.memories.get(id);
78
+ expect(found?.smem.expiresAt).toBe(expiresAt);
79
+ });
80
+ it("gets by id and returns null when missing", async () => {
81
+ const found = await storage.memories.get("nonexistent");
82
+ expect(found).toBeNull();
83
+ });
84
+ it("handles null scope fields", async () => {
85
+ const id = testid("mem");
86
+ const created = await storage.memories.create({
87
+ id,
88
+ scope: {}, // All scope fields null
89
+ kind: "semantic",
90
+ content: { text: "Global memory" },
91
+ });
92
+ expect(created.scope.namespace).toBeUndefined();
93
+ expect(created.scope.entityId).toBeUndefined();
94
+ expect(created.scope.agentId).toBeUndefined();
95
+ const found = await storage.memories.get(id);
96
+ expect(found?.scope.namespace).toBeUndefined();
97
+ });
98
+ it("stores and retrieves metadata", async () => {
99
+ const id = testid("mem");
100
+ const metadata = { confidence: 0.95, source: "user_input", version: 2 };
101
+ const created = await storage.memories.create({
102
+ id,
103
+ scope: { namespace: "default" },
104
+ kind: "semantic",
105
+ content: { text: "Test" },
106
+ metadata,
107
+ });
108
+ expect(created.metadata).toEqual(metadata);
109
+ const found = await storage.memories.get(id);
110
+ expect(found?.metadata).toEqual(metadata);
111
+ });
112
+ it("sets timestamps on create", async () => {
113
+ const before = Date.now();
114
+ const created = await storage.memories.create({
115
+ id: testid("mem"),
116
+ scope: { namespace: "default" },
117
+ kind: "semantic",
118
+ content: { text: "Test" },
119
+ });
120
+ const after = Date.now();
121
+ expect(created.timestamp).toBeGreaterThanOrEqual(before);
122
+ expect(created.timestamp).toBeLessThanOrEqual(after);
123
+ expect(created.createdAt).toBeGreaterThanOrEqual(before);
124
+ expect(created.updatedAt).toBeGreaterThanOrEqual(before);
125
+ });
126
+ });
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=delete.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"delete.test.d.ts","sourceRoot":"","sources":["../../../src/memory/__tests__/delete.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,96 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from "vitest";
2
+ import { create_client, create_storage, testid } from "../../__tests__/helpers.js";
3
+ describe("LibSQLMemoryStore delete", () => {
4
+ let client;
5
+ let storage;
6
+ beforeEach(async () => {
7
+ client = create_client();
8
+ storage = create_storage(client);
9
+ await storage.memories.list(); // init
10
+ });
11
+ afterEach(() => {
12
+ client.close();
13
+ });
14
+ it("deletes by id", async () => {
15
+ const id = testid("mem");
16
+ await storage.memories.create({
17
+ id,
18
+ scope: { namespace: "default" },
19
+ kind: "semantic",
20
+ content: { text: "To be deleted" },
21
+ });
22
+ // Verify exists
23
+ const before = await storage.memories.get(id);
24
+ expect(before).not.toBeNull();
25
+ // Delete
26
+ await storage.memories.delete(id);
27
+ // Verify gone
28
+ const after = await storage.memories.get(id);
29
+ expect(after).toBeNull();
30
+ });
31
+ it("delete is idempotent (no error for missing)", async () => {
32
+ // Should not throw
33
+ await storage.memories.delete("nonexistent");
34
+ });
35
+ it("mdelete removes multiple ids", async () => {
36
+ const ids = [testid("m1"), testid("m2"), testid("m3")];
37
+ // Create memories
38
+ for (const id of ids) {
39
+ await storage.memories.create({
40
+ id,
41
+ scope: { namespace: "default" },
42
+ kind: "semantic",
43
+ content: { text: `Memory ${id}` },
44
+ });
45
+ }
46
+ // Verify all exist
47
+ for (const id of ids) {
48
+ expect(await storage.memories.get(id)).not.toBeNull();
49
+ }
50
+ // Delete all
51
+ await storage.memories.mdelete(ids);
52
+ // Verify all gone
53
+ for (const id of ids) {
54
+ expect(await storage.memories.get(id)).toBeNull();
55
+ }
56
+ });
57
+ it("mdelete handles partial matches", async () => {
58
+ const existing = testid("existing");
59
+ await storage.memories.create({
60
+ id: existing,
61
+ scope: { namespace: "default" },
62
+ kind: "semantic",
63
+ content: { text: "Exists" },
64
+ });
65
+ // Delete mix of existing and non-existing
66
+ await storage.memories.mdelete([existing, "nonexistent-1", "nonexistent-2"]);
67
+ // Existing should be deleted
68
+ expect(await storage.memories.get(existing)).toBeNull();
69
+ });
70
+ it("mdelete handles empty array", async () => {
71
+ // Create a memory to ensure it's not affected
72
+ const id = testid("mem");
73
+ await storage.memories.create({
74
+ id,
75
+ scope: { namespace: "default" },
76
+ kind: "semantic",
77
+ content: { text: "Should remain" },
78
+ });
79
+ // Should not throw and not affect anything
80
+ await storage.memories.mdelete([]);
81
+ // Memory should still exist
82
+ const found = await storage.memories.get(id);
83
+ expect(found).not.toBeNull();
84
+ });
85
+ it("mdelete with single id", async () => {
86
+ const id = testid("mem");
87
+ await storage.memories.create({
88
+ id,
89
+ scope: { namespace: "default" },
90
+ kind: "semantic",
91
+ content: { text: "Single" },
92
+ });
93
+ await storage.memories.mdelete([id]);
94
+ expect(await storage.memories.get(id)).toBeNull();
95
+ });
96
+ });
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=list.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list.test.d.ts","sourceRoot":"","sources":["../../../src/memory/__tests__/list.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,168 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from "vitest";
2
+ import { create_client, create_storage } from "../../__tests__/helpers.js";
3
+ describe("LibSQLMemoryStore list", () => {
4
+ let client;
5
+ let storage;
6
+ beforeEach(async () => {
7
+ client = create_client();
8
+ storage = create_storage(client);
9
+ await storage.memories.list(); // init
10
+ // Seed test data
11
+ const now = Date.now();
12
+ await storage.memories.create({
13
+ id: "m1",
14
+ scope: { namespace: "ns1", entityId: "user-1", agentId: "agent-1" },
15
+ kind: "semantic",
16
+ collection: "facts",
17
+ content: { text: "Fact 1" },
18
+ wmem: true,
19
+ timestamp: now,
20
+ });
21
+ await storage.memories.create({
22
+ id: "m2",
23
+ scope: { namespace: "ns1", entityId: "user-1", agentId: "agent-1" },
24
+ kind: "semantic",
25
+ collection: "preferences",
26
+ content: { text: "Preference 1" },
27
+ wmem: false,
28
+ smem: { expiresAt: now + 3600000 },
29
+ timestamp: now + 100,
30
+ });
31
+ await storage.memories.create({
32
+ id: "m3",
33
+ scope: { namespace: "ns1", entityId: "user-2", agentId: "agent-1" },
34
+ kind: "episodic",
35
+ collection: "facts",
36
+ content: { text: "Fact 2" },
37
+ wmem: true,
38
+ timestamp: now + 200,
39
+ });
40
+ await storage.memories.create({
41
+ id: "m4",
42
+ scope: { namespace: "ns2", entityId: "user-1", agentId: "agent-2" },
43
+ kind: "semantic",
44
+ collection: "facts",
45
+ content: { text: "Fact 3" },
46
+ timestamp: now + 300,
47
+ });
48
+ });
49
+ afterEach(() => {
50
+ client.close();
51
+ });
52
+ it("lists all memories without filter", async () => {
53
+ const memories = await storage.memories.list();
54
+ expect(memories.length).toBe(4);
55
+ });
56
+ it("filters by namespace", async () => {
57
+ const memories = await storage.memories.list({
58
+ filter: { scope: { namespace: "ns1" } },
59
+ });
60
+ expect(memories.length).toBe(3);
61
+ expect(memories.every((m) => m.scope.namespace === "ns1")).toBe(true);
62
+ });
63
+ it("filters by entityId", async () => {
64
+ const memories = await storage.memories.list({
65
+ filter: { scope: { entityId: "user-1" } },
66
+ });
67
+ expect(memories.length).toBe(3);
68
+ expect(memories.every((m) => m.scope.entityId === "user-1")).toBe(true);
69
+ });
70
+ it("filters by agentId", async () => {
71
+ const memories = await storage.memories.list({
72
+ filter: { scope: { agentId: "agent-1" } },
73
+ });
74
+ expect(memories.length).toBe(3);
75
+ });
76
+ it("filters by multiple scope fields", async () => {
77
+ const memories = await storage.memories.list({
78
+ filter: {
79
+ scope: { namespace: "ns1", entityId: "user-1" },
80
+ },
81
+ });
82
+ expect(memories.length).toBe(2);
83
+ });
84
+ it("filters by collection", async () => {
85
+ const memories = await storage.memories.list({
86
+ filter: { collections: ["facts"] },
87
+ });
88
+ expect(memories.length).toBe(3);
89
+ expect(memories.every((m) => m.collection === "facts")).toBe(true);
90
+ });
91
+ it("filters by multiple collections", async () => {
92
+ const memories = await storage.memories.list({
93
+ filter: { collections: ["facts", "preferences"] },
94
+ });
95
+ expect(memories.length).toBe(4);
96
+ });
97
+ it("filters by wmem", async () => {
98
+ const wmemTrue = await storage.memories.list({
99
+ filter: { wmem: true },
100
+ });
101
+ expect(wmemTrue.length).toBe(2);
102
+ expect(wmemTrue.every((m) => m.wmem === true)).toBe(true);
103
+ const wmemFalse = await storage.memories.list({
104
+ filter: { wmem: false },
105
+ });
106
+ expect(wmemFalse.length).toBe(2);
107
+ });
108
+ it("filters by smem (has expiration)", async () => {
109
+ const hasSmem = await storage.memories.list({
110
+ filter: { smem: true },
111
+ });
112
+ expect(hasSmem.length).toBe(1);
113
+ expect(hasSmem[0].id).toBe("m2");
114
+ const noSmem = await storage.memories.list({
115
+ filter: { smem: false },
116
+ });
117
+ expect(noSmem.length).toBe(3);
118
+ });
119
+ it("filters by timestamp range", async () => {
120
+ const now = Date.now();
121
+ const m1 = await storage.memories.get("m1");
122
+ const m2 = await storage.memories.get("m2");
123
+ const memories = await storage.memories.list({
124
+ filter: {
125
+ after: m1.timestamp - 1,
126
+ before: m2.timestamp + 1,
127
+ },
128
+ });
129
+ expect(memories.length).toBe(2);
130
+ expect(memories.map((m) => m.id).sort()).toEqual(["m1", "m2"]);
131
+ });
132
+ it("orders by timestamp desc (default)", async () => {
133
+ const memories = await storage.memories.list();
134
+ // Should be in desc order by default
135
+ for (let i = 1; i < memories.length; i++) {
136
+ expect(memories[i - 1].timestamp).toBeGreaterThanOrEqual(memories[i].timestamp);
137
+ }
138
+ });
139
+ it("orders by timestamp asc", async () => {
140
+ const memories = await storage.memories.list({ order: "asc" });
141
+ for (let i = 1; i < memories.length; i++) {
142
+ expect(memories[i - 1].timestamp).toBeLessThanOrEqual(memories[i].timestamp);
143
+ }
144
+ });
145
+ it("applies limit", async () => {
146
+ const memories = await storage.memories.list({ limit: 2 });
147
+ expect(memories.length).toBe(2);
148
+ });
149
+ it("applies offset", async () => {
150
+ const all = await storage.memories.list({ order: "asc" });
151
+ const offset = await storage.memories.list({ order: "asc", offset: 2 });
152
+ expect(offset.length).toBe(2);
153
+ expect(offset[0].id).toBe(all[2].id);
154
+ });
155
+ it("combines filters, order, limit, and offset", async () => {
156
+ const memories = await storage.memories.list({
157
+ filter: {
158
+ scope: { namespace: "ns1" },
159
+ collections: ["facts"],
160
+ },
161
+ order: "asc",
162
+ limit: 1,
163
+ offset: 1,
164
+ });
165
+ expect(memories.length).toBe(1);
166
+ expect(memories[0].id).toBe("m3");
167
+ });
168
+ });
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=sql.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sql.test.d.ts","sourceRoot":"","sources":["../../../src/memory/__tests__/sql.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,159 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { SQL_WHERE, ORDER, SQL_UPDATE } from "../sql.js";
3
+ describe("LibSQL memory SQL codecs", () => {
4
+ describe("SQL_WHERE", () => {
5
+ it("returns empty clause when no filters", () => {
6
+ const result = SQL_WHERE.encode({ filter: undefined });
7
+ expect(result.sql).toBe("");
8
+ expect(result.params).toEqual([]);
9
+ });
10
+ it("encodes scope namespace filter", () => {
11
+ const result = SQL_WHERE.encode({
12
+ filter: { scope: { namespace: "default" } },
13
+ });
14
+ expect(result.sql).toBe("namespace = ?");
15
+ expect(result.params).toEqual(["default"]);
16
+ });
17
+ it("encodes scope entityId filter", () => {
18
+ const result = SQL_WHERE.encode({
19
+ filter: { scope: { entityId: "user-1" } },
20
+ });
21
+ expect(result.sql).toBe("entity_id = ?");
22
+ expect(result.params).toEqual(["user-1"]);
23
+ });
24
+ it("encodes scope agentId filter", () => {
25
+ const result = SQL_WHERE.encode({
26
+ filter: { scope: { agentId: "agent-1" } },
27
+ });
28
+ expect(result.sql).toBe("agent_id = ?");
29
+ expect(result.params).toEqual(["agent-1"]);
30
+ });
31
+ it("encodes full scope filter", () => {
32
+ const result = SQL_WHERE.encode({
33
+ filter: {
34
+ scope: {
35
+ namespace: "default",
36
+ entityId: "user-1",
37
+ agentId: "agent-1",
38
+ },
39
+ },
40
+ });
41
+ expect(result.sql).toBe("namespace = ? AND entity_id = ? AND agent_id = ?");
42
+ expect(result.params).toEqual(["default", "user-1", "agent-1"]);
43
+ });
44
+ it("encodes collections filter with IN clause", () => {
45
+ const result = SQL_WHERE.encode({
46
+ filter: { collections: ["facts", "preferences"] },
47
+ });
48
+ expect(result.sql).toBe("collection IN (?, ?)");
49
+ expect(result.params).toEqual(["facts", "preferences"]);
50
+ });
51
+ it("encodes wmem filter", () => {
52
+ const result = SQL_WHERE.encode({ filter: { wmem: true } });
53
+ expect(result.sql).toBe("wmem = ?");
54
+ expect(result.params).toEqual([1]); // SQLite boolean
55
+ });
56
+ it("encodes smem filter (has valid expiration)", () => {
57
+ const before = Date.now();
58
+ const result = SQL_WHERE.encode({ filter: { smem: true } });
59
+ const after = Date.now();
60
+ // smem=true means has expiration AND not expired
61
+ expect(result.sql).toBe("(smem_expires_at IS NOT NULL AND smem_expires_at > ?)");
62
+ expect(result.params.length).toBe(1);
63
+ expect(result.params[0]).toBeGreaterThanOrEqual(before);
64
+ expect(result.params[0]).toBeLessThanOrEqual(after);
65
+ });
66
+ it("encodes smem=false filter (no expiration or expired)", () => {
67
+ const before = Date.now();
68
+ const result = SQL_WHERE.encode({ filter: { smem: false } });
69
+ const after = Date.now();
70
+ // smem=false means no expiration OR already expired
71
+ expect(result.sql).toBe("(smem_expires_at IS NULL OR smem_expires_at <= ?)");
72
+ expect(result.params.length).toBe(1);
73
+ expect(result.params[0]).toBeGreaterThanOrEqual(before);
74
+ expect(result.params[0]).toBeLessThanOrEqual(after);
75
+ });
76
+ it("encodes timestamp range filters", () => {
77
+ const result = SQL_WHERE.encode({
78
+ filter: { after: 1700000000000, before: 1700100000000 },
79
+ });
80
+ expect(result.sql).toBe("timestamp > ? AND timestamp < ?");
81
+ expect(result.params).toEqual([1700000000000, 1700100000000]);
82
+ });
83
+ it("combines multiple filters with AND", () => {
84
+ const result = SQL_WHERE.encode({
85
+ filter: {
86
+ scope: { namespace: "default" },
87
+ wmem: true,
88
+ collections: ["facts"],
89
+ },
90
+ });
91
+ expect(result.sql).toContain("namespace = ?");
92
+ expect(result.sql).toContain("wmem = ?");
93
+ expect(result.sql).toContain("collection IN (?)");
94
+ expect(result.sql.match(/AND/g)?.length).toBe(2);
95
+ });
96
+ });
97
+ describe("ORDER", () => {
98
+ it("returns default ordering (desc)", () => {
99
+ const result = ORDER.encode({ order: undefined });
100
+ expect(result).toBe("timestamp DESC");
101
+ });
102
+ it("encodes asc order", () => {
103
+ const result = ORDER.encode({ order: "asc" });
104
+ expect(result).toBe("timestamp ASC");
105
+ });
106
+ it("encodes desc order", () => {
107
+ const result = ORDER.encode({ order: "desc" });
108
+ expect(result).toBe("timestamp DESC");
109
+ });
110
+ });
111
+ describe("SQL_UPDATE", () => {
112
+ it("encodes content update with JSON stringify", () => {
113
+ const content = { text: "Updated content" };
114
+ const result = SQL_UPDATE.encode({ patch: { id: "m1", content } });
115
+ expect(result.sql).toContain("content = ?");
116
+ expect(result.params).toContain(JSON.stringify(content));
117
+ });
118
+ it("encodes wmem flag update", () => {
119
+ const result = SQL_UPDATE.encode({ patch: { id: "m1", wmem: true } });
120
+ expect(result.sql).toContain("wmem = ?");
121
+ expect(result.params).toContain(1); // SQLite boolean
122
+ });
123
+ it("encodes smem expiration update", () => {
124
+ const result = SQL_UPDATE.encode({
125
+ patch: { id: "m1", smem: { expiresAt: 1700100000000 } },
126
+ });
127
+ expect(result.sql).toContain("smem_expires_at = ?");
128
+ expect(result.params).toContain(1700100000000);
129
+ });
130
+ it("encodes metadata update", () => {
131
+ const metadata = { confidence: 0.95 };
132
+ const result = SQL_UPDATE.encode({ patch: { id: "m1", metadata } });
133
+ expect(result.sql).toContain("metadata = ?");
134
+ expect(result.params).toContain(JSON.stringify(metadata));
135
+ });
136
+ it("always includes updated_at", () => {
137
+ const before = Date.now();
138
+ const result = SQL_UPDATE.encode({ patch: { id: "m1", wmem: false } });
139
+ const after = Date.now();
140
+ expect(result.sql).toContain("updated_at = ?");
141
+ const updatedAt = result.params.find((p) => typeof p === "number" && p >= before && p <= after);
142
+ expect(updatedAt).toBeDefined();
143
+ });
144
+ it("combines multiple updates", () => {
145
+ const result = SQL_UPDATE.encode({
146
+ patch: {
147
+ id: "m1",
148
+ content: { text: "New" },
149
+ wmem: true,
150
+ metadata: { edited: true },
151
+ },
152
+ });
153
+ expect(result.sql).toContain("content = ?");
154
+ expect(result.sql).toContain("wmem = ?");
155
+ expect(result.sql).toContain("metadata = ?");
156
+ expect(result.sql).toContain("updated_at = ?");
157
+ });
158
+ });
159
+ });
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=update.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"update.test.d.ts","sourceRoot":"","sources":["../../../src/memory/__tests__/update.test.ts"],"names":[],"mappings":""}