clawlet 0.3.0 → 0.4.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.
package/src/storage.ts CHANGED
@@ -5,10 +5,16 @@ export class LibSqlKeyValueStorage {
5
5
  private client: Client;
6
6
  private tableName: string;
7
7
 
8
- constructor(url: string, authToken?: string, tableName = 'kv_store') {
8
+
9
+ private constructor(url: string, authToken?: string, tableName = 'kv_store') {
10
+ this.client = authToken ? createClient({ url, authToken }) : createClient({ url });
9
11
  this.tableName = tableName;
10
- this.client = createClient({ url, authToken });
11
- this.init();
12
+ }
13
+
14
+ static async create<T>(url: string, authToken?: string, tableName = 'kv_store') {
15
+ const s = new LibSqlKeyValueStorage(url, authToken, tableName);
16
+ await s.init();
17
+ return s;
12
18
  }
13
19
 
14
20
  private async init() {
@@ -61,130 +67,176 @@ export class LibSqlKeyValueStorage {
61
67
  // --- B. List Storage (für History/Logs) ---
62
68
  export class LibSqlListStorage<T = any> {
63
69
  private client: Client;
64
- private tableName: string;
65
-
66
- constructor(url: string, authToken?: string, tableName = 'list_store') {
67
- this.tableName = tableName;
68
- this.client = createClient({ url, authToken });
69
- this.init();
70
- }
71
-
72
- private async init() {
73
- await this.client.execute(`
74
- CREATE TABLE IF NOT EXISTS ${this.tableName} (
75
- id INTEGER PRIMARY KEY AUTOINCREMENT,
76
- item TEXT,
77
- created_at DATETIME DEFAULT CURRENT_TIMESTAMP
78
- )
79
- `);
80
- }
81
-
82
- async push(item: T) {
83
- await this.client.execute({
84
- sql: `INSERT INTO ${this.tableName} (item) VALUES (?)`,
85
- args: [JSON.stringify(item)]
86
- });
87
- }
88
-
89
- // Bulk Insert für Performance
90
- async pushMany(items: T[]) {
91
- const promises = items.map(item => this.client.execute({
92
- sql: `INSERT INTO ${this.tableName} (item) VALUES (?)`,
93
- args: [JSON.stringify(item)]
94
- }));
95
- await Promise.all(promises);
96
- }
97
-
98
- async getAll(): Promise<T[]> {
99
- const rs = await this.client.execute(
100
- `SELECT item FROM ${this.tableName} ORDER BY id ASC`
101
- );
102
- return rs.rows.map(row => JSON.parse(row.item as string));
103
- }
104
-
105
- // Optional: Nur die letzten N Nachrichten holen (Kontext-Fenster!)
106
- async getRecent(limit: number): Promise<T[]> {
107
- // Trick: Erst sortieren DESC (neueste), limitieren, dann wieder ASC sortieren
108
- const rs = await this.client.execute({
109
- sql: `SELECT * FROM (
110
- SELECT item, id FROM ${this.tableName} ORDER BY id DESC LIMIT ?
111
- ) ORDER BY id ASC`,
112
- args: [limit]
113
- });
114
- return rs.rows.map(row => JSON.parse(row.item as string));
115
- }
116
-
117
- async count(): Promise<number> {
118
- const rs = await this.client.execute(
119
- `SELECT COUNT(*) as cnt FROM ${this.tableName}`
120
- );
121
- return Number(rs.rows[0]?.cnt ?? 0);
122
- }
70
+ private tableName = 'list_items';
123
71
 
124
- async clear() {
125
- await this.client.execute(`DELETE FROM ${this.tableName}`);
126
- // Reset autoincrement so IDs stay clean
127
- await this.client.execute(
128
- `DELETE FROM sqlite_sequence WHERE name = '${this.tableName}'`
129
- );
72
+ private constructor(url: string, authToken?: string) {
73
+ this.client = authToken ? createClient({ url, authToken }) : createClient({ url });
130
74
  }
131
- }
132
75
 
133
- // --- C. Skill History Storage (single table, partitioned by skill name) ---
134
- export class SkillHistoryStorage<T = any> {
135
- private client: Client;
136
-
137
- constructor(url: string, authToken?: string) {
138
- this.client = createClient({ url, authToken });
139
- this.init();
76
+ static async create<T>(url: string, authToken?: string) {
77
+ const s = new LibSqlListStorage(url, authToken);
78
+ await s.init();
79
+ return s;
140
80
  }
141
81
 
142
82
  private async init() {
143
83
  await this.client.execute(`
144
- CREATE TABLE IF NOT EXISTS skill_history (
84
+ CREATE TABLE IF NOT EXISTS ${this.tableName} (
145
85
  id INTEGER PRIMARY KEY AUTOINCREMENT,
146
86
  name TEXT NOT NULL,
147
- item TEXT,
87
+ item TEXT NOT NULL,
148
88
  created_at DATETIME DEFAULT CURRENT_TIMESTAMP
149
89
  )
150
90
  `);
151
91
  }
152
92
 
153
- async push(name: string, item: T) {
93
+ async push(name: string, item: T): Promise<void> {
154
94
  await this.client.execute({
155
- sql: `INSERT INTO skill_history (name, item) VALUES (?, ?)`,
95
+ sql: `INSERT INTO ${this.tableName} (name, item) VALUES (?, ?)`,
156
96
  args: [name, JSON.stringify(item)]
157
97
  });
158
98
  }
159
99
 
160
- async pushMany(name: string, items: T[]) {
161
- const promises = items.map(item => this.client.execute({
162
- sql: `INSERT INTO skill_history (name, item) VALUES (?, ?)`,
163
- args: [name, JSON.stringify(item)]
164
- }));
165
- await Promise.all(promises);
100
+ async pushMany(name: string, items: T[]): Promise<void> {
101
+ for (const item of items) {
102
+ this.push(name, item);
103
+ }
104
+ }
105
+
106
+ async replaceAll(name: string, items: T[]): Promise<void> {
107
+ const tx = await this.client.transaction();
108
+ try {
109
+ await tx.execute({
110
+ sql: `DELETE FROM ${this.tableName} WHERE name = ?`,
111
+ args: [name],
112
+ });
113
+ for (const item of items) {
114
+ await tx.execute({
115
+ sql: `INSERT INTO ${this.tableName} (name, item) VALUES (?, ?)`,
116
+ args: [name, JSON.stringify(item)]
117
+ });
118
+ }
119
+ await tx.commit();
120
+ } catch (e) {
121
+ await tx.rollback();
122
+ throw e;
123
+ }
166
124
  }
167
125
 
168
126
  async getAll(name: string): Promise<T[]> {
169
127
  const rs = await this.client.execute({
170
- sql: `SELECT item FROM skill_history WHERE name = ? ORDER BY id ASC`,
128
+ sql: `SELECT item FROM ${this.tableName} WHERE name = ? ORDER BY id ASC`,
171
129
  args: [name]
172
130
  });
173
131
  return rs.rows.map(row => JSON.parse(row.item as string));
174
132
  }
175
-
176
- async clear(name: string) {
177
- await this.client.execute({
178
- sql: `DELETE FROM skill_history WHERE name = ?`,
133
+
134
+ async count(name: string): Promise<number> {
135
+ const rs = await this.client.execute({
136
+ sql: `SELECT COUNT(*) as cnt FROM ${this.tableName} WHERE name = ?`,
179
137
  args: [name]
180
138
  });
139
+ return Number(rs.rows[0]?.cnt ?? 0);
181
140
  }
182
141
 
183
- async count(name: string): Promise<number> {
184
- const rs = await this.client.execute({
185
- sql: `SELECT COUNT(*) as cnt FROM skill_history WHERE name = ?`,
142
+ async clear(name: string): Promise<void> {
143
+ await this.client.execute({
144
+ sql: `DELETE FROM ${this.tableName} WHERE name = ?`,
186
145
  args: [name]
187
146
  });
188
- return Number(rs.rows[0]?.cnt ?? 0);
189
147
  }
190
148
  }
149
+
150
+
151
+ export class LibSqlFiFoStorage<T> {
152
+ private client: Client;
153
+ private tableName = 'queue_items';
154
+
155
+ constructor(url: string, authToken?: string) {
156
+ this.client = authToken ? createClient({ url, authToken }) : createClient({ url });
157
+ this.init();
158
+ }
159
+
160
+ private async init(): Promise<void> {
161
+ await this.client.execute(`
162
+ CREATE TABLE IF NOT EXISTS ${this.tableName} (
163
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
164
+ queue_name TEXT NOT NULL,
165
+ value TEXT NOT NULL,
166
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
167
+ );
168
+ `);
169
+ }
170
+
171
+ public async push(queue: string, item: T): Promise<void> {
172
+ await this.client.execute({
173
+ sql: `INSERT INTO ${this.tableName} (queue_name, value) VALUES (?, ?)`,
174
+ args: [queue, JSON.stringify(item)],
175
+ });
176
+ }
177
+
178
+ public async pushMany(queue: string, items: T[]): Promise<void> {
179
+ const tx = await this.client.transaction();
180
+ try {
181
+ for (const item of items) {
182
+ await tx.execute({
183
+ sql: `INSERT INTO ${this.tableName} (queue_name, value) VALUES (?, ?)`,
184
+ args: [queue, JSON.stringify(item)],
185
+ });
186
+ }
187
+ await tx.commit();
188
+ } catch (e) {
189
+ await tx.rollback();
190
+ throw e;
191
+ }
192
+ }
193
+
194
+ public async empty(queue: string): Promise<boolean> {
195
+ return (await this.count(queue)) === 0;
196
+ }
197
+
198
+ public async pop(queue: string): Promise<T | null> {
199
+ const tx = await this.client.transaction();
200
+ try {
201
+ const rs = await tx.execute({
202
+ sql: `SELECT id, value FROM ${this.tableName} WHERE queue_name = ? ORDER BY id ASC LIMIT 1`,
203
+ args: [queue],
204
+ });
205
+
206
+ if (rs.rows.length === 0) {
207
+ await tx.commit();
208
+ return null;
209
+ }
210
+
211
+ const row = rs.rows[0] as any;
212
+ const id = row.id;
213
+
214
+ await tx.execute({
215
+ sql: `DELETE FROM ${this.tableName} WHERE id = ?`,
216
+ args: [id!],
217
+ });
218
+
219
+ await tx.commit();
220
+
221
+ return JSON.parse(row.value as string) as T;
222
+ } catch (e) {
223
+ await tx.rollback();
224
+ throw e;
225
+ }
226
+ }
227
+
228
+ public async count(queue: string): Promise<number> {
229
+ const rs = await this.client.execute({
230
+ sql: `SELECT COUNT(*) as count FROM ${this.tableName} WHERE queue_name = ?`,
231
+ args: [queue],
232
+ });
233
+ return (rs.rows[0]?.count as number) ?? 0;
234
+ }
235
+
236
+ public async clear(queue: string): Promise<void> {
237
+ await this.client.execute({
238
+ sql: `DELETE FROM ${this.tableName} WHERE queue_name = ?`,
239
+ args: [queue],
240
+ });
241
+ }
242
+ }