@aigne/afs-sqlite 1.0.1-beta

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 (76) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/LICENSE.md +93 -0
  3. package/README.md +277 -0
  4. package/lib/cjs/actions/built-in.d.ts +5 -0
  5. package/lib/cjs/actions/built-in.js +165 -0
  6. package/lib/cjs/actions/registry.d.ts +49 -0
  7. package/lib/cjs/actions/registry.js +102 -0
  8. package/lib/cjs/actions/types.d.ts +51 -0
  9. package/lib/cjs/actions/types.js +2 -0
  10. package/lib/cjs/config.d.ts +89 -0
  11. package/lib/cjs/config.js +33 -0
  12. package/lib/cjs/index.d.ts +13 -0
  13. package/lib/cjs/index.js +47 -0
  14. package/lib/cjs/node/builder.d.ts +43 -0
  15. package/lib/cjs/node/builder.js +187 -0
  16. package/lib/cjs/operations/crud.d.ts +64 -0
  17. package/lib/cjs/operations/crud.js +225 -0
  18. package/lib/cjs/operations/query-builder.d.ts +37 -0
  19. package/lib/cjs/operations/query-builder.js +102 -0
  20. package/lib/cjs/operations/search.d.ts +75 -0
  21. package/lib/cjs/operations/search.js +172 -0
  22. package/lib/cjs/package.json +3 -0
  23. package/lib/cjs/router/path-router.d.ts +38 -0
  24. package/lib/cjs/router/path-router.js +90 -0
  25. package/lib/cjs/router/types.d.ts +30 -0
  26. package/lib/cjs/router/types.js +2 -0
  27. package/lib/cjs/schema/introspector.d.ts +48 -0
  28. package/lib/cjs/schema/introspector.js +186 -0
  29. package/lib/cjs/schema/types.d.ts +104 -0
  30. package/lib/cjs/schema/types.js +13 -0
  31. package/lib/cjs/sqlite-afs.d.ts +144 -0
  32. package/lib/cjs/sqlite-afs.js +337 -0
  33. package/lib/dts/actions/built-in.d.ts +5 -0
  34. package/lib/dts/actions/registry.d.ts +49 -0
  35. package/lib/dts/actions/types.d.ts +51 -0
  36. package/lib/dts/config.d.ts +89 -0
  37. package/lib/dts/index.d.ts +13 -0
  38. package/lib/dts/node/builder.d.ts +43 -0
  39. package/lib/dts/operations/crud.d.ts +64 -0
  40. package/lib/dts/operations/query-builder.d.ts +37 -0
  41. package/lib/dts/operations/search.d.ts +75 -0
  42. package/lib/dts/router/path-router.d.ts +38 -0
  43. package/lib/dts/router/types.d.ts +30 -0
  44. package/lib/dts/schema/introspector.d.ts +48 -0
  45. package/lib/dts/schema/types.d.ts +104 -0
  46. package/lib/dts/sqlite-afs.d.ts +144 -0
  47. package/lib/esm/actions/built-in.d.ts +5 -0
  48. package/lib/esm/actions/built-in.js +162 -0
  49. package/lib/esm/actions/registry.d.ts +49 -0
  50. package/lib/esm/actions/registry.js +98 -0
  51. package/lib/esm/actions/types.d.ts +51 -0
  52. package/lib/esm/actions/types.js +1 -0
  53. package/lib/esm/config.d.ts +89 -0
  54. package/lib/esm/config.js +30 -0
  55. package/lib/esm/index.d.ts +13 -0
  56. package/lib/esm/index.js +17 -0
  57. package/lib/esm/node/builder.d.ts +43 -0
  58. package/lib/esm/node/builder.js +177 -0
  59. package/lib/esm/operations/crud.d.ts +64 -0
  60. package/lib/esm/operations/crud.js +221 -0
  61. package/lib/esm/operations/query-builder.d.ts +37 -0
  62. package/lib/esm/operations/query-builder.js +92 -0
  63. package/lib/esm/operations/search.d.ts +75 -0
  64. package/lib/esm/operations/search.js +167 -0
  65. package/lib/esm/package.json +3 -0
  66. package/lib/esm/router/path-router.d.ts +38 -0
  67. package/lib/esm/router/path-router.js +83 -0
  68. package/lib/esm/router/types.d.ts +30 -0
  69. package/lib/esm/router/types.js +1 -0
  70. package/lib/esm/schema/introspector.d.ts +48 -0
  71. package/lib/esm/schema/introspector.js +182 -0
  72. package/lib/esm/schema/types.d.ts +104 -0
  73. package/lib/esm/schema/types.js +10 -0
  74. package/lib/esm/sqlite-afs.d.ts +144 -0
  75. package/lib/esm/sqlite-afs.js +333 -0
  76. package/package.json +71 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,29 @@
