@dexto/storage 1.6.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 (158) hide show
  1. package/LICENSE +44 -0
  2. package/README.md +80 -0
  3. package/dist/blob/factories/index.cjs +31 -0
  4. package/dist/blob/factories/index.d.cts +6 -0
  5. package/dist/blob/factories/index.d.ts +6 -0
  6. package/dist/blob/factories/index.d.ts.map +1 -0
  7. package/dist/blob/factories/index.js +6 -0
  8. package/dist/blob/factories/local.cjs +38 -0
  9. package/dist/blob/factories/local.d.cts +21 -0
  10. package/dist/blob/factories/local.d.ts +17 -0
  11. package/dist/blob/factories/local.d.ts.map +1 -0
  12. package/dist/blob/factories/local.js +14 -0
  13. package/dist/blob/factories/memory.cjs +44 -0
  14. package/dist/blob/factories/memory.d.cts +21 -0
  15. package/dist/blob/factories/memory.d.ts +17 -0
  16. package/dist/blob/factories/memory.d.ts.map +1 -0
  17. package/dist/blob/factories/memory.js +20 -0
  18. package/dist/blob/factory.cjs +16 -0
  19. package/dist/blob/factory.d.cts +36 -0
  20. package/dist/blob/factory.d.ts +35 -0
  21. package/dist/blob/factory.d.ts.map +1 -0
  22. package/dist/blob/factory.js +0 -0
  23. package/dist/blob/index.cjs +45 -0
  24. package/dist/blob/index.d.cts +8 -0
  25. package/dist/blob/index.d.ts +26 -0
  26. package/dist/blob/index.d.ts.map +1 -0
  27. package/dist/blob/index.js +19 -0
  28. package/dist/blob/local-blob-store.cjs +532 -0
  29. package/dist/blob/local-blob-store.d.cts +56 -0
  30. package/dist/blob/local-blob-store.d.ts +54 -0
  31. package/dist/blob/local-blob-store.d.ts.map +1 -0
  32. package/dist/blob/local-blob-store.js +498 -0
  33. package/dist/blob/memory-blob-store.cjs +327 -0
  34. package/dist/blob/memory-blob-store.d.cts +69 -0
  35. package/dist/blob/memory-blob-store.d.ts +67 -0
  36. package/dist/blob/memory-blob-store.d.ts.map +1 -0
  37. package/dist/blob/memory-blob-store.js +303 -0
  38. package/dist/blob/schemas.cjs +52 -0
  39. package/dist/blob/schemas.d.cts +87 -0
  40. package/dist/blob/schemas.d.ts +86 -0
  41. package/dist/blob/schemas.d.ts.map +1 -0
  42. package/dist/blob/schemas.js +25 -0
  43. package/dist/blob/types.cjs +16 -0
  44. package/dist/blob/types.d.cts +1 -0
  45. package/dist/blob/types.d.ts +2 -0
  46. package/dist/blob/types.d.ts.map +1 -0
  47. package/dist/blob/types.js +0 -0
  48. package/dist/cache/factories/index.cjs +31 -0
  49. package/dist/cache/factories/index.d.cts +6 -0
  50. package/dist/cache/factories/index.d.ts +6 -0
  51. package/dist/cache/factories/index.d.ts.map +1 -0
  52. package/dist/cache/factories/index.js +6 -0
  53. package/dist/cache/factories/memory.cjs +39 -0
  54. package/dist/cache/factories/memory.d.cts +21 -0
  55. package/dist/cache/factories/memory.d.ts +17 -0
  56. package/dist/cache/factories/memory.d.ts.map +1 -0
  57. package/dist/cache/factories/memory.js +15 -0
  58. package/dist/cache/factories/redis.cjs +65 -0
  59. package/dist/cache/factories/redis.d.cts +24 -0
  60. package/dist/cache/factories/redis.d.ts +20 -0
  61. package/dist/cache/factories/redis.d.ts.map +1 -0
  62. package/dist/cache/factories/redis.js +31 -0
  63. package/dist/cache/factory.cjs +16 -0
  64. package/dist/cache/factory.d.cts +42 -0
  65. package/dist/cache/factory.d.ts +41 -0
  66. package/dist/cache/factory.d.ts.map +1 -0
  67. package/dist/cache/factory.js +0 -0
  68. package/dist/cache/index.cjs +42 -0
  69. package/dist/cache/index.d.cts +7 -0
  70. package/dist/cache/index.d.ts +25 -0
  71. package/dist/cache/index.d.ts.map +1 -0
  72. package/dist/cache/index.js +17 -0
  73. package/dist/cache/memory-cache-store.cjs +106 -0
  74. package/dist/cache/memory-cache-store.d.cts +27 -0
  75. package/dist/cache/memory-cache-store.d.ts +25 -0
  76. package/dist/cache/memory-cache-store.d.ts.map +1 -0
  77. package/dist/cache/memory-cache-store.js +82 -0
  78. package/dist/cache/redis-store.cjs +176 -0
  79. package/dist/cache/redis-store.d.cts +34 -0
  80. package/dist/cache/redis-store.d.ts +32 -0
  81. package/dist/cache/redis-store.d.ts.map +1 -0
  82. package/dist/cache/redis-store.js +152 -0
  83. package/dist/cache/schemas.cjs +70 -0
  84. package/dist/cache/schemas.d.cts +93 -0
  85. package/dist/cache/schemas.d.ts +91 -0
  86. package/dist/cache/schemas.d.ts.map +1 -0
  87. package/dist/cache/schemas.js +43 -0
  88. package/dist/cache/types.cjs +16 -0
  89. package/dist/cache/types.d.cts +1 -0
  90. package/dist/cache/types.d.ts +2 -0
  91. package/dist/cache/types.d.ts.map +1 -0
  92. package/dist/cache/types.js +0 -0
  93. package/dist/database/factories/index.cjs +34 -0
  94. package/dist/database/factories/index.d.cts +7 -0
  95. package/dist/database/factories/index.d.ts +7 -0
  96. package/dist/database/factories/index.d.ts.map +1 -0
  97. package/dist/database/factories/index.js +8 -0
  98. package/dist/database/factories/memory.cjs +39 -0
  99. package/dist/database/factories/memory.d.cts +20 -0
  100. package/dist/database/factories/memory.d.ts +16 -0
  101. package/dist/database/factories/memory.d.ts.map +1 -0
  102. package/dist/database/factories/memory.js +15 -0
  103. package/dist/database/factories/postgres.cjs +61 -0
  104. package/dist/database/factories/postgres.d.cts +23 -0
  105. package/dist/database/factories/postgres.d.ts +19 -0
  106. package/dist/database/factories/postgres.d.ts.map +1 -0
  107. package/dist/database/factories/postgres.js +27 -0
  108. package/dist/database/factories/sqlite.cjs +65 -0
  109. package/dist/database/factories/sqlite.d.cts +24 -0
  110. package/dist/database/factories/sqlite.d.ts +20 -0
  111. package/dist/database/factories/sqlite.d.ts.map +1 -0
  112. package/dist/database/factories/sqlite.js +31 -0
  113. package/dist/database/factory.cjs +16 -0
  114. package/dist/database/factory.d.cts +42 -0
  115. package/dist/database/factory.d.ts +41 -0
  116. package/dist/database/factory.d.ts.map +1 -0
  117. package/dist/database/factory.js +0 -0
  118. package/dist/database/index.cjs +46 -0
  119. package/dist/database/index.d.cts +8 -0
  120. package/dist/database/index.d.ts +26 -0
  121. package/dist/database/index.d.ts.map +1 -0
  122. package/dist/database/index.js +24 -0
  123. package/dist/database/memory-database-store.cjs +121 -0
  124. package/dist/database/memory-database-store.d.cts +30 -0
  125. package/dist/database/memory-database-store.d.ts +28 -0
  126. package/dist/database/memory-database-store.d.ts.map +1 -0
  127. package/dist/database/memory-database-store.js +97 -0
  128. package/dist/database/postgres-store.cjs +342 -0
  129. package/dist/database/postgres-store.d.cts +57 -0
  130. package/dist/database/postgres-store.d.ts +55 -0
  131. package/dist/database/postgres-store.d.ts.map +1 -0
  132. package/dist/database/postgres-store.js +318 -0
  133. package/dist/database/schemas.cjs +82 -0
  134. package/dist/database/schemas.d.cts +127 -0
  135. package/dist/database/schemas.d.ts +125 -0
  136. package/dist/database/schemas.d.ts.map +1 -0
  137. package/dist/database/schemas.js +54 -0
  138. package/dist/database/sqlite-store.cjs +270 -0
  139. package/dist/database/sqlite-store.d.cts +35 -0
  140. package/dist/database/sqlite-store.d.ts +33 -0
  141. package/dist/database/sqlite-store.d.ts.map +1 -0
  142. package/dist/database/sqlite-store.js +236 -0
  143. package/dist/database/types.cjs +16 -0
  144. package/dist/database/types.d.cts +1 -0
  145. package/dist/database/types.d.ts +2 -0
  146. package/dist/database/types.d.ts.map +1 -0
  147. package/dist/database/types.js +0 -0
  148. package/dist/index.cjs +82 -0
  149. package/dist/index.d.cts +24 -0
  150. package/dist/index.d.ts +25 -0
  151. package/dist/index.d.ts.map +1 -0
  152. package/dist/index.js +50 -0
  153. package/dist/schemas.cjs +67 -0
  154. package/dist/schemas.d.cts +72 -0
  155. package/dist/schemas.d.ts +70 -0
  156. package/dist/schemas.d.ts.map +1 -0
  157. package/dist/schemas.js +46 -0
  158. package/package.json +55 -0
