@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.
- package/CHANGELOG.md +29 -0
- package/LICENSE.md +93 -0
- package/README.md +277 -0
- package/lib/cjs/actions/built-in.d.ts +5 -0
- package/lib/cjs/actions/built-in.js +165 -0
- package/lib/cjs/actions/registry.d.ts +49 -0
- package/lib/cjs/actions/registry.js +102 -0
- package/lib/cjs/actions/types.d.ts +51 -0
- package/lib/cjs/actions/types.js +2 -0
- package/lib/cjs/config.d.ts +89 -0
- package/lib/cjs/config.js +33 -0
- package/lib/cjs/index.d.ts +13 -0
- package/lib/cjs/index.js +47 -0
- package/lib/cjs/node/builder.d.ts +43 -0
- package/lib/cjs/node/builder.js +187 -0
- package/lib/cjs/operations/crud.d.ts +64 -0
- package/lib/cjs/operations/crud.js +225 -0
- package/lib/cjs/operations/query-builder.d.ts +37 -0
- package/lib/cjs/operations/query-builder.js +102 -0
- package/lib/cjs/operations/search.d.ts +75 -0
- package/lib/cjs/operations/search.js +172 -0
- package/lib/cjs/package.json +3 -0
- package/lib/cjs/router/path-router.d.ts +38 -0
- package/lib/cjs/router/path-router.js +90 -0
- package/lib/cjs/router/types.d.ts +30 -0
- package/lib/cjs/router/types.js +2 -0
- package/lib/cjs/schema/introspector.d.ts +48 -0
- package/lib/cjs/schema/introspector.js +186 -0
- package/lib/cjs/schema/types.d.ts +104 -0
- package/lib/cjs/schema/types.js +13 -0
- package/lib/cjs/sqlite-afs.d.ts +144 -0
- package/lib/cjs/sqlite-afs.js +337 -0
- package/lib/dts/actions/built-in.d.ts +5 -0
- package/lib/dts/actions/registry.d.ts +49 -0
- package/lib/dts/actions/types.d.ts +51 -0
- package/lib/dts/config.d.ts +89 -0
- package/lib/dts/index.d.ts +13 -0
- package/lib/dts/node/builder.d.ts +43 -0
- package/lib/dts/operations/crud.d.ts +64 -0
- package/lib/dts/operations/query-builder.d.ts +37 -0
- package/lib/dts/operations/search.d.ts +75 -0
- package/lib/dts/router/path-router.d.ts +38 -0
- package/lib/dts/router/types.d.ts +30 -0
- package/lib/dts/schema/introspector.d.ts +48 -0
- package/lib/dts/schema/types.d.ts +104 -0
- package/lib/dts/sqlite-afs.d.ts +144 -0
- package/lib/esm/actions/built-in.d.ts +5 -0
- package/lib/esm/actions/built-in.js +162 -0
- package/lib/esm/actions/registry.d.ts +49 -0
- package/lib/esm/actions/registry.js +98 -0
- package/lib/esm/actions/types.d.ts +51 -0
- package/lib/esm/actions/types.js +1 -0
- package/lib/esm/config.d.ts +89 -0
- package/lib/esm/config.js +30 -0
- package/lib/esm/index.d.ts +13 -0
- package/lib/esm/index.js +17 -0
- package/lib/esm/node/builder.d.ts +43 -0
- package/lib/esm/node/builder.js +177 -0
- package/lib/esm/operations/crud.d.ts +64 -0
- package/lib/esm/operations/crud.js +221 -0
- package/lib/esm/operations/query-builder.d.ts +37 -0
- package/lib/esm/operations/query-builder.js +92 -0
- package/lib/esm/operations/search.d.ts +75 -0
- package/lib/esm/operations/search.js +167 -0
- package/lib/esm/package.json +3 -0
- package/lib/esm/router/path-router.d.ts +38 -0
- package/lib/esm/router/path-router.js +83 -0
- package/lib/esm/router/types.d.ts +30 -0
- package/lib/esm/router/types.js +1 -0
- package/lib/esm/schema/introspector.d.ts +48 -0
- package/lib/esm/schema/introspector.js +182 -0
- package/lib/esm/schema/types.d.ts +104 -0
- package/lib/esm/schema/types.js +10 -0
- package/lib/esm/sqlite-afs.d.ts +144 -0
- package/lib/esm/sqlite-afs.js +333 -0
- package/package.json +71 -0
|
@@ -0,0 +1,337 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SQLiteAFS = void 0;
|
|
4
|
+
const sqlite_1 = require("@aigne/sqlite");
|
|
5
|
+
const built_in_js_1 = require("./actions/built-in.js");
|
|
6
|
+
const registry_js_1 = require("./actions/registry.js");
|
|
7
|
+
const config_js_1 = require("./config.js");
|
|
8
|
+
const builder_js_1 = require("./node/builder.js");
|
|
9
|
+
const crud_js_1 = require("./operations/crud.js");
|
|
10
|
+
const search_js_1 = require("./operations/search.js");
|
|
11
|
+
const path_router_js_1 = require("./router/path-router.js");
|
|
12
|
+
const introspector_js_1 = require("./schema/introspector.js");
|
|
13
|
+
/**
|
|
14
|
+
* SQLite AFS Module
|
|
15
|
+
*
|
|
16
|
+
* Exposes SQLite databases as AFS nodes with full CRUD support,
|
|
17
|
+
* schema introspection, FTS5 search, and virtual paths (@attr, @meta, @actions).
|
|
18
|
+
*/
|
|
19
|
+
class SQLiteAFS {
|
|
20
|
+
options;
|
|
21
|
+
name;
|
|
22
|
+
description;
|
|
23
|
+
accessMode;
|
|
24
|
+
db;
|
|
25
|
+
schemas = new Map();
|
|
26
|
+
router;
|
|
27
|
+
crud;
|
|
28
|
+
ftsSearch;
|
|
29
|
+
actions;
|
|
30
|
+
ftsConfig;
|
|
31
|
+
initialized = false;
|
|
32
|
+
constructor(options) {
|
|
33
|
+
this.options = options;
|
|
34
|
+
this.name = options.name ?? "sqlite-afs";
|
|
35
|
+
this.description = options.description ?? `SQLite database: ${options.url}`;
|
|
36
|
+
this.accessMode = options.accessMode ?? "readwrite";
|
|
37
|
+
this.ftsConfig = (0, search_js_1.createFTSConfig)(options.fts);
|
|
38
|
+
this.actions = new registry_js_1.ActionsRegistry();
|
|
39
|
+
(0, built_in_js_1.registerBuiltInActions)(this.actions);
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Returns the Zod schema for configuration validation
|
|
43
|
+
*/
|
|
44
|
+
static schema() {
|
|
45
|
+
return config_js_1.sqliteAFSConfigSchema;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Loads a module instance from configuration
|
|
49
|
+
*/
|
|
50
|
+
static async load({ parsed }) {
|
|
51
|
+
const validated = config_js_1.sqliteAFSConfigSchema.parse(parsed);
|
|
52
|
+
return new SQLiteAFS(validated);
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Called when the module is mounted to AFS
|
|
56
|
+
*/
|
|
57
|
+
async onMount(_afs) {
|
|
58
|
+
await this.initialize();
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Initializes the database connection and introspects schema
|
|
62
|
+
*/
|
|
63
|
+
async initialize() {
|
|
64
|
+
if (this.initialized)
|
|
65
|
+
return;
|
|
66
|
+
// Initialize database connection
|
|
67
|
+
this.db = await (0, sqlite_1.initDatabase)({
|
|
68
|
+
url: this.options.url,
|
|
69
|
+
wal: this.options.wal ?? true,
|
|
70
|
+
});
|
|
71
|
+
// Cast db to LibSQLDatabase for operations
|
|
72
|
+
const db = this.db;
|
|
73
|
+
// Introspect database schema
|
|
74
|
+
const introspector = new introspector_js_1.SchemaIntrospector();
|
|
75
|
+
this.schemas = await introspector.introspect(db, {
|
|
76
|
+
tables: this.options.tables,
|
|
77
|
+
excludeTables: this.options.excludeTables,
|
|
78
|
+
});
|
|
79
|
+
// Initialize components
|
|
80
|
+
this.router = (0, path_router_js_1.createPathRouter)();
|
|
81
|
+
this.crud = new crud_js_1.CRUDOperations(db, this.schemas, "");
|
|
82
|
+
this.ftsSearch = new search_js_1.FTSSearch(db, this.schemas, this.ftsConfig, "");
|
|
83
|
+
this.initialized = true;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Ensures the module is initialized
|
|
87
|
+
*/
|
|
88
|
+
async ensureInitialized() {
|
|
89
|
+
if (!this.initialized) {
|
|
90
|
+
await this.initialize();
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Lists entries at a path
|
|
95
|
+
*/
|
|
96
|
+
async list(path, options) {
|
|
97
|
+
await this.ensureInitialized();
|
|
98
|
+
const match = (0, path_router_js_1.matchPath)(this.router, path);
|
|
99
|
+
if (!match) {
|
|
100
|
+
return { data: [] };
|
|
101
|
+
}
|
|
102
|
+
switch (match.action) {
|
|
103
|
+
case "listTables":
|
|
104
|
+
return this.crud.listTables();
|
|
105
|
+
case "listTable":
|
|
106
|
+
if (!match.params.table)
|
|
107
|
+
return { data: [] };
|
|
108
|
+
return this.crud.listTable(match.params.table, options);
|
|
109
|
+
case "listAttributes":
|
|
110
|
+
if (!match.params.table || !match.params.pk)
|
|
111
|
+
return { data: [] };
|
|
112
|
+
return this.crud.listAttributes(match.params.table, match.params.pk);
|
|
113
|
+
case "listActions":
|
|
114
|
+
if (!match.params.table || !match.params.pk)
|
|
115
|
+
return { data: [] };
|
|
116
|
+
return this.listActions(match.params.table, match.params.pk);
|
|
117
|
+
default:
|
|
118
|
+
return { data: [] };
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Reads an entry at a path
|
|
123
|
+
*/
|
|
124
|
+
async read(path, _options) {
|
|
125
|
+
await this.ensureInitialized();
|
|
126
|
+
const match = (0, path_router_js_1.matchPath)(this.router, path);
|
|
127
|
+
if (!match) {
|
|
128
|
+
return {};
|
|
129
|
+
}
|
|
130
|
+
switch (match.action) {
|
|
131
|
+
case "readRow":
|
|
132
|
+
if (!match.params.table || !match.params.pk)
|
|
133
|
+
return {};
|
|
134
|
+
return this.crud.readRow(match.params.table, match.params.pk);
|
|
135
|
+
case "getSchema":
|
|
136
|
+
if (!match.params.table)
|
|
137
|
+
return {};
|
|
138
|
+
return this.crud.getSchema(match.params.table);
|
|
139
|
+
case "getAttribute":
|
|
140
|
+
if (!match.params.table || !match.params.pk || !match.params.column)
|
|
141
|
+
return {};
|
|
142
|
+
return this.crud.getAttribute(match.params.table, match.params.pk, match.params.column);
|
|
143
|
+
case "getMeta":
|
|
144
|
+
if (!match.params.table || !match.params.pk)
|
|
145
|
+
return {};
|
|
146
|
+
return this.crud.getMeta(match.params.table, match.params.pk);
|
|
147
|
+
default:
|
|
148
|
+
return {};
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Writes an entry at a path
|
|
153
|
+
*/
|
|
154
|
+
async write(path, content, _options) {
|
|
155
|
+
await this.ensureInitialized();
|
|
156
|
+
if (this.accessMode === "readonly") {
|
|
157
|
+
throw new Error("Module is in readonly mode");
|
|
158
|
+
}
|
|
159
|
+
const match = (0, path_router_js_1.matchPath)(this.router, path);
|
|
160
|
+
if (!match) {
|
|
161
|
+
throw new Error(`Invalid path: ${path}`);
|
|
162
|
+
}
|
|
163
|
+
switch (match.action) {
|
|
164
|
+
case "createRow":
|
|
165
|
+
if (!match.params.table) {
|
|
166
|
+
throw new Error("Table name required for create");
|
|
167
|
+
}
|
|
168
|
+
return this.crud.createRow(match.params.table, content.content ?? content);
|
|
169
|
+
case "readRow":
|
|
170
|
+
if (!match.params.table || !match.params.pk) {
|
|
171
|
+
throw new Error("Table and primary key required for update");
|
|
172
|
+
}
|
|
173
|
+
return this.crud.updateRow(match.params.table, match.params.pk, content.content ?? content);
|
|
174
|
+
case "executeAction":
|
|
175
|
+
if (!match.params.table || !match.params.action) {
|
|
176
|
+
throw new Error("Table and action name required");
|
|
177
|
+
}
|
|
178
|
+
return this.executeAction(match.params.table, match.params.pk, match.params.action, content.content ?? content);
|
|
179
|
+
default:
|
|
180
|
+
throw new Error(`Write not supported for path: ${path}`);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Deletes an entry at a path
|
|
185
|
+
*/
|
|
186
|
+
async delete(path, _options) {
|
|
187
|
+
await this.ensureInitialized();
|
|
188
|
+
if (this.accessMode === "readonly") {
|
|
189
|
+
throw new Error("Module is in readonly mode");
|
|
190
|
+
}
|
|
191
|
+
const match = (0, path_router_js_1.matchPath)(this.router, path);
|
|
192
|
+
if (!match || match.action !== "readRow") {
|
|
193
|
+
throw new Error(`Delete not supported for path: ${path}`);
|
|
194
|
+
}
|
|
195
|
+
if (!match.params.table || !match.params.pk) {
|
|
196
|
+
throw new Error("Table and primary key required for delete");
|
|
197
|
+
}
|
|
198
|
+
return this.crud.deleteRow(match.params.table, match.params.pk);
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Searches for entries matching a query
|
|
202
|
+
*/
|
|
203
|
+
async search(path, query, options) {
|
|
204
|
+
await this.ensureInitialized();
|
|
205
|
+
// If path specifies a table, search only that table
|
|
206
|
+
const match = (0, path_router_js_1.matchPath)(this.router, path);
|
|
207
|
+
if (match?.params.table) {
|
|
208
|
+
return this.ftsSearch.searchTable(match.params.table, query, options);
|
|
209
|
+
}
|
|
210
|
+
// Otherwise search all FTS-enabled tables
|
|
211
|
+
return this.ftsSearch.search(query, options);
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Executes a module operation
|
|
215
|
+
*/
|
|
216
|
+
async exec(path, args, _options) {
|
|
217
|
+
await this.ensureInitialized();
|
|
218
|
+
const match = (0, path_router_js_1.matchPath)(this.router, path);
|
|
219
|
+
if (match?.action === "executeAction" && match.params.table && match.params.action) {
|
|
220
|
+
const result = await this.executeAction(match.params.table, match.params.pk, match.params.action, args);
|
|
221
|
+
return { data: result.data };
|
|
222
|
+
}
|
|
223
|
+
throw new Error(`Exec not supported for path: ${path}`);
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Lists available actions for a row
|
|
227
|
+
*/
|
|
228
|
+
listActions(table, pk) {
|
|
229
|
+
const actionNames = this.actions.listNames({ rowLevel: true });
|
|
230
|
+
return {
|
|
231
|
+
data: (0, builder_js_1.buildActionsListEntry)(table, pk, actionNames, { basePath: "" }),
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Executes an action
|
|
236
|
+
*/
|
|
237
|
+
async executeAction(table, pk, actionName, params) {
|
|
238
|
+
const schema = this.schemas.get(table);
|
|
239
|
+
if (!schema) {
|
|
240
|
+
throw new Error(`Table '${table}' not found`);
|
|
241
|
+
}
|
|
242
|
+
// Get row data if pk is provided
|
|
243
|
+
let row;
|
|
244
|
+
if (pk) {
|
|
245
|
+
const readResult = await this.crud.readRow(table, pk);
|
|
246
|
+
row = readResult.data?.content;
|
|
247
|
+
}
|
|
248
|
+
const ctx = {
|
|
249
|
+
db: this.db,
|
|
250
|
+
schemas: this.schemas,
|
|
251
|
+
table,
|
|
252
|
+
pk,
|
|
253
|
+
row,
|
|
254
|
+
module: {
|
|
255
|
+
refreshSchema: () => this.refreshSchema(),
|
|
256
|
+
exportTable: (t, f) => this.exportTable(t, f),
|
|
257
|
+
},
|
|
258
|
+
};
|
|
259
|
+
const result = await this.actions.execute(actionName, ctx, params);
|
|
260
|
+
if (!result.success) {
|
|
261
|
+
throw new Error(result.message ?? "Action failed");
|
|
262
|
+
}
|
|
263
|
+
return {
|
|
264
|
+
data: {
|
|
265
|
+
id: `${table}:${pk ?? ""}:@actions:${actionName}`,
|
|
266
|
+
path: pk ? `/${table}/${pk}/@actions/${actionName}` : `/${table}/@actions/${actionName}`,
|
|
267
|
+
content: result.data,
|
|
268
|
+
},
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Refreshes the schema cache
|
|
273
|
+
*/
|
|
274
|
+
async refreshSchema() {
|
|
275
|
+
const db = this.db;
|
|
276
|
+
const introspector = new introspector_js_1.SchemaIntrospector();
|
|
277
|
+
this.schemas = await introspector.introspect(db, {
|
|
278
|
+
tables: this.options.tables,
|
|
279
|
+
excludeTables: this.options.excludeTables,
|
|
280
|
+
});
|
|
281
|
+
this.crud.setSchemas(this.schemas);
|
|
282
|
+
this.ftsSearch.setSchemas(this.schemas);
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Exports table data in specified format
|
|
286
|
+
*/
|
|
287
|
+
async exportTable(table, format) {
|
|
288
|
+
const listResult = await this.crud.listTable(table, { limit: 10000 });
|
|
289
|
+
if (format === "csv") {
|
|
290
|
+
const schema = this.schemas.get(table);
|
|
291
|
+
if (!schema)
|
|
292
|
+
throw new Error(`Table '${table}' not found`);
|
|
293
|
+
const headers = schema.columns.map((c) => c.name).join(",");
|
|
294
|
+
const rows = listResult.data.map((entry) => {
|
|
295
|
+
const content = entry.content;
|
|
296
|
+
return schema.columns
|
|
297
|
+
.map((c) => {
|
|
298
|
+
const val = content[c.name];
|
|
299
|
+
if (val === null || val === undefined)
|
|
300
|
+
return "";
|
|
301
|
+
if (typeof val === "string" && (val.includes(",") || val.includes('"'))) {
|
|
302
|
+
return `"${val.replace(/"/g, '""')}"`;
|
|
303
|
+
}
|
|
304
|
+
return String(val);
|
|
305
|
+
})
|
|
306
|
+
.join(",");
|
|
307
|
+
});
|
|
308
|
+
return `${headers}\n${rows.join("\n")}`;
|
|
309
|
+
}
|
|
310
|
+
// Default: JSON
|
|
311
|
+
return listResult.data.map((entry) => entry.content);
|
|
312
|
+
}
|
|
313
|
+
/**
|
|
314
|
+
* Registers a custom action
|
|
315
|
+
*/
|
|
316
|
+
registerAction(name, handler, options) {
|
|
317
|
+
this.actions.registerSimple(name, async (ctx, params) => ({
|
|
318
|
+
success: true,
|
|
319
|
+
data: await handler(ctx, params),
|
|
320
|
+
}), options);
|
|
321
|
+
}
|
|
322
|
+
/**
|
|
323
|
+
* Gets table schemas (for external access)
|
|
324
|
+
*/
|
|
325
|
+
getSchemas() {
|
|
326
|
+
return this.schemas;
|
|
327
|
+
}
|
|
328
|
+
/**
|
|
329
|
+
* Gets the database instance (for advanced operations)
|
|
330
|
+
*/
|
|
331
|
+
getDatabase() {
|
|
332
|
+
return this.db;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
exports.SQLiteAFS = SQLiteAFS;
|
|
336
|
+
// Type check to ensure SQLiteAFS implements AFSModuleClass
|
|
337
|
+
const _typeCheck = SQLiteAFS;
|
|
@@ -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
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { LibSQLDatabase } from "drizzle-orm/libsql";
|
|
2
|
+
import type { TableSchema } from "../schema/types.js";
|
|
3
|
+
/**
|
|
4
|
+
* Context provided to action handlers
|
|
5
|
+
*/
|
|
6
|
+
export interface ActionContext {
|
|
7
|
+
/** Database instance */
|
|
8
|
+
db: LibSQLDatabase;
|
|
9
|
+
/** All table schemas */
|
|
10
|
+
schemas: Map<string, TableSchema>;
|
|
11
|
+
/** Table this action is being executed on */
|
|
12
|
+
table: string;
|
|
13
|
+
/** Primary key of the row (if row-level action) */
|
|
14
|
+
pk?: string;
|
|
15
|
+
/** The row data (if available) */
|
|
16
|
+
row?: Record<string, unknown>;
|
|
17
|
+
/** Reference to the parent module for advanced operations */
|
|
18
|
+
module: {
|
|
19
|
+
refreshSchema(): Promise<void>;
|
|
20
|
+
exportTable(table: string, format: string): Promise<unknown>;
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Action handler function signature
|
|
25
|
+
*/
|
|
26
|
+
export type ActionHandler = (ctx: ActionContext, params: Record<string, unknown>) => Promise<ActionResult>;
|
|
27
|
+
/**
|
|
28
|
+
* Result from an action execution
|
|
29
|
+
*/
|
|
30
|
+
export interface ActionResult {
|
|
31
|
+
success: boolean;
|
|
32
|
+
data?: unknown;
|
|
33
|
+
message?: string;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Action definition with metadata
|
|
37
|
+
*/
|
|
38
|
+
export interface ActionDefinition {
|
|
39
|
+
/** Action name */
|
|
40
|
+
name: string;
|
|
41
|
+
/** Description of what the action does */
|
|
42
|
+
description?: string;
|
|
43
|
+
/** Whether this action is available at table level (vs row level) */
|
|
44
|
+
tableLevel?: boolean;
|
|
45
|
+
/** Whether this action is available at row level */
|
|
46
|
+
rowLevel?: boolean;
|
|
47
|
+
/** Input schema for the action parameters */
|
|
48
|
+
inputSchema?: Record<string, unknown>;
|
|
49
|
+
/** The handler function */
|
|
50
|
+
handler: ActionHandler;
|
|
51
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { type AFSAccessMode } from "@aigne/afs";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
/**
|
|
4
|
+
* FTS (Full-Text Search) configuration schema
|
|
5
|
+
*/
|
|
6
|
+
export declare const ftsConfigSchema: z.ZodOptional<z.ZodObject<{
|
|
7
|
+
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
8
|
+
tables: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodArray<z.ZodString, "many">>>;
|
|
9
|
+
}, "strip", z.ZodTypeAny, {
|
|
10
|
+
enabled: boolean;
|
|
11
|
+
tables?: Record<string, string[]> | undefined;
|
|
12
|
+
}, {
|
|
13
|
+
enabled?: boolean | undefined;
|
|
14
|
+
tables?: Record<string, string[]> | undefined;
|
|
15
|
+
}>>;
|
|
16
|
+
/**
|
|
17
|
+
* SQLite AFS module configuration schema
|
|
18
|
+
*/
|
|
19
|
+
export declare const sqliteAFSConfigSchema: z.ZodObject<{
|
|
20
|
+
url: z.ZodString;
|
|
21
|
+
name: z.ZodOptional<z.ZodString>;
|
|
22
|
+
description: z.ZodOptional<z.ZodString>;
|
|
23
|
+
accessMode: z.ZodOptional<z.ZodEnum<["readonly", "readwrite"]>>;
|
|
24
|
+
tables: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
25
|
+
excludeTables: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
26
|
+
fts: z.ZodOptional<z.ZodObject<{
|
|
27
|
+
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
28
|
+
tables: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodArray<z.ZodString, "many">>>;
|
|
29
|
+
}, "strip", z.ZodTypeAny, {
|
|
30
|
+
enabled: boolean;
|
|
31
|
+
tables?: Record<string, string[]> | undefined;
|
|
32
|
+
}, {
|
|
33
|
+
enabled?: boolean | undefined;
|
|
34
|
+
tables?: Record<string, string[]> | undefined;
|
|
35
|
+
}>>;
|
|
36
|
+
wal: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
37
|
+
}, "strip", z.ZodTypeAny, {
|
|
38
|
+
url: string;
|
|
39
|
+
wal: boolean;
|
|
40
|
+
tables?: string[] | undefined;
|
|
41
|
+
name?: string | undefined;
|
|
42
|
+
description?: string | undefined;
|
|
43
|
+
accessMode?: "readonly" | "readwrite" | undefined;
|
|
44
|
+
excludeTables?: string[] | undefined;
|
|
45
|
+
fts?: {
|
|
46
|
+
enabled: boolean;
|
|
47
|
+
tables?: Record<string, string[]> | undefined;
|
|
48
|
+
} | undefined;
|
|
49
|
+
}, {
|
|
50
|
+
url: string;
|
|
51
|
+
tables?: string[] | undefined;
|
|
52
|
+
name?: string | undefined;
|
|
53
|
+
description?: string | undefined;
|
|
54
|
+
accessMode?: "readonly" | "readwrite" | undefined;
|
|
55
|
+
excludeTables?: string[] | undefined;
|
|
56
|
+
fts?: {
|
|
57
|
+
enabled?: boolean | undefined;
|
|
58
|
+
tables?: Record<string, string[]> | undefined;
|
|
59
|
+
} | undefined;
|
|
60
|
+
wal?: boolean | undefined;
|
|
61
|
+
}>;
|
|
62
|
+
/**
|
|
63
|
+
* SQLite AFS module configuration type
|
|
64
|
+
*/
|
|
65
|
+
export type SQLiteAFSConfig = z.infer<typeof sqliteAFSConfigSchema>;
|
|
66
|
+
/**
|
|
67
|
+
* SQLite AFS module options (after parsing)
|
|
68
|
+
*/
|
|
69
|
+
export interface SQLiteAFSOptions {
|
|
70
|
+
/** SQLite database URL */
|
|
71
|
+
url: string;
|
|
72
|
+
/** Module name */
|
|
73
|
+
name?: string;
|
|
74
|
+
/** Module description */
|
|
75
|
+
description?: string;
|
|
76
|
+
/** Access mode */
|
|
77
|
+
accessMode?: AFSAccessMode;
|
|
78
|
+
/** Tables to expose */
|
|
79
|
+
tables?: string[];
|
|
80
|
+
/** Tables to exclude */
|
|
81
|
+
excludeTables?: string[];
|
|
82
|
+
/** FTS configuration */
|
|
83
|
+
fts?: {
|
|
84
|
+
enabled?: boolean;
|
|
85
|
+
tables?: Record<string, string[]>;
|
|
86
|
+
};
|
|
87
|
+
/** Enable WAL mode */
|
|
88
|
+
wal?: boolean;
|
|
89
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export { registerBuiltInActions } from "./actions/built-in.js";
|
|
2
|
+
export { ActionsRegistry } from "./actions/registry.js";
|
|
3
|
+
export type { ActionContext, ActionDefinition, ActionHandler, ActionResult, } from "./actions/types.js";
|
|
4
|
+
export { type SQLiteAFSConfig, type SQLiteAFSOptions, sqliteAFSConfigSchema } from "./config.js";
|
|
5
|
+
export { type BuildEntryOptions, buildActionsListEntry, buildAttributeEntry, buildAttributeListEntry, buildMetaEntry, buildRowEntry, buildSchemaEntry, buildSearchEntry, buildTableEntry, } from "./node/builder.js";
|
|
6
|
+
export { CRUDOperations } from "./operations/crud.js";
|
|
7
|
+
export { buildDelete, buildGetLastRowId, buildInsert, buildSelectAll, buildSelectByPK, buildUpdate, } from "./operations/query-builder.js";
|
|
8
|
+
export { createFTSConfig, type FTSConfig, FTSSearch, type FTSTableConfig, } from "./operations/search.js";
|
|
9
|
+
export { buildPath, createPathRouter, getVirtualPathType, isVirtualPath, matchPath, } from "./router/path-router.js";
|
|
10
|
+
export type { RouteAction, RouteData, RouteMatch, RouteParams } from "./router/types.js";
|
|
11
|
+
export { SchemaIntrospector } from "./schema/introspector.js";
|
|
12
|
+
export type { ColumnInfo, ForeignKeyInfo, IndexInfo, PragmaForeignKeyRow, PragmaIndexListRow, PragmaTableInfoRow, TableSchema, } from "./schema/types.js";
|
|
13
|
+
export { SQLiteAFS } from "./sqlite-afs.js";
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { AFSEntry } from "@aigne/afs";
|
|
2
|
+
import type { TableSchema } from "../schema/types.js";
|
|
3
|
+
/**
|
|
4
|
+
* Options for building an AFSEntry
|
|
5
|
+
*/
|
|
6
|
+
export interface BuildEntryOptions {
|
|
7
|
+
/** Base path prefix (e.g., empty string or module mount path) */
|
|
8
|
+
basePath?: string;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Builds an AFSEntry from a database row
|
|
12
|
+
*/
|
|
13
|
+
export declare function buildRowEntry(table: string, schema: TableSchema, row: Record<string, unknown>, options?: BuildEntryOptions): AFSEntry;
|
|
14
|
+
/**
|
|
15
|
+
* Builds an AFSEntry for a table listing
|
|
16
|
+
*/
|
|
17
|
+
export declare function buildTableEntry(table: string, schema: TableSchema, options?: BuildEntryOptions & {
|
|
18
|
+
rowCount?: number;
|
|
19
|
+
}): AFSEntry;
|
|
20
|
+
/**
|
|
21
|
+
* Builds an AFSEntry for table schema
|
|
22
|
+
*/
|
|
23
|
+
export declare function buildSchemaEntry(table: string, schema: TableSchema, options?: BuildEntryOptions): AFSEntry;
|
|
24
|
+
/**
|
|
25
|
+
* Builds an AFSEntry for an attribute (single column value)
|
|
26
|
+
*/
|
|
27
|
+
export declare function buildAttributeEntry(table: string, pk: string, column: string, value: unknown, options?: BuildEntryOptions): AFSEntry;
|
|
28
|
+
/**
|
|
29
|
+
* Builds an AFSEntry listing all attributes for a row
|
|
30
|
+
*/
|
|
31
|
+
export declare function buildAttributeListEntry(table: string, schema: TableSchema, pk: string, row: Record<string, unknown>, options?: BuildEntryOptions): AFSEntry[];
|
|
32
|
+
/**
|
|
33
|
+
* Builds an AFSEntry for row metadata
|
|
34
|
+
*/
|
|
35
|
+
export declare function buildMetaEntry(table: string, schema: TableSchema, pk: string, row: Record<string, unknown>, options?: BuildEntryOptions): AFSEntry;
|
|
36
|
+
/**
|
|
37
|
+
* Builds AFSEntry for actions list
|
|
38
|
+
*/
|
|
39
|
+
export declare function buildActionsListEntry(table: string, pk: string, actions: string[], options?: BuildEntryOptions): AFSEntry[];
|
|
40
|
+
/**
|
|
41
|
+
* Builds a search result entry with highlights
|
|
42
|
+
*/
|
|
43
|
+
export declare function buildSearchEntry(table: string, schema: TableSchema, row: Record<string, unknown>, snippet?: string, options?: BuildEntryOptions): AFSEntry;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import type { AFSDeleteResult, AFSListOptions, AFSListResult, AFSReadResult, AFSWriteResult } from "@aigne/afs";
|
|
2
|
+
import type { LibSQLDatabase } from "drizzle-orm/libsql";
|
|
3
|
+
import type { TableSchema } from "../schema/types.js";
|
|
4
|
+
/**
|
|
5
|
+
* CRUD operations for SQLite AFS
|
|
6
|
+
*/
|
|
7
|
+
export declare class CRUDOperations {
|
|
8
|
+
private db;
|
|
9
|
+
private schemas;
|
|
10
|
+
private basePath;
|
|
11
|
+
constructor(db: LibSQLDatabase, schemas: Map<string, TableSchema>, basePath?: string);
|
|
12
|
+
/**
|
|
13
|
+
* Lists all tables
|
|
14
|
+
*/
|
|
15
|
+
listTables(): Promise<AFSListResult>;
|
|
16
|
+
/**
|
|
17
|
+
* Lists rows in a table
|
|
18
|
+
*/
|
|
19
|
+
listTable(table: string, options?: AFSListOptions): Promise<AFSListResult>;
|
|
20
|
+
/**
|
|
21
|
+
* Reads a single row by primary key
|
|
22
|
+
*/
|
|
23
|
+
readRow(table: string, pk: string): Promise<AFSReadResult>;
|
|
24
|
+
/**
|
|
25
|
+
* Gets table schema
|
|
26
|
+
*/
|
|
27
|
+
getSchema(table: string): AFSReadResult;
|
|
28
|
+
/**
|
|
29
|
+
* Lists attributes (columns) for a row
|
|
30
|
+
*/
|
|
31
|
+
listAttributes(table: string, pk: string): Promise<AFSListResult>;
|
|
32
|
+
/**
|
|
33
|
+
* Gets a single attribute (column value) for a row
|
|
34
|
+
*/
|
|
35
|
+
getAttribute(table: string, pk: string, column: string): Promise<AFSReadResult>;
|
|
36
|
+
/**
|
|
37
|
+
* Gets row metadata
|
|
38
|
+
*/
|
|
39
|
+
getMeta(table: string, pk: string): Promise<AFSReadResult>;
|
|
40
|
+
/**
|
|
41
|
+
* Creates a new row in a table
|
|
42
|
+
*/
|
|
43
|
+
createRow(table: string, content: Record<string, unknown>): Promise<AFSWriteResult>;
|
|
44
|
+
/**
|
|
45
|
+
* Updates an existing row
|
|
46
|
+
*/
|
|
47
|
+
updateRow(table: string, pk: string, content: Record<string, unknown>): Promise<AFSWriteResult>;
|
|
48
|
+
/**
|
|
49
|
+
* Deletes a row by primary key
|
|
50
|
+
*/
|
|
51
|
+
deleteRow(table: string, pk: string): Promise<AFSDeleteResult>;
|
|
52
|
+
/**
|
|
53
|
+
* Checks if a table exists
|
|
54
|
+
*/
|
|
55
|
+
hasTable(table: string): boolean;
|
|
56
|
+
/**
|
|
57
|
+
* Gets the schema for a table
|
|
58
|
+
*/
|
|
59
|
+
getTableSchema(table: string): TableSchema | undefined;
|
|
60
|
+
/**
|
|
61
|
+
* Updates the schemas map (after refresh)
|
|
62
|
+
*/
|
|
63
|
+
setSchemas(schemas: Map<string, TableSchema>): void;
|
|
64
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { TableSchema } from "../schema/types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Builds a SELECT query string for a single row by primary key
|
|
4
|
+
*/
|
|
5
|
+
export declare function buildSelectByPK(tableName: string, schema: TableSchema, pk: string): string;
|
|
6
|
+
/**
|
|
7
|
+
* Builds a SELECT query string for listing rows with optional limit and offset
|
|
8
|
+
*/
|
|
9
|
+
export declare function buildSelectAll(tableName: string, options?: {
|
|
10
|
+
limit?: number;
|
|
11
|
+
offset?: number;
|
|
12
|
+
orderBy?: [string, "asc" | "desc"][];
|
|
13
|
+
}): string;
|
|
14
|
+
/**
|
|
15
|
+
* Builds an INSERT query string from content object
|
|
16
|
+
*/
|
|
17
|
+
export declare function buildInsert(tableName: string, schema: TableSchema, content: Record<string, unknown>): string;
|
|
18
|
+
/**
|
|
19
|
+
* Builds an UPDATE query string from content object
|
|
20
|
+
*/
|
|
21
|
+
export declare function buildUpdate(tableName: string, schema: TableSchema, pk: string, content: Record<string, unknown>): string;
|
|
22
|
+
/**
|
|
23
|
+
* Builds a DELETE query string by primary key
|
|
24
|
+
*/
|
|
25
|
+
export declare function buildDelete(tableName: string, schema: TableSchema, pk: string): string;
|
|
26
|
+
/**
|
|
27
|
+
* Formats a value for SQL insertion
|
|
28
|
+
*/
|
|
29
|
+
export declare function formatValue(value: unknown): string;
|
|
30
|
+
/**
|
|
31
|
+
* Escapes a string for safe SQL insertion
|
|
32
|
+
*/
|
|
33
|
+
export declare function escapeSQLString(str: string): string;
|
|
34
|
+
/**
|
|
35
|
+
* Gets the last inserted rowid query string
|
|
36
|
+
*/
|
|
37
|
+
export declare function buildGetLastRowId(): string;
|