@mostajs/orm 1.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.
Files changed (50) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +548 -0
  3. package/dist/core/base-repository.d.ts +26 -0
  4. package/dist/core/base-repository.js +82 -0
  5. package/dist/core/config.d.ts +62 -0
  6. package/dist/core/config.js +116 -0
  7. package/dist/core/errors.d.ts +30 -0
  8. package/dist/core/errors.js +49 -0
  9. package/dist/core/factory.d.ts +41 -0
  10. package/dist/core/factory.js +142 -0
  11. package/dist/core/normalizer.d.ts +9 -0
  12. package/dist/core/normalizer.js +19 -0
  13. package/dist/core/registry.d.ts +43 -0
  14. package/dist/core/registry.js +78 -0
  15. package/dist/core/types.d.ts +228 -0
  16. package/dist/core/types.js +5 -0
  17. package/dist/dialects/abstract-sql.dialect.d.ts +113 -0
  18. package/dist/dialects/abstract-sql.dialect.js +1071 -0
  19. package/dist/dialects/cockroachdb.dialect.d.ts +2 -0
  20. package/dist/dialects/cockroachdb.dialect.js +23 -0
  21. package/dist/dialects/db2.dialect.d.ts +2 -0
  22. package/dist/dialects/db2.dialect.js +190 -0
  23. package/dist/dialects/hana.dialect.d.ts +2 -0
  24. package/dist/dialects/hana.dialect.js +199 -0
  25. package/dist/dialects/hsqldb.dialect.d.ts +2 -0
  26. package/dist/dialects/hsqldb.dialect.js +114 -0
  27. package/dist/dialects/mariadb.dialect.d.ts +2 -0
  28. package/dist/dialects/mariadb.dialect.js +87 -0
  29. package/dist/dialects/mongo.dialect.d.ts +2 -0
  30. package/dist/dialects/mongo.dialect.js +480 -0
  31. package/dist/dialects/mssql.dialect.d.ts +27 -0
  32. package/dist/dialects/mssql.dialect.js +127 -0
  33. package/dist/dialects/mysql.dialect.d.ts +24 -0
  34. package/dist/dialects/mysql.dialect.js +101 -0
  35. package/dist/dialects/oracle.dialect.d.ts +2 -0
  36. package/dist/dialects/oracle.dialect.js +206 -0
  37. package/dist/dialects/postgres.dialect.d.ts +26 -0
  38. package/dist/dialects/postgres.dialect.js +105 -0
  39. package/dist/dialects/spanner.dialect.d.ts +2 -0
  40. package/dist/dialects/spanner.dialect.js +259 -0
  41. package/dist/dialects/sqlite.dialect.d.ts +2 -0
  42. package/dist/dialects/sqlite.dialect.js +1027 -0
  43. package/dist/dialects/sybase.dialect.d.ts +2 -0
  44. package/dist/dialects/sybase.dialect.js +119 -0
  45. package/dist/index.d.ts +8 -0
  46. package/dist/index.js +26 -0
  47. package/docs/api-reference.md +1009 -0
  48. package/docs/dialects.md +673 -0
  49. package/docs/tutorial.md +846 -0
  50. package/package.json +91 -0
