@mostajs/orm 1.2.0 → 1.3.1
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 +52 -0
- package/dist/bridge/jar-upload.d.ts +47 -0
- package/dist/bridge/jar-upload.js +139 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -490,6 +490,58 @@ DB_SCHEMA_STRATEGY=update # in .env
|
|
|
490
490
|
| `DB_CACHE_ENABLED` | `false` | Query result cache |
|
|
491
491
|
| `DB_CACHE_TTL` | `300` | Cache TTL in seconds |
|
|
492
492
|
|
|
493
|
+
| `MOSTA_BRIDGE_AUTOSTART` | `true` | JDBC bridge auto-start: `true`, `false`, `detect` |
|
|
494
|
+
| `MOSTA_BRIDGE_PORT_BASE` | `8765` | First bridge HTTP port |
|
|
495
|
+
| `MOSTA_BRIDGE_PORT_INCREMENT` | `true` | Auto-increment ports for multiple bridges |
|
|
496
|
+
| `MOSTA_JAR_DIR` | auto-detect | Directory for JDBC JAR files |
|
|
497
|
+
| `MOSTA_BRIDGE_JAVA` | auto-detect | Path to MostaJdbcBridge.java |
|
|
498
|
+
| `MOSTA_BRIDGE_MAX_RETRIES` | `3` | Max bridge start attempts before giving up |
|
|
499
|
+
| `MOSTA_BRIDGE_TIMEOUT` | `15000` | Bridge health check timeout (ms) |
|
|
500
|
+
|
|
501
|
+
---
|
|
502
|
+
|
|
503
|
+
## JDBC Bridge & JAR Upload
|
|
504
|
+
|
|
505
|
+
MostaORM includes a universal JDBC bridge for databases without npm drivers (HyperSQL, Oracle, DB2, SAP HANA, Sybase). The bridge is a Java HTTP server (`MostaJdbcBridge.java`) that translates HTTP requests into JDBC calls.
|
|
506
|
+
|
|
507
|
+
### JAR Management API
|
|
508
|
+
|
|
509
|
+
MostaORM exports functions for managing JDBC driver JARs:
|
|
510
|
+
|
|
511
|
+
```typescript
|
|
512
|
+
import {
|
|
513
|
+
saveJarFile, // Save an uploaded JAR to jar_files/
|
|
514
|
+
deleteJarFile, // Delete a JAR from jar_files/
|
|
515
|
+
listJarFiles, // List all JARs with dialect detection
|
|
516
|
+
getJdbcDialectStatus, // Status of each JDBC dialect (JAR present?)
|
|
517
|
+
detectDialectFromJar, // Detect dialect from JAR filename
|
|
518
|
+
} from '@mostajs/orm'
|
|
519
|
+
```
|
|
520
|
+
|
|
521
|
+
### Next.js Route (via @mostajs/setup)
|
|
522
|
+
|
|
523
|
+
```typescript
|
|
524
|
+
// src/app/api/setup/upload-jar/route.ts
|
|
525
|
+
import { createUploadJarHandlers } from '@mostajs/setup/api/upload-jar'
|
|
526
|
+
|
|
527
|
+
const { GET, POST, DELETE } = createUploadJarHandlers()
|
|
528
|
+
export { GET, POST, DELETE }
|
|
529
|
+
```
|
|
530
|
+
|
|
531
|
+
- **GET** — list JARs and JDBC dialect status
|
|
532
|
+
- **POST** — upload a `.jar` file (multipart/form-data, field `jar`)
|
|
533
|
+
- **DELETE** — remove a JAR (`{ "fileName": "hsqldb-2.7.2.jar" }`)
|
|
534
|
+
|
|
535
|
+
### BridgeManager (multi-bridge)
|
|
536
|
+
|
|
537
|
+
```typescript
|
|
538
|
+
import { BridgeManager } from '@mostajs/orm'
|
|
539
|
+
|
|
540
|
+
const manager = BridgeManager.getInstance()
|
|
541
|
+
// Bridges are managed automatically by the dialect's connect()
|
|
542
|
+
// Multiple bridges can run simultaneously on incrementing ports
|
|
543
|
+
```
|
|
544
|
+
|
|
493
545
|
---
|
|
494
546
|
|
|
495
547
|
## Complete Example — Blog API
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { type JdbcDriverInfo } from './jdbc-registry.js';
|
|
2
|
+
import type { DialectType } from '../core/types.js';
|
|
3
|
+
export interface JarUploadResult {
|
|
4
|
+
ok: boolean;
|
|
5
|
+
fileName?: string;
|
|
6
|
+
dialect?: string;
|
|
7
|
+
jarDir?: string;
|
|
8
|
+
replaced?: string;
|
|
9
|
+
error?: string;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Detect which dialect a JAR file belongs to based on its filename.
|
|
13
|
+
*/
|
|
14
|
+
export declare function detectDialectFromJar(fileName: string): {
|
|
15
|
+
dialect: DialectType;
|
|
16
|
+
info: JdbcDriverInfo;
|
|
17
|
+
} | null;
|
|
18
|
+
/**
|
|
19
|
+
* List all JAR files currently in the jar_files directory.
|
|
20
|
+
*/
|
|
21
|
+
export declare function listJarFiles(): {
|
|
22
|
+
fileName: string;
|
|
23
|
+
dialect: string | null;
|
|
24
|
+
label: string | null;
|
|
25
|
+
}[];
|
|
26
|
+
/**
|
|
27
|
+
* Save an uploaded JAR file to the jar_files directory.
|
|
28
|
+
* If a JAR for the same dialect already exists, it is replaced.
|
|
29
|
+
*
|
|
30
|
+
* @param fileName - Original filename (e.g. "hsqldb-2.7.2.jar")
|
|
31
|
+
* @param data - File content as Buffer or Uint8Array
|
|
32
|
+
*/
|
|
33
|
+
export declare function saveJarFile(fileName: string, data: Buffer | Uint8Array): JarUploadResult;
|
|
34
|
+
/**
|
|
35
|
+
* Delete a JAR file from the jar_files directory.
|
|
36
|
+
*/
|
|
37
|
+
export declare function deleteJarFile(fileName: string): JarUploadResult;
|
|
38
|
+
/**
|
|
39
|
+
* Get the list of JDBC-eligible dialects with their JAR status.
|
|
40
|
+
*/
|
|
41
|
+
export declare function getJdbcDialectStatus(): {
|
|
42
|
+
dialect: DialectType;
|
|
43
|
+
label: string;
|
|
44
|
+
jarPrefix: string;
|
|
45
|
+
hasJar: boolean;
|
|
46
|
+
jarFile: string | null;
|
|
47
|
+
}[];
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
// JAR Upload Handler — receives a JAR file and saves it to jar_files/
|
|
2
|
+
// Used by mosta-setup ReconfigPanel for JDBC dialect configuration
|
|
3
|
+
// Author: Dr Hamid MADANI drmdh@msn.com
|
|
4
|
+
import { existsSync, mkdirSync, writeFileSync, readdirSync, unlinkSync } from 'fs';
|
|
5
|
+
import { join, dirname } from 'path';
|
|
6
|
+
import { fileURLToPath } from 'url';
|
|
7
|
+
import { JDBC_REGISTRY } from './jdbc-registry.js';
|
|
8
|
+
// Resolve jar_files directory
|
|
9
|
+
const __filename_resolved = typeof __filename !== 'undefined'
|
|
10
|
+
? __filename
|
|
11
|
+
: fileURLToPath(import.meta.url);
|
|
12
|
+
const __dirname_resolved = dirname(__filename_resolved);
|
|
13
|
+
function getJarDir() {
|
|
14
|
+
if (process.env.MOSTA_JAR_DIR)
|
|
15
|
+
return process.env.MOSTA_JAR_DIR;
|
|
16
|
+
const candidates = [
|
|
17
|
+
join(process.cwd(), 'jar_files'),
|
|
18
|
+
join(__dirname_resolved, '..', '..', 'jar_files'),
|
|
19
|
+
join(__dirname_resolved, '..', '..', '..', 'jar_files'),
|
|
20
|
+
];
|
|
21
|
+
for (const dir of candidates) {
|
|
22
|
+
if (existsSync(dir))
|
|
23
|
+
return dir;
|
|
24
|
+
}
|
|
25
|
+
return candidates[0]; // Default: cwd/jar_files
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Detect which dialect a JAR file belongs to based on its filename.
|
|
29
|
+
*/
|
|
30
|
+
export function detectDialectFromJar(fileName) {
|
|
31
|
+
const lowerName = fileName.toLowerCase();
|
|
32
|
+
for (const [dialect, info] of Object.entries(JDBC_REGISTRY)) {
|
|
33
|
+
if (info && lowerName.startsWith(info.jarPrefix)) {
|
|
34
|
+
return { dialect: dialect, info };
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* List all JAR files currently in the jar_files directory.
|
|
41
|
+
*/
|
|
42
|
+
export function listJarFiles() {
|
|
43
|
+
const dir = getJarDir();
|
|
44
|
+
if (!existsSync(dir))
|
|
45
|
+
return [];
|
|
46
|
+
return readdirSync(dir)
|
|
47
|
+
.filter(f => f.endsWith('.jar'))
|
|
48
|
+
.map(fileName => {
|
|
49
|
+
const detected = detectDialectFromJar(fileName);
|
|
50
|
+
return {
|
|
51
|
+
fileName,
|
|
52
|
+
dialect: detected?.dialect ?? null,
|
|
53
|
+
label: detected?.info.label ?? null,
|
|
54
|
+
};
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Save an uploaded JAR file to the jar_files directory.
|
|
59
|
+
* If a JAR for the same dialect already exists, it is replaced.
|
|
60
|
+
*
|
|
61
|
+
* @param fileName - Original filename (e.g. "hsqldb-2.7.2.jar")
|
|
62
|
+
* @param data - File content as Buffer or Uint8Array
|
|
63
|
+
*/
|
|
64
|
+
export function saveJarFile(fileName, data) {
|
|
65
|
+
// Validate filename
|
|
66
|
+
if (!fileName.endsWith('.jar')) {
|
|
67
|
+
return { ok: false, error: 'Le fichier doit etre un .jar' };
|
|
68
|
+
}
|
|
69
|
+
// Sanitize filename
|
|
70
|
+
const safeName = fileName.replace(/[^a-zA-Z0-9._-]/g, '');
|
|
71
|
+
if (!safeName || safeName.length < 5) {
|
|
72
|
+
return { ok: false, error: 'Nom de fichier invalide' };
|
|
73
|
+
}
|
|
74
|
+
// Detect dialect
|
|
75
|
+
const detected = detectDialectFromJar(safeName);
|
|
76
|
+
// Ensure jar_files directory exists
|
|
77
|
+
const dir = getJarDir();
|
|
78
|
+
if (!existsSync(dir)) {
|
|
79
|
+
mkdirSync(dir, { recursive: true });
|
|
80
|
+
}
|
|
81
|
+
// Remove existing JAR for the same dialect (if any)
|
|
82
|
+
let replaced;
|
|
83
|
+
if (detected) {
|
|
84
|
+
try {
|
|
85
|
+
const existing = readdirSync(dir)
|
|
86
|
+
.filter(f => f.startsWith(detected.info.jarPrefix) && f.endsWith('.jar'));
|
|
87
|
+
for (const old of existing) {
|
|
88
|
+
if (old !== safeName) {
|
|
89
|
+
unlinkSync(join(dir, old));
|
|
90
|
+
replaced = old;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
// Non-critical
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
// Write file
|
|
99
|
+
writeFileSync(join(dir, safeName), data);
|
|
100
|
+
return {
|
|
101
|
+
ok: true,
|
|
102
|
+
fileName: safeName,
|
|
103
|
+
dialect: detected?.dialect ?? undefined,
|
|
104
|
+
jarDir: dir,
|
|
105
|
+
replaced,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Delete a JAR file from the jar_files directory.
|
|
110
|
+
*/
|
|
111
|
+
export function deleteJarFile(fileName) {
|
|
112
|
+
const safeName = fileName.replace(/[^a-zA-Z0-9._-]/g, '');
|
|
113
|
+
const dir = getJarDir();
|
|
114
|
+
const filePath = join(dir, safeName);
|
|
115
|
+
if (!existsSync(filePath)) {
|
|
116
|
+
return { ok: false, error: `Fichier ${safeName} introuvable` };
|
|
117
|
+
}
|
|
118
|
+
unlinkSync(filePath);
|
|
119
|
+
return { ok: true, fileName: safeName };
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Get the list of JDBC-eligible dialects with their JAR status.
|
|
123
|
+
*/
|
|
124
|
+
export function getJdbcDialectStatus() {
|
|
125
|
+
const dir = getJarDir();
|
|
126
|
+
const files = existsSync(dir) ? readdirSync(dir).filter(f => f.endsWith('.jar')) : [];
|
|
127
|
+
return Object.entries(JDBC_REGISTRY).map(([dialect, info]) => {
|
|
128
|
+
if (!info)
|
|
129
|
+
return null;
|
|
130
|
+
const match = files.find(f => f.startsWith(info.jarPrefix));
|
|
131
|
+
return {
|
|
132
|
+
dialect: dialect,
|
|
133
|
+
label: info.label,
|
|
134
|
+
jarPrefix: info.jarPrefix,
|
|
135
|
+
hasJar: !!match,
|
|
136
|
+
jarFile: match ?? null,
|
|
137
|
+
};
|
|
138
|
+
}).filter(Boolean);
|
|
139
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -8,7 +8,9 @@ export { normalizeDoc, normalizeDocs } from './core/normalizer.js';
|
|
|
8
8
|
export { JdbcNormalizer, parseUri } from './bridge/JdbcNormalizer.js';
|
|
9
9
|
export { JDBC_REGISTRY, hasJdbcDriver, getJdbcDriverInfo } from './bridge/jdbc-registry.js';
|
|
10
10
|
export { BridgeManager } from './bridge/BridgeManager.js';
|
|
11
|
+
export { saveJarFile, deleteJarFile, listJarFiles, detectDialectFromJar, getJdbcDialectStatus, } from './bridge/jar-upload.js';
|
|
11
12
|
export type { BridgeInstance } from './bridge/BridgeManager.js';
|
|
13
|
+
export type { JarUploadResult } from './bridge/jar-upload.js';
|
|
12
14
|
export type { JdbcDriverInfo } from './bridge/jdbc-registry.js';
|
|
13
15
|
export type { JdbcBridgeConfig } from './bridge/JdbcNormalizer.js';
|
|
14
16
|
export { MostaORMError, EntityNotFoundError, ConnectionError, ValidationError, DialectNotFoundError, } from './core/errors.js';
|
package/dist/index.js
CHANGED
|
@@ -26,6 +26,7 @@ export { normalizeDoc, normalizeDocs } from './core/normalizer.js';
|
|
|
26
26
|
export { JdbcNormalizer, parseUri } from './bridge/JdbcNormalizer.js';
|
|
27
27
|
export { JDBC_REGISTRY, hasJdbcDriver, getJdbcDriverInfo } from './bridge/jdbc-registry.js';
|
|
28
28
|
export { BridgeManager } from './bridge/BridgeManager.js';
|
|
29
|
+
export { saveJarFile, deleteJarFile, listJarFiles, detectDialectFromJar, getJdbcDialectStatus, } from './bridge/jar-upload.js';
|
|
29
30
|
// ============================================================
|
|
30
31
|
// Errors
|
|
31
32
|
// ============================================================
|
package/package.json
CHANGED