@iamkirbki/database-handler-core 2.0.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.
@@ -0,0 +1,130 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ import Table from "./Table";
11
+ import Query from "./Query";
12
+ /**
13
+ * Main Database class for interacting with SQLite databases
14
+ *
15
+ * @example
16
+ * ```typescript
17
+ * import { Database } from 'kirbkis-bettersqlite3-handler';
18
+ *
19
+ * // Create or open a database
20
+ * const db = new Database('./myapp.db');
21
+ *
22
+ * // Access a table
23
+ * const users = db.Table('users');
24
+ *
25
+ * // Create a new table
26
+ * const posts = db.CreateTable('posts');
27
+ * ```
28
+ */
29
+ export default class Database {
30
+ /**
31
+ * Creates a new Database instance
32
+ *
33
+ * @param dbPath - Path to the SQLite database file (absolute or relative to process.cwd())
34
+ *
35
+ * @example
36
+ * ```typescript
37
+ * // Relative path
38
+ * const db = new Database('./data/app.db');
39
+ *
40
+ * // Absolute path
41
+ * const db = new Database('/var/data/app.db');
42
+ *
43
+ * // In-memory database
44
+ * const db = new Database(':memory:');
45
+ * ```
46
+ */
47
+ constructor(adapter) {
48
+ this.adapter = adapter;
49
+ }
50
+ /**
51
+ * Get a Table instance to interact with an existing table
52
+ *
53
+ * @param name - Name of the table
54
+ * @returns Table instance for querying and manipulating data
55
+ * @throws Error if the table does not exist
56
+ *
57
+ * @example
58
+ * ```typescript
59
+ * const users = await db.Table('users');
60
+ * const allUsers = await users.Records();
61
+ * ```
62
+ */
63
+ Table(name) {
64
+ return __awaiter(this, void 0, void 0, function* () {
65
+ // Validator.ValidateTableName(name);
66
+ return yield Table.create(name, this.adapter);
67
+ });
68
+ }
69
+ // TODO Make primary key required
70
+ /**
71
+ * Create a new table with specified columns
72
+ * Validates table name, column names, and column types before creation
73
+ * Uses CREATE TABLE IF NOT EXISTS to avoid errors if table already exists
74
+ *
75
+ * @param name - Name of the table to create
76
+ * @param columns - Object mapping column names to their type definitions
77
+ * @returns Table instance for the newly created table
78
+ * @throws Error if table name, column names, or column types are invalid
79
+ *
80
+ * @example
81
+ * ```typescript
82
+ * // Create a users table
83
+ * const users = await db.CreateTable('users', {
84
+ * id: 'INTEGER PRIMARY KEY AUTOINCREMENT',
85
+ * name: 'TEXT NOT NULL',
86
+ * email: 'TEXT UNIQUE',
87
+ * age: 'INTEGER',
88
+ * created_at: 'DATETIME DEFAULT CURRENT_TIMESTAMP'
89
+ * });
90
+ *
91
+ * // Table is now ready to use
92
+ * await users.Insert({ name: 'John', email: 'john@example.com', age: 30 });
93
+ * ```
94
+ */
95
+ CreateTable(name, columns) {
96
+ return __awaiter(this, void 0, void 0, function* () {
97
+ // Validator.ValidateTableName(name);
98
+ const names = Object.keys(columns || {}).map((colName) => {
99
+ // Validator.ValidateColumnName(colName);
100
+ return colName;
101
+ });
102
+ const colsDef = names.map(colName => {
103
+ const colType = columns[colName];
104
+ // Validator.ValidateColumnType(colType);
105
+ return `"${colName}" ${colType}`;
106
+ }).join(", ");
107
+ const stmt = yield this.adapter.prepare(`CREATE TABLE IF NOT EXISTS "${name}" (${colsDef});`);
108
+ yield stmt.run();
109
+ return yield Table.create(name, this.adapter, true);
110
+ });
111
+ }
112
+ /**
113
+ * Create a Query object for executing custom SQL queries
114
+ *
115
+ * @param table - Table object for validation and context
116
+ * @param query - The SQL query string with ? placeholders
117
+ * @returns Query instance for executing the query
118
+ *
119
+ * @example
120
+ * ```typescript
121
+ * const users = db.Table('users');
122
+ * const query = db.Query(users, 'SELECT * FROM users WHERE age > ? AND status = ?');
123
+ * query.Parameters = { age: 18, status: 'active' };
124
+ * const results = query.All();
125
+ * ```
126
+ */
127
+ Query(table, query) {
128
+ return new Query(table, query, this.adapter);
129
+ }
130
+ }
package/dist/Query.js ADDED
@@ -0,0 +1,186 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ import Record from "@core/Record";
11
+ /**
12
+ * Query class for executing custom SQL queries
13
+ *
14
+ * Features:
15
+ * - Supports named parameters using @fieldName syntax
16
+ * - Provides type-safe query execution (Run, All, Get)
17
+ * - Transaction support for atomic multi-insert/update operations
18
+ *
19
+ * @example
20
+ * ```typescript
21
+ * const users = db.Table('users');
22
+ *
23
+ * // SELECT query with parameters
24
+ * const query = db.Query(users, 'SELECT * FROM users WHERE age > @age AND status = @status');
25
+ * query.Parameters = { age: 18, status: 'active' };
26
+ * const results = query.All();
27
+ *
28
+ * // INSERT query
29
+ * const insert = db.Query(users, 'INSERT INTO users (name, email) VALUES (@name, @email)');
30
+ * insert.Parameters = { name: 'John', email: 'john@example.com' };
31
+ * insert.Run();
32
+ *
33
+ * // Transaction for multiple inserts
34
+ * insert.Transaction([
35
+ * { name: 'John', email: 'john@example.com' },
36
+ * { name: 'Jane', email: 'jane@example.com' }
37
+ * ]);
38
+ * ```
39
+ */
40
+ export default class Query {
41
+ /**
42
+ * Creates a Query instance (usually called via db.Query() method)
43
+ *
44
+ * @param Table - Table instance for validation context
45
+ * @param Query - SQL query string with @fieldName placeholders for parameters
46
+ * @param DB - Database connection instance
47
+ *
48
+ * @example
49
+ * ```typescript
50
+ * // Direct instantiation (not recommended - use db.Query() instead)
51
+ * const query = new Query(
52
+ * usersTable,
53
+ * 'SELECT * FROM users WHERE id = @id',
54
+ * db
55
+ * );
56
+ * query.Parameters = { id: 1 };
57
+ *
58
+ * // Recommended approach
59
+ * const query = db.Query(usersTable, 'SELECT * FROM users WHERE id = @id');
60
+ * query.Parameters = { id: 1 };
61
+ * ```
62
+ */
63
+ constructor(Table, Query, adapter) {
64
+ this.query = "";
65
+ this.Parameters = {};
66
+ this.Table = Table;
67
+ this.query = Query;
68
+ this.adapter = adapter;
69
+ }
70
+ /**
71
+ * Execute a query that modifies data (INSERT, UPDATE, DELETE)
72
+ *
73
+ * @template Type - Expected return type (typically { lastInsertRowid: number, changes: number })
74
+ * @returns Result object with lastInsertRowid and changes count
75
+ *
76
+ * @example
77
+ * ```typescript
78
+ * // INSERT query
79
+ * const query = db.Query(users, 'INSERT INTO users (name, age) VALUES (@name, @age)');
80
+ * query.Parameters = { name: 'John', age: 30 };
81
+ * const result = query.Run<{ lastInsertRowid: number, changes: number }>();
82
+ * console.log(`Inserted ID: ${result.lastInsertRowid}`);
83
+ *
84
+ * // UPDATE query
85
+ * const update = db.Query(users, 'UPDATE users SET age = @age WHERE id = @id');
86
+ * update.Parameters = { age: 31, id: 1 };
87
+ * const updateResult = update.Run<{ changes: number }>();
88
+ * console.log(`Updated ${updateResult.changes} rows`);
89
+ * ```
90
+ */
91
+ Run() {
92
+ return __awaiter(this, void 0, void 0, function* () {
93
+ const stmt = yield this.adapter.prepare(this.query);
94
+ return yield stmt.run(this.Parameters);
95
+ });
96
+ }
97
+ /**
98
+ * Execute a SELECT query and return all matching rows as Record objects
99
+ * Each row is wrapped in a Record instance for convenient updates/deletes
100
+ *
101
+ * @template Type - Expected row type
102
+ * @returns Array of Record objects containing the query results
103
+ *
104
+ * @example
105
+ * ```typescript
106
+ * interface User {
107
+ * id: number;
108
+ * name: string;
109
+ * age: number;
110
+ * }
111
+ *
112
+ * const query = db.Query(users, 'SELECT * FROM users WHERE age > @age');
113
+ * query.Parameters = { age: 18 };
114
+ * const results = query.All<User>();
115
+ *
116
+ * // Each result is a Record object
117
+ * results.forEach(user => {
118
+ * console.log(user.values); // { id: 1, name: 'John', age: 30 }
119
+ * user.Update({ age: 31 }); // Can update directly
120
+ * });
121
+ * ```
122
+ */
123
+ All() {
124
+ return __awaiter(this, void 0, void 0, function* () {
125
+ const stmt = yield this.adapter.prepare(this.query);
126
+ let results = yield stmt.all(this.Parameters);
127
+ // This is a fix for a bug where id's passed as numbers don't match string ids in the db
128
+ if (results.length === 0 && this.Parameters.id) {
129
+ this.Parameters.id = this.Parameters.id.toString();
130
+ results = (yield stmt.all(this.Parameters));
131
+ }
132
+ return results.map(res => new Record(res, this.adapter, this.Table));
133
+ });
134
+ }
135
+ /**
136
+ * Execute a SELECT query and return the first matching row as a Record object
137
+ * Returns undefined if no rows match the query
138
+ *
139
+ * @template Type - Expected row type
140
+ * @returns Single Record object or undefined if no match found
141
+ *
142
+ * @example
143
+ * ```typescript
144
+ * interface User {
145
+ * id: number;
146
+ * name: string;
147
+ * email: string;
148
+ * }
149
+ *
150
+ * const query = db.Query(users, 'SELECT * FROM users WHERE id = @id');
151
+ * query.Parameters = { id: 1 };
152
+ * const user = query.Get<User>();
153
+ *
154
+ * if (user) {
155
+ * console.log(user.values); // { id: 1, name: 'John', email: 'john@example.com' }
156
+ * user.Update({ email: 'newemail@example.com' });
157
+ * }
158
+ * ```
159
+ */
160
+ Get() {
161
+ return __awaiter(this, void 0, void 0, function* () {
162
+ const stmt = yield this.adapter.prepare(this.query);
163
+ const results = yield stmt.get(this.Parameters);
164
+ return results ? new Record(results, this.adapter, this.Table) : undefined;
165
+ });
166
+ }
167
+ Transaction(paramList) {
168
+ return __awaiter(this, void 0, void 0, function* () {
169
+ const stmt = yield this.adapter.prepare(this.query);
170
+ const transactionFn = yield this.adapter.transaction((paramsArray) => {
171
+ for (const params of paramsArray) {
172
+ // Use runSync for better-sqlite3 transactions (must be synchronous)
173
+ // For other adapters, this method should be implemented appropriately
174
+ if (stmt.runSync) {
175
+ stmt.runSync(params);
176
+ }
177
+ else {
178
+ // Fallback: call run without await (may not work for all adapters)
179
+ stmt.run(params);
180
+ }
181
+ }
182
+ });
183
+ transactionFn(paramList);
184
+ });
185
+ }
186
+ }
package/dist/Record.js ADDED
@@ -0,0 +1,172 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ import { inspect } from "util";
11
+ import Query from "@core/Query";
12
+ /**
13
+ * Record class represents a single database row with methods for updates and deletion
14
+ * Automatically returned by Table.Records() and Table.Record() methods
15
+ *
16
+ * @example
17
+ * ```typescript
18
+ * const user = table.Record({ where: { id: 1 } });
19
+ *
20
+ * // Access values
21
+ * console.log(user?.values);
22
+ *
23
+ * // Update the record
24
+ * user?.Update({ name: 'John Doe', age: 31 });
25
+ *
26
+ * // Delete the record
27
+ * user?.Delete();
28
+ *
29
+ * // JSON serialization works automatically
30
+ * console.log(JSON.stringify(user)); // {"id": 1, "name": "John Doe", ...}
31
+ * ```
32
+ */
33
+ export default class Record {
34
+ /**
35
+ * Creates a Record instance (typically called internally by Table methods)
36
+ *
37
+ * @param values - Object containing column names and their values
38
+ * @param db - Database connection instance
39
+ * @param tableName - Name of the table this record belongs to
40
+ */
41
+ constructor(values, adapter, table) {
42
+ this._values = {};
43
+ this._values = values;
44
+ this.adapter = adapter;
45
+ this._table = table;
46
+ }
47
+ /**
48
+ * Get the raw values object for this record
49
+ *
50
+ * @returns Object containing all column values
51
+ *
52
+ * @example
53
+ * ```typescript
54
+ * const user = table.Record({ where: { id: 1 } });
55
+ * console.log(user?.values); // { id: 1, name: 'John', email: 'john@example.com' }
56
+ * ```
57
+ */
58
+ get values() {
59
+ return this._values;
60
+ }
61
+ ;
62
+ /**
63
+ * Update this record in the database
64
+ * Updates both the database and the local values
65
+ *
66
+ * @template TEntity - Record type that must include an 'id' property (number or string)
67
+ * @param newValues - Object with column names and new values to update
68
+ * @throws Error if the record doesn't have an 'id' field
69
+ *
70
+ * @example
71
+ * ```typescript
72
+ * const user = table.Record({ where: { id: 1 } });
73
+ * user?.Update({ name: 'Jane Doe', age: 28 });
74
+ * // Database is updated and user.values reflects the changes
75
+ * ```
76
+ */
77
+ Update(newValues) {
78
+ return __awaiter(this, void 0, void 0, function* () {
79
+ const setClauses = Object.keys(newValues)
80
+ .map(key => `${key} = @${key}`)
81
+ .join(", ");
82
+ // Use all current values as WHERE clause to identify the record
83
+ const originalValues = this._values;
84
+ const whereClauses = Object.keys(originalValues)
85
+ .map(key => `${key} = @where_${key}`)
86
+ .join(" AND ");
87
+ const query = `UPDATE "${this._table.Name}" SET ${setClauses} WHERE ${whereClauses};`;
88
+ const _query = new Query(this._table, query, this.adapter);
89
+ const params = Object.assign({}, newValues);
90
+ Object.entries(originalValues).forEach(([key, value]) => {
91
+ params[`where_${key}`] = value;
92
+ });
93
+ _query.Parameters = params;
94
+ yield _query.Run();
95
+ this._values = Object.assign(Object.assign({}, this._values), newValues);
96
+ });
97
+ }
98
+ /**
99
+ * Delete this record from the database
100
+ * Uses all current field values as WHERE clause conditions
101
+ *
102
+ * @example
103
+ * ```typescript
104
+ * const user = table.Record({ where: { id: 1 } });
105
+ * user?.Delete();
106
+ * // Record is permanently deleted from the database
107
+ * ```
108
+ */
109
+ Delete() {
110
+ return __awaiter(this, void 0, void 0, function* () {
111
+ const whereClauses = Object.keys(this._values)
112
+ .map(key => `${key} = @${key}`)
113
+ .join(" AND ");
114
+ const _query = new Query(this._table, `DELETE FROM "${this._table.Name}" WHERE ${whereClauses};`, this.adapter);
115
+ _query.Parameters = Object.assign({}, this._values);
116
+ yield _query.Run();
117
+ });
118
+ }
119
+ /**
120
+ * Returns the values object when JSON.stringify() is called
121
+ * Allows seamless JSON serialization of Record objects
122
+ *
123
+ * @returns The values object
124
+ *
125
+ * @example
126
+ * ```typescript
127
+ * const user = table.Record({ where: { id: 1 } });
128
+ * console.log(JSON.stringify(user));
129
+ * // Output: {"id":1,"name":"John","email":"john@example.com"}
130
+ * ```
131
+ */
132
+ toJSON() {
133
+ return this._values;
134
+ }
135
+ /**
136
+ * Returns a formatted string representation of the record
137
+ *
138
+ * @returns Pretty-printed JSON string of the values
139
+ *
140
+ * @example
141
+ * ```typescript
142
+ * const user = table.Record({ where: { id: 1 } });
143
+ * console.log(user?.toString());
144
+ * // Output:
145
+ * // {
146
+ * // "id": 1,
147
+ * // "name": "John",
148
+ * // "email": "john@example.com"
149
+ * // }
150
+ * ```
151
+ */
152
+ toString() {
153
+ return JSON.stringify(this._values, null, 2);
154
+ }
155
+ /**
156
+ * Custom inspect method for console.log() and Node.js REPL
157
+ * Makes Record objects display their values directly instead of the class structure
158
+ *
159
+ * @returns The values object for display
160
+ *
161
+ * @example
162
+ * ```typescript
163
+ * const user = table.Record({ where: { id: 1 } });
164
+ * console.log(user);
165
+ * // Output: { id: 1, name: 'John', email: 'john@example.com' }
166
+ * // Instead of: Record { _db: ..., _values: {...}, _tableName: '...' }
167
+ * ```
168
+ */
169
+ [inspect.custom]() {
170
+ return this._values;
171
+ }
172
+ }
package/dist/Schema.js ADDED
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Schema module - placeholder for future schema definition and migration features
3
+ *
4
+ * Future capabilities:
5
+ * - Schema versioning and migrations
6
+ * - Schema validation and comparison
7
+ * - Automatic migration generation
8
+ * - Schema import/export
9
+ *
10
+ * @module Schema
11
+ * @packageDocumentation
12
+ */
13
+ export {};