@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.
- package/LICENSE +21 -0
- package/README.md +548 -0
- package/dist/core/base-repository.d.ts +26 -0
- package/dist/core/base-repository.js +82 -0
- package/dist/core/config.d.ts +62 -0
- package/dist/core/config.js +116 -0
- package/dist/core/errors.d.ts +30 -0
- package/dist/core/errors.js +49 -0
- package/dist/core/factory.d.ts +41 -0
- package/dist/core/factory.js +142 -0
- package/dist/core/normalizer.d.ts +9 -0
- package/dist/core/normalizer.js +19 -0
- package/dist/core/registry.d.ts +43 -0
- package/dist/core/registry.js +78 -0
- package/dist/core/types.d.ts +228 -0
- package/dist/core/types.js +5 -0
- package/dist/dialects/abstract-sql.dialect.d.ts +113 -0
- package/dist/dialects/abstract-sql.dialect.js +1071 -0
- package/dist/dialects/cockroachdb.dialect.d.ts +2 -0
- package/dist/dialects/cockroachdb.dialect.js +23 -0
- package/dist/dialects/db2.dialect.d.ts +2 -0
- package/dist/dialects/db2.dialect.js +190 -0
- package/dist/dialects/hana.dialect.d.ts +2 -0
- package/dist/dialects/hana.dialect.js +199 -0
- package/dist/dialects/hsqldb.dialect.d.ts +2 -0
- package/dist/dialects/hsqldb.dialect.js +114 -0
- package/dist/dialects/mariadb.dialect.d.ts +2 -0
- package/dist/dialects/mariadb.dialect.js +87 -0
- package/dist/dialects/mongo.dialect.d.ts +2 -0
- package/dist/dialects/mongo.dialect.js +480 -0
- package/dist/dialects/mssql.dialect.d.ts +27 -0
- package/dist/dialects/mssql.dialect.js +127 -0
- package/dist/dialects/mysql.dialect.d.ts +24 -0
- package/dist/dialects/mysql.dialect.js +101 -0
- package/dist/dialects/oracle.dialect.d.ts +2 -0
- package/dist/dialects/oracle.dialect.js +206 -0
- package/dist/dialects/postgres.dialect.d.ts +26 -0
- package/dist/dialects/postgres.dialect.js +105 -0
- package/dist/dialects/spanner.dialect.d.ts +2 -0
- package/dist/dialects/spanner.dialect.js +259 -0
- package/dist/dialects/sqlite.dialect.d.ts +2 -0
- package/dist/dialects/sqlite.dialect.js +1027 -0
- package/dist/dialects/sybase.dialect.d.ts +2 -0
- package/dist/dialects/sybase.dialect.js +119 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +26 -0
- package/docs/api-reference.md +1009 -0
- package/docs/dialects.md +673 -0
- package/docs/tutorial.md +846 -0
- package/package.json +91 -0
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import type { DialectType } from './types.js';
|
|
2
|
+
export interface DialectConfig {
|
|
3
|
+
/** npm install command for the driver */
|
|
4
|
+
installHint: string;
|
|
5
|
+
/** Human-readable label */
|
|
6
|
+
label: string;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Dialect metadata registry.
|
|
10
|
+
* Connection config (dialect + URI) comes from .env.local, NOT from here.
|
|
11
|
+
*
|
|
12
|
+
* .env.local format (decommenter UN bloc) :
|
|
13
|
+
*
|
|
14
|
+
* DB_DIALECT=mongodb
|
|
15
|
+
* SGBD_URI=mongodb://user:pass@localhost:27017/mydb
|
|
16
|
+
*
|
|
17
|
+
* #DB_DIALECT=sqlite
|
|
18
|
+
* #SGBD_URI=./data/myapp.db
|
|
19
|
+
*
|
|
20
|
+
* #DB_DIALECT=postgres
|
|
21
|
+
* #SGBD_URI=postgresql://user:pass@localhost:5432/mydb
|
|
22
|
+
*
|
|
23
|
+
* #DB_DIALECT=mysql
|
|
24
|
+
* #SGBD_URI=mysql://user:pass@localhost:3306/mydb
|
|
25
|
+
*
|
|
26
|
+
* #DB_DIALECT=mariadb
|
|
27
|
+
* #SGBD_URI=mariadb://user:pass@localhost:3306/mydb
|
|
28
|
+
*
|
|
29
|
+
* #DB_DIALECT=oracle
|
|
30
|
+
* #SGBD_URI=oracle://user:pass@localhost:1521/mydb
|
|
31
|
+
*
|
|
32
|
+
* #DB_DIALECT=mssql
|
|
33
|
+
* #SGBD_URI=mssql://user:pass@localhost:1433/mydb
|
|
34
|
+
*
|
|
35
|
+
* #DB_DIALECT=cockroachdb
|
|
36
|
+
* #SGBD_URI=postgresql://user:pass@localhost:26257/mydb
|
|
37
|
+
*
|
|
38
|
+
* #DB_DIALECT=db2
|
|
39
|
+
* #SGBD_URI=db2://user:pass@localhost:50000/mydb
|
|
40
|
+
*
|
|
41
|
+
* #DB_DIALECT=hana
|
|
42
|
+
* #SGBD_URI=hana://user:pass@localhost:30015
|
|
43
|
+
*
|
|
44
|
+
* #DB_DIALECT=hsqldb
|
|
45
|
+
* #SGBD_URI=hsqldb:hsql://localhost:9001/mydb
|
|
46
|
+
*
|
|
47
|
+
* #DB_DIALECT=spanner
|
|
48
|
+
* #SGBD_URI=spanner://project/instance/mydb
|
|
49
|
+
*
|
|
50
|
+
* #DB_DIALECT=sybase
|
|
51
|
+
* #SGBD_URI=sybase://user:pass@localhost:5000/mydb
|
|
52
|
+
*/
|
|
53
|
+
export declare const DIALECT_CONFIGS: Record<DialectType, DialectConfig>;
|
|
54
|
+
/**
|
|
55
|
+
* Get the list of supported dialect types
|
|
56
|
+
*/
|
|
57
|
+
export declare function getSupportedDialects(): DialectType[];
|
|
58
|
+
/**
|
|
59
|
+
* Get metadata for a specific dialect.
|
|
60
|
+
* Throws if the dialect is not supported.
|
|
61
|
+
*/
|
|
62
|
+
export declare function getDialectConfig(dialect: DialectType): DialectConfig;
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dialect metadata registry.
|
|
3
|
+
* Connection config (dialect + URI) comes from .env.local, NOT from here.
|
|
4
|
+
*
|
|
5
|
+
* .env.local format (decommenter UN bloc) :
|
|
6
|
+
*
|
|
7
|
+
* DB_DIALECT=mongodb
|
|
8
|
+
* SGBD_URI=mongodb://user:pass@localhost:27017/mydb
|
|
9
|
+
*
|
|
10
|
+
* #DB_DIALECT=sqlite
|
|
11
|
+
* #SGBD_URI=./data/myapp.db
|
|
12
|
+
*
|
|
13
|
+
* #DB_DIALECT=postgres
|
|
14
|
+
* #SGBD_URI=postgresql://user:pass@localhost:5432/mydb
|
|
15
|
+
*
|
|
16
|
+
* #DB_DIALECT=mysql
|
|
17
|
+
* #SGBD_URI=mysql://user:pass@localhost:3306/mydb
|
|
18
|
+
*
|
|
19
|
+
* #DB_DIALECT=mariadb
|
|
20
|
+
* #SGBD_URI=mariadb://user:pass@localhost:3306/mydb
|
|
21
|
+
*
|
|
22
|
+
* #DB_DIALECT=oracle
|
|
23
|
+
* #SGBD_URI=oracle://user:pass@localhost:1521/mydb
|
|
24
|
+
*
|
|
25
|
+
* #DB_DIALECT=mssql
|
|
26
|
+
* #SGBD_URI=mssql://user:pass@localhost:1433/mydb
|
|
27
|
+
*
|
|
28
|
+
* #DB_DIALECT=cockroachdb
|
|
29
|
+
* #SGBD_URI=postgresql://user:pass@localhost:26257/mydb
|
|
30
|
+
*
|
|
31
|
+
* #DB_DIALECT=db2
|
|
32
|
+
* #SGBD_URI=db2://user:pass@localhost:50000/mydb
|
|
33
|
+
*
|
|
34
|
+
* #DB_DIALECT=hana
|
|
35
|
+
* #SGBD_URI=hana://user:pass@localhost:30015
|
|
36
|
+
*
|
|
37
|
+
* #DB_DIALECT=hsqldb
|
|
38
|
+
* #SGBD_URI=hsqldb:hsql://localhost:9001/mydb
|
|
39
|
+
*
|
|
40
|
+
* #DB_DIALECT=spanner
|
|
41
|
+
* #SGBD_URI=spanner://project/instance/mydb
|
|
42
|
+
*
|
|
43
|
+
* #DB_DIALECT=sybase
|
|
44
|
+
* #SGBD_URI=sybase://user:pass@localhost:5000/mydb
|
|
45
|
+
*/
|
|
46
|
+
export const DIALECT_CONFIGS = {
|
|
47
|
+
mongodb: {
|
|
48
|
+
installHint: 'npm install mongoose',
|
|
49
|
+
label: 'MongoDB',
|
|
50
|
+
},
|
|
51
|
+
sqlite: {
|
|
52
|
+
installHint: 'npm install better-sqlite3',
|
|
53
|
+
label: 'SQLite',
|
|
54
|
+
},
|
|
55
|
+
postgres: {
|
|
56
|
+
installHint: 'npm install pg',
|
|
57
|
+
label: 'PostgreSQL',
|
|
58
|
+
},
|
|
59
|
+
mysql: {
|
|
60
|
+
installHint: 'npm install mysql2',
|
|
61
|
+
label: 'MySQL',
|
|
62
|
+
},
|
|
63
|
+
mariadb: {
|
|
64
|
+
installHint: 'npm install mariadb',
|
|
65
|
+
label: 'MariaDB',
|
|
66
|
+
},
|
|
67
|
+
oracle: {
|
|
68
|
+
installHint: 'npm install oracledb',
|
|
69
|
+
label: 'Oracle Database',
|
|
70
|
+
},
|
|
71
|
+
mssql: {
|
|
72
|
+
installHint: 'npm install mssql',
|
|
73
|
+
label: 'SQL Server',
|
|
74
|
+
},
|
|
75
|
+
cockroachdb: {
|
|
76
|
+
installHint: 'npm install pg',
|
|
77
|
+
label: 'CockroachDB',
|
|
78
|
+
},
|
|
79
|
+
db2: {
|
|
80
|
+
installHint: 'npm install ibm_db',
|
|
81
|
+
label: 'IBM DB2',
|
|
82
|
+
},
|
|
83
|
+
hana: {
|
|
84
|
+
installHint: 'npm install @sap/hana-client',
|
|
85
|
+
label: 'SAP HANA',
|
|
86
|
+
},
|
|
87
|
+
hsqldb: {
|
|
88
|
+
installHint: 'npm install hsqldb (Java driver)',
|
|
89
|
+
label: 'HyperSQL',
|
|
90
|
+
},
|
|
91
|
+
spanner: {
|
|
92
|
+
installHint: 'npm install @google-cloud/spanner',
|
|
93
|
+
label: 'Google Cloud Spanner',
|
|
94
|
+
},
|
|
95
|
+
sybase: {
|
|
96
|
+
installHint: 'npm install sybase',
|
|
97
|
+
label: 'Sybase ASE',
|
|
98
|
+
},
|
|
99
|
+
};
|
|
100
|
+
/**
|
|
101
|
+
* Get the list of supported dialect types
|
|
102
|
+
*/
|
|
103
|
+
export function getSupportedDialects() {
|
|
104
|
+
return Object.keys(DIALECT_CONFIGS);
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Get metadata for a specific dialect.
|
|
108
|
+
* Throws if the dialect is not supported.
|
|
109
|
+
*/
|
|
110
|
+
export function getDialectConfig(dialect) {
|
|
111
|
+
const config = DIALECT_CONFIGS[dialect];
|
|
112
|
+
if (!config) {
|
|
113
|
+
throw new Error(`Unknown dialect "${dialect}". Supported: ${getSupportedDialects().join(', ')}`);
|
|
114
|
+
}
|
|
115
|
+
return config;
|
|
116
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Base error class for all MostaORM errors
|
|
3
|
+
*/
|
|
4
|
+
export declare class MostaORMError extends Error {
|
|
5
|
+
constructor(message: string);
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Thrown when an entity is not found by ID or filter
|
|
9
|
+
*/
|
|
10
|
+
export declare class EntityNotFoundError extends MostaORMError {
|
|
11
|
+
constructor(entityName: string, id?: string);
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Thrown when a database connection fails
|
|
15
|
+
*/
|
|
16
|
+
export declare class ConnectionError extends MostaORMError {
|
|
17
|
+
constructor(dialect: string, cause?: string);
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Thrown when entity data fails validation
|
|
21
|
+
*/
|
|
22
|
+
export declare class ValidationError extends MostaORMError {
|
|
23
|
+
constructor(entityName: string, details: string);
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Thrown when a dialect is not found or not supported
|
|
27
|
+
*/
|
|
28
|
+
export declare class DialectNotFoundError extends MostaORMError {
|
|
29
|
+
constructor(dialect: string);
|
|
30
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
// MostaORM Error Classes
|
|
2
|
+
// Author: Dr Hamid MADANI drmdh@msn.com
|
|
3
|
+
/**
|
|
4
|
+
* Base error class for all MostaORM errors
|
|
5
|
+
*/
|
|
6
|
+
export class MostaORMError extends Error {
|
|
7
|
+
constructor(message) {
|
|
8
|
+
super(message);
|
|
9
|
+
this.name = 'MostaORMError';
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Thrown when an entity is not found by ID or filter
|
|
14
|
+
*/
|
|
15
|
+
export class EntityNotFoundError extends MostaORMError {
|
|
16
|
+
constructor(entityName, id) {
|
|
17
|
+
super(id
|
|
18
|
+
? `${entityName} with id "${id}" not found`
|
|
19
|
+
: `${entityName} not found`);
|
|
20
|
+
this.name = 'EntityNotFoundError';
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Thrown when a database connection fails
|
|
25
|
+
*/
|
|
26
|
+
export class ConnectionError extends MostaORMError {
|
|
27
|
+
constructor(dialect, cause) {
|
|
28
|
+
super(`Failed to connect to ${dialect}${cause ? `: ${cause}` : ''}`);
|
|
29
|
+
this.name = 'ConnectionError';
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Thrown when entity data fails validation
|
|
34
|
+
*/
|
|
35
|
+
export class ValidationError extends MostaORMError {
|
|
36
|
+
constructor(entityName, details) {
|
|
37
|
+
super(`Validation failed for ${entityName}: ${details}`);
|
|
38
|
+
this.name = 'ValidationError';
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Thrown when a dialect is not found or not supported
|
|
43
|
+
*/
|
|
44
|
+
export class DialectNotFoundError extends MostaORMError {
|
|
45
|
+
constructor(dialect) {
|
|
46
|
+
super(`Dialect "${dialect}" is not supported`);
|
|
47
|
+
this.name = 'DialectNotFoundError';
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { IDialect, DialectType, ConnectionConfig, EntitySchema } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Read the database configuration from environment variables.
|
|
4
|
+
*
|
|
5
|
+
* Required env vars:
|
|
6
|
+
* DB_DIALECT = mongodb | sqlite | postgres | mysql | mariadb | oracle | mssql | cockroachdb | db2 | hana | hsqldb | spanner | sybase
|
|
7
|
+
* SGBD_URI = connection string
|
|
8
|
+
*
|
|
9
|
+
* Throws if DB_DIALECT or SGBD_URI is missing.
|
|
10
|
+
*/
|
|
11
|
+
export declare function getConfigFromEnv(): ConnectionConfig;
|
|
12
|
+
/**
|
|
13
|
+
* Initialize and connect the dialect.
|
|
14
|
+
* Returns a singleton — subsequent calls return the same instance.
|
|
15
|
+
*/
|
|
16
|
+
export declare function getDialect(config?: ConnectionConfig): Promise<IDialect>;
|
|
17
|
+
/**
|
|
18
|
+
* Get the current dialect type without connecting.
|
|
19
|
+
*/
|
|
20
|
+
export declare function getCurrentDialectType(): DialectType;
|
|
21
|
+
/**
|
|
22
|
+
* Disconnect the current dialect and reset the singleton.
|
|
23
|
+
*/
|
|
24
|
+
export declare function disconnectDialect(): Promise<void>;
|
|
25
|
+
/**
|
|
26
|
+
* Test the connection with a given config without changing the active dialect.
|
|
27
|
+
* Used by the setup wizard to validate before committing.
|
|
28
|
+
*/
|
|
29
|
+
export declare function testConnection(config: ConnectionConfig): Promise<boolean>;
|
|
30
|
+
/**
|
|
31
|
+
* Simplified high-level API: connect to a database and optionally register schemas.
|
|
32
|
+
* Returns the connected dialect instance.
|
|
33
|
+
*
|
|
34
|
+
* Example:
|
|
35
|
+
* const dialect = await createConnection({
|
|
36
|
+
* dialect: 'sqlite',
|
|
37
|
+
* uri: './my.db',
|
|
38
|
+
* schemaStrategy: 'update',
|
|
39
|
+
* }, [ContactSchema, OrderSchema]);
|
|
40
|
+
*/
|
|
41
|
+
export declare function createConnection(config: ConnectionConfig, schemas?: EntitySchema[]): Promise<IDialect>;
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { getDialectConfig, getSupportedDialects } from './config.js';
|
|
2
|
+
import { getAllSchemas, registerSchemas } from './registry.js';
|
|
3
|
+
/** Singleton dialect instance */
|
|
4
|
+
let currentDialect = null;
|
|
5
|
+
let currentConfig = null;
|
|
6
|
+
/**
|
|
7
|
+
* Dynamically load a dialect adapter module.
|
|
8
|
+
* Only the selected dialect is loaded — no unused drivers in memory.
|
|
9
|
+
*/
|
|
10
|
+
async function loadDialectModule(dialect) {
|
|
11
|
+
switch (dialect) {
|
|
12
|
+
case 'mongodb': return import('../dialects/mongo.dialect.js');
|
|
13
|
+
case 'sqlite': return import('../dialects/sqlite.dialect.js');
|
|
14
|
+
case 'postgres': return import('../dialects/postgres.dialect.js');
|
|
15
|
+
case 'mysql': return import('../dialects/mysql.dialect.js');
|
|
16
|
+
case 'mariadb': return import('../dialects/mariadb.dialect.js');
|
|
17
|
+
case 'oracle': return import('../dialects/oracle.dialect.js');
|
|
18
|
+
case 'mssql': return import('../dialects/mssql.dialect.js');
|
|
19
|
+
case 'cockroachdb': return import('../dialects/cockroachdb.dialect.js');
|
|
20
|
+
case 'db2': return import('../dialects/db2.dialect.js');
|
|
21
|
+
case 'hana': return import('../dialects/hana.dialect.js');
|
|
22
|
+
case 'hsqldb': return import('../dialects/hsqldb.dialect.js');
|
|
23
|
+
case 'spanner': return import('../dialects/spanner.dialect.js');
|
|
24
|
+
case 'sybase': return import('../dialects/sybase.dialect.js');
|
|
25
|
+
default:
|
|
26
|
+
throw new Error(`No loader for dialect "${dialect}". Supported: ${getSupportedDialects().join(', ')}`);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Read the database configuration from environment variables.
|
|
31
|
+
*
|
|
32
|
+
* Required env vars:
|
|
33
|
+
* DB_DIALECT = mongodb | sqlite | postgres | mysql | mariadb | oracle | mssql | cockroachdb | db2 | hana | hsqldb | spanner | sybase
|
|
34
|
+
* SGBD_URI = connection string
|
|
35
|
+
*
|
|
36
|
+
* Throws if DB_DIALECT or SGBD_URI is missing.
|
|
37
|
+
*/
|
|
38
|
+
export function getConfigFromEnv() {
|
|
39
|
+
const dialect = process.env.DB_DIALECT;
|
|
40
|
+
const uri = process.env.SGBD_URI;
|
|
41
|
+
if (!dialect) {
|
|
42
|
+
throw new Error('DB_DIALECT is not defined in environment\n' +
|
|
43
|
+
`Supported: ${getSupportedDialects().join(', ')}`);
|
|
44
|
+
}
|
|
45
|
+
// Validate dialect name
|
|
46
|
+
getDialectConfig(dialect);
|
|
47
|
+
if (!uri) {
|
|
48
|
+
throw new Error(`SGBD_URI is not defined in environment\n` +
|
|
49
|
+
`DB_DIALECT=${dialect} requires a connection string in SGBD_URI`);
|
|
50
|
+
}
|
|
51
|
+
return {
|
|
52
|
+
dialect,
|
|
53
|
+
uri,
|
|
54
|
+
// Hibernate-inspired properties from env
|
|
55
|
+
showSql: process.env.DB_SHOW_SQL === 'true',
|
|
56
|
+
formatSql: process.env.DB_FORMAT_SQL === 'true',
|
|
57
|
+
schemaStrategy: process.env.DB_SCHEMA_STRATEGY || 'none',
|
|
58
|
+
poolSize: process.env.DB_POOL_SIZE ? Number(process.env.DB_POOL_SIZE) : undefined,
|
|
59
|
+
cacheEnabled: process.env.DB_CACHE_ENABLED === 'true',
|
|
60
|
+
cacheTtlSeconds: process.env.DB_CACHE_TTL ? Number(process.env.DB_CACHE_TTL) : undefined,
|
|
61
|
+
batchSize: process.env.DB_BATCH_SIZE ? Number(process.env.DB_BATCH_SIZE) : undefined,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Initialize and connect the dialect.
|
|
66
|
+
* Returns a singleton — subsequent calls return the same instance.
|
|
67
|
+
*/
|
|
68
|
+
export async function getDialect(config) {
|
|
69
|
+
if (currentDialect) {
|
|
70
|
+
return currentDialect;
|
|
71
|
+
}
|
|
72
|
+
const cfg = config || getConfigFromEnv();
|
|
73
|
+
const dialectCfg = getDialectConfig(cfg.dialect);
|
|
74
|
+
try {
|
|
75
|
+
const mod = await loadDialectModule(cfg.dialect);
|
|
76
|
+
currentDialect = mod.createDialect();
|
|
77
|
+
await currentDialect.connect(cfg);
|
|
78
|
+
currentConfig = cfg;
|
|
79
|
+
// Hibernate SessionFactory — register all entity models on first connection
|
|
80
|
+
await currentDialect.initSchema(getAllSchemas());
|
|
81
|
+
return currentDialect;
|
|
82
|
+
}
|
|
83
|
+
catch (err) {
|
|
84
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
85
|
+
if (message.includes('Cannot find module') || message.includes('MODULE_NOT_FOUND')) {
|
|
86
|
+
throw new Error(`Dialect "${dialectCfg.label}" requires a driver. Install it:\n ${dialectCfg.installHint}`);
|
|
87
|
+
}
|
|
88
|
+
throw err;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Get the current dialect type without connecting.
|
|
93
|
+
*/
|
|
94
|
+
export function getCurrentDialectType() {
|
|
95
|
+
if (currentConfig)
|
|
96
|
+
return currentConfig.dialect;
|
|
97
|
+
return process.env.DB_DIALECT || 'mongodb';
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Disconnect the current dialect and reset the singleton.
|
|
101
|
+
*/
|
|
102
|
+
export async function disconnectDialect() {
|
|
103
|
+
if (currentDialect) {
|
|
104
|
+
await currentDialect.disconnect();
|
|
105
|
+
currentDialect = null;
|
|
106
|
+
currentConfig = null;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Test the connection with a given config without changing the active dialect.
|
|
111
|
+
* Used by the setup wizard to validate before committing.
|
|
112
|
+
*/
|
|
113
|
+
export async function testConnection(config) {
|
|
114
|
+
try {
|
|
115
|
+
const mod = await loadDialectModule(config.dialect);
|
|
116
|
+
const dialect = mod.createDialect();
|
|
117
|
+
await dialect.connect(config);
|
|
118
|
+
const ok = await dialect.testConnection();
|
|
119
|
+
await dialect.disconnect();
|
|
120
|
+
return ok;
|
|
121
|
+
}
|
|
122
|
+
catch {
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Simplified high-level API: connect to a database and optionally register schemas.
|
|
128
|
+
* Returns the connected dialect instance.
|
|
129
|
+
*
|
|
130
|
+
* Example:
|
|
131
|
+
* const dialect = await createConnection({
|
|
132
|
+
* dialect: 'sqlite',
|
|
133
|
+
* uri: './my.db',
|
|
134
|
+
* schemaStrategy: 'update',
|
|
135
|
+
* }, [ContactSchema, OrderSchema]);
|
|
136
|
+
*/
|
|
137
|
+
export async function createConnection(config, schemas) {
|
|
138
|
+
if (schemas) {
|
|
139
|
+
registerSchemas(schemas);
|
|
140
|
+
}
|
|
141
|
+
return getDialect(config);
|
|
142
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Normalize a database document: _id → id, remove __v
|
|
3
|
+
* Works for any dialect output (MongoDB returns _id, SQL returns id)
|
|
4
|
+
*/
|
|
5
|
+
export declare function normalizeDoc<T = Record<string, unknown>>(doc: any): T;
|
|
6
|
+
/**
|
|
7
|
+
* Normalize an array of documents
|
|
8
|
+
*/
|
|
9
|
+
export declare function normalizeDocs<T = Record<string, unknown>>(docs: any[]): T[];
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// Document normalizer — _id → id conversion
|
|
2
|
+
// Author: Dr Hamid MADANI drmdh@msn.com
|
|
3
|
+
/**
|
|
4
|
+
* Normalize a database document: _id → id, remove __v
|
|
5
|
+
* Works for any dialect output (MongoDB returns _id, SQL returns id)
|
|
6
|
+
*/
|
|
7
|
+
export function normalizeDoc(doc) {
|
|
8
|
+
if (!doc)
|
|
9
|
+
return doc;
|
|
10
|
+
const { _id, __v, ...rest } = doc;
|
|
11
|
+
const id = _id?.toString?.() ?? _id ?? rest.id;
|
|
12
|
+
return { id, ...rest };
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Normalize an array of documents
|
|
16
|
+
*/
|
|
17
|
+
export function normalizeDocs(docs) {
|
|
18
|
+
return docs.map(d => normalizeDoc(d));
|
|
19
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { EntitySchema } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Register a single entity schema into the registry.
|
|
4
|
+
* Equivalent to Hibernate's @Entity annotation scanning.
|
|
5
|
+
*/
|
|
6
|
+
export declare function registerSchema(schema: EntitySchema): void;
|
|
7
|
+
/**
|
|
8
|
+
* Register multiple entity schemas at once (batch registration).
|
|
9
|
+
*/
|
|
10
|
+
export declare function registerSchemas(schemas: EntitySchema[]): void;
|
|
11
|
+
/**
|
|
12
|
+
* Get an entity schema by entity name (e.g. 'Client', 'Ticket')
|
|
13
|
+
* Throws if the entity is not registered.
|
|
14
|
+
*/
|
|
15
|
+
export declare function getSchema(entityName: string): EntitySchema;
|
|
16
|
+
/**
|
|
17
|
+
* Get an entity schema by collection/table name (e.g. 'clients', 'tickets')
|
|
18
|
+
*/
|
|
19
|
+
export declare function getSchemaByCollection(collection: string): EntitySchema | undefined;
|
|
20
|
+
/**
|
|
21
|
+
* Get all registered entity schemas
|
|
22
|
+
*/
|
|
23
|
+
export declare function getAllSchemas(): EntitySchema[];
|
|
24
|
+
/**
|
|
25
|
+
* Get all entity names
|
|
26
|
+
*/
|
|
27
|
+
export declare function getEntityNames(): string[];
|
|
28
|
+
/**
|
|
29
|
+
* Check if an entity is registered
|
|
30
|
+
*/
|
|
31
|
+
export declare function hasSchema(entityName: string): boolean;
|
|
32
|
+
/**
|
|
33
|
+
* Validate all schemas: check that relation targets exist in the registry.
|
|
34
|
+
* Call this at application startup to catch configuration errors early.
|
|
35
|
+
*/
|
|
36
|
+
export declare function validateSchemas(): {
|
|
37
|
+
valid: boolean;
|
|
38
|
+
errors: string[];
|
|
39
|
+
};
|
|
40
|
+
/**
|
|
41
|
+
* Clear all registered schemas (useful for testing)
|
|
42
|
+
*/
|
|
43
|
+
export declare function clearRegistry(): void;
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/** Map of entity name -> EntitySchema */
|
|
2
|
+
const registry = new Map();
|
|
3
|
+
/** Map of collection name -> EntitySchema */
|
|
4
|
+
const collectionMap = new Map();
|
|
5
|
+
/**
|
|
6
|
+
* Register a single entity schema into the registry.
|
|
7
|
+
* Equivalent to Hibernate's @Entity annotation scanning.
|
|
8
|
+
*/
|
|
9
|
+
export function registerSchema(schema) {
|
|
10
|
+
registry.set(schema.name, schema);
|
|
11
|
+
collectionMap.set(schema.collection, schema);
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Register multiple entity schemas at once (batch registration).
|
|
15
|
+
*/
|
|
16
|
+
export function registerSchemas(schemas) {
|
|
17
|
+
for (const schema of schemas) {
|
|
18
|
+
registerSchema(schema);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Get an entity schema by entity name (e.g. 'Client', 'Ticket')
|
|
23
|
+
* Throws if the entity is not registered.
|
|
24
|
+
*/
|
|
25
|
+
export function getSchema(entityName) {
|
|
26
|
+
const schema = registry.get(entityName);
|
|
27
|
+
if (!schema) {
|
|
28
|
+
throw new Error(`Entity "${entityName}" is not registered. ` +
|
|
29
|
+
`Available entities: ${Array.from(registry.keys()).join(', ')}`);
|
|
30
|
+
}
|
|
31
|
+
return schema;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Get an entity schema by collection/table name (e.g. 'clients', 'tickets')
|
|
35
|
+
*/
|
|
36
|
+
export function getSchemaByCollection(collection) {
|
|
37
|
+
return collectionMap.get(collection);
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Get all registered entity schemas
|
|
41
|
+
*/
|
|
42
|
+
export function getAllSchemas() {
|
|
43
|
+
return Array.from(registry.values());
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Get all entity names
|
|
47
|
+
*/
|
|
48
|
+
export function getEntityNames() {
|
|
49
|
+
return Array.from(registry.keys());
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Check if an entity is registered
|
|
53
|
+
*/
|
|
54
|
+
export function hasSchema(entityName) {
|
|
55
|
+
return registry.has(entityName);
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Validate all schemas: check that relation targets exist in the registry.
|
|
59
|
+
* Call this at application startup to catch configuration errors early.
|
|
60
|
+
*/
|
|
61
|
+
export function validateSchemas() {
|
|
62
|
+
const errors = [];
|
|
63
|
+
for (const schema of registry.values()) {
|
|
64
|
+
for (const [field, relation] of Object.entries(schema.relations)) {
|
|
65
|
+
if (!registry.has(relation.target)) {
|
|
66
|
+
errors.push(`${schema.name}.${field} references unknown entity "${relation.target}"`);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return { valid: errors.length === 0, errors };
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Clear all registered schemas (useful for testing)
|
|
74
|
+
*/
|
|
75
|
+
export function clearRegistry() {
|
|
76
|
+
registry.clear();
|
|
77
|
+
collectionMap.clear();
|
|
78
|
+
}
|