@@ -0,0 +1,2 @@
1
+ import type { IDialect } from '../core/types.js';
2
+ export declare function createDialect(): IDialect;
@@ -0,0 +1,23 @@
1
+ // CockroachDB Dialect — extends PostgresDialect
2
+ // Equivalent to org.hibernate.dialect.CockroachDialect (Hibernate ORM 6.4)
3
+ // Wire-compatible with PostgreSQL but DDL differences
4
+ // Driver: npm install pg (same as PostgreSQL)
5
+ // Author: Dr Hamid MADANI drmdh@msn.com
6
+ import { PostgresDialect } from './postgres.dialect.js';
7
+ // ============================================================
8
+ // CockroachDBDialect
9
+ // ============================================================
10
+ class CockroachDBDialect extends PostgresDialect {
11
+ dialectType = 'cockroachdb';
12
+ // CockroachDB uses STRING internally (alias for TEXT)
13
+ // We keep TEXT for cross-compatibility — CockroachDB accepts it
14
+ // CockroachDB supports RETURNING like Postgres
15
+ // CockroachDB supports IF NOT EXISTS for tables and indexes
16
+ getDialectLabel() { return 'CockroachDB'; }
17
+ }
18
+ // ============================================================
19
+ // Factory export
20
+ // ============================================================
21
+ export function createDialect() {
22
+ return new CockroachDBDialect();
23
+ }
@@ -0,0 +1,2 @@
1
+ import type { IDialect } from '../core/types.js';
2
+ export declare function createDialect(): IDialect;
@@ -0,0 +1,190 @@
1
+ // IBM DB2 Dialect — extends AbstractSqlDialect
2
+ // Equivalent to org.hibernate.dialect.DB2Dialect (Hibernate ORM 6.4)
3
+ // Driver: npm install ibm_db
4
+ // Author: Dr Hamid MADANI drmdh@msn.com
5
+ import { AbstractSqlDialect } from './abstract-sql.dialect.js';
6
+ // ============================================================
7
+ // Type Mapping — DAL FieldType → DB2 column type
8
+ // ============================================================
9
+ const DB2_TYPE_MAP = {
10
+ string: 'VARCHAR(4000)',
11
+ number: 'DOUBLE',
12
+ boolean: 'BOOLEAN',
13
+ date: 'TIMESTAMP',
14
+ json: 'CLOB',
15
+ array: 'CLOB',
16
+ };
17
+ // ============================================================
18
+ // DB2Dialect
19
+ // ============================================================
20
+ class DB2Dialect extends AbstractSqlDialect {
21
+ dialectType = 'db2';
22
+ conn = null;
23
+ ibmDb = null;
24
+ // --- Abstract implementations ---
25
+ quoteIdentifier(name) {
26
+ return `"${name}"`;
27
+ }
28
+ getPlaceholder(_index) {
29
+ return '?';
30
+ }
31
+ fieldToSqlType(field) {
32
+ return DB2_TYPE_MAP[field.type] || 'VARCHAR(4000)';
33
+ }
34
+ getIdColumnType() {
35
+ return 'VARCHAR(36)';
36
+ }
37
+ getTableListQuery() {
38
+ return "SELECT tabname as name FROM syscat.tables WHERE tabschema = CURRENT SCHEMA AND type = 'T'";
39
+ }
40
+ // --- Hooks ---
41
+ // DB2 11.5+ supports IF NOT EXISTS, but we stay safe
42
+ supportsIfNotExists() { return false; }
43
+ supportsReturning() { return false; }
44
+ serializeBoolean(v) { return v; }
45
+ deserializeBoolean(v) {
46
+ return v === true || v === 1 || v === '1' || v === 'true';
47
+ }
48
+ /** DB2 LIKE is case-sensitive — use UPPER() for case-insensitive search */
49
+ buildRegexCondition(col, flags) {
50
+ if (flags?.includes('i')) {
51
+ return `UPPER(${col}) LIKE UPPER(${this.nextPlaceholder()})`;
52
+ }
53
+ return `${col} LIKE ${this.nextPlaceholder()}`;
54
+ }
55
+ // DB2 uses FETCH FIRST n ROWS ONLY
56
+ buildLimitOffset(options) {
57
+ if (!options?.limit && !options?.skip)
58
+ return '';
59
+ let sql = '';
60
+ if (options.skip)
61
+ sql += ` OFFSET ${options.skip} ROWS`;
62
+ if (options.limit)
63
+ sql += ` FETCH FIRST ${options.limit} ROWS ONLY`;
64
+ return sql;
65
+ }
66
+ // DB2: no IF NOT EXISTS, wrap with existence check
67
+ getCreateTablePrefix(tableName) {
68
+ return `CREATE TABLE ${this.quoteIdentifier(tableName)}`;
69
+ }
70
+ getCreateIndexPrefix(indexName, unique) {
71
+ const u = unique ? 'UNIQUE ' : '';
72
+ return `CREATE ${u}INDEX ${this.quoteIdentifier(indexName)}`;
73
+ }
74
+ // --- Connection ---
75
+ async doConnect(config) {
76
+ try {
77
+ this.ibmDb = await import(/* webpackIgnore: true */ 'ibm_db');
78
+ const open = this.ibmDb.open
79
+ || this.ibmDb.default.open;
80
+ this.conn = await open(config.uri);
81
+ }
82
+ catch (e) {
83
+ throw new Error(`IBM DB2 driver not found. Install it: npm install ibm_db\n` +
84
+ `Original error: ${e instanceof Error ? e.message : String(e)}`);
85
+ }
86
+ }
87
+ async doDisconnect() {
88
+ if (this.conn) {
89
+ await this.conn.close();
90
+ this.conn = null;
91
+ }
92
+ }
93
+ async doTestConnection() {
94
+ if (!this.conn)
95
+ return false;
96
+ const rows = await this.conn.query('SELECT 1 FROM SYSIBM.SYSDUMMY1');
97
+ return Array.isArray(rows);
98
+ }
99
+ // --- Query execution ---
100
+ async executeQuery(sql, params) {
101
+ if (!this.conn)
102
+ throw new Error('DB2 not connected. Call connect() first.');
103
+ return new Promise((resolve, reject) => {
104
+ this.conn.query(sql, params, (err, rows) => {
105
+ if (err)
106
+ reject(err);
107
+ else
108
+ resolve(rows ?? []);
109
+ });
110
+ });
111
+ }
112
+ async executeRun(sql, params) {
113
+ if (!this.conn)
114
+ throw new Error('DB2 not connected. Call connect() first.');
115
+ return new Promise((resolve, reject) => {
116
+ this.conn.query(sql, params, (err) => {
117
+ if (err)
118
+ reject(err);
119
+ else
120
+ resolve({ changes: 0 }); // ibm_db doesn't directly return affected rows from query
121
+ });
122
+ });
123
+ }
124
+ // Override initSchema to handle DB2's lack of IF NOT EXISTS
125
+ async initSchema(schemas) {
126
+ this.schemas = schemas;
127
+ const strategy = this.config?.schemaStrategy ?? 'none';
128
+ this.log('INIT_SCHEMA', `strategy=${strategy}`, { entities: schemas.map(s => s.name) });
129
+ if (strategy === 'none')
130
+ return;
131
+ if (strategy === 'validate') {
132
+ for (const schema of schemas) {
133
+ const exists = await this.tableExists(schema.collection);
134
+ if (!exists) {
135
+ throw new Error(`Schema validation failed: table "${schema.collection}" does not exist ` +
136
+ `(entity: ${schema.name}). Set schemaStrategy to "update" or "create".`);
137
+ }
138
+ }
139
+ return;
140
+ }
141
+ for (const schema of schemas) {
142
+ const exists = await this.tableExists(schema.collection);
143
+ if (!exists) {
144
+ const createSql = this.generateCreateTable(schema);
145
+ this.log('DDL', schema.collection, createSql);
146
+ await this.executeRun(createSql, []);
147
+ }
148
+ const indexStatements = this.generateIndexes(schema);
149
+ for (const stmt of indexStatements) {
150
+ try {
151
+ await this.executeRun(stmt, []);
152
+ }
153
+ catch {
154
+ // Index may already exist
155
+ }
156
+ }
157
+ }
158
+ // Junction tables
159
+ for (const schema of schemas) {
160
+ for (const [, rel] of Object.entries(schema.relations)) {
161
+ if (rel.type === 'many-to-many' && rel.through) {
162
+ const exists = await this.tableExists(rel.through);
163
+ if (exists)
164
+ continue;
165
+ const targetSchema = schemas.find(s => s.name === rel.target);
166
+ if (!targetSchema)
167
+ continue;
168
+ const sourceKey = `${schema.name.toLowerCase()}Id`;
169
+ const targetKey = `${rel.target.toLowerCase()}Id`;
170
+ const q = (n) => this.quoteIdentifier(n);
171
+ const idType = this.getIdColumnType();
172
+ const ddl = `CREATE TABLE ${q(rel.through)} (
173
+ ${q(sourceKey)} ${idType} NOT NULL,
174
+ ${q(targetKey)} ${idType} NOT NULL,
175
+ PRIMARY KEY (${q(sourceKey)}, ${q(targetKey)})
176
+ )`;
177
+ this.log('DDL_JUNCTION', rel.through, ddl);
178
+ await this.executeRun(ddl, []);
179
+ }
180
+ }
181
+ }
182
+ }
183
+ getDialectLabel() { return 'DB2'; }
184
+ }
185
+ // ============================================================
186
+ // Factory export
187
+ // ============================================================
188
+ export function createDialect() {
189
+ return new DB2Dialect();
190
+ }
@@ -0,0 +1,2 @@
1
+ import type { IDialect } from '../core/types.js';
2
+ export declare function createDialect(): IDialect;
@@ -0,0 +1,199 @@
1
+ // SAP HANA Dialect — extends AbstractSqlDialect
2
+ // Equivalent to org.hibernate.dialect.HANADialect (Hibernate ORM 6.4)
3
+ // Driver: npm install @sap/hana-client
4
+ // Author: Dr Hamid MADANI drmdh@msn.com
5
+ import { AbstractSqlDialect } from './abstract-sql.dialect.js';
6
+ // ============================================================
7
+ // Type Mapping — DAL FieldType → HANA column type
8
+ // ============================================================
9
+ const HANA_TYPE_MAP = {
10
+ string: 'NVARCHAR(5000)',
11
+ number: 'DOUBLE',
12
+ boolean: 'BOOLEAN',
13
+ date: 'TIMESTAMP',
14
+ json: 'NCLOB',
15
+ array: 'NCLOB',
16
+ };
17
+ // ============================================================
18
+ // HANADialect
19
+ // ============================================================
20
+ class HANADialect extends AbstractSqlDialect {
21
+ dialectType = 'hana';
22
+ conn = null;
23
+ hanaClient = null;
24
+ // --- Abstract implementations ---
25
+ quoteIdentifier(name) {
26
+ return `"${name}"`;
27
+ }
28
+ getPlaceholder(_index) {
29
+ return '?';
30
+ }
31
+ fieldToSqlType(field) {
32
+ return HANA_TYPE_MAP[field.type] || 'NVARCHAR(5000)';
33
+ }
34
+ getIdColumnType() {
35
+ return 'NVARCHAR(36)';
36
+ }
37
+ getTableListQuery() {
38
+ return "SELECT table_name as name FROM tables WHERE schema_name = CURRENT_SCHEMA";
39
+ }
40
+ // --- Hooks ---
41
+ // HANA doesn't support IF NOT EXISTS for tables
42
+ supportsIfNotExists() { return false; }
43
+ supportsReturning() { return false; }
44
+ serializeBoolean(v) { return v; }
45
+ deserializeBoolean(v) {
46
+ return v === true || v === 1 || v === '1' || v === 'TRUE' || v === 'true';
47
+ }
48
+ /** HANA LIKE is case-sensitive — use UPPER() for case-insensitive search */
49
+ buildRegexCondition(col, flags) {
50
+ if (flags?.includes('i')) {
51
+ return `UPPER(${col}) LIKE UPPER(${this.nextPlaceholder()})`;
52
+ }
53
+ return `${col} LIKE ${this.nextPlaceholder()}`;
54
+ }
55
+ // HANA supports LIMIT/OFFSET natively
56
+ // (default buildLimitOffset from AbstractSqlDialect works)
57
+ getCreateTablePrefix(tableName) {
58
+ return `CREATE TABLE ${this.quoteIdentifier(tableName)}`;
59
+ }
60
+ getCreateIndexPrefix(indexName, unique) {
61
+ const u = unique ? 'UNIQUE ' : '';
62
+ return `CREATE ${u}INDEX ${this.quoteIdentifier(indexName)}`;
63
+ }
64
+ // --- Connection ---
65
+ async doConnect(config) {
66
+ try {
67
+ this.hanaClient = await import(/* webpackIgnore: true */ '@sap/hana-client');
68
+ const createConnection = this.hanaClient.createConnection
69
+ || this.hanaClient.default.createConnection;
70
+ this.conn = createConnection();
71
+ await new Promise((resolve, reject) => {
72
+ this.conn.connect(this.parseHanaUri(config.uri), (err) => err ? reject(err) : resolve());
73
+ });
74
+ }
75
+ catch (e) {
76
+ throw new Error(`SAP HANA driver not found. Install it: npm install @sap/hana-client\n` +
77
+ `Original error: ${e instanceof Error ? e.message : String(e)}`);
78
+ }
79
+ }
80
+ async doDisconnect() {
81
+ if (this.conn) {
82
+ await new Promise((resolve) => {
83
+ this.conn.disconnect(() => resolve());
84
+ });
85
+ this.conn = null;
86
+ }
87
+ }
88
+ async doTestConnection() {
89
+ if (!this.conn)
90
+ return false;
91
+ const rows = await this.executeQuery('SELECT 1 FROM DUMMY', []);
92
+ return Array.isArray(rows);
93
+ }
94
+ // --- Query execution ---
95
+ async executeQuery(sql, params) {
96
+ if (!this.conn)
97
+ throw new Error('HANA not connected. Call connect() first.');
98
+ return new Promise((resolve, reject) => {
99
+ this.conn.exec(sql, params, (err, rows) => {
100
+ if (err)
101
+ reject(err);
102
+ else
103
+ resolve(rows ?? []);
104
+ });
105
+ });
106
+ }
107
+ async executeRun(sql, params) {
108
+ if (!this.conn)
109
+ throw new Error('HANA not connected. Call connect() first.');
110
+ return new Promise((resolve, reject) => {
111
+ this.conn.exec(sql, params, (err, affectedRows) => {
112
+ if (err)
113
+ reject(err);
114
+ else
115
+ resolve({ changes: affectedRows ?? 0 });
116
+ });
117
+ });
118
+ }
119
+ // Override initSchema to handle HANA's lack of IF NOT EXISTS
120
+ async initSchema(schemas) {
121
+ this.schemas = schemas;
122
+ const strategy = this.config?.schemaStrategy ?? 'none';
123
+ this.log('INIT_SCHEMA', `strategy=${strategy}`, { entities: schemas.map(s => s.name) });
124
+ if (strategy === 'none')
125
+ return;
126
+ if (strategy === 'validate') {
127
+ for (const schema of schemas) {
128
+ const exists = await this.tableExists(schema.collection);
129
+ if (!exists) {
130
+ throw new Error(`Schema validation failed: table "${schema.collection}" does not exist ` +
131
+ `(entity: ${schema.name}). Set schemaStrategy to "update" or "create".`);
132
+ }
133
+ }
134
+ return;
135
+ }
136
+ for (const schema of schemas) {
137
+ const exists = await this.tableExists(schema.collection);
138
+ if (!exists) {
139
+ const createSql = this.generateCreateTable(schema);
140
+ this.log('DDL', schema.collection, createSql);
141
+ await this.executeRun(createSql, []);
142
+ }
143
+ const indexStatements = this.generateIndexes(schema);
144
+ for (const stmt of indexStatements) {
145
+ try {
146
+ await this.executeRun(stmt, []);
147
+ }
148
+ catch {
149
+ // Index may already exist
150
+ }
151
+ }
152
+ }
153
+ // Junction tables
154
+ for (const schema of schemas) {
155
+ for (const [, rel] of Object.entries(schema.relations)) {
156
+ if (rel.type === 'many-to-many' && rel.through) {
157
+ const exists = await this.tableExists(rel.through);
158
+ if (exists)
159
+ continue;
160
+ const targetSchema = schemas.find(s => s.name === rel.target);
161
+ if (!targetSchema)
162
+ continue;
163
+ const sourceKey = `${schema.name.toLowerCase()}Id`;
164
+ const targetKey = `${rel.target.toLowerCase()}Id`;
165
+ const q = (n) => this.quoteIdentifier(n);
166
+ const idType = this.getIdColumnType();
167
+ const ddl = `CREATE TABLE ${q(rel.through)} (
168
+ ${q(sourceKey)} ${idType} NOT NULL,
169
+ ${q(targetKey)} ${idType} NOT NULL,
170
+ PRIMARY KEY (${q(sourceKey)}, ${q(targetKey)})
171
+ )`;
172
+ this.log('DDL_JUNCTION', rel.through, ddl);
173
+ await this.executeRun(ddl, []);
174
+ }
175
+ }
176
+ }
177
+ }
178
+ parseHanaUri(uri) {
179
+ try {
180
+ const url = new URL(uri.replace(/^hana:/, 'http:'));
181
+ return {
182
+ serverNode: `${url.hostname || 'localhost'}:${url.port || 30015}`,
183
+ uid: url.username || 'SYSTEM',
184
+ pwd: url.password || '',
185
+ databaseName: url.pathname.replace(/^\//, '') || undefined,
186
+ };
187
+ }
188
+ catch {
189
+ return { serverNode: 'localhost:30015' };
190
+ }
191
+ }
192
+ getDialectLabel() { return 'HANA'; }
193
+ }
194
+ // ============================================================
195
+ // Factory export
196
+ // ============================================================
197
+ export function createDialect() {
198
+ return new HANADialect();
199
+ }
@@ -0,0 +1,2 @@
1
+ import type { IDialect } from '../core/types.js';
2
+ export declare function createDialect(): IDialect;
@@ -0,0 +1,114 @@
1
+ // HyperSQL (HSQLDB) Dialect — extends AbstractSqlDialect
2
+ // Equivalent to org.hibernate.dialect.HSQLDialect (Hibernate ORM 6.4)
3
+ // HSQLDB is a Java database — accessed via HTTP/JDBC bridge or REST API
4
+ // Driver: HTTP fetch (no npm driver — uses Java HTTP API bridge)
5
+ // Author: Dr Hamid MADANI drmdh@msn.com
6
+ import { AbstractSqlDialect } from './abstract-sql.dialect.js';
7
+ // ============================================================
8
+ // Type Mapping — DAL FieldType → HSQLDB column type
9
+ // ============================================================
10
+ const HSQL_TYPE_MAP = {
11
+ string: 'VARCHAR(4000)',
12
+ number: 'DOUBLE',
13
+ boolean: 'BOOLEAN',
14
+ date: 'TIMESTAMP',
15
+ json: 'CLOB',
16
+ array: 'CLOB',
17
+ };
18
+ // ============================================================
19
+ // HSQLDialect
20
+ // ============================================================
21
+ class HSQLDialect extends AbstractSqlDialect {
22
+ dialectType = 'hsqldb';
23
+ baseUrl = '';
24
+ connected = false;
25
+ // --- Abstract implementations ---
26
+ quoteIdentifier(name) {
27
+ return `"${name}"`;
28
+ }
29
+ getPlaceholder(_index) {
30
+ return '?';
31
+ }
32
+ fieldToSqlType(field) {
33
+ return HSQL_TYPE_MAP[field.type] || 'VARCHAR(4000)';
34
+ }
35
+ getIdColumnType() {
36
+ return 'VARCHAR(36)';
37
+ }
38
+ getTableListQuery() {
39
+ return "SELECT table_name as name FROM information_schema.tables WHERE table_schema = 'PUBLIC'";
40
+ }
41
+ // --- Hooks ---
42
+ supportsIfNotExists() { return true; }
43
+ supportsReturning() { return false; }
44
+ serializeBoolean(v) { return v; }
45
+ deserializeBoolean(v) {
46
+ return v === true || v === 1 || v === '1' || v === 'TRUE' || v === 'true';
47
+ }
48
+ // HSQLDB supports LIMIT/OFFSET natively
49
+ // (default buildLimitOffset from AbstractSqlDialect works)
50
+ // --- Connection (HTTP bridge to HSQLDB server) ---
51
+ async doConnect(config) {
52
+ // URI format: http://host:port/dbname or hsqldb://host:port/dbname
53
+ this.baseUrl = config.uri
54
+ .replace(/^hsqldb:\/\//, 'http://')
55
+ .replace(/\/$/, '');
56
+ // Test connectivity
57
+ try {
58
+ await this.httpPost('SELECT 1 FROM INFORMATION_SCHEMA.SYSTEM_USERS', []);
59
+ this.connected = true;
60
+ }
61
+ catch (e) {
62
+ throw new Error(`HSQLDB HTTP bridge not reachable at ${this.baseUrl}.\n` +
63
+ `Ensure the HSQLDB server is running with HTTP API enabled.\n` +
64
+ `Original error: ${e instanceof Error ? e.message : String(e)}`);
65
+ }
66
+ }
67
+ async doDisconnect() {
68
+ this.connected = false;
69
+ this.baseUrl = '';
70
+ }
71
+ async doTestConnection() {
72
+ if (!this.connected)
73
+ return false;
74
+ try {
75
+ await this.httpPost('SELECT 1 FROM INFORMATION_SCHEMA.SYSTEM_USERS', []);
76
+ return true;
77
+ }
78
+ catch {
79
+ return false;
80
+ }
81
+ }
82
+ // --- Query execution via HTTP bridge ---
83
+ async executeQuery(sql, params) {
84
+ if (!this.connected)
85
+ throw new Error('HSQLDB not connected. Call connect() first.');
86
+ return this.httpPost(sql, params);
87
+ }
88
+ async executeRun(sql, params) {
89
+ if (!this.connected)
90
+ throw new Error('HSQLDB not connected. Call connect() first.');
91
+ const result = await this.httpPost(sql, params);
92
+ return { changes: result?.changes ?? 0 };
93
+ }
94
+ /** Send SQL to HSQLDB HTTP bridge */
95
+ async httpPost(sql, params) {
96
+ const response = await fetch(`${this.baseUrl}/query`, {
97
+ method: 'POST',
98
+ headers: { 'Content-Type': 'application/json' },
99
+ body: JSON.stringify({ sql, params }),
100
+ });
101
+ if (!response.ok) {
102
+ const text = await response.text();
103
+ throw new Error(`HSQLDB query failed (${response.status}): ${text}`);
104
+ }
105
+ return response.json();
106
+ }
107
+ getDialectLabel() { return 'HSQLDB'; }
108
+ }
109
+ // ============================================================
110
+ // Factory export
111
+ // ============================================================
112
+ export function createDialect() {
113
+ return new HSQLDialect();
114
+ }
@@ -0,0 +1,2 @@
1
+ import type { IDialect } from '../core/types.js';
2
+ export declare function createDialect(): IDialect;
@@ -0,0 +1,87 @@
1
+ // MariaDB Dialect — extends MySQLDialect
2
+ // Equivalent to org.hibernate.dialect.MariaDBDialect (Hibernate ORM 6.4)
3
+ // MariaDB extends MySQLDialect — MySQL-compatible with some differences
4
+ // Driver: npm install mariadb (native MariaDB driver, better performance than mysql2)
5
+ // Author: Dr Hamid MADANI drmdh@msn.com
6
+ import { MySQLDialect } from './mysql.dialect.js';
7
+ // ============================================================
8
+ // MariaDBDialect
9
+ // ============================================================
10
+ class MariaDBDialect extends MySQLDialect {
11
+ dialectType = 'mariadb';
12
+ // MariaDB supports RETURNING since 10.5
13
+ supportsReturning() { return true; }
14
+ // Override connection to use native mariadb driver
15
+ async doConnect(config) {
16
+ try {
17
+ const mariadb = await import(/* webpackIgnore: true */ 'mariadb');
18
+ const createPool = mariadb.default?.createPool || mariadb.createPool;
19
+ this.pool = createPool({
20
+ ...this.parseUri(config.uri),
21
+ connectionLimit: config.poolSize ?? 10,
22
+ });
23
+ }
24
+ catch {
25
+ // Fallback to mysql2 driver (cross-compatible)
26
+ try {
27
+ await super.doConnect(config);
28
+ }
29
+ catch (e) {
30
+ throw new Error(`MariaDB driver not found. Install it: npm install mariadb\n` +
31
+ `Or use MySQL-compatible driver: npm install mysql2\n` +
32
+ `Original error: ${e instanceof Error ? e.message : String(e)}`);
33
+ }
34
+ }
35
+ }
36
+ // Override executeQuery to handle mariadb driver's different API
37
+ async executeQuery(sql, params) {
38
+ if (!this.pool)
39
+ throw new Error('MariaDB not connected. Call connect() first.');
40
+ try {
41
+ const rows = await this.pool.query(sql, params);
42
+ // mariadb driver returns rows directly (may include meta at the end)
43
+ if (Array.isArray(rows)) {
44
+ return rows.filter((r) => typeof r === 'object' && r !== null && !('affectedRows' in r));
45
+ }
46
+ return rows;
47
+ }
48
+ catch {
49
+ // Fallback: try mysql2-style execute
50
+ return super.executeQuery(sql, params);
51
+ }
52
+ }
53
+ async executeRun(sql, params) {
54
+ if (!this.pool)
55
+ throw new Error('MariaDB not connected. Call connect() first.');
56
+ try {
57
+ const result = await this.pool.query(sql, params);
58
+ return { changes: result.affectedRows ?? 0 };
59
+ }
60
+ catch {
61
+ return super.executeRun(sql, params);
62
+ }
63
+ }
64
+ /** Parse a MySQL/MariaDB URI into connection options */
65
+ parseUri(uri) {
66
+ try {
67
+ const url = new URL(uri.replace(/^mariadb:/, 'http:').replace(/^mysql:/, 'http:'));
68
+ return {
69
+ host: url.hostname || 'localhost',
70
+ port: url.port ? parseInt(url.port) : 3306,
71
+ user: url.username || undefined,
72
+ password: url.password || undefined,
73
+ database: url.pathname.replace(/^\//, '') || undefined,
74
+ };
75
+ }
76
+ catch {
77
+ return { host: 'localhost', port: 3306 };
78
+ }
79
+ }
80
+ getDialectLabel() { return 'MariaDB'; }
81
+ }
82
+ // ============================================================
83
+ // Factory export
84
+ // ============================================================
85
+ export function createDialect() {
86
+ return new MariaDBDialect();
87
+ }
@@ -0,0 +1,2 @@
1
+ import type { IDialect } from '../core/types.js';
2
+ export declare function createDialect(): IDialect;