@zhin.js/database 1.0.44 → 1.0.47

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.
@@ -1,317 +0,0 @@
1
- import * as fs from 'node:fs';
2
- import path from 'node:path';
3
- import { Dialect } from '../base/index.js';
4
- import { Registry } from '../registry.js';
5
- import { Database } from '../base/index.js';
6
- import { Column, Transaction, TransactionOptions } from '../types.js';
7
- import { RelatedDatabase } from '../type/related/database.js';
8
-
9
- export interface SQLiteDialectConfig {
10
- filename: string;
11
- mode?: string;
12
- }
13
-
14
- export class SQLiteDialect<S extends Record<string, object> = Record<string, object>> extends Dialect<SQLiteDialectConfig, S, string> {
15
- /** Node 内置 SQLite 连接(node:sqlite),在 connect() 中动态加载 */
16
- private db: { prepare(sql: string): any; close(): void } | null = null;
17
-
18
- constructor(config: SQLiteDialectConfig) {
19
- super('sqlite', config);
20
- }
21
-
22
- isConnected(): boolean {
23
- return this.db !== null;
24
- }
25
-
26
- async connect(): Promise<void> {
27
- try {
28
- const { createRequire } = await import('node:module');
29
- const require = createRequire(import.meta.url);
30
- const { DatabaseSync } = require('node:sqlite');
31
- const dirname = path.dirname(this.config.filename);
32
- if (!fs.existsSync(dirname)) {
33
- fs.mkdirSync(dirname, { recursive: true });
34
- }
35
- this.db = new DatabaseSync(this.config.filename);
36
- } catch (error) {
37
- throw new Error(`SQLite 连接失败(需要 Node.js 22.5+,推荐 24+): ${error}`);
38
- }
39
- }
40
-
41
- async disconnect(): Promise<void> {
42
- if (this.db) {
43
- try {
44
- this.db.close();
45
- } finally {
46
- this.db = null;
47
- }
48
- }
49
- }
50
-
51
- async healthCheck(): Promise<boolean> {
52
- return this.isConnected();
53
- }
54
-
55
- /** node:sqlite 仅支持 number/string/bigint/buffer/null;JSON 列在库内为 TEXT,对象需序列化 */
56
- private prepareBindParams(params: any[]): any[] {
57
- return params.map((v) => {
58
- if (v == null) return null;
59
- if (typeof v === 'bigint') return v;
60
- if (v instanceof Date) return v.toISOString();
61
- if (typeof v === 'boolean') return v ? 1 : 0;
62
- if (Buffer.isBuffer(v)) return v;
63
- if (typeof v === 'object') return JSON.stringify(v);
64
- return v;
65
- });
66
- }
67
-
68
- async query<U = any>(sql: string, params?: any[]): Promise<U> {
69
- if (!this.db) throw new Error('SQLite 未连接');
70
- const trimmedSql = sql.trim().toLowerCase();
71
- const isSelect = trimmedSql.startsWith('select');
72
- const isInsert = trimmedSql.startsWith('insert');
73
- const isUpdateOrDelete = trimmedSql.startsWith('update') || trimmedSql.startsWith('delete');
74
- const args = this.prepareBindParams(params ?? []);
75
-
76
- try {
77
- const stmt = this.db.prepare(sql);
78
- if (isSelect) {
79
- const rows = stmt.all(...args) as any[];
80
- return this.processQueryResults(rows) as U;
81
- }
82
- if (isInsert) {
83
- const result = stmt.run(...args) as { changes: number | bigint; lastInsertRowid: number | bigint };
84
- return {
85
- lastID: Number(result.lastInsertRowid),
86
- changes: Number(result.changes),
87
- } as U;
88
- }
89
- if (isUpdateOrDelete) {
90
- const result = stmt.run(...args) as { changes: number | bigint };
91
- return Number(result.changes) as U;
92
- }
93
- stmt.run(...args);
94
- return undefined as U;
95
- } catch (err) {
96
- throw err;
97
- }
98
- }
99
-
100
- /**
101
- * 处理查询结果,移除字符串字段的多余引号
102
- */
103
- private processQueryResults(data: any): any {
104
- if (!data) return data;
105
-
106
- if (Array.isArray(data)) {
107
- return data.map(row => this.processRowData(row));
108
- } else if (typeof data === 'object') {
109
- return this.processRowData(data);
110
- }
111
-
112
- return data;
113
- }
114
-
115
- /**
116
- * 处理单行数据
117
- */
118
- private processRowData(row: any): any {
119
- if (!row || typeof row !== 'object') return row;
120
-
121
- const processedRow: any = {};
122
-
123
- for (const [key, value] of Object.entries(row)) {
124
- processedRow[key] = this.processFieldValue(value);
125
- }
126
-
127
- return processedRow;
128
- }
129
-
130
- /**
131
- * 处理字段值,移除多余的引号并解析 JSON
132
- */
133
- private processFieldValue(value: any): any {
134
- if (typeof value !== 'string') return value;
135
-
136
- // 移除字符串两端的引号(如果存在)
137
- if ((value.startsWith("'") && value.endsWith("'")) ||
138
- (value.startsWith('"') && value.endsWith('"'))) {
139
- const unquoted = value.slice(1, -1);
140
-
141
- // 尝试解析为 JSON(用于 json 类型字段)
142
- if (unquoted.startsWith('{') || unquoted.startsWith('[')) {
143
- try {
144
- return JSON.parse(unquoted);
145
- } catch {
146
- // 如果解析失败,返回去除引号的字符串
147
- return unquoted;
148
- }
149
- }
150
-
151
- return unquoted;
152
- }
153
-
154
- // 尝试解析没有引号包裹但看起来像 JSON 的值(SQLite 驱动返回的 json 类型字段)
155
- if (value.startsWith('{') || value.startsWith('[')) {
156
- try {
157
- return JSON.parse(value);
158
- } catch {
159
- // 解析失败,返回原始字符串
160
- }
161
- }
162
-
163
- return value;
164
- }
165
-
166
- async dispose(): Promise<void> {
167
- if (this.db) {
168
- try {
169
- this.db.close();
170
- } finally {
171
- this.db = null;
172
- }
173
- }
174
- }
175
-
176
- // SQL generation methods
177
- mapColumnType(type: string): string {
178
- const typeMap: Record<string, string> = {
179
- 'text': 'TEXT',
180
- 'integer': 'INTEGER',
181
- 'float': 'REAL',
182
- 'boolean': 'INTEGER',
183
- 'date': 'TEXT',
184
- 'json': 'TEXT'
185
- };
186
- return typeMap[type.toLowerCase()] || 'TEXT';
187
- }
188
-
189
- quoteIdentifier(identifier: string): string {
190
- return `"${identifier}"`;
191
- }
192
-
193
- getParameterPlaceholder(index: number): string {
194
- return '?';
195
- }
196
-
197
- getStatementTerminator(): string {
198
- return ';';
199
- }
200
-
201
- formatBoolean(value: boolean): string {
202
- return value ? '1' : '0';
203
- }
204
-
205
- formatDate(value: Date): string {
206
- return `'${value.toISOString()}'`;
207
- }
208
-
209
- formatJson(value: any): string {
210
- return `'${JSON.stringify(value).replace(/'/g, "''")}'`;
211
- }
212
-
213
- escapeString(value: string): string {
214
- return value.replace(/'/g, "''");
215
- }
216
-
217
- formatDefaultValue(value: any): string {
218
- if (typeof value === 'string') {
219
- return `'${this.escapeString(value)}'`;
220
- } else if (typeof value === 'number' || typeof value === 'boolean') {
221
- return value.toString();
222
- } else if (value instanceof Date) {
223
- return this.formatDate(value);
224
- } else if (value === null) {
225
- return 'NULL';
226
- } else if (typeof value === 'object') {
227
- return this.formatJson(value);
228
- } else {
229
- throw new Error(`Unsupported default value type: ${typeof value}`);
230
- }
231
- }
232
-
233
- formatLimit(limit: number): string {
234
- return `LIMIT ${limit}`;
235
- }
236
-
237
- formatOffset(offset: number): string {
238
- return `OFFSET ${offset}`;
239
- }
240
-
241
- formatLimitOffset(limit: number, offset: number): string {
242
- return `LIMIT ${limit} OFFSET ${offset}`;
243
- }
244
-
245
- formatCreateTable<T extends keyof S>(tableName: T, columns: string[]): string {
246
- return `CREATE TABLE IF NOT EXISTS ${this.quoteIdentifier(String(tableName))} (${columns.join(', ')})`;
247
- }
248
-
249
- formatColumnDefinition(field: string, column: Column<any>): string {
250
- const name = this.quoteIdentifier(String(field));
251
- const type = this.mapColumnType(column.type);
252
- const length = column.length ? `(${column.length})` : '';
253
- const nullable = column.nullable === false ? ' NOT NULL' : '';
254
- const primary = column.primary ? ' PRIMARY KEY' : '';
255
- const unique = column.unique ? ' UNIQUE' : '';
256
- const defaultVal = column.default !== undefined
257
- ? ` DEFAULT ${this.formatDefaultValue(column.default)}`
258
- : '';
259
-
260
- return `${name} ${type}${length}${primary}${unique}${nullable}${defaultVal}`;
261
- }
262
-
263
- formatAlterTable<T extends keyof S>(tableName: T, alterations: string[]): string {
264
- return `ALTER TABLE ${this.quoteIdentifier(String(tableName))} ${alterations.join(', ')}`;
265
- }
266
-
267
- formatDropTable<T extends keyof S>(tableName: T, ifExists?: boolean): string {
268
- const ifExistsClause = ifExists ? 'IF EXISTS ' : '';
269
- return `DROP TABLE ${ifExistsClause}${this.quoteIdentifier(String(tableName))}`;
270
- }
271
-
272
- formatDropIndex<T extends keyof S>(indexName: string, tableName: T, ifExists?: boolean): string {
273
- const ifExistsClause = ifExists ? 'IF EXISTS ' : '';
274
- return `DROP INDEX ${ifExistsClause}${this.quoteIdentifier(indexName)}`;
275
- }
276
-
277
- // ============================================================================
278
- // Transaction Support
279
- // ============================================================================
280
-
281
- /**
282
- * SQLite 支持事务
283
- */
284
- supportsTransactions(): boolean {
285
- return true;
286
- }
287
-
288
- /**
289
- * 开始事务
290
- */
291
- async beginTransaction(options?: TransactionOptions): Promise<Transaction> {
292
- const dialect = this;
293
-
294
- // 开始事务
295
- await this.query('BEGIN TRANSACTION');
296
-
297
- return {
298
- async commit(): Promise<void> {
299
- await dialect.query('COMMIT');
300
- },
301
-
302
- async rollback(): Promise<void> {
303
- await dialect.query('ROLLBACK');
304
- },
305
-
306
- async query<T = any>(sql: string, params?: any[]): Promise<T> {
307
- return dialect.query<T>(sql, params);
308
- }
309
- };
310
- }
311
- }
312
- export class Sqlite<S extends Record<string, object> = Record<string, object>> extends RelatedDatabase<SQLiteDialectConfig, S> {
313
- constructor(config: SQLiteDialectConfig, definitions?: Database.DefinitionObj<S>) {
314
- super(new SQLiteDialect<S>(config), definitions);
315
- }
316
- }
317
- Registry.register('sqlite', Sqlite);
package/src/index.ts DELETED
@@ -1,19 +0,0 @@
1
- export * from './types.js';
2
- export * from './base/model.js';
3
- export * from './base/dialect.js';
4
- export * from './base/database.js';
5
- export * from './type/related/database.js';
6
- export * from './type/related/model.js';
7
- export * from './type/document/database.js';
8
- export * from './type/document/model.js';
9
- export * from './type/keyvalue/database.js';
10
- export * from './type/keyvalue/model.js';
11
- export * from './registry.js';
12
- export * from './dialects/memory.js';
13
- export * from './dialects/mysql.js';
14
- export * from './dialects/pg.js';
15
- export * from './dialects/redis.js';
16
- export * from './dialects/sqlite.js';
17
- export * from './dialects/mongodb.js';
18
- export * from './migration.js';
19
-