1
+ # Changelog
2
+
3
+ ## [1.0.1-beta](https://github.com/AIGNE-io/aigne-framework/compare/afs-sqlite-v1.0.0...afs-sqlite-v1.0.1-beta) (2026-01-15)
4
+
5
+
6
+ ### Dependencies
7
+
8
+ * The following workspace dependencies were updated
9
+ * devDependencies
10
+ * @aigne/test-utils bumped to 0.5.69-beta.23
11
+
12
+ ## 1.0.0 (2026-01-15)
13
+
14
+
15
+ ### Features
16
+
17
+ * **afs:** add generic AFS adapter for sqlite databases ([#908](https://github.com/AIGNE-io/aigne-framework/issues/908)) ([b9b5a8f](https://github.com/AIGNE-io/aigne-framework/commit/b9b5a8fc2680e8e3ae7f28dd720b0089520981b9))
18
+
19
+
20
+ ### Bug Fixes
21
+
22
+ * update @aigne/afs-sqlite release config ([ef0f254](https://github.com/AIGNE-io/aigne-framework/commit/ef0f2547920e0e95545c57c7dd55ff059b5a2e7a))
23
+
24
+
25
+ ### Dependencies
26
+
27
+ * The following workspace dependencies were updated
28
+ * devDependencies
29
+ * @aigne/test-utils bumped to 0.5.69-beta.22
package/LICENSE.md ADDED
@@ -0,0 +1,93 @@
1
+ Elastic License 2.0
2
+
3
+ URL: https://www.elastic.co/licensing/elastic-license
4
+
5
+ ## Acceptance
6
+
7
+ By using the software, you agree to all of the terms and conditions below.
8
+
9
+ ## Copyright License
10
+
11
+ The licensor grants you a non-exclusive, royalty-free, worldwide,
12
+ non-sublicensable, non-transferable license to use, copy, distribute, make
13
+ available, and prepare derivative works of the software, in each case subject to
14
+ the limitations and conditions below.
15
+
16
+ ## Limitations
17
+
18
+ You may not provide the software to third parties as a hosted or managed
19
+ service, where the service provides users with access to any substantial set of
20
+ the features or functionality of the software.
21
+
22
+ You may not move, change, disable, or circumvent the license key functionality
23
+ in the software, and you may not remove or obscure any functionality in the
24
+ software that is protected by the license key.
25
+
26
+ You may not alter, remove, or obscure any licensing, copyright, or other notices
27
+ of the licensor in the software. Any use of the licensor’s trademarks is subject
28
+ to applicable law.
29
+
30
+ ## Patents
31
+
32
+ The licensor grants you a license, under any patent claims the licensor can
33
+ license, or becomes able to license, to make, have made, use, sell, offer for
34
+ sale, import and have imported the software, in each case subject to the
35
+ limitations and conditions in this license. This license does not cover any
36
+ patent claims that you cause to be infringed by modifications or additions to
37
+ the software. If you or your company make any written claim that the software
38
+ infringes or contributes to infringement of any patent, your patent license for
39
+ the software granted under these terms ends immediately. If your company makes
40
+ such a claim, your patent license ends immediately for work on behalf of your
41
+ company.
42
+
43
+ ## Notices
44
+
45
+ You must ensure that anyone who gets a copy of any part of the software from you
46
+ also gets a copy of these terms.
47
+
48
+ If you modify the software, you must include in any modified copies of the
49
+ software prominent notices stating that you have modified the software.
50
+
51
+ ## No Other Rights
52
+
53
+ These terms do not imply any licenses other than those expressly granted in
54
+ these terms.
55
+
56
+ ## Termination
57
+
58
+ If you use the software in violation of these terms, such use is not licensed,
59
+ and your licenses will automatically terminate. If the licensor provides you
60
+ with a notice of your violation, and you cease all violation of this license no
61
+ later than 30 days after you receive that notice, your licenses will be
62
+ reinstated retroactively. However, if you violate these terms after such
63
+ reinstatement, any additional violation of these terms will cause your licenses
64
+ to terminate automatically and permanently.
65
+
66
+ ## No Liability
67
+
68
+ *As far as the law allows, the software comes as is, without any warranty or
69
+ condition, and the licensor will not be liable to you for any damages arising
70
+ out of these terms or the use or nature of the software, under any kind of
71
+ legal claim.*
72
+
73
+ ## Definitions
74
+
75
+ The **licensor** is the entity offering these terms, and the **software** is the
76
+ software the licensor makes available under these terms, including any portion
77
+ of it.
78
+
79
+ **you** refers to the individual or entity agreeing to these terms.
80
+
81
+ **your company** is any legal entity, sole proprietorship, or other kind of
82
+ organization that you work for, plus all organizations that have control over,
83
+ are under the control of, or are under common control with that
84
+ organization. **control** means ownership of substantially all the assets of an
85
+ entity, or the power to direct its management and policies by vote, contract, or
86
+ otherwise. Control can be direct or indirect.
87
+
88
+ **your licenses** are all the licenses granted to you for the software under
89
+ these terms.
90
+
91
+ **use** means anything you do with the software requiring one of your licenses.
92
+
93
+ **trademark** means trademarks, service marks, and similar rights.
package/README.md ADDED
@@ -0,0 +1,277 @@
1
+ # @aigne/afs-sqlite
2
+
3
+ **@aigne/afs-sqlite** is an AFS module that exposes SQLite databases as virtual file system nodes with full CRUD support, schema introspection, FTS5 search, and virtual paths.
4
+
5
+ ## Overview
6
+
7
+ This module allows AI agents to interact with SQLite databases through the AFS interface. It automatically introspects database schemas and exposes tables, rows, and columns as navigable paths.
8
+
9
+ ## Features
10
+
11
+ - **Schema Introspection**: Automatically discovers tables, columns, primary keys, foreign keys, and indexes
12
+ - **CRUD Operations**: Full create, read, update, delete support for database rows
13
+ - **FTS5 Search**: Full-text search support with SQLite FTS5
14
+ - **Virtual Paths**: Access row attributes (`@attr`), metadata (`@meta`), schema (`@schema`), and actions (`@actions`)
15
+ - **Built-in Actions**: validate, duplicate, refresh, export, count
16
+ - **Custom Actions**: Register custom actions for domain-specific operations
17
+ - **Readonly Mode**: Optional readonly access mode for safety
18
+
19
+ ## Installation
20
+
21
+ ```bash
22
+ npm install @aigne/afs-sqlite
23
+ # or
24
+ yarn add @aigne/afs-sqlite
25
+ # or
26
+ pnpm add @aigne/afs-sqlite
27
+ ```
28
+
29
+ ## Quick Start
30
+
31
+ ```typescript
32
+ import { AFS } from "@aigne/afs";
33
+ import { SQLiteAFS } from "@aigne/afs-sqlite";
34
+
35
+ // Create AFS instance
36
+ const afs = new AFS();
37
+
38
+ // Mount SQLite module
39
+ afs.mount(new SQLiteAFS({
40
+ url: "file:./database.sqlite3",
41
+ accessMode: "readwrite"
42
+ }));
43
+
44
+ // List all tables
45
+ const tables = await afs.list("/modules/sqlite-afs");
46
+
47
+ // List rows in a table
48
+ const users = await afs.list("/modules/sqlite-afs/users");
49
+
50
+ // Read a specific row
51
+ const user = await afs.read("/modules/sqlite-afs/users/1");
52
+
53
+ // Create a new row
54
+ await afs.write("/modules/sqlite-afs/users", {
55
+ content: { name: "John", email: "john@example.com" }
56
+ });
57
+
58
+ // Update an existing row
59
+ await afs.write("/modules/sqlite-afs/users/1", {
60
+ content: { name: "John Doe" }
61
+ });
62
+
63
+ // Delete a row
64
+ await afs.delete("/modules/sqlite-afs/users/1");
65
+
66
+ // Search for content
67
+ const results = await afs.search("/modules/sqlite-afs", "john");
68
+ ```
69
+
70
+ ## Configuration
71
+
72
+ ```typescript
73
+ interface SQLiteAFSOptions {
74
+ // Database connection URL (required)
75
+ url: string;
76
+
77
+ // Module name (default: "sqlite-afs")
78
+ name?: string;
79
+
80
+ // Module description
81
+ description?: string;
82
+
83
+ // Access mode: "readonly" or "readwrite" (default: "readwrite")
84
+ accessMode?: "readonly" | "readwrite";
85
+
86
+ // Enable WAL mode (default: true)
87
+ wal?: boolean;
88
+
89
+ // Table whitelist (optional, include only these tables)
90
+ tables?: string[];
91
+
92
+ // Table blacklist (optional, exclude these tables)
93
+ excludeTables?: string[];
94
+
95
+ // FTS configuration (optional)
96
+ fts?: {
97
+ enabled?: boolean;
98
+ tables?: Record<string, string[]>; // table -> columns to index
99
+ };
100
+ }
101
+ ```
102
+
103
+ ## Path Structure
104
+
105
+ The module exposes the following path patterns:
106
+
107
+ | Path | Description |
108
+ |------|-------------|
109
+ | `/` | List all tables |
110
+ | `/:table` | List rows in table or create new row |
111
+ | `/:table/@schema` | Get table schema |
112
+ | `/:table/:pk` | Read/update/delete specific row |
113
+ | `/:table/:pk/@attr` | List row attributes (columns) |
114
+ | `/:table/:pk/@attr/:column` | Get specific column value |
115
+ | `/:table/:pk/@meta` | Get row metadata |
116
+ | `/:table/:pk/@actions` | List available actions |
117
+ | `/:table/:pk/@actions/:action` | Execute an action |
118
+
119
+ ## Built-in Actions
120
+
121
+ ### Row-level Actions
122
+
123
+ - **validate**: Validate row data against schema constraints (NOT NULL, foreign keys)
124
+ - **duplicate**: Create a copy of the row
125
+
126
+ ### Table-level Actions
127
+
128
+ - **refresh**: Refresh the schema cache
129
+ - **count**: Get total row count
130
+ - **export**: Export table data as JSON or CSV
131
+
132
+ ```typescript
133
+ // Validate a row
134
+ const result = await afs.write("/modules/sqlite-afs/users/1/@actions/validate", {
135
+ content: {}
136
+ });
137
+
138
+ // Duplicate a row
139
+ const result = await afs.write("/modules/sqlite-afs/users/1/@actions/duplicate", {
140
+ content: {}
141
+ });
142
+
143
+ // Export table data
144
+ const jsonData = await sqliteAfs.exportTable("users", "json");
145
+ const csvData = await sqliteAfs.exportTable("users", "csv");
146
+ ```
147
+
148
+ ## Custom Actions
149
+
150
+ Register custom actions for domain-specific operations:
151
+
152
+ ```typescript
153
+ const sqliteAfs = new SQLiteAFS({ url: "file:./db.sqlite3" });
154
+
155
+ sqliteAfs.registerAction(
156
+ "archive",
157
+ async (ctx, params) => {
158
+ // ctx contains: db, schemas, table, pk, row, module
159
+ await ctx.db.run(sql.raw(`UPDATE "${ctx.table}" SET archived = 1 WHERE id = '${ctx.pk}'`));
160
+ return { archived: true };
161
+ },
162
+ {
163
+ description: "Archive the row",
164
+ rowLevel: true,
165
+ tableLevel: false
166
+ }
167
+ );
168
+ ```
169
+
170
+ ## FTS5 Search
171
+
172
+ Enable full-text search for specific columns:
173
+
174
+ ```typescript
175
+ const sqliteAfs = new SQLiteAFS({
176
+ url: "file:./db.sqlite3",
177
+ fts: {
178
+ enabled: true,
179
+ tables: {
180
+ posts: ["title", "content"],
181
+ users: ["name", "bio"]
182
+ }
183
+ }
184
+ });
185
+
186
+ // Search across all FTS-enabled tables
187
+ const results = await afs.search("/modules/sqlite-afs", "search query");
188
+
189
+ // Search within a specific table
190
+ const postResults = await afs.search("/modules/sqlite-afs/posts", "search query");
191
+ ```
192
+
193
+ ## Advanced Usage
194
+
195
+ ### Access Database Directly
196
+
197
+ ```typescript
198
+ const db = sqliteAfs.getDatabase();
199
+ await db.run(sql.raw("SELECT * FROM users"));
200
+ ```
201
+
202
+ ### Get Schema Information
203
+
204
+ ```typescript
205
+ const schemas = sqliteAfs.getSchemas();
206
+ const usersSchema = schemas.get("users");
207
+ console.log(usersSchema.columns);
208
+ console.log(usersSchema.primaryKey);
209
+ console.log(usersSchema.foreignKeys);
210
+ ```
211
+
212
+ ### Refresh Schema After External Changes
213
+
214
+ ```typescript
215
+ await sqliteAfs.refreshSchema();
216
+ ```
217
+
218
+ ## Integration with AI Agents
219
+
220
+ ```typescript
221
+ import { AIAgent, AIGNE } from "@aigne/core";
222
+ import { AFS } from "@aigne/afs";
223
+ import { SQLiteAFS } from "@aigne/afs-sqlite";
224
+
225
+ const afs = new AFS();
226
+ afs.mount(new SQLiteAFS({
227
+ url: "file:./database.sqlite3",
228
+ accessMode: "readwrite"
229
+ }));
230
+
231
+ const agent = AIAgent.from({
232
+ name: "database-assistant",
233
+ afs: afs
234
+ });
235
+
236
+ const context = aigne.newContext();
237
+ const result = await context.invoke(agent, {
238
+ message: "Show me all users in the database"
239
+ });
240
+ ```
241
+
242
+ ## YAML Configuration
243
+
244
+ SQLiteAFS can be configured via YAML when using the AIGNE loader:
245
+
246
+ ```yaml
247
+ afs:
248
+ modules:
249
+ - module: "@aigne/afs-sqlite"
250
+ options:
251
+ url: "file:./database.sqlite3"
252
+ access_mode: readwrite
253
+ tables:
254
+ - users
255
+ - posts
256
+ fts:
257
+ enabled: true
258
+ tables:
259
+ posts:
260
+ - title
261
+ - content
262
+ ```
263
+
264
+ ## Related Packages
265
+
266
+ - [@aigne/afs](../core/README.md) - Core AFS package
267
+ - [@aigne/afs-history](../history/README.md) - History tracking module
268
+ - [@aigne/afs-local-fs](../local-fs/README.md) - Local file system module
269
+ - [@aigne/sqlite](../../packages/sqlite/README.md) - SQLite utilities
270
+
271
+ ## TypeScript Support
272
+
273
+ This package includes full TypeScript type definitions.
274
+
275
+ ## License
276
+
277
+ [Elastic-2.0](../../LICENSE.md)
@@ -0,0 +1,5 @@
1
+ import type { ActionsRegistry } from "./registry.js";
2
+ /**
3
+ * Registers built-in actions to the registry
4
+ */
5
+ export declare function registerBuiltInActions(registry: ActionsRegistry): void;
@@ -0,0 +1,165 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.registerBuiltInActions = registerBuiltInActions;
4
+ const sqlite_1 = require("@aigne/sqlite");
5
+ /**
6
+ * Executes a raw SQL query and returns all rows
7
+ */
8
+ async function execAll(db, query) {
9
+ return db.all(sqlite_1.sql.raw(query)).execute();
10
+ }
11
+ /**
12
+ * Executes a raw SQL query (for INSERT, UPDATE, DELETE)
13
+ */
14
+ async function execRun(db, query) {
15
+ await db.run(sqlite_1.sql.raw(query)).execute();
16
+ }
17
+ /**
18
+ * Registers built-in actions to the registry
19
+ */
20
+ function registerBuiltInActions(registry) {
21
+ // Refresh schema action (table level)
22
+ registry.register({
23
+ name: "refresh",
24
+ description: "Refresh the schema cache for this module",
25
+ tableLevel: true,
26
+ rowLevel: false,
27
+ handler: async (ctx) => {
28
+ await ctx.module.refreshSchema();
29
+ return {
30
+ success: true,
31
+ message: "Schema refreshed successfully",
32
+ };
33
+ },
34
+ });
35
+ // Export table action (table level)
36
+ registry.register({
37
+ name: "export",
38
+ description: "Export table data in specified format (json, csv)",
39
+ tableLevel: true,
40
+ rowLevel: false,
41
+ inputSchema: {
42
+ type: "object",
43
+ properties: {
44
+ format: {
45
+ type: "string",
46
+ enum: ["json", "csv"],
47
+ default: "json",
48
+ },
49
+ },
50
+ },
51
+ handler: async (ctx, params) => {
52
+ const format = params.format ?? "json";
53
+ const data = await ctx.module.exportTable(ctx.table, format);
54
+ return {
55
+ success: true,
56
+ data,
57
+ };
58
+ },
59
+ });
60
+ // Count rows action (table level)
61
+ registry.register({
62
+ name: "count",
63
+ description: "Get the total row count for this table",
64
+ tableLevel: true,
65
+ rowLevel: false,
66
+ handler: async (ctx) => {
67
+ const result = await execAll(ctx.db, `SELECT COUNT(*) as count FROM "${ctx.table}"`);
68
+ return {
69
+ success: true,
70
+ data: { count: result[0]?.count ?? 0 },
71
+ };
72
+ },
73
+ });
74
+ // Duplicate row action (row level)
75
+ registry.register({
76
+ name: "duplicate",
77
+ description: "Create a copy of this row",
78
+ tableLevel: false,
79
+ rowLevel: true,
80
+ handler: async (ctx) => {
81
+ if (!ctx.row) {
82
+ return { success: false, message: "Row data not available" };
83
+ }
84
+ const schema = ctx.schemas.get(ctx.table);
85
+ if (!schema) {
86
+ return { success: false, message: `Table '${ctx.table}' not found` };
87
+ }
88
+ // Create a copy without the primary key
89
+ const pkColumn = schema.primaryKey[0] ?? "rowid";
90
+ const rowCopy = { ...ctx.row };
91
+ delete rowCopy[pkColumn];
92
+ delete rowCopy.rowid;
93
+ // Build insert query
94
+ const columns = Object.keys(rowCopy);
95
+ const values = columns.map((col) => formatValueForSQL(rowCopy[col]));
96
+ await execRun(ctx.db, `INSERT INTO "${ctx.table}" (${columns.map((c) => `"${c}"`).join(", ")}) VALUES (${values.join(", ")})`);
97
+ // Get the new row's ID
98
+ const lastIdResult = await execAll(ctx.db, "SELECT last_insert_rowid() as id");
99
+ return {
100
+ success: true,
101
+ data: { newId: lastIdResult[0]?.id },
102
+ message: "Row duplicated successfully",
103
+ };
104
+ },
105
+ });
106
+ // Validate row action (row level)
107
+ registry.register({
108
+ name: "validate",
109
+ description: "Validate row data against schema constraints",
110
+ tableLevel: false,
111
+ rowLevel: true,
112
+ handler: async (ctx) => {
113
+ if (!ctx.row) {
114
+ return { success: false, message: "Row data not available" };
115
+ }
116
+ const schema = ctx.schemas.get(ctx.table);
117
+ if (!schema) {
118
+ return { success: false, message: `Table '${ctx.table}' not found` };
119
+ }
120
+ const errors = [];
121
+ // Check NOT NULL constraints
122
+ for (const col of schema.columns) {
123
+ if (col.notnull && (ctx.row[col.name] === null || ctx.row[col.name] === undefined)) {
124
+ errors.push(`Column '${col.name}' cannot be null`);
125
+ }
126
+ }
127
+ // Check foreign key references
128
+ for (const fk of schema.foreignKeys) {
129
+ const value = ctx.row[fk.from];
130
+ if (value !== null && value !== undefined) {
131
+ const refResult = await execAll(ctx.db, `SELECT COUNT(*) as count FROM "${fk.table}" WHERE "${fk.to}" = '${String(value).replace(/'/g, "''")}'`);
132
+ if (refResult[0]?.count === 0) {
133
+ errors.push(`Foreign key violation: ${fk.from} references non-existent ${fk.table}.${fk.to}`);
134
+ }
135
+ }
136
+ }
137
+ return {
138
+ success: errors.length === 0,
139
+ data: { errors, valid: errors.length === 0 },
140
+ message: errors.length > 0 ? `Validation failed: ${errors.join("; ")}` : "Row is valid",
141
+ };
142
+ },
143
+ });
144
+ }
145
+ /**
146
+ * Formats a value for SQL insertion
147
+ */
148
+ function formatValueForSQL(value) {
149
+ if (value === null || value === undefined) {
150
+ return "NULL";
151
+ }
152
+ if (typeof value === "number") {
153
+ return String(value);
154
+ }
155
+ if (typeof value === "boolean") {
156
+ return value ? "1" : "0";
157
+ }
158
+ if (value instanceof Date) {
159
+ return `'${value.toISOString()}'`;
160
+ }
161
+ if (typeof value === "object") {
162
+ return `'${JSON.stringify(value).replace(/'/g, "''")}'`;
163
+ }
164
+ return `'${String(value).replace(/'/g, "''")}'`;
165
+ }
@@ -0,0 +1,49 @@
1
+ import type { ActionContext, ActionDefinition, ActionHandler, ActionResult } from "./types.js";
2
+ /**
3
+ * Registry for managing action handlers
4
+ */
5
+ export declare class ActionsRegistry {
6
+ private handlers;
7
+ /**
8
+ * Registers an action handler
9
+ */
10
+ register(definition: ActionDefinition): void;
11
+ /**
12
+ * Registers a simple action with just name and handler
13
+ */
14
+ registerSimple(name: string, handler: ActionHandler, options?: {
15
+ description?: string;
16
+ tableLevel?: boolean;
17
+ rowLevel?: boolean;
18
+ }): void;
19
+ /**
20
+ * Unregisters an action
21
+ */
22
+ unregister(name: string): boolean;
23
+ /**
24
+ * Checks if an action is registered
25
+ */
26
+ has(name: string): boolean;
27
+ /**
28
+ * Gets an action definition
29
+ */
30
+ get(name: string): ActionDefinition | undefined;
31
+ /**
32
+ * Lists all registered actions
33
+ */
34
+ list(options?: {
35
+ tableLevel?: boolean;
36
+ rowLevel?: boolean;
37
+ }): ActionDefinition[];
38
+ /**
39
+ * Lists action names
40
+ */
41
+ listNames(options?: {
42
+ tableLevel?: boolean;
43
+ rowLevel?: boolean;
44
+ }): string[];
45
+ /**
46
+ * Executes an action
47
+ */
48
+ execute(name: string, ctx: ActionContext, params?: Record<string, unknown>): Promise<ActionResult>;
49
+ }