@neupgroup/mapper 1.2.2 → 1.2.4
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/README.md +3 -704
- package/dist/adapters/api-adapter.d.ts +45 -0
- package/dist/adapters/api-adapter.js +170 -0
- package/dist/adapters/index.d.ts +34 -0
- package/dist/adapters/index.js +91 -0
- package/dist/adapters/mongodb-adapter.d.ts +56 -0
- package/dist/adapters/mongodb-adapter.js +227 -0
- package/dist/adapters/mysql-adapter.d.ts +38 -0
- package/dist/adapters/mysql-adapter.js +166 -0
- package/dist/adapters/postgres-adapter.d.ts +52 -0
- package/dist/adapters/postgres-adapter.js +204 -0
- package/dist/config.d.ts +2 -2
- package/dist/config.js +1 -1
- package/dist/connector.d.ts +57 -0
- package/dist/connector.js +114 -0
- package/dist/errors.d.ts +29 -0
- package/dist/errors.js +50 -0
- package/dist/fluent-mapper.d.ts +30 -2
- package/dist/fluent-mapper.js +157 -5
- package/dist/index.d.ts +53 -13
- package/dist/index.js +188 -69
- package/dist/mapper.d.ts +2 -1
- package/dist/mapper.js +5 -1
- package/dist/orm/index.d.ts +3 -3
- package/dist/usage_example.d.ts +1 -0
- package/dist/usage_example.js +37 -0
- package/dist/usage_example_v2.d.ts +1 -0
- package/dist/usage_example_v2.js +19 -0
- package/package.json +23 -23
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { DbAdapter, QueryOptions, DocumentData } from '../orm/types';
|
|
2
|
+
export interface APIAdapterConfig {
|
|
3
|
+
baseUrl: string;
|
|
4
|
+
headers?: Record<string, string>;
|
|
5
|
+
timeout?: number;
|
|
6
|
+
endpoints?: {
|
|
7
|
+
get?: string;
|
|
8
|
+
getOne?: string;
|
|
9
|
+
create?: string;
|
|
10
|
+
update?: string;
|
|
11
|
+
delete?: string;
|
|
12
|
+
};
|
|
13
|
+
queryParamMapping?: {
|
|
14
|
+
filters?: string;
|
|
15
|
+
limit?: string;
|
|
16
|
+
offset?: string;
|
|
17
|
+
sort?: string;
|
|
18
|
+
fields?: string;
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* REST API Adapter
|
|
23
|
+
* Works with standard REST APIs
|
|
24
|
+
*/
|
|
25
|
+
export declare class APIAdapter implements DbAdapter {
|
|
26
|
+
private config;
|
|
27
|
+
constructor(config: APIAdapterConfig);
|
|
28
|
+
private buildUrl;
|
|
29
|
+
private buildQueryParams;
|
|
30
|
+
private fetch;
|
|
31
|
+
get(options: QueryOptions): Promise<DocumentData[]>;
|
|
32
|
+
getOne(options: QueryOptions): Promise<DocumentData | null>;
|
|
33
|
+
getDocuments(options: QueryOptions): Promise<DocumentData[]>;
|
|
34
|
+
addDocument(collectionName: string, data: DocumentData): Promise<string>;
|
|
35
|
+
updateDocument(collectionName: string, docId: string, data: DocumentData): Promise<void>;
|
|
36
|
+
deleteDocument(collectionName: string, docId: string): Promise<void>;
|
|
37
|
+
/**
|
|
38
|
+
* Make a custom API request
|
|
39
|
+
*/
|
|
40
|
+
request(method: string, endpoint: string, data?: any, customHeaders?: Record<string, string>): Promise<any>;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Factory function to create API adapter
|
|
44
|
+
*/
|
|
45
|
+
export declare function createAPIAdapter(config: APIAdapterConfig): APIAdapter;
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* REST API Adapter
|
|
3
|
+
* Works with standard REST APIs
|
|
4
|
+
*/
|
|
5
|
+
export class APIAdapter {
|
|
6
|
+
constructor(config) {
|
|
7
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
|
|
8
|
+
this.config = {
|
|
9
|
+
baseUrl: config.baseUrl.replace(/\/$/, ''), // Remove trailing slash
|
|
10
|
+
headers: config.headers || {},
|
|
11
|
+
timeout: config.timeout || 30000,
|
|
12
|
+
endpoints: {
|
|
13
|
+
get: ((_a = config.endpoints) === null || _a === void 0 ? void 0 : _a.get) || '/{collection}',
|
|
14
|
+
getOne: ((_b = config.endpoints) === null || _b === void 0 ? void 0 : _b.getOne) || '/{collection}/{id}',
|
|
15
|
+
create: ((_c = config.endpoints) === null || _c === void 0 ? void 0 : _c.create) || '/{collection}',
|
|
16
|
+
update: ((_d = config.endpoints) === null || _d === void 0 ? void 0 : _d.update) || '/{collection}/{id}',
|
|
17
|
+
delete: ((_e = config.endpoints) === null || _e === void 0 ? void 0 : _e.delete) || '/{collection}/{id}',
|
|
18
|
+
},
|
|
19
|
+
queryParamMapping: {
|
|
20
|
+
filters: ((_f = config.queryParamMapping) === null || _f === void 0 ? void 0 : _f.filters) || 'filter',
|
|
21
|
+
limit: ((_g = config.queryParamMapping) === null || _g === void 0 ? void 0 : _g.limit) || 'limit',
|
|
22
|
+
offset: ((_h = config.queryParamMapping) === null || _h === void 0 ? void 0 : _h.offset) || 'offset',
|
|
23
|
+
sort: ((_j = config.queryParamMapping) === null || _j === void 0 ? void 0 : _j.sort) || 'sort',
|
|
24
|
+
fields: ((_k = config.queryParamMapping) === null || _k === void 0 ? void 0 : _k.fields) || 'fields',
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
buildUrl(template, params) {
|
|
29
|
+
let url = template;
|
|
30
|
+
for (const [key, value] of Object.entries(params)) {
|
|
31
|
+
url = url.replace(`{${key}}`, encodeURIComponent(value));
|
|
32
|
+
}
|
|
33
|
+
return `${this.config.baseUrl}${url}`;
|
|
34
|
+
}
|
|
35
|
+
buildQueryParams(options) {
|
|
36
|
+
const params = new URLSearchParams();
|
|
37
|
+
// Add filters
|
|
38
|
+
if (options.filters.length > 0) {
|
|
39
|
+
const filterObj = {};
|
|
40
|
+
options.filters.forEach(f => {
|
|
41
|
+
filterObj[f.field] = { [f.operator || 'eq']: f.value };
|
|
42
|
+
});
|
|
43
|
+
params.append(this.config.queryParamMapping.filters, JSON.stringify(filterObj));
|
|
44
|
+
}
|
|
45
|
+
// Add raw where if present
|
|
46
|
+
if (options.rawWhere) {
|
|
47
|
+
params.append(this.config.queryParamMapping.filters, options.rawWhere);
|
|
48
|
+
}
|
|
49
|
+
// Add limit
|
|
50
|
+
if (options.limit !== null) {
|
|
51
|
+
params.append(this.config.queryParamMapping.limit, String(options.limit));
|
|
52
|
+
}
|
|
53
|
+
// Add offset
|
|
54
|
+
if (options.offset !== null) {
|
|
55
|
+
params.append(this.config.queryParamMapping.offset, String(options.offset));
|
|
56
|
+
}
|
|
57
|
+
// Add sort
|
|
58
|
+
if (options.sortBy) {
|
|
59
|
+
const sortValue = `${options.sortBy.direction === 'desc' ? '-' : ''}${options.sortBy.field}`;
|
|
60
|
+
params.append(this.config.queryParamMapping.sort, sortValue);
|
|
61
|
+
}
|
|
62
|
+
// Add fields
|
|
63
|
+
if (options.fields.length > 0) {
|
|
64
|
+
params.append(this.config.queryParamMapping.fields, options.fields.join(','));
|
|
65
|
+
}
|
|
66
|
+
return params;
|
|
67
|
+
}
|
|
68
|
+
async fetch(url, options = {}) {
|
|
69
|
+
const controller = new AbortController();
|
|
70
|
+
const timeoutId = setTimeout(() => controller.abort(), this.config.timeout);
|
|
71
|
+
try {
|
|
72
|
+
const response = await fetch(url, {
|
|
73
|
+
...options,
|
|
74
|
+
headers: {
|
|
75
|
+
'Content-Type': 'application/json',
|
|
76
|
+
...this.config.headers,
|
|
77
|
+
...options.headers,
|
|
78
|
+
},
|
|
79
|
+
signal: controller.signal,
|
|
80
|
+
});
|
|
81
|
+
clearTimeout(timeoutId);
|
|
82
|
+
if (!response.ok) {
|
|
83
|
+
const errorText = await response.text();
|
|
84
|
+
throw new Error(`API request failed: ${response.status} ${response.statusText}\n${errorText}`);
|
|
85
|
+
}
|
|
86
|
+
const contentType = response.headers.get('content-type');
|
|
87
|
+
if (contentType && contentType.includes('application/json')) {
|
|
88
|
+
return await response.json();
|
|
89
|
+
}
|
|
90
|
+
return await response.text();
|
|
91
|
+
}
|
|
92
|
+
catch (error) {
|
|
93
|
+
clearTimeout(timeoutId);
|
|
94
|
+
if (error.name === 'AbortError') {
|
|
95
|
+
throw new Error(`API request timeout after ${this.config.timeout}ms`);
|
|
96
|
+
}
|
|
97
|
+
throw error;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
async get(options) {
|
|
101
|
+
const url = this.buildUrl(this.config.endpoints.get, {
|
|
102
|
+
collection: options.collectionName,
|
|
103
|
+
});
|
|
104
|
+
const params = this.buildQueryParams(options);
|
|
105
|
+
const fullUrl = `${url}?${params.toString()}`;
|
|
106
|
+
const response = await this.fetch(fullUrl, { method: 'GET' });
|
|
107
|
+
// Handle different response formats
|
|
108
|
+
if (Array.isArray(response)) {
|
|
109
|
+
return response;
|
|
110
|
+
}
|
|
111
|
+
if (response.data && Array.isArray(response.data)) {
|
|
112
|
+
return response.data;
|
|
113
|
+
}
|
|
114
|
+
if (response.results && Array.isArray(response.results)) {
|
|
115
|
+
return response.results;
|
|
116
|
+
}
|
|
117
|
+
return [response];
|
|
118
|
+
}
|
|
119
|
+
async getOne(options) {
|
|
120
|
+
const results = await this.get({ ...options, limit: 1 });
|
|
121
|
+
return results[0] || null;
|
|
122
|
+
}
|
|
123
|
+
async getDocuments(options) {
|
|
124
|
+
return this.get(options);
|
|
125
|
+
}
|
|
126
|
+
async addDocument(collectionName, data) {
|
|
127
|
+
var _a;
|
|
128
|
+
const url = this.buildUrl(this.config.endpoints.create, { collection: collectionName });
|
|
129
|
+
const response = await this.fetch(url, {
|
|
130
|
+
method: 'POST',
|
|
131
|
+
body: JSON.stringify(data),
|
|
132
|
+
});
|
|
133
|
+
// Try to extract ID from response
|
|
134
|
+
return response.id || response._id || ((_a = response.data) === null || _a === void 0 ? void 0 : _a.id) || String(Date.now());
|
|
135
|
+
}
|
|
136
|
+
async updateDocument(collectionName, docId, data) {
|
|
137
|
+
const url = this.buildUrl(this.config.endpoints.update, {
|
|
138
|
+
collection: collectionName,
|
|
139
|
+
id: docId,
|
|
140
|
+
});
|
|
141
|
+
await this.fetch(url, {
|
|
142
|
+
method: 'PUT',
|
|
143
|
+
body: JSON.stringify(data),
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
async deleteDocument(collectionName, docId) {
|
|
147
|
+
const url = this.buildUrl(this.config.endpoints.delete, {
|
|
148
|
+
collection: collectionName,
|
|
149
|
+
id: docId,
|
|
150
|
+
});
|
|
151
|
+
await this.fetch(url, { method: 'DELETE' });
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Make a custom API request
|
|
155
|
+
*/
|
|
156
|
+
async request(method, endpoint, data, customHeaders) {
|
|
157
|
+
const url = `${this.config.baseUrl}${endpoint}`;
|
|
158
|
+
return this.fetch(url, {
|
|
159
|
+
method,
|
|
160
|
+
body: data ? JSON.stringify(data) : undefined,
|
|
161
|
+
headers: customHeaders,
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Factory function to create API adapter
|
|
167
|
+
*/
|
|
168
|
+
export function createAPIAdapter(config) {
|
|
169
|
+
return new APIAdapter(config);
|
|
170
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export { MySQLAdapter, createMySQLAdapter, type MySQLConfig } from './mysql-adapter.js';
|
|
2
|
+
export { PostgreSQLAdapter, createPostgreSQLAdapter, type PostgreSQLConfig } from './postgres-adapter.js';
|
|
3
|
+
export { MongoDBAdapter, createMongoDBAdapter, type MongoDBConfig } from './mongodb-adapter.js';
|
|
4
|
+
export { APIAdapter, createAPIAdapter, type APIAdapterConfig } from './api-adapter.js';
|
|
5
|
+
import type { DbAdapter } from '../orm/types.js';
|
|
6
|
+
import { type MySQLConfig } from './mysql-adapter.js';
|
|
7
|
+
import { type PostgreSQLConfig } from './postgres-adapter.js';
|
|
8
|
+
import { type MongoDBConfig } from './mongodb-adapter.js';
|
|
9
|
+
import { type APIAdapterConfig } from './api-adapter.js';
|
|
10
|
+
export type AdapterConfig = {
|
|
11
|
+
type: 'mysql';
|
|
12
|
+
config: MySQLConfig;
|
|
13
|
+
} | {
|
|
14
|
+
type: 'postgres' | 'postgresql' | 'sql';
|
|
15
|
+
config: PostgreSQLConfig;
|
|
16
|
+
} | {
|
|
17
|
+
type: 'mongodb' | 'mongo';
|
|
18
|
+
config: MongoDBConfig;
|
|
19
|
+
} | {
|
|
20
|
+
type: 'api' | 'rest';
|
|
21
|
+
config: APIAdapterConfig;
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* Auto-create adapter based on connection type
|
|
25
|
+
*/
|
|
26
|
+
export declare function createAdapter(adapterConfig: AdapterConfig): DbAdapter;
|
|
27
|
+
/**
|
|
28
|
+
* Create adapter from connection URL
|
|
29
|
+
*/
|
|
30
|
+
export declare function createAdapterFromUrl(url: string): DbAdapter;
|
|
31
|
+
/**
|
|
32
|
+
* Helper to auto-attach adapter to connection
|
|
33
|
+
*/
|
|
34
|
+
export declare function autoAttachAdapter(connections: any, connectionName: string, connectionType: string, connectionKey: any): void;
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
// Export all adapters
|
|
2
|
+
export { MySQLAdapter, createMySQLAdapter } from './mysql-adapter.js';
|
|
3
|
+
export { PostgreSQLAdapter, createPostgreSQLAdapter } from './postgres-adapter.js';
|
|
4
|
+
export { MongoDBAdapter, createMongoDBAdapter } from './mongodb-adapter.js';
|
|
5
|
+
export { APIAdapter, createAPIAdapter } from './api-adapter.js';
|
|
6
|
+
import { createMySQLAdapter } from './mysql-adapter.js';
|
|
7
|
+
import { createPostgreSQLAdapter } from './postgres-adapter.js';
|
|
8
|
+
import { createMongoDBAdapter } from './mongodb-adapter.js';
|
|
9
|
+
import { createAPIAdapter } from './api-adapter.js';
|
|
10
|
+
/**
|
|
11
|
+
* Auto-create adapter based on connection type
|
|
12
|
+
*/
|
|
13
|
+
export function createAdapter(adapterConfig) {
|
|
14
|
+
switch (adapterConfig.type) {
|
|
15
|
+
case 'mysql':
|
|
16
|
+
return createMySQLAdapter(adapterConfig.config);
|
|
17
|
+
case 'postgres':
|
|
18
|
+
case 'postgresql':
|
|
19
|
+
case 'sql':
|
|
20
|
+
return createPostgreSQLAdapter(adapterConfig.config);
|
|
21
|
+
case 'mongodb':
|
|
22
|
+
case 'mongo':
|
|
23
|
+
return createMongoDBAdapter(adapterConfig.config);
|
|
24
|
+
case 'api':
|
|
25
|
+
case 'rest':
|
|
26
|
+
return createAPIAdapter(adapterConfig.config);
|
|
27
|
+
default:
|
|
28
|
+
throw new Error(`Unknown adapter type: ${adapterConfig.type}`);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Create adapter from connection URL
|
|
33
|
+
*/
|
|
34
|
+
export function createAdapterFromUrl(url) {
|
|
35
|
+
const urlObj = new URL(url);
|
|
36
|
+
const protocol = urlObj.protocol.replace(':', '');
|
|
37
|
+
switch (protocol) {
|
|
38
|
+
case 'mysql':
|
|
39
|
+
return createMySQLAdapter({
|
|
40
|
+
host: urlObj.hostname,
|
|
41
|
+
port: urlObj.port ? parseInt(urlObj.port) : 3306,
|
|
42
|
+
user: urlObj.username,
|
|
43
|
+
password: urlObj.password,
|
|
44
|
+
database: urlObj.pathname.replace('/', ''),
|
|
45
|
+
});
|
|
46
|
+
case 'postgres':
|
|
47
|
+
case 'postgresql':
|
|
48
|
+
return createPostgreSQLAdapter({
|
|
49
|
+
host: urlObj.hostname,
|
|
50
|
+
port: urlObj.port ? parseInt(urlObj.port) : 5432,
|
|
51
|
+
user: urlObj.username,
|
|
52
|
+
password: urlObj.password,
|
|
53
|
+
database: urlObj.pathname.replace('/', ''),
|
|
54
|
+
});
|
|
55
|
+
case 'mongodb':
|
|
56
|
+
return createMongoDBAdapter({
|
|
57
|
+
uri: url,
|
|
58
|
+
database: urlObj.pathname.replace('/', ''),
|
|
59
|
+
});
|
|
60
|
+
case 'http':
|
|
61
|
+
case 'https':
|
|
62
|
+
return createAPIAdapter({
|
|
63
|
+
baseUrl: url,
|
|
64
|
+
});
|
|
65
|
+
default:
|
|
66
|
+
throw new Error(`Unsupported protocol: ${protocol}`);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Helper to auto-attach adapter to connection
|
|
71
|
+
*/
|
|
72
|
+
export function autoAttachAdapter(connections, connectionName, connectionType, connectionKey) {
|
|
73
|
+
try {
|
|
74
|
+
let adapter;
|
|
75
|
+
// If connectionKey has a URL, use it
|
|
76
|
+
if (connectionKey.url) {
|
|
77
|
+
adapter = createAdapterFromUrl(connectionKey.url);
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
// Create adapter based on type
|
|
81
|
+
adapter = createAdapter({
|
|
82
|
+
type: connectionType,
|
|
83
|
+
config: connectionKey,
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
connections.attachAdapter(connectionName, adapter);
|
|
87
|
+
}
|
|
88
|
+
catch (error) {
|
|
89
|
+
console.warn(`Failed to auto-attach adapter for ${connectionName}:`, error.message);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type { DbAdapter, QueryOptions, DocumentData } from '../orm/types';
|
|
2
|
+
export interface MongoDBConfig {
|
|
3
|
+
uri: string;
|
|
4
|
+
database: string;
|
|
5
|
+
options?: {
|
|
6
|
+
maxPoolSize?: number;
|
|
7
|
+
minPoolSize?: number;
|
|
8
|
+
serverSelectionTimeoutMS?: number;
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* MongoDB Database Adapter
|
|
13
|
+
* Requires: npm install mongodb
|
|
14
|
+
*/
|
|
15
|
+
export declare class MongoDBAdapter implements DbAdapter {
|
|
16
|
+
private config;
|
|
17
|
+
private client;
|
|
18
|
+
private db;
|
|
19
|
+
private mongodb;
|
|
20
|
+
constructor(config: MongoDBConfig);
|
|
21
|
+
/**
|
|
22
|
+
* Connect to MongoDB
|
|
23
|
+
*/
|
|
24
|
+
connect(): Promise<void>;
|
|
25
|
+
/**
|
|
26
|
+
* Ensure connection is established
|
|
27
|
+
*/
|
|
28
|
+
private ensureConnected;
|
|
29
|
+
private buildMongoQuery;
|
|
30
|
+
get(options: QueryOptions): Promise<DocumentData[]>;
|
|
31
|
+
getOne(options: QueryOptions): Promise<DocumentData | null>;
|
|
32
|
+
getDocuments(options: QueryOptions): Promise<DocumentData[]>;
|
|
33
|
+
addDocument(collectionName: string, data: DocumentData): Promise<string>;
|
|
34
|
+
updateDocument(collectionName: string, docId: string, data: DocumentData): Promise<void>;
|
|
35
|
+
deleteDocument(collectionName: string, docId: string): Promise<void>;
|
|
36
|
+
/**
|
|
37
|
+
* Close the MongoDB connection
|
|
38
|
+
*/
|
|
39
|
+
close(): Promise<void>;
|
|
40
|
+
/**
|
|
41
|
+
* Execute aggregation pipeline
|
|
42
|
+
*/
|
|
43
|
+
aggregate(collectionName: string, pipeline: any[]): Promise<DocumentData[]>;
|
|
44
|
+
/**
|
|
45
|
+
* Create an index
|
|
46
|
+
*/
|
|
47
|
+
createIndex(collectionName: string, fields: any, options?: any): Promise<string>;
|
|
48
|
+
/**
|
|
49
|
+
* Get collection statistics
|
|
50
|
+
*/
|
|
51
|
+
getStats(collectionName: string): Promise<any>;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Factory function to create MongoDB adapter
|
|
55
|
+
*/
|
|
56
|
+
export declare function createMongoDBAdapter(config: MongoDBConfig): MongoDBAdapter;
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MongoDB Database Adapter
|
|
3
|
+
* Requires: npm install mongodb
|
|
4
|
+
*/
|
|
5
|
+
export class MongoDBAdapter {
|
|
6
|
+
constructor(config) {
|
|
7
|
+
this.config = config;
|
|
8
|
+
try {
|
|
9
|
+
// Dynamically import mongodb to avoid bundling if not used
|
|
10
|
+
this.mongodb = require('mongodb');
|
|
11
|
+
}
|
|
12
|
+
catch (error) {
|
|
13
|
+
throw new Error(`Failed to initialize MongoDB adapter: ${error.message}\n` +
|
|
14
|
+
`Make sure to install mongodb: npm install mongodb`);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Connect to MongoDB
|
|
19
|
+
*/
|
|
20
|
+
async connect() {
|
|
21
|
+
var _a, _b, _c;
|
|
22
|
+
if (this.client)
|
|
23
|
+
return;
|
|
24
|
+
const { MongoClient } = this.mongodb;
|
|
25
|
+
this.client = new MongoClient(this.config.uri, {
|
|
26
|
+
maxPoolSize: ((_a = this.config.options) === null || _a === void 0 ? void 0 : _a.maxPoolSize) || 10,
|
|
27
|
+
minPoolSize: ((_b = this.config.options) === null || _b === void 0 ? void 0 : _b.minPoolSize) || 2,
|
|
28
|
+
serverSelectionTimeoutMS: ((_c = this.config.options) === null || _c === void 0 ? void 0 : _c.serverSelectionTimeoutMS) || 5000,
|
|
29
|
+
});
|
|
30
|
+
await this.client.connect();
|
|
31
|
+
this.db = this.client.db(this.config.database);
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Ensure connection is established
|
|
35
|
+
*/
|
|
36
|
+
async ensureConnected() {
|
|
37
|
+
if (!this.client) {
|
|
38
|
+
await this.connect();
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
buildMongoQuery(options) {
|
|
42
|
+
const query = {};
|
|
43
|
+
// Handle raw WHERE clause
|
|
44
|
+
if (options.rawWhere) {
|
|
45
|
+
try {
|
|
46
|
+
return JSON.parse(options.rawWhere);
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
return {};
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
// Build query from filters
|
|
53
|
+
for (const filter of options.filters) {
|
|
54
|
+
const { field, operator, value } = filter;
|
|
55
|
+
switch (operator) {
|
|
56
|
+
case '=':
|
|
57
|
+
query[field] = value;
|
|
58
|
+
break;
|
|
59
|
+
case '>':
|
|
60
|
+
query[field] = { $gt: value };
|
|
61
|
+
break;
|
|
62
|
+
case '<':
|
|
63
|
+
query[field] = { $lt: value };
|
|
64
|
+
break;
|
|
65
|
+
case '>=':
|
|
66
|
+
query[field] = { $gte: value };
|
|
67
|
+
break;
|
|
68
|
+
case '<=':
|
|
69
|
+
query[field] = { $lte: value };
|
|
70
|
+
break;
|
|
71
|
+
case '!=':
|
|
72
|
+
query[field] = { $ne: value };
|
|
73
|
+
break;
|
|
74
|
+
case 'IN':
|
|
75
|
+
query[field] = { $in: Array.isArray(value) ? value : [value] };
|
|
76
|
+
break;
|
|
77
|
+
case 'LIKE':
|
|
78
|
+
// Convert SQL LIKE to MongoDB regex
|
|
79
|
+
const pattern = value.replace(/%/g, '.*').replace(/_/g, '.');
|
|
80
|
+
query[field] = { $regex: new RegExp(pattern, 'i') };
|
|
81
|
+
break;
|
|
82
|
+
default:
|
|
83
|
+
query[field] = value;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return query;
|
|
87
|
+
}
|
|
88
|
+
async get(options) {
|
|
89
|
+
await this.ensureConnected();
|
|
90
|
+
const collection = this.db.collection(options.collectionName);
|
|
91
|
+
const query = this.buildMongoQuery(options);
|
|
92
|
+
let cursor = collection.find(query);
|
|
93
|
+
// Add projection (field selection)
|
|
94
|
+
if (options.fields.length > 0) {
|
|
95
|
+
const projection = {};
|
|
96
|
+
options.fields.forEach(field => {
|
|
97
|
+
projection[field] = 1;
|
|
98
|
+
});
|
|
99
|
+
cursor = cursor.project(projection);
|
|
100
|
+
}
|
|
101
|
+
// Add sorting
|
|
102
|
+
if (options.sortBy) {
|
|
103
|
+
const sort = {};
|
|
104
|
+
sort[options.sortBy.field] = options.sortBy.direction === 'asc' ? 1 : -1;
|
|
105
|
+
cursor = cursor.sort(sort);
|
|
106
|
+
}
|
|
107
|
+
// Add limit and offset
|
|
108
|
+
if (options.offset !== null) {
|
|
109
|
+
cursor = cursor.skip(options.offset);
|
|
110
|
+
}
|
|
111
|
+
if (options.limit !== null) {
|
|
112
|
+
cursor = cursor.limit(options.limit);
|
|
113
|
+
}
|
|
114
|
+
const results = await cursor.toArray();
|
|
115
|
+
// Convert _id to string for consistency
|
|
116
|
+
return results.map((doc) => {
|
|
117
|
+
var _a;
|
|
118
|
+
return ({
|
|
119
|
+
...doc,
|
|
120
|
+
_id: (_a = doc._id) === null || _a === void 0 ? void 0 : _a.toString(),
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
async getOne(options) {
|
|
125
|
+
var _a;
|
|
126
|
+
await this.ensureConnected();
|
|
127
|
+
const collection = this.db.collection(options.collectionName);
|
|
128
|
+
const query = this.buildMongoQuery(options);
|
|
129
|
+
const projection = {};
|
|
130
|
+
if (options.fields.length > 0) {
|
|
131
|
+
options.fields.forEach(field => {
|
|
132
|
+
projection[field] = 1;
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
const result = await collection.findOne(query, { projection });
|
|
136
|
+
if (!result)
|
|
137
|
+
return null;
|
|
138
|
+
return {
|
|
139
|
+
...result,
|
|
140
|
+
_id: (_a = result._id) === null || _a === void 0 ? void 0 : _a.toString(),
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
async getDocuments(options) {
|
|
144
|
+
return this.get(options);
|
|
145
|
+
}
|
|
146
|
+
async addDocument(collectionName, data) {
|
|
147
|
+
await this.ensureConnected();
|
|
148
|
+
const collection = this.db.collection(collectionName);
|
|
149
|
+
const result = await collection.insertOne(data);
|
|
150
|
+
return result.insertedId.toString();
|
|
151
|
+
}
|
|
152
|
+
async updateDocument(collectionName, docId, data) {
|
|
153
|
+
await this.ensureConnected();
|
|
154
|
+
const collection = this.db.collection(collectionName);
|
|
155
|
+
const { ObjectId } = this.mongodb;
|
|
156
|
+
// Try to convert docId to ObjectId, fallback to string
|
|
157
|
+
let query;
|
|
158
|
+
try {
|
|
159
|
+
query = { _id: new ObjectId(docId) };
|
|
160
|
+
}
|
|
161
|
+
catch {
|
|
162
|
+
query = { _id: docId };
|
|
163
|
+
}
|
|
164
|
+
await collection.updateOne(query, { $set: data });
|
|
165
|
+
}
|
|
166
|
+
async deleteDocument(collectionName, docId) {
|
|
167
|
+
await this.ensureConnected();
|
|
168
|
+
const collection = this.db.collection(collectionName);
|
|
169
|
+
const { ObjectId } = this.mongodb;
|
|
170
|
+
// Try to convert docId to ObjectId, fallback to string
|
|
171
|
+
let query;
|
|
172
|
+
try {
|
|
173
|
+
query = { _id: new ObjectId(docId) };
|
|
174
|
+
}
|
|
175
|
+
catch {
|
|
176
|
+
query = { _id: docId };
|
|
177
|
+
}
|
|
178
|
+
await collection.deleteOne(query);
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Close the MongoDB connection
|
|
182
|
+
*/
|
|
183
|
+
async close() {
|
|
184
|
+
if (this.client) {
|
|
185
|
+
await this.client.close();
|
|
186
|
+
this.client = null;
|
|
187
|
+
this.db = null;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Execute aggregation pipeline
|
|
192
|
+
*/
|
|
193
|
+
async aggregate(collectionName, pipeline) {
|
|
194
|
+
await this.ensureConnected();
|
|
195
|
+
const collection = this.db.collection(collectionName);
|
|
196
|
+
const results = await collection.aggregate(pipeline).toArray();
|
|
197
|
+
return results.map((doc) => {
|
|
198
|
+
var _a;
|
|
199
|
+
return ({
|
|
200
|
+
...doc,
|
|
201
|
+
_id: (_a = doc._id) === null || _a === void 0 ? void 0 : _a.toString(),
|
|
202
|
+
});
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Create an index
|
|
207
|
+
*/
|
|
208
|
+
async createIndex(collectionName, fields, options) {
|
|
209
|
+
await this.ensureConnected();
|
|
210
|
+
const collection = this.db.collection(collectionName);
|
|
211
|
+
return await collection.createIndex(fields, options);
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Get collection statistics
|
|
215
|
+
*/
|
|
216
|
+
async getStats(collectionName) {
|
|
217
|
+
await this.ensureConnected();
|
|
218
|
+
const collection = this.db.collection(collectionName);
|
|
219
|
+
return await collection.stats();
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Factory function to create MongoDB adapter
|
|
224
|
+
*/
|
|
225
|
+
export function createMongoDBAdapter(config) {
|
|
226
|
+
return new MongoDBAdapter(config);
|
|
227
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { DbAdapter, QueryOptions, DocumentData } from '../orm/types';
|
|
2
|
+
export interface MySQLConfig {
|
|
3
|
+
host: string;
|
|
4
|
+
port?: number;
|
|
5
|
+
user: string;
|
|
6
|
+
password: string;
|
|
7
|
+
database: string;
|
|
8
|
+
connectionLimit?: number;
|
|
9
|
+
ssl?: boolean;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* MySQL Database Adapter
|
|
13
|
+
* Requires: npm install mysql2
|
|
14
|
+
*/
|
|
15
|
+
export declare class MySQLAdapter implements DbAdapter {
|
|
16
|
+
private pool;
|
|
17
|
+
private mysql;
|
|
18
|
+
constructor(config: MySQLConfig);
|
|
19
|
+
private buildWhereClause;
|
|
20
|
+
get(options: QueryOptions): Promise<DocumentData[]>;
|
|
21
|
+
getOne(options: QueryOptions): Promise<DocumentData | null>;
|
|
22
|
+
getDocuments(options: QueryOptions): Promise<DocumentData[]>;
|
|
23
|
+
addDocument(collectionName: string, data: DocumentData): Promise<string>;
|
|
24
|
+
updateDocument(collectionName: string, docId: string, data: DocumentData): Promise<void>;
|
|
25
|
+
deleteDocument(collectionName: string, docId: string): Promise<void>;
|
|
26
|
+
/**
|
|
27
|
+
* Close all connections in the pool
|
|
28
|
+
*/
|
|
29
|
+
close(): Promise<void>;
|
|
30
|
+
/**
|
|
31
|
+
* Execute a raw SQL query
|
|
32
|
+
*/
|
|
33
|
+
raw(sql: string, values?: any[]): Promise<any>;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Factory function to create MySQL adapter
|
|
37
|
+
*/
|
|
38
|
+
export declare function createMySQLAdapter(config: MySQLConfig): MySQLAdapter;
|