@neupgroup/mapper 1.3.0 → 1.4.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/dist/adapters/api-adapter.d.ts +1 -4
- package/dist/adapters/api-adapter.js +15 -8
- package/dist/adapters/index.d.ts +5 -0
- package/dist/adapters/index.js +14 -0
- package/dist/adapters/sqlite-adapter.d.ts +36 -0
- package/dist/adapters/sqlite-adapter.js +172 -0
- package/dist/cli/create-connection.d.ts +2 -0
- package/dist/cli/create-connection.js +45 -0
- package/dist/cli/create-migration.d.ts +2 -0
- package/dist/cli/create-migration.js +63 -0
- package/dist/docs.d.ts +1 -1
- package/dist/docs.js +34 -0
- package/dist/fluent-mapper.d.ts +35 -5
- package/dist/fluent-mapper.js +113 -2
- package/dist/index.d.ts +2 -1
- package/dist/index.js +1 -0
- package/dist/mapper.d.ts +1 -1
- package/dist/mapper.js +4 -0
- package/dist/migrator.d.ts +22 -0
- package/dist/migrator.js +65 -0
- package/package.json +8 -2
|
@@ -34,10 +34,7 @@ export declare class APIAdapter implements DbAdapter {
|
|
|
34
34
|
addDocument(collectionName: string, data: DocumentData): Promise<string>;
|
|
35
35
|
updateDocument(collectionName: string, docId: string, data: DocumentData): Promise<void>;
|
|
36
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>;
|
|
37
|
+
request(method: string, endpoint: string, data?: any, customHeaders?: Record<string, string | string[]>): Promise<any>;
|
|
41
38
|
}
|
|
42
39
|
/**
|
|
43
40
|
* Factory function to create API adapter
|
|
@@ -69,13 +69,23 @@ export class APIAdapter {
|
|
|
69
69
|
const controller = new AbortController();
|
|
70
70
|
const timeoutId = setTimeout(() => controller.abort(), this.config.timeout);
|
|
71
71
|
try {
|
|
72
|
+
const headers = new Headers({
|
|
73
|
+
'Content-Type': 'application/json',
|
|
74
|
+
...this.config.headers,
|
|
75
|
+
});
|
|
76
|
+
if (options.headers) {
|
|
77
|
+
Object.entries(options.headers).forEach(([key, value]) => {
|
|
78
|
+
if (Array.isArray(value)) {
|
|
79
|
+
value.forEach(v => headers.append(key, v));
|
|
80
|
+
}
|
|
81
|
+
else if (value !== undefined) {
|
|
82
|
+
headers.set(key, value);
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
}
|
|
72
86
|
const response = await fetch(url, {
|
|
73
87
|
...options,
|
|
74
|
-
headers
|
|
75
|
-
'Content-Type': 'application/json',
|
|
76
|
-
...this.config.headers,
|
|
77
|
-
...options.headers,
|
|
78
|
-
},
|
|
88
|
+
headers,
|
|
79
89
|
signal: controller.signal,
|
|
80
90
|
});
|
|
81
91
|
clearTimeout(timeoutId);
|
|
@@ -150,9 +160,6 @@ export class APIAdapter {
|
|
|
150
160
|
});
|
|
151
161
|
await this.fetch(url, { method: 'DELETE' });
|
|
152
162
|
}
|
|
153
|
-
/**
|
|
154
|
-
* Make a custom API request
|
|
155
|
-
*/
|
|
156
163
|
async request(method, endpoint, data, customHeaders) {
|
|
157
164
|
const url = `${this.config.baseUrl}${endpoint}`;
|
|
158
165
|
return this.fetch(url, {
|
package/dist/adapters/index.d.ts
CHANGED
|
@@ -2,11 +2,13 @@ export { MySQLAdapter, createMySQLAdapter, type MySQLConfig } from './mysql-adap
|
|
|
2
2
|
export { PostgreSQLAdapter, createPostgreSQLAdapter, type PostgreSQLConfig } from './postgres-adapter.js';
|
|
3
3
|
export { MongoDBAdapter, createMongoDBAdapter, type MongoDBConfig } from './mongodb-adapter.js';
|
|
4
4
|
export { APIAdapter, createAPIAdapter, type APIAdapterConfig } from './api-adapter.js';
|
|
5
|
+
export { SQLiteAdapter, createSQLiteAdapter, type SQLiteConfig } from './sqlite-adapter.js';
|
|
5
6
|
import type { DbAdapter } from '../orm/types.js';
|
|
6
7
|
import { type MySQLConfig } from './mysql-adapter.js';
|
|
7
8
|
import { type PostgreSQLConfig } from './postgres-adapter.js';
|
|
8
9
|
import { type MongoDBConfig } from './mongodb-adapter.js';
|
|
9
10
|
import { type APIAdapterConfig } from './api-adapter.js';
|
|
11
|
+
import { type SQLiteConfig } from './sqlite-adapter.js';
|
|
10
12
|
export type AdapterConfig = {
|
|
11
13
|
type: 'mysql';
|
|
12
14
|
config: MySQLConfig;
|
|
@@ -19,6 +21,9 @@ export type AdapterConfig = {
|
|
|
19
21
|
} | {
|
|
20
22
|
type: 'api' | 'rest';
|
|
21
23
|
config: APIAdapterConfig;
|
|
24
|
+
} | {
|
|
25
|
+
type: 'sqlite' | 'sqlite3';
|
|
26
|
+
config: SQLiteConfig;
|
|
22
27
|
};
|
|
23
28
|
/**
|
|
24
29
|
* Auto-create adapter based on connection type
|
package/dist/adapters/index.js
CHANGED
|
@@ -3,10 +3,12 @@ export { MySQLAdapter, createMySQLAdapter } from './mysql-adapter.js';
|
|
|
3
3
|
export { PostgreSQLAdapter, createPostgreSQLAdapter } from './postgres-adapter.js';
|
|
4
4
|
export { MongoDBAdapter, createMongoDBAdapter } from './mongodb-adapter.js';
|
|
5
5
|
export { APIAdapter, createAPIAdapter } from './api-adapter.js';
|
|
6
|
+
export { SQLiteAdapter, createSQLiteAdapter } from './sqlite-adapter.js';
|
|
6
7
|
import { createMySQLAdapter } from './mysql-adapter.js';
|
|
7
8
|
import { createPostgreSQLAdapter } from './postgres-adapter.js';
|
|
8
9
|
import { createMongoDBAdapter } from './mongodb-adapter.js';
|
|
9
10
|
import { createAPIAdapter } from './api-adapter.js';
|
|
11
|
+
import { createSQLiteAdapter } from './sqlite-adapter.js';
|
|
10
12
|
/**
|
|
11
13
|
* Auto-create adapter based on connection type
|
|
12
14
|
*/
|
|
@@ -24,6 +26,9 @@ export function createAdapter(adapterConfig) {
|
|
|
24
26
|
case 'api':
|
|
25
27
|
case 'rest':
|
|
26
28
|
return createAPIAdapter(adapterConfig.config);
|
|
29
|
+
case 'sqlite':
|
|
30
|
+
case 'sqlite3':
|
|
31
|
+
return createSQLiteAdapter(adapterConfig.config);
|
|
27
32
|
default:
|
|
28
33
|
throw new Error(`Unknown adapter type: ${adapterConfig.type}`);
|
|
29
34
|
}
|
|
@@ -32,6 +37,10 @@ export function createAdapter(adapterConfig) {
|
|
|
32
37
|
* Create adapter from connection URL
|
|
33
38
|
*/
|
|
34
39
|
export function createAdapterFromUrl(url) {
|
|
40
|
+
// Handle shorthand SQLite local files
|
|
41
|
+
if (url.endsWith('.db') || url.endsWith('.sqlite')) {
|
|
42
|
+
return createSQLiteAdapter({ filename: url });
|
|
43
|
+
}
|
|
35
44
|
const urlObj = new URL(url);
|
|
36
45
|
const protocol = urlObj.protocol.replace(':', '');
|
|
37
46
|
switch (protocol) {
|
|
@@ -62,6 +71,11 @@ export function createAdapterFromUrl(url) {
|
|
|
62
71
|
return createAPIAdapter({
|
|
63
72
|
baseUrl: url,
|
|
64
73
|
});
|
|
74
|
+
case 'sqlite':
|
|
75
|
+
case 'sqlite3':
|
|
76
|
+
return createSQLiteAdapter({
|
|
77
|
+
filename: url.replace('sqlite://', '').replace('sqlite3://', ''),
|
|
78
|
+
});
|
|
65
79
|
default:
|
|
66
80
|
throw new Error(`Unsupported protocol: ${protocol}`);
|
|
67
81
|
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { DbAdapter, QueryOptions, DocumentData } from '../orm/types';
|
|
2
|
+
export interface SQLiteConfig {
|
|
3
|
+
filename: string;
|
|
4
|
+
mode?: number;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* SQLite Database Adapter
|
|
8
|
+
* Requires: npm install sqlite3
|
|
9
|
+
*/
|
|
10
|
+
export declare class SQLiteAdapter implements DbAdapter {
|
|
11
|
+
private db;
|
|
12
|
+
private sqlite3;
|
|
13
|
+
constructor(config: SQLiteConfig);
|
|
14
|
+
private run;
|
|
15
|
+
private all;
|
|
16
|
+
private getRow;
|
|
17
|
+
private buildWhereClause;
|
|
18
|
+
get(options: QueryOptions): Promise<DocumentData[]>;
|
|
19
|
+
getOne(options: QueryOptions): Promise<DocumentData | null>;
|
|
20
|
+
getDocuments(options: QueryOptions): Promise<DocumentData[]>;
|
|
21
|
+
addDocument(collectionName: string, data: DocumentData): Promise<string>;
|
|
22
|
+
updateDocument(collectionName: string, docId: string, data: DocumentData): Promise<void>;
|
|
23
|
+
deleteDocument(collectionName: string, docId: string): Promise<void>;
|
|
24
|
+
/**
|
|
25
|
+
* Close the database connection
|
|
26
|
+
*/
|
|
27
|
+
close(): Promise<void>;
|
|
28
|
+
/**
|
|
29
|
+
* Execute a raw SQL query
|
|
30
|
+
*/
|
|
31
|
+
raw(sql: string, values?: any[]): Promise<any>;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Factory function to create SQLite adapter
|
|
35
|
+
*/
|
|
36
|
+
export declare function createSQLiteAdapter(config: SQLiteConfig): SQLiteAdapter;
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SQLite Database Adapter
|
|
3
|
+
* Requires: npm install sqlite3
|
|
4
|
+
*/
|
|
5
|
+
export class SQLiteAdapter {
|
|
6
|
+
constructor(config) {
|
|
7
|
+
try {
|
|
8
|
+
// Dynamically import sqlite3 to avoid bundling if not used
|
|
9
|
+
this.sqlite3 = require('sqlite3').verbose();
|
|
10
|
+
this.db = new this.sqlite3.Database(config.filename, config.mode);
|
|
11
|
+
}
|
|
12
|
+
catch (error) {
|
|
13
|
+
throw new Error(`Failed to initialize SQLite adapter: ${error.message}\n` +
|
|
14
|
+
`Make sure to install sqlite3: npm install sqlite3`);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
run(sql, params = []) {
|
|
18
|
+
return new Promise((resolve, reject) => {
|
|
19
|
+
this.db.run(sql, params, function (err) {
|
|
20
|
+
if (err)
|
|
21
|
+
reject(err);
|
|
22
|
+
else
|
|
23
|
+
resolve(this);
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
all(sql, params = []) {
|
|
28
|
+
return new Promise((resolve, reject) => {
|
|
29
|
+
this.db.all(sql, params, (err, rows) => {
|
|
30
|
+
if (err)
|
|
31
|
+
reject(err);
|
|
32
|
+
else
|
|
33
|
+
resolve(rows);
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
getRow(sql, params = []) {
|
|
38
|
+
return new Promise((resolve, reject) => {
|
|
39
|
+
this.db.get(sql, params, (err, row) => {
|
|
40
|
+
if (err)
|
|
41
|
+
reject(err);
|
|
42
|
+
else
|
|
43
|
+
resolve(row);
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
buildWhereClause(options) {
|
|
48
|
+
const values = [];
|
|
49
|
+
const conditions = [];
|
|
50
|
+
// Handle raw WHERE clause
|
|
51
|
+
if (options.rawWhere) {
|
|
52
|
+
return { sql: options.rawWhere, values: [] };
|
|
53
|
+
}
|
|
54
|
+
// Build WHERE conditions from filters
|
|
55
|
+
for (const filter of options.filters) {
|
|
56
|
+
const { field, operator, value } = filter;
|
|
57
|
+
switch (operator) {
|
|
58
|
+
case '=':
|
|
59
|
+
case '>':
|
|
60
|
+
case '<':
|
|
61
|
+
case '>=':
|
|
62
|
+
case '<=':
|
|
63
|
+
case '!=':
|
|
64
|
+
conditions.push(`"${field}" ${operator} ?`);
|
|
65
|
+
values.push(value);
|
|
66
|
+
break;
|
|
67
|
+
case 'LIKE':
|
|
68
|
+
conditions.push(`"${field}" LIKE ?`);
|
|
69
|
+
values.push(value);
|
|
70
|
+
break;
|
|
71
|
+
case 'IN':
|
|
72
|
+
const placeholders = Array.isArray(value) ? value.map(() => '?').join(', ') : '?';
|
|
73
|
+
conditions.push(`"${field}" IN (${placeholders})`);
|
|
74
|
+
if (Array.isArray(value)) {
|
|
75
|
+
values.push(...value);
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
values.push(value);
|
|
79
|
+
}
|
|
80
|
+
break;
|
|
81
|
+
default:
|
|
82
|
+
conditions.push(`"${field}" = ?`);
|
|
83
|
+
values.push(value);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
const sql = conditions.length > 0 ? conditions.join(' AND ') : '';
|
|
87
|
+
return { sql, values };
|
|
88
|
+
}
|
|
89
|
+
async get(options) {
|
|
90
|
+
const fields = options.fields && options.fields.length > 0
|
|
91
|
+
? options.fields.map(f => `"${f}"`).join(', ')
|
|
92
|
+
: '*';
|
|
93
|
+
let sql = `SELECT ${fields} FROM "${options.collectionName}"`;
|
|
94
|
+
const values = [];
|
|
95
|
+
// Add WHERE clause
|
|
96
|
+
const { sql: whereSql, values: whereValues } = this.buildWhereClause(options);
|
|
97
|
+
if (whereSql) {
|
|
98
|
+
sql += ` WHERE ${whereSql}`;
|
|
99
|
+
values.push(...whereValues);
|
|
100
|
+
}
|
|
101
|
+
// Add ORDER BY
|
|
102
|
+
if (options.sortBy) {
|
|
103
|
+
sql += ` ORDER BY "${options.sortBy.field}" ${options.sortBy.direction.toUpperCase()}`;
|
|
104
|
+
}
|
|
105
|
+
// Add LIMIT and OFFSET
|
|
106
|
+
if (options.limit !== null) {
|
|
107
|
+
sql += ` LIMIT ?`;
|
|
108
|
+
values.push(options.limit);
|
|
109
|
+
}
|
|
110
|
+
if (options.offset !== null) {
|
|
111
|
+
sql += ` OFFSET ?`;
|
|
112
|
+
values.push(options.offset);
|
|
113
|
+
}
|
|
114
|
+
return this.all(sql, values);
|
|
115
|
+
}
|
|
116
|
+
async getOne(options) {
|
|
117
|
+
const results = await this.get({ ...options, limit: 1 });
|
|
118
|
+
return results[0] || null;
|
|
119
|
+
}
|
|
120
|
+
async getDocuments(options) {
|
|
121
|
+
return this.get(options);
|
|
122
|
+
}
|
|
123
|
+
async addDocument(collectionName, data) {
|
|
124
|
+
const fields = Object.keys(data);
|
|
125
|
+
const placeholders = fields.map(() => '?').join(', ');
|
|
126
|
+
const values = Object.values(data);
|
|
127
|
+
const sql = `INSERT INTO "${collectionName}" (${fields.map(f => `"${f}"`).join(', ')}) VALUES (${placeholders})`;
|
|
128
|
+
const result = await this.run(sql, values);
|
|
129
|
+
return String(result.lastID);
|
|
130
|
+
}
|
|
131
|
+
async updateDocument(collectionName, docId, data) {
|
|
132
|
+
const fields = Object.keys(data);
|
|
133
|
+
const setClause = fields.map(f => `"${f}" = ?`).join(', ');
|
|
134
|
+
const values = [...Object.values(data), docId];
|
|
135
|
+
const sql = `UPDATE "${collectionName}" SET ${setClause} WHERE id = ?`;
|
|
136
|
+
await this.run(sql, values);
|
|
137
|
+
}
|
|
138
|
+
async deleteDocument(collectionName, docId) {
|
|
139
|
+
const sql = `DELETE FROM "${collectionName}" WHERE id = ?`;
|
|
140
|
+
await this.run(sql, [docId]);
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Close the database connection
|
|
144
|
+
*/
|
|
145
|
+
async close() {
|
|
146
|
+
return new Promise((resolve, reject) => {
|
|
147
|
+
this.db.close((err) => {
|
|
148
|
+
if (err)
|
|
149
|
+
reject(err);
|
|
150
|
+
else
|
|
151
|
+
resolve();
|
|
152
|
+
});
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Execute a raw SQL query
|
|
157
|
+
*/
|
|
158
|
+
async raw(sql, values) {
|
|
159
|
+
if (sql.trim().toUpperCase().startsWith('SELECT')) {
|
|
160
|
+
return this.all(sql, values || []);
|
|
161
|
+
}
|
|
162
|
+
else {
|
|
163
|
+
return this.run(sql, values || []);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Factory function to create SQLite adapter
|
|
169
|
+
*/
|
|
170
|
+
export function createSQLiteAdapter(config) {
|
|
171
|
+
return new SQLiteAdapter(config);
|
|
172
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import * as fs from 'fs';
|
|
3
|
+
import * as path from 'path';
|
|
4
|
+
const args = process.argv.slice(2);
|
|
5
|
+
const connectionName = args[0];
|
|
6
|
+
const type = args[1] || 'api';
|
|
7
|
+
if (!connectionName) {
|
|
8
|
+
console.error('Usage: npm run create-connection <connectionName> [type]');
|
|
9
|
+
process.exit(1);
|
|
10
|
+
}
|
|
11
|
+
const connectionDir = path.resolve(process.cwd(), 'src/connection');
|
|
12
|
+
if (!fs.existsSync(connectionDir))
|
|
13
|
+
fs.mkdirSync(connectionDir, { recursive: true });
|
|
14
|
+
const filePath = path.join(connectionDir, `${connectionName}.ts`);
|
|
15
|
+
let configTemplate = '';
|
|
16
|
+
if (type === 'mysql') {
|
|
17
|
+
configTemplate = `
|
|
18
|
+
type: 'mysql',
|
|
19
|
+
host: 'localhost',
|
|
20
|
+
port: 3306,
|
|
21
|
+
user: 'root',
|
|
22
|
+
password: '',
|
|
23
|
+
database: '${connectionName}'
|
|
24
|
+
`;
|
|
25
|
+
}
|
|
26
|
+
else if (type === 'sqlite') {
|
|
27
|
+
configTemplate = `
|
|
28
|
+
type: 'sqlite',
|
|
29
|
+
filename: './${connectionName}.db'
|
|
30
|
+
`;
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
configTemplate = `
|
|
34
|
+
type: '${type}',
|
|
35
|
+
// Add configuration here
|
|
36
|
+
`;
|
|
37
|
+
}
|
|
38
|
+
const fileContent = `import { ConnectionConfig } from '@neupgroup/mapper';
|
|
39
|
+
|
|
40
|
+
export const config: ConnectionConfig = {
|
|
41
|
+
${configTemplate}
|
|
42
|
+
};
|
|
43
|
+
`;
|
|
44
|
+
fs.writeFileSync(filePath, fileContent.trim());
|
|
45
|
+
console.log(`Created connection file: ${filePath} `);
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import * as fs from 'fs';
|
|
3
|
+
import * as path from 'path';
|
|
4
|
+
const args = process.argv.slice(2);
|
|
5
|
+
const tableName = args[0];
|
|
6
|
+
const remarks = args[1] || '';
|
|
7
|
+
if (!tableName) {
|
|
8
|
+
console.error('Usage: npm run create-migration <tableName> [remarks]');
|
|
9
|
+
process.exit(1);
|
|
10
|
+
}
|
|
11
|
+
// Ensure directories exist
|
|
12
|
+
const migrationDir = path.resolve(process.cwd(), 'src/migration');
|
|
13
|
+
const schemasDir = path.resolve(process.cwd(), 'src/schemas');
|
|
14
|
+
if (!fs.existsSync(migrationDir))
|
|
15
|
+
fs.mkdirSync(migrationDir, { recursive: true });
|
|
16
|
+
if (!fs.existsSync(schemasDir))
|
|
17
|
+
fs.mkdirSync(schemasDir, { recursive: true });
|
|
18
|
+
// Generate timestamp
|
|
19
|
+
const now = new Date();
|
|
20
|
+
const timestamp = now.toISOString().replace(/[-:T.]/g, '').slice(0, 14); // YYYYMMDDHHMMSS format roughly
|
|
21
|
+
// Actually better manual format: YYYYMMDD_HHMMSS
|
|
22
|
+
const pad = (n) => n.toString().padStart(2, '0');
|
|
23
|
+
const tsResult = `${now.getFullYear()}${pad(now.getMonth() + 1)}${pad(now.getDate())}_${pad(now.getHours())}${pad(now.getMinutes())}${pad(now.getSeconds())}`;
|
|
24
|
+
const fileName = `${tsResult}_${tableName}${remarks ? '_' + remarks : ''}.ts`;
|
|
25
|
+
const filePath = path.join(migrationDir, fileName);
|
|
26
|
+
const fileContent = `
|
|
27
|
+
import { Mapper, TableMigrator } from '@neupgroup/mapper';
|
|
28
|
+
|
|
29
|
+
export async function up() {
|
|
30
|
+
// const table = Mapper.schemas().table('${tableName}');
|
|
31
|
+
// table.addColumn('id').type('int').isPrimary().autoIncrement().exec();
|
|
32
|
+
// ... add more columns
|
|
33
|
+
console.log('Migrating up: ${tableName}');
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export async function down() {
|
|
37
|
+
// Drop table or revert changes
|
|
38
|
+
console.log('Migrating down: ${tableName}');
|
|
39
|
+
}
|
|
40
|
+
`;
|
|
41
|
+
fs.writeFileSync(filePath, fileContent.trim());
|
|
42
|
+
console.log(`Created migration file: ${filePath}`);
|
|
43
|
+
// Create or update schema definition
|
|
44
|
+
const schemaFilePath = path.join(schemasDir, `${tableName}.ts`);
|
|
45
|
+
if (!fs.existsSync(schemaFilePath)) {
|
|
46
|
+
const schemaContent = `
|
|
47
|
+
export const ${tableName} = {
|
|
48
|
+
fields: [
|
|
49
|
+
// { name: 'id', type: 'int', isPrimary: true, autoIncrement: true }
|
|
50
|
+
],
|
|
51
|
+
insertableFields: [],
|
|
52
|
+
updatableFields: [],
|
|
53
|
+
massUpdateable: false,
|
|
54
|
+
massDeletable: false,
|
|
55
|
+
usesConnection: 'default' // Update this
|
|
56
|
+
};
|
|
57
|
+
`;
|
|
58
|
+
fs.writeFileSync(schemaFilePath, schemaContent.trim());
|
|
59
|
+
console.log(`Created schema definition: ${schemaFilePath}`);
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
console.log(`Schema file already exists: ${schemaFilePath}`);
|
|
63
|
+
}
|
package/dist/docs.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export declare const documentationMd = "\n# Mapper Library Documentation\n\nWelcome to `@neupgroup/mapper`. This guide covers:\n- Installation\n- Configuring connections (DSL and UI)\n- Using connections in code\n- Creating schemas (ORM)\n- Configuring and using schemas\n- CRUD operations: insert, update, delete, fetch\n- Error handling and troubleshooting\n\n---\n\n## Installation\n\n- Install from npm:\n `npm install @neupgroup/mapper`\n- In this workspace, the app depends on the library via `workspace:*`. Build the library when you update it:\n `cd library && npm run build`\n- Import helpers:\n `import { parseConnectionsDsl, toNormalizedConnections } from '@neupgroup/mapper'`\n\n---\n\n## Configure Connections\n\nYou can configure connections in two ways:\n\n1) DSL File (recommended)\n2) UI Configure page (runtime setup)\n\n### 1) DSL Format\n\nCreate `connections.dsl` at your project root:\n\n```\nconnections = [\n mysql_prod: {\n type: mysql\n host: 127.0.0.1\n port: 3306\n user: root\n password: \"s3cr3t\"\n database: appdb\n }\n\n mongo_dev: {\n type: mongodb\n uri: \"mongodb://127.0.0.1:27017\"\n database: devdb\n }\n\n firestore_local: {\n type: firestore\n projectId: my-project\n applicationDefault: true\n }\n\n http_api: {\n type: api\n baseUrl: \"https://api.example.com\"\n token: \"abc123\"\n }\n]\n```\n\nNotes:\n- `type` (or `dbType`) defaults to `api` if omitted.\n- Values can be unquoted or quoted; comments using `#` are ignored.\n\nParse and normalize:\n\n```ts\nimport { parseConnectionsDsl, toNormalizedConnections } from '@neupgroup/mapper'\n\nconst text = await fs.promises.readFile('connections.dsl', 'utf8')\nconst envMap = parseConnectionsDsl(text)\nconst connections = toNormalizedConnections(envMap)\n// connections: Array<{ name, type, key }>\n```\n\n### 2) UI Configure Page\n\n- Go to `/configure` to define connections at runtime.\n- Use \"Generate collective env\" to produce `connections.dsl` from configured connections.\n- Download the file and commit it or load at startup.\n\n---\n\n## Use Connections in Code\n\n### Normalize and route by type\n\n```ts\nimport { parseConnectionsDsl, toNormalizedConnections } from '@neupgroup/mapper'\nimport { connection, schema } from '@neupgroup/mapper'\n\nconst text = await fs.promises.readFile('connections.dsl', 'utf8')\nconst envMap = parseConnectionsDsl(text)\nconst conns = toNormalizedConnections(envMap)\n\n// Register connections\nconst conRegistry = connection()\nfor (const c of conns) {\n conRegistry.register({ name: c.name, type: c.type, key: c.key })\n}\n\n// Use with schemas\nconst sm = schema(conRegistry)\n```\n\n### Direct construction\n\n```ts\nconst conRegistry = connection()\nconRegistry.create('mysql_prod', 'mysql').key({\n host: '127.0.0.1',\n port: 3306,\n user: 'root',\n password: 's3cr3t',\n database: 'appdb',\n})\n```\n\n---\n\n## Schemas and Models\n\nSchemas define structure for collections/tables bound to a connection.\n\n### Define and register a schema\n\n```ts\nimport { schema } from '@neupgroup/mapper'\n\nconst sm = schema(conRegistry)\n\nsm.create('User')\n .use({ connection: 'mysql_prod', collection: 'users' })\n .setStructure({\n id: 'string primary',\n email: 'string unique',\n name: 'string editable',\n createdAt: 'date',\n '?field': 'allow-undefined', // optional: permit fields not listed\n })\n\nconst User = sm.use('User')\n```\n\n---\n\n## CRUD Operations\n\nAll operations return Promises and may throw on errors.\n\n### Insert\n\n```ts\nconst createdId = await User.add({\n id: 'u_123',\n email: 'alice@example.com',\n name: 'Alice',\n createdAt: new Date(),\n})\n```\n\n### Update\n\n```ts\nawait User.where(['id', 'u_123']).to({ name: 'Alice Cooper' }).updateOne()\n```\n\n### Delete\n\n```ts\nawait User.where(['id', 'u_123']).deleteOne()\n```\n\n### Fetch / Query\n\n```ts\nconst one = await User.where(['id', 'u_123']).getOne()\nconst many = await User.where('email', '%@example.com', 'like').get()\n```\n\n---\n\n## Schema Configuration Details\n\n- `primary`: marks primary key; used for updates/deletes.\n- `unique`: enforces uniqueness in supported backends.\n- `editable`: indicates fields commonly modified via UI.\n- `type`: one of `string`, `number`, `boolean`, `date`, `int`.\n- `?field`: enables accepting fields not defined in the schema.\n\n---\n\n## Error Handling\n\nWrap operations in `try/catch` and inspect known error shapes.\n\n```ts\ntry {\n const user = await User.where(['id', 'u_404']).getOne()\n if (!user) {\n // handle not found gracefully\n }\n} catch (err) {\n if (err && typeof err === 'object' && 'code' in err) {\n console.error('Database error code:', (err as any).code)\n }\n console.error('Unexpected error', err)\n}\n```\n\nRecommendations:\n- Validate required creds before initializing connections.\n- Prefer parameterized queries or ORM filters over string concatenation.\n- Log request IDs and timestamps for audit trails.\n\n---\n\n## Troubleshooting\n\n- \"Connection refused\": check host/port/firewall and credentials.\n- \"Authentication failed\": verify tokens/passwords and token scopes.\n- \"Timeout\": review network paths and optimize query.\n- \"Schema mismatch\": ensure field names/types match the backend.\n\n---\n\n## API Quick Reference\n\n- `parseConnectionsDsl(text)` \u2192 Map of connectionName \u2192 key/value creds\n- `toNormalizedConnections(map)` \u2192 Array of { name, type, key }\n- `connection()` \u2192 Connection registry (create/register/list/get)\n- `schema(connections?)` \u2192 Schema manager; define, register, and use schemas\n\n---\n\n## End-to-End Example\n\n```ts\nimport { parseConnectionsDsl, toNormalizedConnections, connection, schema } from '@neupgroup/mapper'\n\nconst text = await fs.promises.readFile('connections.dsl', 'utf8')\nconst map = parseConnectionsDsl(text)\nconst conns = toNormalizedConnections(map)\n\nconst conRegistry = connection()\nfor (const c of conns) conRegistry.register({ name: c.name, type: c.type, key: c.key })\n\nconst sm = schema(conRegistry)\nsm.create('Product')\n .use({ connection: conns[0].name, collection: 'products' })\n .setStructure({\n id: 'string primary',\n title: 'string',\n price: 'number',\n tags: 'string',\n })\nconst Product = sm.use('Product')\n\nawait Product.add({ id: 'p_1', title: 'Widget', price: 9.99, tags: 'sale' })\nawait Product.where(['id', 'p_1']).to({ price: 7.99 }).updateOne()\nconst items = await Product.where('price', 10, '<').get()\nawait Product.where(['id', 'p_1']).deleteOne()\n```\n";
|
|
1
|
+
export declare const documentationMd = "\n# Mapper Library Documentation\n\nWelcome to `@neupgroup/mapper`. This guide covers:\n- Installation\n- Configuring connections (DSL and UI)\n- Using connections in code\n- Creating schemas (ORM)\n- Configuring and using schemas\n- CRUD operations: insert, update, delete, fetch\n- Error handling and troubleshooting\n\n---\n\n## Installation\n\n- Install from npm:\n `npm install @neupgroup/mapper`\n- In this workspace, the app depends on the library via `workspace:*`. Build the library when you update it:\n `cd library && npm run build`\n- Import helpers:\n `import { parseConnectionsDsl, toNormalizedConnections } from '@neupgroup/mapper'`\n\n---\n\n## Configure Connections\n\nYou can configure connections in two ways:\n\n1) DSL File (recommended)\n2) UI Configure page (runtime setup)\n\n### 1) DSL Format\n\nCreate `connections.dsl` at your project root:\n\n```\nconnections = [\n mysql_prod: {\n type: mysql\n host: 127.0.0.1\n port: 3306\n user: root\n password: \"s3cr3t\"\n database: appdb\n }\n\n mongo_dev: {\n type: mongodb\n uri: \"mongodb://127.0.0.1:27017\"\n database: devdb\n }\n\n firestore_local: {\n type: firestore\n projectId: my-project\n applicationDefault: true\n }\n\n http_api: {\n type: api\n baseUrl: \"https://api.example.com\"\n token: \"abc123\"\n }\n\n sqlite_local: {\n type: sqlite\n filename: \"./data.db\"\n }\n]\n```\n\nNotes:\n- `type` (or `dbType`) defaults to `api` if omitted.\n- For SQLite, use `filename` to specify the path to the database file.\n- Values can be unquoted or quoted; comments using `#` are ignored.\n\nParse and normalize:\n\n```ts\nimport { parseConnectionsDsl, toNormalizedConnections } from '@neupgroup/mapper'\n\nconst text = await fs.promises.readFile('connections.dsl', 'utf8')\nconst envMap = parseConnectionsDsl(text)\nconst connections = toNormalizedConnections(envMap)\n// connections: Array<{ name, type, key }>\n```\n\n### 2) UI Configure Page\n\n- Go to `/configure` to define connections at runtime.\n- Use \"Generate collective env\" to produce `connections.dsl` from configured connections.\n- Download the file and commit it or load at startup.\n\n---\n\n## Use Connections in Code\n\n### Normalize and route by type\n\n```ts\nimport { parseConnectionsDsl, toNormalizedConnections } from '@neupgroup/mapper'\nimport { connection, schema } from '@neupgroup/mapper'\n\nconst text = await fs.promises.readFile('connections.dsl', 'utf8')\nconst envMap = parseConnectionsDsl(text)\nconst conns = toNormalizedConnections(envMap)\n\n// Register connections\nconst conRegistry = connection()\nfor (const c of conns) {\n conRegistry.register({ name: c.name, type: c.type, key: c.key })\n}\n\n// Use with schemas\nconst sm = schema(conRegistry)\n```\n\n### Direct construction\n\n```ts\nconst conRegistry = connection()\nconRegistry.create('mysql_prod', 'mysql').key({\n host: '127.0.0.1',\n port: 3306,\n user: 'root',\n password: 's3cr3t',\n database: 'appdb',\n})\n```\n\n---\n\n## Schemas and Models\n\nSchemas define structure for collections/tables bound to a connection.\n\n### Define and register a schema\n\n```ts\nimport { schema } from '@neupgroup/mapper'\n\nconst sm = schema(conRegistry)\n\nsm.create('User')\n .use({ connection: 'mysql_prod', collection: 'users' })\n .setStructure({\n id: 'string primary',\n email: 'string unique',\n name: 'string editable',\n createdAt: 'date',\n '?field': 'allow-undefined', // optional: permit fields not listed\n })\n\nconst User = sm.use('User')\n```\n\n---\n\n## CRUD Operations\n\nAll operations return Promises and may throw on errors.\n\n### Insert\n\n```ts\nconst createdId = await User.add({\n id: 'u_123',\n email: 'alice@example.com',\n name: 'Alice',\n createdAt: new Date(),\n})\n```\n\n### Update\n\n```ts\nawait User.where(['id', 'u_123']).to({ name: 'Alice Cooper' }).updateOne()\n```\n\n### Delete\n\n```ts\nawait User.where(['id', 'u_123']).deleteOne()\n```\n\n### Fetch / Query\n\n```ts\nconst one = await User.where(['id', 'u_123']).getOne()\nconst many = await User.where('email', '%@example.com', 'like').get()\n```\n\n---\n\n## Fluent API Requests\n\nFor API connections, you can make fluent HTTP requests without defining a full schema.\n\n```ts\nconst conn = Mapper.connection({ type: 'api', url: 'https://api.example.com' });\n\n// Simple POST\nawait conn.path(\"/users\").post({ name: 'John Doe' });\n\n// Chained paths and headers\nconst result = await conn.path(\"/v1\")\n .path(\"/users/123\")\n .header(\"Authorization\", \"Bearer my-token\")\n .headers({ \"X-Custom\": \"value\" })\n .get();\n\n// Support for multiple values for the same header\nawait conn.path(\"/notify\")\n .header(\"X-Tag\", \"news\")\n .header(\"X-Tag\", \"alerts\")\n .post({ body: \"Hello\" });\n```\n\nAvailable methods: `.get()`, `.post(data)`, `.put(data)`, `.patch(data)`, `.delete()`.\n\n---\n\n## Schema Configuration Details\n\n- `primary`: marks primary key; used for updates/deletes.\n- `unique`: enforces uniqueness in supported backends.\n- `editable`: indicates fields commonly modified via UI.\n- `type`: one of `string`, `number`, `boolean`, `date`, `int`.\n- `?field`: enables accepting fields not defined in the schema.\n\n---\n\n## Error Handling\n\nWrap operations in `try/catch` and inspect known error shapes.\n\n```ts\ntry {\n const user = await User.where(['id', 'u_404']).getOne()\n if (!user) {\n // handle not found gracefully\n }\n} catch (err) {\n if (err && typeof err === 'object' && 'code' in err) {\n console.error('Database error code:', (err as any).code)\n }\n console.error('Unexpected error', err)\n}\n```\n\nRecommendations:\n- Validate required creds before initializing connections.\n- Prefer parameterized queries or ORM filters over string concatenation.\n- Log request IDs and timestamps for audit trails.\n\n---\n\n## Troubleshooting\n\n- \"Connection refused\": check host/port/firewall and credentials.\n- \"Authentication failed\": verify tokens/passwords and token scopes.\n- \"Timeout\": review network paths and optimize query.\n- \"Schema mismatch\": ensure field names/types match the backend.\n\n---\n\n## API Quick Reference\n\n- `parseConnectionsDsl(text)` \u2192 Map of connectionName \u2192 key/value creds\n- `toNormalizedConnections(map)` \u2192 Array of { name, type, key }\n- `connection()` \u2192 Connection registry (create/register/list/get)\n- `schema(connections?)` \u2192 Schema manager; define, register, and use schemas\n\n---\n\n## End-to-End Example\n\n```ts\nimport { parseConnectionsDsl, toNormalizedConnections, connection, schema } from '@neupgroup/mapper'\n\nconst text = await fs.promises.readFile('connections.dsl', 'utf8')\nconst map = parseConnectionsDsl(text)\nconst conns = toNormalizedConnections(map)\n\nconst conRegistry = connection()\nfor (const c of conns) conRegistry.register({ name: c.name, type: c.type, key: c.key })\n\nconst sm = schema(conRegistry)\nsm.create('Product')\n .use({ connection: conns[0].name, collection: 'products' })\n .setStructure({\n id: 'string primary',\n title: 'string',\n price: 'number',\n tags: 'string',\n })\nconst Product = sm.use('Product')\n\nawait Product.add({ id: 'p_1', title: 'Widget', price: 9.99, tags: 'sale' })\nawait Product.where(['id', 'p_1']).to({ price: 7.99 }).updateOne()\nconst items = await Product.where('price', 10, '<').get()\nawait Product.where(['id', 'p_1']).deleteOne()\n```\n";
|
|
2
2
|
export declare function markdownToHtml(md: string): string;
|
|
3
3
|
export declare function getDocumentationHtml(): string;
|
package/dist/docs.js
CHANGED
|
@@ -65,11 +65,17 @@ connections = [
|
|
|
65
65
|
baseUrl: "https://api.example.com"
|
|
66
66
|
token: "abc123"
|
|
67
67
|
}
|
|
68
|
+
|
|
69
|
+
sqlite_local: {
|
|
70
|
+
type: sqlite
|
|
71
|
+
filename: "./data.db"
|
|
72
|
+
}
|
|
68
73
|
]
|
|
69
74
|
\`\`\`
|
|
70
75
|
|
|
71
76
|
Notes:
|
|
72
77
|
- \`type\` (or \`dbType\`) defaults to \`api\` if omitted.
|
|
78
|
+
- For SQLite, use \`filename\` to specify the path to the database file.
|
|
73
79
|
- Values can be unquoted or quoted; comments using \`#\` are ignored.
|
|
74
80
|
|
|
75
81
|
Parse and normalize:
|
|
@@ -190,6 +196,34 @@ const many = await User.where('email', '%@example.com', 'like').get()
|
|
|
190
196
|
|
|
191
197
|
---
|
|
192
198
|
|
|
199
|
+
## Fluent API Requests
|
|
200
|
+
|
|
201
|
+
For API connections, you can make fluent HTTP requests without defining a full schema.
|
|
202
|
+
|
|
203
|
+
\`\`\`ts
|
|
204
|
+
const conn = Mapper.connection({ type: 'api', url: 'https://api.example.com' });
|
|
205
|
+
|
|
206
|
+
// Simple POST
|
|
207
|
+
await conn.path("/users").post({ name: 'John Doe' });
|
|
208
|
+
|
|
209
|
+
// Chained paths and headers
|
|
210
|
+
const result = await conn.path("/v1")
|
|
211
|
+
.path("/users/123")
|
|
212
|
+
.header("Authorization", "Bearer my-token")
|
|
213
|
+
.headers({ "X-Custom": "value" })
|
|
214
|
+
.get();
|
|
215
|
+
|
|
216
|
+
// Support for multiple values for the same header
|
|
217
|
+
await conn.path("/notify")
|
|
218
|
+
.header("X-Tag", "news")
|
|
219
|
+
.header("X-Tag", "alerts")
|
|
220
|
+
.post({ body: "Hello" });
|
|
221
|
+
\`\`\`
|
|
222
|
+
|
|
223
|
+
Available methods: \`.get()\`, \`.post(data)\`, \`.put(data)\`, \`.patch(data)\`, \`.delete()\`.
|
|
224
|
+
|
|
225
|
+
---
|
|
226
|
+
|
|
193
227
|
## Schema Configuration Details
|
|
194
228
|
|
|
195
229
|
- \`primary\`: marks primary key; used for updates/deletes.
|
package/dist/fluent-mapper.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { SchemaManager } from './index.js';
|
|
2
|
+
import { TableMigrator } from './migrator.js';
|
|
2
3
|
export declare class FluentQueryBuilder {
|
|
3
4
|
private mapper;
|
|
4
5
|
private schemaName;
|
|
@@ -48,6 +49,22 @@ export declare class FluentSchemaCollectionBuilder {
|
|
|
48
49
|
[key: string]: any;
|
|
49
50
|
}>): FluentMapper;
|
|
50
51
|
}
|
|
52
|
+
export declare class FluentApiRequestBuilder {
|
|
53
|
+
private mapper;
|
|
54
|
+
private connectionName;
|
|
55
|
+
private _path;
|
|
56
|
+
private _headers;
|
|
57
|
+
constructor(mapper: any, connectionName: string, path?: string);
|
|
58
|
+
path(p: string): this;
|
|
59
|
+
header(key: string | Record<string, string | string[]>, value?: string | string[]): this;
|
|
60
|
+
headers(h: Record<string, string | string[]> | any[]): this;
|
|
61
|
+
get(): Promise<any>;
|
|
62
|
+
post(data?: any): Promise<any>;
|
|
63
|
+
put(data?: any): Promise<any>;
|
|
64
|
+
patch(data?: any): Promise<any>;
|
|
65
|
+
delete(): Promise<any>;
|
|
66
|
+
private execute;
|
|
67
|
+
}
|
|
51
68
|
export declare class FluentConnectionSelector {
|
|
52
69
|
private mapper;
|
|
53
70
|
private connectionName;
|
|
@@ -57,15 +74,23 @@ export declare class FluentConnectionSelector {
|
|
|
57
74
|
table(tableName: string): FluentQueryBuilder;
|
|
58
75
|
collection(collectionName: string): FluentQueryBuilder;
|
|
59
76
|
schemas(schemaName: string): FluentSchemaWrapper;
|
|
77
|
+
path(path: string): FluentApiRequestBuilder;
|
|
78
|
+
header(key: string | Record<string, string | string[]>, value?: string | string[]): FluentApiRequestBuilder;
|
|
79
|
+
headers(headers: Record<string, string> | any[]): FluentApiRequestBuilder;
|
|
80
|
+
get(): Promise<any>;
|
|
81
|
+
post(data?: any): Promise<any>;
|
|
82
|
+
put(data?: any): Promise<any>;
|
|
83
|
+
patch(data?: any): Promise<any>;
|
|
84
|
+
delete(): Promise<any>;
|
|
60
85
|
}
|
|
61
86
|
export declare class FluentMapper {
|
|
62
87
|
private mapper;
|
|
63
88
|
constructor(mapper: any);
|
|
64
89
|
query(schemaName: string): FluentQueryBuilder;
|
|
65
|
-
makeConnection(name: string, type: 'mysql' | 'sql' | 'firestore' | 'mongodb' | 'api', config: Record<string, any>): FluentConnectionBuilder;
|
|
90
|
+
makeConnection(name: string, type: 'mysql' | 'sql' | 'firestore' | 'mongodb' | 'api' | 'sqlite', config: Record<string, any>): FluentConnectionBuilder;
|
|
66
91
|
useConnection(connectionName: string): FluentConnectionSelector;
|
|
67
92
|
connection(connectionOrConfig: string | Record<string, any>): FluentConnectionSelector;
|
|
68
|
-
makeTempConnection(type: 'mysql' | 'sql' | 'firestore' | 'mongodb' | 'api', config: Record<string, any>): FluentConnectionBuilder;
|
|
93
|
+
makeTempConnection(type: 'mysql' | 'sql' | 'firestore' | 'mongodb' | 'api' | 'sqlite', config: Record<string, any>): FluentConnectionBuilder;
|
|
69
94
|
get(schemaName: string, filters?: Record<string, any>): Promise<Record<string, any>[]>;
|
|
70
95
|
getOne(schemaName: string, filters?: Record<string, any>): Promise<Record<string, any> | null>;
|
|
71
96
|
add(schemaName: string, data: Record<string, any>): Promise<any>;
|
|
@@ -75,12 +100,12 @@ export declare class FluentMapper {
|
|
|
75
100
|
export declare class StaticMapper {
|
|
76
101
|
private static instance;
|
|
77
102
|
private static getFluentMapper;
|
|
78
|
-
static makeConnection(name: string, type: 'mysql' | 'sql' | 'firestore' | 'mongodb' | 'api', config: Record<string, any>): FluentConnectionBuilder;
|
|
79
|
-
static makeTempConnection(type: 'mysql' | 'sql' | 'firestore' | 'mongodb' | 'api', config: Record<string, any>): FluentConnectionBuilder;
|
|
103
|
+
static makeConnection(name: string, type: 'mysql' | 'sql' | 'firestore' | 'mongodb' | 'api' | 'sqlite', config: Record<string, any>): FluentConnectionBuilder;
|
|
104
|
+
static makeTempConnection(type: 'mysql' | 'sql' | 'firestore' | 'mongodb' | 'api' | 'sqlite', config: Record<string, any>): FluentConnectionBuilder;
|
|
80
105
|
static query(schemaName: string): FluentQueryBuilder;
|
|
81
106
|
static connection(connectionOrConfig: string | Record<string, any>): FluentConnectionSelector;
|
|
82
107
|
static useConnection(connectionName: string): FluentConnectionSelector;
|
|
83
|
-
static schemas(name
|
|
108
|
+
static schemas(name?: string): FluentSchemaWrapper | SchemaManagerWrapper;
|
|
84
109
|
static get(schemaName: string, filters?: Record<string, any>): Promise<Record<string, any>[]>;
|
|
85
110
|
static getOne(schemaName: string, filters?: Record<string, any>): Promise<Record<string, any> | null>;
|
|
86
111
|
static add(schemaName: string, data: Record<string, any>): Promise<any>;
|
|
@@ -106,3 +131,8 @@ export declare class FluentSchemaWrapper {
|
|
|
106
131
|
offset(n: number): FluentQueryBuilder;
|
|
107
132
|
insert(data: Record<string, any>): Promise<any>;
|
|
108
133
|
}
|
|
134
|
+
export declare class SchemaManagerWrapper {
|
|
135
|
+
private manager;
|
|
136
|
+
constructor(manager: SchemaManager);
|
|
137
|
+
table(name: string): TableMigrator;
|
|
138
|
+
}
|
package/dist/fluent-mapper.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { createMapper } from './mapper.js';
|
|
2
|
+
import { TableMigrator } from './migrator.js';
|
|
2
3
|
export class FluentQueryBuilder {
|
|
3
4
|
constructor(mapper, schemaName) {
|
|
4
5
|
this.mapper = mapper;
|
|
@@ -109,6 +110,80 @@ export class FluentSchemaCollectionBuilder {
|
|
|
109
110
|
return new FluentMapper(this.mapper);
|
|
110
111
|
}
|
|
111
112
|
}
|
|
113
|
+
export class FluentApiRequestBuilder {
|
|
114
|
+
constructor(mapper, connectionName, path = '') {
|
|
115
|
+
this.mapper = mapper;
|
|
116
|
+
this.connectionName = connectionName;
|
|
117
|
+
this._path = '';
|
|
118
|
+
this._headers = {};
|
|
119
|
+
this._path = path;
|
|
120
|
+
}
|
|
121
|
+
path(p) {
|
|
122
|
+
if (this._path === '') {
|
|
123
|
+
this._path = p;
|
|
124
|
+
}
|
|
125
|
+
else {
|
|
126
|
+
if (!p.startsWith('/') && !this._path.endsWith('/')) {
|
|
127
|
+
this._path += '/';
|
|
128
|
+
}
|
|
129
|
+
this._path += p;
|
|
130
|
+
}
|
|
131
|
+
return this;
|
|
132
|
+
}
|
|
133
|
+
header(key, value) {
|
|
134
|
+
if (typeof key === 'object') {
|
|
135
|
+
Object.entries(key).forEach(([k, v]) => this.header(k, v));
|
|
136
|
+
}
|
|
137
|
+
else if (value !== undefined) {
|
|
138
|
+
if (this._headers[key]) {
|
|
139
|
+
const existing = this._headers[key];
|
|
140
|
+
if (Array.isArray(existing)) {
|
|
141
|
+
if (Array.isArray(value)) {
|
|
142
|
+
existing.push(...value);
|
|
143
|
+
}
|
|
144
|
+
else {
|
|
145
|
+
existing.push(value);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
else {
|
|
149
|
+
this._headers[key] = [existing, ...(Array.isArray(value) ? value : [value])];
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
else {
|
|
153
|
+
this._headers[key] = value;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
else {
|
|
157
|
+
// Check for "Key: Value" string
|
|
158
|
+
if (key.includes(':')) {
|
|
159
|
+
const [k, ...v] = key.split(':');
|
|
160
|
+
this.header(k.trim(), v.join(':').trim());
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
return this;
|
|
164
|
+
}
|
|
165
|
+
headers(h) {
|
|
166
|
+
if (Array.isArray(h)) {
|
|
167
|
+
h.forEach(item => this.header(item));
|
|
168
|
+
}
|
|
169
|
+
else {
|
|
170
|
+
this.header(h);
|
|
171
|
+
}
|
|
172
|
+
return this;
|
|
173
|
+
}
|
|
174
|
+
async get() { return this.execute('GET'); }
|
|
175
|
+
async post(data) { return this.execute('POST', data); }
|
|
176
|
+
async put(data) { return this.execute('PUT', data); }
|
|
177
|
+
async patch(data) { return this.execute('PATCH', data); }
|
|
178
|
+
async delete() { return this.execute('DELETE'); }
|
|
179
|
+
async execute(method, data) {
|
|
180
|
+
const adapter = this.mapper.getConnections().getAdapter(this.connectionName);
|
|
181
|
+
if (!adapter || typeof adapter.request !== 'function') {
|
|
182
|
+
throw new Error(`Connection "${this.connectionName}" does not support custom requests.`);
|
|
183
|
+
}
|
|
184
|
+
return adapter.request(method, this._path, data, this._headers);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
112
187
|
export class FluentConnectionSelector {
|
|
113
188
|
constructor(mapper, connectionName) {
|
|
114
189
|
this.mapper = mapper;
|
|
@@ -129,6 +204,31 @@ export class FluentConnectionSelector {
|
|
|
129
204
|
schemas(schemaName) {
|
|
130
205
|
return new FluentSchemaWrapper(this.mapper.getSchemaManager(), schemaName, this.connectionName);
|
|
131
206
|
}
|
|
207
|
+
// API Request methods
|
|
208
|
+
path(path) {
|
|
209
|
+
return new FluentApiRequestBuilder(this.mapper, this.connectionName, path);
|
|
210
|
+
}
|
|
211
|
+
header(key, value) {
|
|
212
|
+
return new FluentApiRequestBuilder(this.mapper, this.connectionName).header(key, value);
|
|
213
|
+
}
|
|
214
|
+
headers(headers) {
|
|
215
|
+
return new FluentApiRequestBuilder(this.mapper, this.connectionName).headers(headers);
|
|
216
|
+
}
|
|
217
|
+
get() {
|
|
218
|
+
return new FluentApiRequestBuilder(this.mapper, this.connectionName).get();
|
|
219
|
+
}
|
|
220
|
+
post(data) {
|
|
221
|
+
return new FluentApiRequestBuilder(this.mapper, this.connectionName).post(data);
|
|
222
|
+
}
|
|
223
|
+
put(data) {
|
|
224
|
+
return new FluentApiRequestBuilder(this.mapper, this.connectionName).put(data);
|
|
225
|
+
}
|
|
226
|
+
patch(data) {
|
|
227
|
+
return new FluentApiRequestBuilder(this.mapper, this.connectionName).patch(data);
|
|
228
|
+
}
|
|
229
|
+
delete() {
|
|
230
|
+
return new FluentApiRequestBuilder(this.mapper, this.connectionName).delete();
|
|
231
|
+
}
|
|
132
232
|
}
|
|
133
233
|
export class FluentMapper {
|
|
134
234
|
constructor(mapper) {
|
|
@@ -209,8 +309,10 @@ export class StaticMapper {
|
|
|
209
309
|
return StaticMapper.connection(connectionName);
|
|
210
310
|
}
|
|
211
311
|
static schemas(name) {
|
|
212
|
-
|
|
213
|
-
|
|
312
|
+
if (name) {
|
|
313
|
+
return new FluentSchemaWrapper(StaticMapper.getFluentMapper().mapper.getSchemaManager(), name);
|
|
314
|
+
}
|
|
315
|
+
return new SchemaManagerWrapper(StaticMapper.getFluentMapper().mapper.getSchemaManager());
|
|
214
316
|
}
|
|
215
317
|
// Direct static methods
|
|
216
318
|
static async get(schemaName, filters) {
|
|
@@ -321,3 +423,12 @@ export class FluentSchemaWrapper {
|
|
|
321
423
|
// It is NOT exported. I need to export it or duplicate logic.
|
|
322
424
|
// I'll export it from index.ts.
|
|
323
425
|
import { parseDescriptorStructure } from './index.js'; // fixed import at bottom
|
|
426
|
+
export class SchemaManagerWrapper {
|
|
427
|
+
constructor(manager) {
|
|
428
|
+
this.manager = manager;
|
|
429
|
+
}
|
|
430
|
+
table(name) {
|
|
431
|
+
// This allows Mapper.schemas().table('name') to return a migrator
|
|
432
|
+
return new TableMigrator(name);
|
|
433
|
+
}
|
|
434
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { DbAdapter, QueryOptions } from './orm/index.js';
|
|
2
2
|
export type ColumnType = 'string' | 'number' | 'boolean' | 'date' | 'int';
|
|
3
|
-
export type ConnectionType = 'mysql' | 'sql' | 'firestore' | 'mongodb' | 'api';
|
|
3
|
+
export type ConnectionType = 'mysql' | 'sql' | 'firestore' | 'mongodb' | 'api' | 'sqlite';
|
|
4
4
|
export interface Field {
|
|
5
5
|
name: string;
|
|
6
6
|
type: ColumnType;
|
|
@@ -135,3 +135,4 @@ export { MySQLAdapter, createMySQLAdapter, PostgreSQLAdapter, createPostgreSQLAd
|
|
|
135
135
|
export type { MySQLConfig, PostgreSQLConfig, MongoDBConfig, APIAdapterConfig, AdapterConfig } from './adapters/index.js';
|
|
136
136
|
export { MapperError, AdapterMissingError, UpdatePayloadMissingError, DocumentMissingIdError, ConnectionExistingError, ConnectionUnknownError, SchemaExistingError, SchemaMissingError, SchemaConfigurationError, } from './errors.js';
|
|
137
137
|
export { Connector, mapper } from './connector.js';
|
|
138
|
+
export { TableMigrator, ColumnBuilder } from './migrator.js';
|
package/dist/index.js
CHANGED
|
@@ -404,3 +404,4 @@ export { ConfigBasedMapper, ConfigLoader, createConfigMapper, getConfigMapper, c
|
|
|
404
404
|
export { MySQLAdapter, createMySQLAdapter, PostgreSQLAdapter, createPostgreSQLAdapter, MongoDBAdapter, createMongoDBAdapter, APIAdapter, createAPIAdapter, createAdapter, createAdapterFromUrl, autoAttachAdapter } from './adapters/index.js';
|
|
405
405
|
export { MapperError, AdapterMissingError, UpdatePayloadMissingError, DocumentMissingIdError, ConnectionExistingError, ConnectionUnknownError, SchemaExistingError, SchemaMissingError, SchemaConfigurationError, } from './errors.js';
|
|
406
406
|
export { Connector, mapper } from './connector.js';
|
|
407
|
+
export { TableMigrator, ColumnBuilder } from './migrator.js';
|
package/dist/mapper.d.ts
CHANGED
|
@@ -12,7 +12,7 @@ export declare class Mapper {
|
|
|
12
12
|
private applyConfig;
|
|
13
13
|
private applyDefaultConfig;
|
|
14
14
|
private createSchema;
|
|
15
|
-
connect(name: string, type: 'mysql' | 'sql' | 'firestore' | 'mongodb' | 'api', config: Record<string, any>): this;
|
|
15
|
+
connect(name: string, type: 'mysql' | 'sql' | 'firestore' | 'mongodb' | 'api' | 'sqlite', config: Record<string, any>): this;
|
|
16
16
|
schema(name: string): ReturnType<SchemaManager['create']>;
|
|
17
17
|
use(schemaName: string): ReturnType<SchemaManager['use']>;
|
|
18
18
|
get(schemaName: string, filters?: Record<string, any>): Promise<Record<string, any>[]>;
|
package/dist/mapper.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Connections, SchemaManager } from './index.js';
|
|
2
|
+
import { autoAttachAdapter } from './adapters/index.js';
|
|
2
3
|
export class Mapper {
|
|
3
4
|
constructor() {
|
|
4
5
|
this.configured = false;
|
|
@@ -57,6 +58,8 @@ export class Mapper {
|
|
|
57
58
|
return 'mongodb';
|
|
58
59
|
if (url.includes('firestore'))
|
|
59
60
|
return 'firestore';
|
|
61
|
+
if (url.includes('sqlite') || url.endsWith('.db') || url.endsWith('.sqlite'))
|
|
62
|
+
return 'sqlite';
|
|
60
63
|
return 'api';
|
|
61
64
|
}
|
|
62
65
|
applyConfig(config) {
|
|
@@ -86,6 +89,7 @@ export class Mapper {
|
|
|
86
89
|
// Simplified API methods
|
|
87
90
|
connect(name, type, config) {
|
|
88
91
|
this.connections.create(name, type).key(config);
|
|
92
|
+
autoAttachAdapter(this.connections, name, type, config);
|
|
89
93
|
return this;
|
|
90
94
|
}
|
|
91
95
|
schema(name) {
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export type ColumnType = 'string' | 'number' | 'boolean' | 'date' | 'int';
|
|
2
|
+
export declare class ColumnBuilder {
|
|
3
|
+
private name;
|
|
4
|
+
private def;
|
|
5
|
+
constructor(name: string);
|
|
6
|
+
type(t: ColumnType | string): this;
|
|
7
|
+
isPrimary(): this;
|
|
8
|
+
isUnique(): this;
|
|
9
|
+
notNull(): this;
|
|
10
|
+
autoIncrement(): this;
|
|
11
|
+
default(val: any): this;
|
|
12
|
+
values(vals: any[]): this;
|
|
13
|
+
foreignKey(table: string, column: string): this;
|
|
14
|
+
exec(): Promise<void>;
|
|
15
|
+
getDefinition(): any;
|
|
16
|
+
}
|
|
17
|
+
export declare class TableMigrator {
|
|
18
|
+
private name;
|
|
19
|
+
private columns;
|
|
20
|
+
constructor(name: string);
|
|
21
|
+
addColumn(name: string): ColumnBuilder;
|
|
22
|
+
}
|
package/dist/migrator.js
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
export class ColumnBuilder {
|
|
2
|
+
constructor(name) {
|
|
3
|
+
this.name = name;
|
|
4
|
+
this.def = {
|
|
5
|
+
type: 'string',
|
|
6
|
+
isPrimary: false,
|
|
7
|
+
isUnique: false,
|
|
8
|
+
notNull: false,
|
|
9
|
+
autoIncrement: false,
|
|
10
|
+
defaultValue: undefined,
|
|
11
|
+
enumValues: [],
|
|
12
|
+
foreignKey: null
|
|
13
|
+
};
|
|
14
|
+
this.def.name = name;
|
|
15
|
+
}
|
|
16
|
+
type(t) {
|
|
17
|
+
this.def.type = t;
|
|
18
|
+
return this;
|
|
19
|
+
}
|
|
20
|
+
isPrimary() {
|
|
21
|
+
this.def.isPrimary = true;
|
|
22
|
+
return this;
|
|
23
|
+
}
|
|
24
|
+
isUnique() {
|
|
25
|
+
this.def.isUnique = true;
|
|
26
|
+
return this;
|
|
27
|
+
}
|
|
28
|
+
notNull() {
|
|
29
|
+
this.def.notNull = true;
|
|
30
|
+
return this;
|
|
31
|
+
}
|
|
32
|
+
autoIncrement() {
|
|
33
|
+
this.def.autoIncrement = true;
|
|
34
|
+
return this;
|
|
35
|
+
}
|
|
36
|
+
default(val) {
|
|
37
|
+
this.def.defaultValue = val;
|
|
38
|
+
return this;
|
|
39
|
+
}
|
|
40
|
+
values(vals) {
|
|
41
|
+
this.def.enumValues = vals;
|
|
42
|
+
return this;
|
|
43
|
+
}
|
|
44
|
+
foreignKey(table, column) {
|
|
45
|
+
this.def.foreignKey = { table, column };
|
|
46
|
+
return this;
|
|
47
|
+
}
|
|
48
|
+
async exec() {
|
|
49
|
+
return Promise.resolve();
|
|
50
|
+
}
|
|
51
|
+
getDefinition() {
|
|
52
|
+
return this.def;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
export class TableMigrator {
|
|
56
|
+
constructor(name) {
|
|
57
|
+
this.name = name;
|
|
58
|
+
this.columns = [];
|
|
59
|
+
}
|
|
60
|
+
addColumn(name) {
|
|
61
|
+
const col = new ColumnBuilder(name);
|
|
62
|
+
this.columns.push(col);
|
|
63
|
+
return col;
|
|
64
|
+
}
|
|
65
|
+
}
|
package/package.json
CHANGED
|
@@ -1,17 +1,23 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@neupgroup/mapper",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"description": "Neup.Mapper core library for schema and mapping utilities",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"module": "dist/index.js",
|
|
8
8
|
"types": "dist/index.d.ts",
|
|
9
|
+
"bin": {
|
|
10
|
+
"create-migration": "dist/cli/create-migration.js",
|
|
11
|
+
"create-connection": "dist/cli/create-connection.js"
|
|
12
|
+
},
|
|
9
13
|
"files": [
|
|
10
14
|
"dist"
|
|
11
15
|
],
|
|
12
16
|
"sideEffects": false,
|
|
13
17
|
"scripts": {
|
|
14
|
-
"build": "tsc -p ./tsconfig.json"
|
|
18
|
+
"build": "tsc -p ./tsconfig.json",
|
|
19
|
+
"create-migration": "npx tsx src/cli/create-migration.ts",
|
|
20
|
+
"create-connection": "npx tsx src/cli/create-connection.ts"
|
|
15
21
|
},
|
|
16
22
|
"publishConfig": {
|
|
17
23
|
"access": "public"
|