@@ -0,0 +1,54 @@
1
+ import { z } from "zod";
2
+ import { EnvExpandedString, ErrorScope, ErrorType, StorageErrorCode } from "@dexto/core";
3
+ const DATABASE_TYPES = ["in-memory", "sqlite", "postgres"];
4
+ const BaseDatabaseSchema = z.object({
5
+ maxConnections: z.number().int().positive().optional().describe("Maximum connections"),
6
+ idleTimeoutMillis: z.number().int().positive().optional().describe("Idle timeout in milliseconds"),
7
+ connectionTimeoutMillis: z.number().int().positive().optional().describe("Connection timeout in milliseconds"),
8
+ options: z.record(z.unknown()).optional().describe("Backend-specific options")
9
+ });
10
+ const InMemoryDatabaseSchema = BaseDatabaseSchema.extend({
11
+ type: z.literal("in-memory")
12
+ // In-memory database doesn't need connection options, but inherits pool options for consistency
13
+ }).strict();
14
+ const SqliteDatabaseSchema = BaseDatabaseSchema.extend({
15
+ type: z.literal("sqlite"),
16
+ path: z.string().describe(
17
+ "SQLite database file path (required for SQLite - CLI enrichment provides per-agent path)"
18
+ )
19
+ }).strict();
20
+ const PostgresDatabaseSchema = BaseDatabaseSchema.extend({
21
+ type: z.literal("postgres"),
22
+ url: EnvExpandedString().optional().describe("PostgreSQL connection URL (postgresql://...)"),
23
+ connectionString: EnvExpandedString().optional().describe("PostgreSQL connection string"),
24
+ host: z.string().optional().describe("PostgreSQL host"),
25
+ port: z.number().int().positive().optional().describe("PostgreSQL port"),
26
+ database: z.string().optional().describe("PostgreSQL database name"),
27
+ password: z.string().optional().describe("PostgreSQL password"),
28
+ // TODO: keyPrefix is reserved for future use - allows namespacing keys when multiple
29
+ // agents or environments share the same database (e.g., "dev:agent1:" vs "prod:agent2:")
30
+ keyPrefix: z.string().optional().describe('Optional key prefix for namespacing (e.g., "dev:myagent:")')
31
+ }).strict().superRefine((data, ctx) => {
32
+ if (!data.url && !data.connectionString && !data.host) {
33
+ ctx.addIssue({
34
+ code: z.ZodIssueCode.custom,
35
+ message: "PostgreSQL database requires one of 'url', 'connectionString', or 'host' to be specified",
36
+ path: ["url"],
37
+ params: {
38
+ code: StorageErrorCode.CONNECTION_CONFIG_MISSING,
39
+ scope: ErrorScope.STORAGE,
40
+ type: ErrorType.USER
41
+ }
42
+ });
43
+ }
44
+ });
45
+ const DatabaseConfigSchema = z.object({
46
+ type: z.string().describe("Database backend type identifier")
47
+ }).passthrough().describe("Database configuration (validated by image factory)");
48
+ export {
49
+ DATABASE_TYPES,
50
+ DatabaseConfigSchema,
51
+ InMemoryDatabaseSchema,
52
+ PostgresDatabaseSchema,
53
+ SqliteDatabaseSchema
54
+ };
@@ -0,0 +1,270 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+ var sqlite_store_exports = {};
30
+ __export(sqlite_store_exports, {
31
+ SQLiteStore: () => SQLiteStore
32
+ });
33
+ module.exports = __toCommonJS(sqlite_store_exports);
34
+ var import_path = require("path");
35
+ var import_fs = require("fs");
36
+ var import_core = require("@dexto/core");
37
+ let BetterSqlite3Database;
38
+ class SQLiteStore {
39
+ db = null;
40
+ // Database.Database
41
+ dbPath;
42
+ config;
43
+ logger;
44
+ constructor(config, logger) {
45
+ this.config = config;
46
+ this.dbPath = "";
47
+ this.logger = logger.createChild(import_core.DextoLogComponent.STORAGE);
48
+ }
49
+ initializeTables() {
50
+ this.logger.debug("SQLite initializing database schema...");
51
+ try {
52
+ this.db.exec(`
53
+ CREATE TABLE IF NOT EXISTS kv_store (
54
+ key TEXT PRIMARY KEY,
55
+ value TEXT NOT NULL,
56
+ created_at INTEGER DEFAULT (strftime('%s', 'now')),
57
+ updated_at INTEGER DEFAULT (strftime('%s', 'now'))
58
+ )
59
+ `);
60
+ this.db.exec(`
61
+ CREATE TABLE IF NOT EXISTS list_store (
62
+ key TEXT NOT NULL,
63
+ value TEXT NOT NULL,
64
+ sequence INTEGER,
65
+ created_at INTEGER DEFAULT (strftime('%s', 'now')),
66
+ PRIMARY KEY (key, sequence)
67
+ )
68
+ `);
69
+ this.db.exec(`
70
+ CREATE INDEX IF NOT EXISTS idx_kv_store_key ON kv_store(key);
71
+ CREATE INDEX IF NOT EXISTS idx_list_store_key ON list_store(key);
72
+ CREATE INDEX IF NOT EXISTS idx_list_store_sequence ON list_store(key, sequence);
73
+ `);
74
+ this.logger.debug(
75
+ "SQLite database schema initialized: kv_store, list_store tables with indexes"
76
+ );
77
+ } catch (error) {
78
+ throw import_core.StorageError.migrationFailed(
79
+ error instanceof Error ? error.message : String(error),
80
+ {
81
+ operation: "table_initialization",
82
+ backend: "sqlite"
83
+ }
84
+ );
85
+ }
86
+ }
87
+ async connect() {
88
+ if (this.db) return;
89
+ if (!BetterSqlite3Database) {
90
+ try {
91
+ const module2 = await import("better-sqlite3");
92
+ BetterSqlite3Database = module2.default || module2;
93
+ } catch (error) {
94
+ const err = error;
95
+ if (err.code === "ERR_MODULE_NOT_FOUND") {
96
+ throw import_core.StorageError.dependencyNotInstalled(
97
+ "SQLite",
98
+ "better-sqlite3",
99
+ "npm install better-sqlite3"
100
+ );
101
+ }
102
+ throw import_core.StorageError.connectionFailed(
103
+ `Failed to import better-sqlite3: ${error instanceof Error ? error.message : String(error)}`
104
+ );
105
+ }
106
+ }
107
+ this.dbPath = this.config.path;
108
+ this.logger.info(`SQLite using database file: ${this.dbPath}`);
109
+ const dir = (0, import_path.dirname)(this.dbPath);
110
+ this.logger.debug(`SQLite ensuring directory exists: ${dir}`);
111
+ try {
112
+ (0, import_fs.mkdirSync)(dir, { recursive: true });
113
+ } catch (error) {
114
+ this.logger.debug(`Directory creation result: ${error ? "exists" : "created"}`);
115
+ }
116
+ const sqliteOptions = this.config.options || {};
117
+ this.logger.debug(`SQLite initializing database with config:`, {
118
+ readonly: sqliteOptions.readonly || false,
119
+ fileMustExist: sqliteOptions.fileMustExist || false,
120
+ timeout: sqliteOptions.timeout || 5e3
121
+ });
122
+ this.db = new BetterSqlite3Database(this.dbPath, {
123
+ readonly: sqliteOptions.readonly || false,
124
+ fileMustExist: sqliteOptions.fileMustExist || false,
125
+ timeout: sqliteOptions.timeout || 5e3,
126
+ verbose: sqliteOptions.verbose ? (message, ...additionalArgs) => {
127
+ const messageStr = typeof message === "string" ? message : typeof message === "object" && message !== null ? JSON.stringify(message) : String(message);
128
+ this.logger.debug(
129
+ messageStr,
130
+ additionalArgs.length > 0 ? { args: additionalArgs } : void 0
131
+ );
132
+ } : void 0
133
+ });
134
+ this.db.pragma("journal_mode = WAL");
135
+ this.logger.debug("SQLite enabled WAL mode for better concurrency");
136
+ this.initializeTables();
137
+ this.logger.info(`\u2705 SQLite store successfully connected to: ${this.dbPath}`);
138
+ }
139
+ async disconnect() {
140
+ if (this.db) {
141
+ this.db.close();
142
+ this.db = null;
143
+ }
144
+ }
145
+ isConnected() {
146
+ return this.db !== null;
147
+ }
148
+ getStoreType() {
149
+ return "sqlite";
150
+ }
151
+ // Core operations
152
+ async get(key) {
153
+ this.checkConnection();
154
+ try {
155
+ const row = this.db.prepare("SELECT value FROM kv_store WHERE key = ?").get(key);
156
+ return row ? JSON.parse(row.value) : void 0;
157
+ } catch (error) {
158
+ throw import_core.StorageError.readFailed(
159
+ "get",
160
+ error instanceof Error ? error.message : String(error),
161
+ { key }
162
+ );
163
+ }
164
+ }
165
+ async set(key, value) {
166
+ this.checkConnection();
167
+ try {
168
+ const serialized = JSON.stringify(value);
169
+ this.db.prepare(
170
+ "INSERT OR REPLACE INTO kv_store (key, value, updated_at) VALUES (?, ?, ?)"
171
+ ).run(key, serialized, Date.now());
172
+ } catch (error) {
173
+ throw import_core.StorageError.writeFailed(
174
+ "set",
175
+ error instanceof Error ? error.message : String(error),
176
+ { key }
177
+ );
178
+ }
179
+ }
180
+ async delete(key) {
181
+ this.checkConnection();
182
+ try {
183
+ this.db.prepare("DELETE FROM kv_store WHERE key = ?").run(key);
184
+ this.db.prepare("DELETE FROM list_store WHERE key = ?").run(key);
185
+ } catch (error) {
186
+ throw import_core.StorageError.deleteFailed(
187
+ "delete",
188
+ error instanceof Error ? error.message : String(error),
189
+ { key }
190
+ );
191
+ }
192
+ }
193
+ // List operations
194
+ async list(prefix) {
195
+ this.checkConnection();
196
+ try {
197
+ const kvKeys = this.db.prepare("SELECT key FROM kv_store WHERE key LIKE ?").all(`${prefix}%`);
198
+ const listKeys = this.db.prepare("SELECT DISTINCT key FROM list_store WHERE key LIKE ?").all(`${prefix}%`);
199
+ const allKeys = /* @__PURE__ */ new Set([
200
+ ...kvKeys.map((row) => row.key),
201
+ ...listKeys.map((row) => row.key)
202
+ ]);
203
+ return Array.from(allKeys).sort();
204
+ } catch (error) {
205
+ throw import_core.StorageError.readFailed(
206
+ "list",
207
+ error instanceof Error ? error.message : String(error),
208
+ { prefix }
209
+ );
210
+ }
211
+ }
212
+ async append(key, item) {
213
+ this.checkConnection();
214
+ try {
215
+ const serialized = JSON.stringify(item);
216
+ this.db.prepare(
217
+ "INSERT INTO list_store (key, value, sequence) VALUES (?, ?, (SELECT COALESCE(MAX(sequence), 0) + 1 FROM list_store WHERE key = ?))"
218
+ ).run(key, serialized, key);
219
+ } catch (error) {
220
+ throw import_core.StorageError.writeFailed(
221
+ "append",
222
+ error instanceof Error ? error.message : String(error),
223
+ { key }
224
+ );
225
+ }
226
+ }
227
+ async getRange(key, start, count) {
228
+ this.checkConnection();
229
+ try {
230
+ const rows = this.db.prepare(
231
+ "SELECT value FROM list_store WHERE key = ? ORDER BY sequence ASC LIMIT ? OFFSET ?"
232
+ ).all(key, count, start);
233
+ return rows.map((row) => JSON.parse(row.value));
234
+ } catch (error) {
235
+ throw import_core.StorageError.readFailed(
236
+ "getRange",
237
+ error instanceof Error ? error.message : String(error),
238
+ { key, start, count }
239
+ );
240
+ }
241
+ }
242
+ // Schema management
243
+ checkConnection() {
244
+ if (!this.db) {
245
+ throw import_core.StorageError.notConnected("SQLiteStore");
246
+ }
247
+ }
248
+ // Maintenance operations
249
+ async vacuum() {
250
+ this.checkConnection();
251
+ this.db.exec("VACUUM");
252
+ }
253
+ async getStats() {
254
+ this.checkConnection();
255
+ const kvCount = this.db.prepare("SELECT COUNT(*) as count FROM kv_store").get();
256
+ const listCount = this.db.prepare("SELECT COUNT(*) as count FROM list_store").get();
257
+ const dbSize = this.db.prepare(
258
+ "SELECT page_count * page_size as size FROM pragma_page_count(), pragma_page_size()"
259
+ ).get();
260
+ return {
261
+ kvCount: kvCount.count,
262
+ listCount: listCount.count,
263
+ dbSize: dbSize.size
264
+ };
265
+ }
266
+ }
267
+ // Annotate the CommonJS export names for ESM import in node:
268
+ 0 && (module.exports = {
269
+ SQLiteStore
270
+ });
@@ -0,0 +1,35 @@
1
+ import { Database, Logger } from '@dexto/core';
2
+ import { SqliteDatabaseConfig } from './schemas.cjs';
3
+ import 'zod';
4
+
5
+ /**
6
+ * SQLite database store for local development and production.
7
+ * Implements the Database interface with proper schema and connection handling.
8
+ */
9
+ declare class SQLiteStore implements Database {
10
+ private db;
11
+ private dbPath;
12
+ private config;
13
+ private logger;
14
+ constructor(config: SqliteDatabaseConfig, logger: Logger);
15
+ private initializeTables;
16
+ connect(): Promise<void>;
17
+ disconnect(): Promise<void>;
18
+ isConnected(): boolean;
19
+ getStoreType(): string;
20
+ get<T>(key: string): Promise<T | undefined>;
21
+ set<T>(key: string, value: T): Promise<void>;
22
+ delete(key: string): Promise<void>;
23
+ list(prefix: string): Promise<string[]>;
24
+ append<T>(key: string, item: T): Promise<void>;
25
+ getRange<T>(key: string, start: number, count: number): Promise<T[]>;
26
+ private checkConnection;
27
+ vacuum(): Promise<void>;
28
+ getStats(): Promise<{
29
+ kvCount: number;
30
+ listCount: number;
31
+ dbSize: number;
32
+ }>;
33
+ }
34
+
35
+ export { SQLiteStore };
@@ -0,0 +1,33 @@
1
+ import type { Database } from './types.js';
2
+ import type { Logger } from '@dexto/core';
3
+ import type { SqliteDatabaseConfig } from './schemas.js';
4
+ /**
5
+ * SQLite database store for local development and production.
6
+ * Implements the Database interface with proper schema and connection handling.
7
+ */
8
+ export declare class SQLiteStore implements Database {
9
+ private db;
10
+ private dbPath;
11
+ private config;
12
+ private logger;
13
+ constructor(config: SqliteDatabaseConfig, logger: Logger);
14
+ private initializeTables;
15
+ connect(): Promise<void>;
16
+ disconnect(): Promise<void>;
17
+ isConnected(): boolean;
18
+ getStoreType(): string;
19
+ get<T>(key: string): Promise<T | undefined>;
20
+ set<T>(key: string, value: T): Promise<void>;
21
+ delete(key: string): Promise<void>;
22
+ list(prefix: string): Promise<string[]>;
23
+ append<T>(key: string, item: T): Promise<void>;
24
+ getRange<T>(key: string, start: number, count: number): Promise<T[]>;
25
+ private checkConnection;
26
+ vacuum(): Promise<void>;
27
+ getStats(): Promise<{
28
+ kvCount: number;
29
+ listCount: number;
30
+ dbSize: number;
31
+ }>;
32
+ }
33
+ //# sourceMappingURL=sqlite-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sqlite-store.d.ts","sourceRoot":"","sources":["../../src/database/sqlite-store.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAE1C,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AAKzD;;;GAGG;AACH,qBAAa,WAAY,YAAW,QAAQ;IACxC,OAAO,CAAC,EAAE,CAAoB;IAC9B,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,MAAM,CAAuB;IACrC,OAAO,CAAC,MAAM,CAAS;gBAEX,MAAM,EAAE,oBAAoB,EAAE,MAAM,EAAE,MAAM;IAOxD,OAAO,CAAC,gBAAgB;IA8ClB,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IA2ExB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAOjC,WAAW,IAAI,OAAO;IAItB,YAAY,IAAI,MAAM;IAKhB,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC;IAgB3C,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAkB5C,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAelC,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IA0BvC,MAAM,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAqB9C,QAAQ,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,EAAE,CAAC;IAqB1E,OAAO,CAAC,eAAe;IAOjB,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IAKvB,QAAQ,IAAI,OAAO,CAAC;QACtB,OAAO,EAAE,MAAM,CAAC;QAChB,SAAS,EAAE,MAAM,CAAC;QAClB,MAAM,EAAE,MAAM,CAAC;KAClB,CAAC;CAqBL"}
@@ -0,0 +1,236 @@
1
+ import { dirname } from "path";
2
+ import { mkdirSync } from "fs";
3
+ import { DextoLogComponent, StorageError } from "@dexto/core";
4
+ let BetterSqlite3Database;
5
+ class SQLiteStore {
6
+ db = null;
7
+ // Database.Database
8
+ dbPath;
9
+ config;
10
+ logger;
11
+ constructor(config, logger) {
12
+ this.config = config;
13
+ this.dbPath = "";
14
+ this.logger = logger.createChild(DextoLogComponent.STORAGE);
15
+ }
16
+ initializeTables() {
17
+ this.logger.debug("SQLite initializing database schema...");
18
+ try {
19
+ this.db.exec(`
20
+ CREATE TABLE IF NOT EXISTS kv_store (
21
+ key TEXT PRIMARY KEY,
22
+ value TEXT NOT NULL,
23
+ created_at INTEGER DEFAULT (strftime('%s', 'now')),
24
+ updated_at INTEGER DEFAULT (strftime('%s', 'now'))
25
+ )
26
+ `);
27
+ this.db.exec(`
28
+ CREATE TABLE IF NOT EXISTS list_store (
29
+ key TEXT NOT NULL,
30
+ value TEXT NOT NULL,
31
+ sequence INTEGER,
32
+ created_at INTEGER DEFAULT (strftime('%s', 'now')),
33
+ PRIMARY KEY (key, sequence)
34
+ )
35
+ `);
36
+ this.db.exec(`
37
+ CREATE INDEX IF NOT EXISTS idx_kv_store_key ON kv_store(key);
38
+ CREATE INDEX IF NOT EXISTS idx_list_store_key ON list_store(key);
39
+ CREATE INDEX IF NOT EXISTS idx_list_store_sequence ON list_store(key, sequence);
40
+ `);
41
+ this.logger.debug(
42
+ "SQLite database schema initialized: kv_store, list_store tables with indexes"
43
+ );
44
+ } catch (error) {
45
+ throw StorageError.migrationFailed(
46
+ error instanceof Error ? error.message : String(error),
47
+ {
48
+ operation: "table_initialization",
49
+ backend: "sqlite"
50
+ }
51
+ );
52
+ }
53
+ }
54
+ async connect() {
55
+ if (this.db) return;
56
+ if (!BetterSqlite3Database) {
57
+ try {
58
+ const module = await import("better-sqlite3");
59
+ BetterSqlite3Database = module.default || module;
60
+ } catch (error) {
61
+ const err = error;
62
+ if (err.code === "ERR_MODULE_NOT_FOUND") {
63
+ throw StorageError.dependencyNotInstalled(
64
+ "SQLite",
65
+ "better-sqlite3",
66
+ "npm install better-sqlite3"
67
+ );
68
+ }
69
+ throw StorageError.connectionFailed(
70
+ `Failed to import better-sqlite3: ${error instanceof Error ? error.message : String(error)}`
71
+ );
72
+ }
73
+ }
74
+ this.dbPath = this.config.path;
75
+ this.logger.info(`SQLite using database file: ${this.dbPath}`);
76
+ const dir = dirname(this.dbPath);
77
+ this.logger.debug(`SQLite ensuring directory exists: ${dir}`);
78
+ try {
79
+ mkdirSync(dir, { recursive: true });
80
+ } catch (error) {
81
+ this.logger.debug(`Directory creation result: ${error ? "exists" : "created"}`);
82
+ }
83
+ const sqliteOptions = this.config.options || {};
84
+ this.logger.debug(`SQLite initializing database with config:`, {
85
+ readonly: sqliteOptions.readonly || false,
86
+ fileMustExist: sqliteOptions.fileMustExist || false,
87
+ timeout: sqliteOptions.timeout || 5e3
88
+ });
89
+ this.db = new BetterSqlite3Database(this.dbPath, {
90
+ readonly: sqliteOptions.readonly || false,
91
+ fileMustExist: sqliteOptions.fileMustExist || false,
92
+ timeout: sqliteOptions.timeout || 5e3,
93
+ verbose: sqliteOptions.verbose ? (message, ...additionalArgs) => {
94
+ const messageStr = typeof message === "string" ? message : typeof message === "object" && message !== null ? JSON.stringify(message) : String(message);
95
+ this.logger.debug(
96
+ messageStr,
97
+ additionalArgs.length > 0 ? { args: additionalArgs } : void 0
98
+ );
99
+ } : void 0
100
+ });
101
+ this.db.pragma("journal_mode = WAL");
102
+ this.logger.debug("SQLite enabled WAL mode for better concurrency");
103
+ this.initializeTables();
104
+ this.logger.info(`\u2705 SQLite store successfully connected to: ${this.dbPath}`);
105
+ }
106
+ async disconnect() {
107
+ if (this.db) {
108
+ this.db.close();
109
+ this.db = null;
110
+ }
111
+ }
112
+ isConnected() {
113
+ return this.db !== null;
114
+ }
115
+ getStoreType() {
116
+ return "sqlite";
117
+ }
118
+ // Core operations
119
+ async get(key) {
120
+ this.checkConnection();
121
+ try {
122
+ const row = this.db.prepare("SELECT value FROM kv_store WHERE key = ?").get(key);
123
+ return row ? JSON.parse(row.value) : void 0;
124
+ } catch (error) {
125
+ throw StorageError.readFailed(
126
+ "get",
127
+ error instanceof Error ? error.message : String(error),
128
+ { key }
129
+ );
130
+ }
131
+ }
132
+ async set(key, value) {
133
+ this.checkConnection();
134
+ try {
135
+ const serialized = JSON.stringify(value);
136
+ this.db.prepare(
137
+ "INSERT OR REPLACE INTO kv_store (key, value, updated_at) VALUES (?, ?, ?)"
138
+ ).run(key, serialized, Date.now());
139
+ } catch (error) {
140
+ throw StorageError.writeFailed(
141
+ "set",
142
+ error instanceof Error ? error.message : String(error),
143
+ { key }
144
+ );
145
+ }
146
+ }
147
+ async delete(key) {
148
+ this.checkConnection();
149
+ try {
150
+ this.db.prepare("DELETE FROM kv_store WHERE key = ?").run(key);
151
+ this.db.prepare("DELETE FROM list_store WHERE key = ?").run(key);
152
+ } catch (error) {
153
+ throw StorageError.deleteFailed(
154
+ "delete",
155
+ error instanceof Error ? error.message : String(error),
156
+ { key }
157
+ );
158
+ }
159
+ }
160
+ // List operations
161
+ async list(prefix) {
162
+ this.checkConnection();
163
+ try {
164
+ const kvKeys = this.db.prepare("SELECT key FROM kv_store WHERE key LIKE ?").all(`${prefix}%`);
165
+ const listKeys = this.db.prepare("SELECT DISTINCT key FROM list_store WHERE key LIKE ?").all(`${prefix}%`);
166
+ const allKeys = /* @__PURE__ */ new Set([
167
+ ...kvKeys.map((row) => row.key),
168
+ ...listKeys.map((row) => row.key)
169
+ ]);
170
+ return Array.from(allKeys).sort();
171
+ } catch (error) {
172
+ throw StorageError.readFailed(
173
+ "list",
174
+ error instanceof Error ? error.message : String(error),
175
+ { prefix }
176
+ );
177
+ }
178
+ }
179
+ async append(key, item) {
180
+ this.checkConnection();
181
+ try {
182
+ const serialized = JSON.stringify(item);
183
+ this.db.prepare(
184
+ "INSERT INTO list_store (key, value, sequence) VALUES (?, ?, (SELECT COALESCE(MAX(sequence), 0) + 1 FROM list_store WHERE key = ?))"
185
+ ).run(key, serialized, key);
186
+ } catch (error) {
187
+ throw StorageError.writeFailed(
188
+ "append",
189
+ error instanceof Error ? error.message : String(error),
190
+ { key }
191
+ );
192
+ }
193
+ }
194
+ async getRange(key, start, count) {
195
+ this.checkConnection();
196
+ try {
197
+ const rows = this.db.prepare(
198
+ "SELECT value FROM list_store WHERE key = ? ORDER BY sequence ASC LIMIT ? OFFSET ?"
199
+ ).all(key, count, start);
200
+ return rows.map((row) => JSON.parse(row.value));
201
+ } catch (error) {
202
+ throw StorageError.readFailed(
203
+ "getRange",
204
+ error instanceof Error ? error.message : String(error),
205
+ { key, start, count }
206
+ );
207
+ }
208
+ }
209
+ // Schema management
210
+ checkConnection() {
211
+ if (!this.db) {
212
+ throw StorageError.notConnected("SQLiteStore");
213
+ }
214
+ }
215
+ // Maintenance operations
216
+ async vacuum() {
217
+ this.checkConnection();
218
+ this.db.exec("VACUUM");
219
+ }
220
+ async getStats() {
221
+ this.checkConnection();
222
+ const kvCount = this.db.prepare("SELECT COUNT(*) as count FROM kv_store").get();
223
+ const listCount = this.db.prepare("SELECT COUNT(*) as count FROM list_store").get();
224
+ const dbSize = this.db.prepare(
225
+ "SELECT page_count * page_size as size FROM pragma_page_count(), pragma_page_size()"
226
+ ).get();
227
+ return {
228
+ kvCount: kvCount.count,
229
+ listCount: listCount.count,
230
+ dbSize: dbSize.size
231
+ };
232
+ }
233
+ }
234
+ export {
235
+ SQLiteStore
236
+ };
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __copyProps = (to, from, except, desc) => {
7
+ if (from && typeof from === "object" || typeof from === "function") {
8
+ for (let key of __getOwnPropNames(from))
9
+ if (!__hasOwnProp.call(to, key) && key !== except)
10
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
11
+ }
12
+ return to;
13
+ };
14
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
15
+ var types_exports = {};
16
+ module.exports = __toCommonJS(types_exports);
@@ -0,0 +1 @@
1
+ export { Database } from '@dexto/core';
@@ -0,0 +1,2 @@
1
+ export type { Database } from '@dexto/core';
2
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/database/types.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC"}
File without changes