ee-core 5.0.0-beta.8 → 5.0.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 +214 -40
- package/dist/cjs/jobs/child/jobProcess.js +1 -1
- package/dist/cjs/storage/index.d.ts +15 -0
- package/dist/cjs/storage/index.d.ts.map +1 -1
- package/dist/cjs/storage/index.js +15 -0
- package/dist/cjs/storage/index.js.map +1 -1
- package/dist/cjs/storage/sqliteStorage.d.ts +18 -18
- package/dist/cjs/storage/sqliteStorage.d.ts.map +1 -1
- package/dist/cjs/storage/sqliteStorage.js +63 -20
- package/dist/cjs/storage/sqliteStorage.js.map +1 -1
- package/dist/esm/jobs/child/jobProcess.js +1 -1
- package/dist/esm/storage/index.d.ts +15 -0
- package/dist/esm/storage/index.d.ts.map +1 -1
- package/dist/esm/storage/index.js +15 -0
- package/dist/esm/storage/index.js.map +1 -1
- package/dist/esm/storage/sqliteStorage.d.ts +18 -18
- package/dist/esm/storage/sqliteStorage.d.ts.map +1 -1
- package/dist/esm/storage/sqliteStorage.js +30 -20
- package/dist/esm/storage/sqliteStorage.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@ Enterprise-grade Electron application framework core — TypeScript edition with
|
|
|
4
4
|
|
|
5
5
|
## Overview
|
|
6
6
|
|
|
7
|
-
`ee-core`
|
|
7
|
+
`ee-core` is the runtime foundation for [electron-egg](https://github.com/wallace5303/electron-egg) applications. It handles the complete lifecycle from boot to shutdown: configuration loading, controller registration, IPC communication, window management, logging, storage, background jobs, and cross-process coordination.
|
|
8
8
|
|
|
9
9
|
The package outputs dual module formats (CJS + ESM) via conditional exports in `package.json`, making it compatible with both `require()` and `import` environments.
|
|
10
10
|
|
|
@@ -19,13 +19,13 @@ npm install ee-core
|
|
|
19
19
|
ee-core exposes fine-grained subpath modules. Each can be imported individually:
|
|
20
20
|
|
|
21
21
|
```ts
|
|
22
|
-
// Main entry (boot, app, config, etc.)
|
|
22
|
+
// Main entry (boot, app, config, events, etc.)
|
|
23
23
|
import { ElectronEgg } from 'ee-core';
|
|
24
24
|
|
|
25
25
|
// Specific submodules
|
|
26
26
|
import { ControllerLoader } from 'ee-core/controller';
|
|
27
27
|
import { Cross, cross } from 'ee-core/cross';
|
|
28
|
-
import { extend } from 'ee-core/utils';
|
|
28
|
+
import { extend, is } from 'ee-core/utils';
|
|
29
29
|
import { SqliteStorage } from 'ee-core/storage';
|
|
30
30
|
import { ChildJob, ChildPoolJob, LoadBalancer } from 'ee-core/jobs';
|
|
31
31
|
import type { Config, DevConfig } from 'ee-core/types';
|
|
@@ -35,51 +35,88 @@ Available subpaths:
|
|
|
35
35
|
|
|
36
36
|
| Subpath | Description |
|
|
37
37
|
|---|---|
|
|
38
|
-
| `ee-core` | Main entry — boot, application, config, events |
|
|
39
|
-
| `ee-core/app` | Application lifecycle
|
|
38
|
+
| `ee-core` | Main entry — boot, application, config, events, all modules |
|
|
39
|
+
| `ee-core/app` | Application lifecycle, event bus, directory init |
|
|
40
40
|
| `ee-core/config` | Configuration loading and merging |
|
|
41
|
+
| `ee-core/const` | IPC channel and event constants |
|
|
41
42
|
| `ee-core/controller` | Controller loader and registry |
|
|
42
|
-
| `ee-core/core` | FileLoader
|
|
43
|
-
| `ee-core/cross` |
|
|
43
|
+
| `ee-core/core` | FileLoader, Timing, core utilities |
|
|
44
|
+
| `ee-core/cross` | External process management (Go/Python backends) |
|
|
44
45
|
| `ee-core/electron` | Electron app/window management |
|
|
45
|
-
| `ee-core/exception` |
|
|
46
|
-
| `ee-core/html` | HTML
|
|
46
|
+
| `ee-core/exception` | Global exception handling (uncaught/unhandled) |
|
|
47
|
+
| `ee-core/html` | Static HTML page path utility (failure pages) |
|
|
47
48
|
| `ee-core/jobs` | Background jobs (ChildJob, ChildPoolJob, LoadBalancer) |
|
|
48
49
|
| `ee-core/loader` | File/module loading utilities |
|
|
49
|
-
| `ee-core/log` | Pino
|
|
50
|
-
| `ee-core/message` | IPC message routing |
|
|
51
|
-
| `ee-core/ps` | Process
|
|
52
|
-
| `ee-core/socket` | Socket.IO, HTTP, IPC servers |
|
|
50
|
+
| `ee-core/log` | Pino logging (pino-roll + pino-pretty) |
|
|
51
|
+
| `ee-core/message` | Child process IPC message routing |
|
|
52
|
+
| `ee-core/ps` | Process state, environment detection, path utilities |
|
|
53
|
+
| `ee-core/socket` | Socket.IO, HTTP (Koa), IPC servers |
|
|
53
54
|
| `ee-core/storage` | SQLite storage (better-sqlite3) |
|
|
54
55
|
| `ee-core/types` | TypeScript type definitions |
|
|
55
|
-
| `ee-core/utils` | extend, is, json, helper, pargv, port |
|
|
56
|
+
| `ee-core/utils` | extend, is, json, wrap, helper, pargv, port, ip |
|
|
56
57
|
|
|
57
58
|
## Architecture
|
|
58
59
|
|
|
59
60
|
### Lifecycle
|
|
60
61
|
|
|
61
|
-
|
|
62
|
+
The `ElectronEgg` startup sequence is strictly ordered:
|
|
63
|
+
|
|
64
|
+
```
|
|
65
|
+
init() → loadException → loadConfig → loadDir → loadLog
|
|
66
|
+
run() → loadController → loadSocket → emitLifecycle(Ready) → loadElectron
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Exception handling must register first to catch subsequent errors; config is the foundation for all modules; directories must exist before writing logs; controllers precede communication services; Ready fires after core modules are loaded; Electron starts last.
|
|
70
|
+
|
|
71
|
+
### Lifecycle Events
|
|
72
|
+
|
|
73
|
+
Five framework milestones managed by `EventBus`:
|
|
74
|
+
|
|
75
|
+
| Event | When |
|
|
76
|
+
|---|---|
|
|
77
|
+
| `Ready` | Core modules loaded, before Electron |
|
|
78
|
+
| `ElectronAppReady` | `app.whenReady()` completed |
|
|
79
|
+
| `WindowReady` | BrowserWindow created |
|
|
80
|
+
| `BeforeClose` | Window close triggered |
|
|
81
|
+
| `Preload` | Preload scripts executing |
|
|
82
|
+
|
|
83
|
+
Register handlers via `app.register(eventName, handler)` or custom events via `eventBus.on()/emit()`.
|
|
62
84
|
|
|
63
85
|
### Controller Loading Pipeline
|
|
64
86
|
|
|
65
|
-
- **Build time** (ee-bin): `
|
|
66
|
-
- **
|
|
87
|
+
- **Build time** (ee-bin): `bundleRegistryPlugin` scans `electron/controller/`, computes property paths (`defaultCamelize` with `caseStyle: 'lower'`), generates `app:controller-registry` virtual module setting `global.__EE_CONTROLLER_REGISTRY__` with lazy getters (`get module() { return require(...) }`).
|
|
88
|
+
- **Bundle entry** (`app:bundle-entry`): loads config registry → controller registry → real `main.js`.
|
|
89
|
+
- **Runtime** (ee-core): If `globalThis.__EE_CONTROLLER_REGISTRY__` exists (bundle mode), `FileLoader.parseFromRegistry()` reads from it. Otherwise, falls back to filesystem scanning via `scanDirSync()`.
|
|
90
|
+
|
|
91
|
+
### Configuration Loading Pipeline
|
|
92
|
+
|
|
93
|
+
Same dual-mode pattern as controllers:
|
|
94
|
+
|
|
95
|
+
- **Build time** (ee-bin): `bundleRegistryPlugin` scans `electron/config/`, generates `app:config-registry` virtual module.
|
|
96
|
+
- **Runtime** (ee-core): If `globalThis.__EE_CONFIG_REGISTRY__` exists, `ConfigLoader._loadConfig()` loads by filename. Otherwise, reads from filesystem using `loadFile()`.
|
|
67
97
|
|
|
68
|
-
|
|
98
|
+
Config layering (dev mode only): `config.default.js` → `config.local.js` / `config.prod.js`, merged by `ConfigLoader`.
|
|
69
99
|
|
|
70
|
-
|
|
71
|
-
- Socket.IO (bidirectional real-time)
|
|
72
|
-
- HTTP server (RESTful)
|
|
73
|
-
- Electron IPC (native)
|
|
100
|
+
### Communication Services
|
|
74
101
|
|
|
75
|
-
|
|
102
|
+
Three channels created in order during `loadSocket()`:
|
|
103
|
+
|
|
104
|
+
| Server | Use Case | Implementation |
|
|
105
|
+
|---|---|---|
|
|
106
|
+
| `SocketServer` | Third-party process communication (Go/Python) | Socket.IO |
|
|
107
|
+
| `HttpServer` | RESTful API for external HTTP clients | Koa + middleware |
|
|
108
|
+
| `IpcServer` | Main ↔ renderer process | `ipcMain.handle/on` + `ipcRenderer.invoke/send` |
|
|
109
|
+
|
|
110
|
+
All share `resolveControllerFn()` routing — channel pattern: `controller/{name}/{method}`.
|
|
76
111
|
|
|
77
112
|
### Window Loading Strategy
|
|
78
113
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
114
|
+
`loadServer()` selects strategy based on config and environment:
|
|
115
|
+
|
|
116
|
+
1. **Remote mode** (`remote.enable=true`) → Load remote URL
|
|
117
|
+
2. **Dev mode** → Poll frontend dev server (`waitForUrl`), show loading page first
|
|
118
|
+
3. **Prod + cross takeover** → Wait for cross-process service to be ready
|
|
119
|
+
4. **Prod (default)** → Load local HTML file
|
|
83
120
|
|
|
84
121
|
## Key APIs
|
|
85
122
|
|
|
@@ -89,7 +126,10 @@ Channels follow pattern `controller/{name}/{method}`.
|
|
|
89
126
|
import { ElectronEgg } from 'ee-core';
|
|
90
127
|
|
|
91
128
|
// Start the framework
|
|
92
|
-
const
|
|
129
|
+
const boot = new ElectronEgg();
|
|
130
|
+
|
|
131
|
+
// Async startup (ESM)
|
|
132
|
+
await boot.runAsync();
|
|
93
133
|
```
|
|
94
134
|
|
|
95
135
|
### Configuration
|
|
@@ -98,32 +138,83 @@ const app = new ElectronEgg();
|
|
|
98
138
|
import { getConfig, setConfig } from 'ee-core/config';
|
|
99
139
|
|
|
100
140
|
const config = getConfig();
|
|
141
|
+
|
|
142
|
+
// Child processes can receive config without filesystem access
|
|
143
|
+
setConfig(config);
|
|
101
144
|
```
|
|
102
145
|
|
|
103
146
|
### Controller
|
|
104
147
|
|
|
105
148
|
```ts
|
|
106
|
-
import { getController } from 'ee-core/controller';
|
|
149
|
+
import { getController, getControllers } from 'ee-core/controller';
|
|
107
150
|
|
|
151
|
+
// Get specific controller
|
|
108
152
|
const userController = getController('user');
|
|
153
|
+
|
|
154
|
+
// Get all loaded controllers
|
|
155
|
+
const all = getControllers();
|
|
109
156
|
```
|
|
110
157
|
|
|
111
158
|
### Cross-Process Communication
|
|
112
159
|
|
|
113
160
|
```ts
|
|
114
161
|
import { cross } from 'ee-core/cross';
|
|
162
|
+
import type { CrossTargetConfig } from 'ee-core';
|
|
163
|
+
|
|
164
|
+
// Run Go backend with custom config
|
|
165
|
+
const opt: CrossTargetConfig = {
|
|
166
|
+
name: 'goapp',
|
|
167
|
+
cmd: path.join(getExtraResourcesDir(), 'goapp'),
|
|
168
|
+
directory: getExtraResourcesDir(),
|
|
169
|
+
args: ['--port=7073'],
|
|
170
|
+
appExit: true,
|
|
171
|
+
};
|
|
172
|
+
const entity = await cross.run('go', opt);
|
|
173
|
+
|
|
174
|
+
// Get service URL
|
|
175
|
+
const url = cross.getUrl('goapp');
|
|
176
|
+
|
|
177
|
+
// Kill by name
|
|
178
|
+
cross.killByName('goapp');
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
### Communication Servers
|
|
115
182
|
|
|
116
|
-
|
|
117
|
-
|
|
183
|
+
```ts
|
|
184
|
+
import { getSocketServer, getHttpServer, getIpcServer } from 'ee-core/socket';
|
|
185
|
+
|
|
186
|
+
// All getters return non-null instances after Ready event
|
|
187
|
+
const socket = getSocketServer();
|
|
188
|
+
const http = getHttpServer();
|
|
189
|
+
const ipc = getIpcServer();
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
### Main Window
|
|
193
|
+
|
|
194
|
+
```ts
|
|
195
|
+
import { getMainWindow } from 'ee-core/electron';
|
|
196
|
+
|
|
197
|
+
// Returns BrowserWindow (non-null) after WindowReady event
|
|
198
|
+
const win = getMainWindow();
|
|
199
|
+
win.setMenuBarVisibility(false);
|
|
118
200
|
```
|
|
119
201
|
|
|
120
202
|
### Jobs
|
|
121
203
|
|
|
122
204
|
```ts
|
|
123
|
-
import { ChildJob, ChildPoolJob, LoadBalancer } from 'ee-core/jobs';
|
|
205
|
+
import { ChildJob, ChildPoolJob, LoadBalancer, AlgorithmType } from 'ee-core/jobs';
|
|
124
206
|
|
|
207
|
+
// Single task execution
|
|
125
208
|
const job = new ChildJob();
|
|
126
|
-
const result = await job.exec(
|
|
209
|
+
const result = await job.exec('./jobs/demo.js', { taskName: 'cleanup' });
|
|
210
|
+
|
|
211
|
+
// Process pool with load balancing
|
|
212
|
+
const pool = new ChildPoolJob();
|
|
213
|
+
pool.create(3); // Create 3 workers
|
|
214
|
+
const result = await pool.run('./jobs/demo.js', { taskName: 'batch' });
|
|
215
|
+
|
|
216
|
+
// 8 algorithms: polling, weights, random, specify, weightsPolling,
|
|
217
|
+
// weightsRandom, minimumConnection, weightsMinimumConnection
|
|
127
218
|
```
|
|
128
219
|
|
|
129
220
|
### Storage
|
|
@@ -131,28 +222,99 @@ const result = await job.exec({ name: 'task', script: './jobs/demo.js' });
|
|
|
131
222
|
```ts
|
|
132
223
|
import { SqliteStorage } from 'ee-core/storage';
|
|
133
224
|
|
|
134
|
-
|
|
135
|
-
db
|
|
225
|
+
// Four modes: memory, onlyName, relative, absolute
|
|
226
|
+
const db = new SqliteStorage({ mode: 'onlyName', name: 'app' });
|
|
136
227
|
```
|
|
137
228
|
|
|
138
229
|
### Logging
|
|
139
230
|
|
|
140
231
|
```ts
|
|
141
|
-
import {
|
|
232
|
+
import { logger, coreLogger } from 'ee-core/log';
|
|
233
|
+
|
|
234
|
+
// Supports three call styles: pino standard, pino printf, concatenation
|
|
235
|
+
logger.info('Application started');
|
|
236
|
+
logger.info('User %s logged in', 'admin');
|
|
237
|
+
logger.info('data:', { id: 1, name: 'test' });
|
|
142
238
|
|
|
143
|
-
|
|
239
|
+
// Log files: ee.log, ee-core.log, ee-error.log
|
|
240
|
+
// Rotation: daily/hourly via pino-roll
|
|
241
|
+
// Dev mode: pino-pretty for terminal output
|
|
144
242
|
```
|
|
145
243
|
|
|
146
244
|
### Process/Environment
|
|
147
245
|
|
|
148
246
|
```ts
|
|
149
|
-
import { isDev, getBaseDir,
|
|
247
|
+
import { isDev, isProd, getBaseDir, getDataDir, getLogDir } from 'ee-core/ps';
|
|
248
|
+
import { is } from 'ee-core/utils';
|
|
150
249
|
|
|
151
250
|
if (isDev()) {
|
|
152
251
|
// Development-specific logic
|
|
153
252
|
}
|
|
253
|
+
|
|
254
|
+
// Platform detection
|
|
255
|
+
if (is.macOS()) { /* macOS */ }
|
|
256
|
+
if (is.windows()) { /* Windows */ }
|
|
257
|
+
if (is.linux()) { /* Linux */ }
|
|
258
|
+
if (is.openharmony()) { /* OpenHarmony */ }
|
|
259
|
+
|
|
260
|
+
// Path utilities
|
|
261
|
+
const baseDir = getBaseDir();
|
|
262
|
+
const dataDir = getDataDir();
|
|
263
|
+
const logDir = getLogDir();
|
|
264
|
+
const extraRes = getExtraResourcesDir();
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
### Event Bus
|
|
268
|
+
|
|
269
|
+
```ts
|
|
270
|
+
import { eventBus, BeforeClose } from 'ee-core/app';
|
|
271
|
+
|
|
272
|
+
// Register lifecycle hook
|
|
273
|
+
app.register(BeforeClose, async () => {
|
|
274
|
+
// Cleanup before app closes
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
// Custom events
|
|
278
|
+
eventBus.on('myEvent', (data) => { /* handle */ });
|
|
279
|
+
eventBus.emit('myEvent', { key: 'value' });
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
### Exception Handling
|
|
283
|
+
|
|
284
|
+
```ts
|
|
285
|
+
// Auto-registered during boot. Controls exit behavior:
|
|
286
|
+
// exception.mainExit / childExit / rendererExit
|
|
287
|
+
// Errors logged via coreLogger, dev mode sends to terminal
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
### Utility Functions
|
|
291
|
+
|
|
292
|
+
```ts
|
|
293
|
+
import { extend, strictParse, getPort, parseArgv, sleep } from 'ee-core/utils';
|
|
294
|
+
|
|
295
|
+
// Deep merge with prototype pollution guard
|
|
296
|
+
const merged = extend(true, {}, defaults, userConfig);
|
|
297
|
+
|
|
298
|
+
// Port allocation with two-level locking
|
|
299
|
+
const port = await getPort({ port: 7070 });
|
|
300
|
+
|
|
301
|
+
// CLI argument parser
|
|
302
|
+
const args = parseArgv(process.argv);
|
|
303
|
+
|
|
304
|
+
// Sync sleep (Atomics.wait-based, no subprocess)
|
|
305
|
+
sleep(1000);
|
|
154
306
|
```
|
|
155
307
|
|
|
308
|
+
## TypeScript Types
|
|
309
|
+
|
|
310
|
+
All types are exported from `ee-core/types`:
|
|
311
|
+
|
|
312
|
+
```ts
|
|
313
|
+
import type { Config, CrossTargetConfig, DevConfig, AppInfo } from 'ee-core/types';
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
Key type interfaces: `ElectronEggOptions`, `Config`, `AppInfo`, `CrossTargetConfig`, `CrossRunOptions`, `LoggerConfig`, `ExceptionConfig`, `FileLoaderOptions`, `LoadBalancerOptions`, `JobProcessOptions`, `MessageData`.
|
|
317
|
+
|
|
156
318
|
## Build
|
|
157
319
|
|
|
158
320
|
```bash
|
|
@@ -161,10 +323,21 @@ npm run build:cjs # Build CommonJS only
|
|
|
161
323
|
npm run build:esm # Build ESM only
|
|
162
324
|
npm run typecheck # TypeScript type checking
|
|
163
325
|
npm run test # Run vitest
|
|
326
|
+
npm run test:watch # Run vitest in watch mode
|
|
164
327
|
```
|
|
165
328
|
|
|
166
329
|
The `exports` map in `package.json` is auto-generated by `scripts/gen-exports.js`. Do not edit manually — run `npm run gen-exports` after adding new subpath modules.
|
|
167
330
|
|
|
331
|
+
## Debug Logging
|
|
332
|
+
|
|
333
|
+
ee-core uses the `debug` library for verbose namespace-scoped logging. Enable to see runtime internals (config merging, controller loading, module resolution):
|
|
334
|
+
|
|
335
|
+
```bash
|
|
336
|
+
DEBUG=ee-* node your-app # All ee-* namespaces
|
|
337
|
+
DEBUG=ee-core:config:* node your-app # Config subsystem only
|
|
338
|
+
DEBUG=ee-core:controller:* node your-app # Controller loading only
|
|
339
|
+
```
|
|
340
|
+
|
|
168
341
|
## Requirements
|
|
169
342
|
|
|
170
343
|
- Node.js >= 20.19.0
|
|
@@ -174,5 +347,6 @@ The `exports` map in `package.json` is auto-generated by `scripts/gen-exports.js
|
|
|
174
347
|
## Notes
|
|
175
348
|
|
|
176
349
|
- ESM imports in ee-core source must include `.js` extensions (required by `module: NodeNext`)
|
|
177
|
-
- ee-core is NOT bundled into the Electron main process bundle — it's an esbuild external loaded from `node_modules` at runtime
|
|
178
|
-
-
|
|
350
|
+
- ee-core is NOT bundled into the Electron main process bundle — it's an esbuild external loaded from `node_modules` at runtime. This allows `child_process.fork()` to find `app.js` as a real disk file
|
|
351
|
+
- `getMainWindow()`, `getSocketServer()`, `getHttpServer()`, `getIpcServer()` return non-null instances. If called before the corresponding lifecycle event, they throw an Error indicating a lifecycle ordering bug
|
|
352
|
+
- The `exports` map is auto-generated — run `npm run gen-exports` after adding new modules
|
|
@@ -1,2 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module storage
|
|
3
|
+
* @description Data storage module entry point. Provides SqliteStorage for SQLite database operations.
|
|
4
|
+
*
|
|
5
|
+
* SqliteStorage class is exported statically — loading this module only imports built-in
|
|
6
|
+
* Node.js modules (assert, fs, path), NOT better-sqlite3. The native better-sqlite3 binding
|
|
7
|
+
* is only loaded when `SqliteStorage.init()` is called, via dynamic import().
|
|
8
|
+
*
|
|
9
|
+
* Usage:
|
|
10
|
+
* ```ts
|
|
11
|
+
* import { SqliteStorage } from 'ee-core/storage';
|
|
12
|
+
* const db = new SqliteStorage('myapp.db');
|
|
13
|
+
* await db.init(); // ← here better-sqlite3 is actually loaded
|
|
14
|
+
* ```
|
|
15
|
+
*/
|
|
1
16
|
export { SqliteStorage } from './sqliteStorage.js';
|
|
2
17
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/storage/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/storage/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC"}
|
|
@@ -1,6 +1,21 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.SqliteStorage = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* @module storage
|
|
6
|
+
* @description Data storage module entry point. Provides SqliteStorage for SQLite database operations.
|
|
7
|
+
*
|
|
8
|
+
* SqliteStorage class is exported statically — loading this module only imports built-in
|
|
9
|
+
* Node.js modules (assert, fs, path), NOT better-sqlite3. The native better-sqlite3 binding
|
|
10
|
+
* is only loaded when `SqliteStorage.init()` is called, via dynamic import().
|
|
11
|
+
*
|
|
12
|
+
* Usage:
|
|
13
|
+
* ```ts
|
|
14
|
+
* import { SqliteStorage } from 'ee-core/storage';
|
|
15
|
+
* const db = new SqliteStorage('myapp.db');
|
|
16
|
+
* await db.init(); // ← here better-sqlite3 is actually loaded
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
4
19
|
var sqliteStorage_js_1 = require("./sqliteStorage.js");
|
|
5
20
|
Object.defineProperty(exports, "SqliteStorage", { enumerable: true, get: function () { return sqliteStorage_js_1.SqliteStorage; } });
|
|
6
21
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/storage/index.ts"],"names":[],"mappings":";;;AAAA,uDAAmD;AAA1C,iHAAA,aAAa,OAAA"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/storage/index.ts"],"names":[],"mappings":";;;AAAA;;;;;;;;;;;;;;GAcG;AACH,uDAAmD;AAA1C,iHAAA,aAAa,OAAA"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import Database from 'better-sqlite3';
|
|
1
|
+
import type Database from 'better-sqlite3';
|
|
2
2
|
/**
|
|
3
3
|
* SqliteStorage - SQLite database storage class
|
|
4
4
|
*
|
|
@@ -15,6 +15,12 @@ import Database from 'better-sqlite3';
|
|
|
15
15
|
* Security:
|
|
16
16
|
* The constructor validates the database name for path traversal, disallowing ".." in the name
|
|
17
17
|
* to prevent malicious input from accessing files outside the intended directory.
|
|
18
|
+
*
|
|
19
|
+
* Lazy loading:
|
|
20
|
+
* better-sqlite3 is a native module that requires electron-rebuild for Electron.
|
|
21
|
+
* The constructor only computes paths (synchronous). The `init()` method lazily imports
|
|
22
|
+
* better-sqlite3 and opens the database connection. Projects that don't use SQLite
|
|
23
|
+
* never load the native binding.
|
|
18
24
|
*/
|
|
19
25
|
export declare class SqliteStorage {
|
|
20
26
|
/** Database name (original input value) */
|
|
@@ -25,42 +31,36 @@ export declare class SqliteStorage {
|
|
|
25
31
|
dbDir: string;
|
|
26
32
|
/** Database filename (only the filename part, excluding directory) */
|
|
27
33
|
fileName: string;
|
|
28
|
-
/** better-sqlite3 database instance */
|
|
34
|
+
/** better-sqlite3 database instance (set after init()) */
|
|
29
35
|
db: Database.Database;
|
|
30
36
|
/**
|
|
31
|
-
* Create a SqliteStorage instance
|
|
37
|
+
* Create a SqliteStorage instance (path computation only, DB not yet opened)
|
|
32
38
|
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
* 3. Create the database file directory (_createDatabaseDir)
|
|
37
|
-
* 4. Extract the filename part (_formatFileName)
|
|
38
|
-
* 5. Open the database connection (_initDB)
|
|
39
|
+
* The constructor only validates the name, infers the storage mode, computes paths,
|
|
40
|
+
* and creates the database directory. It does NOT open the database connection —
|
|
41
|
+
* call `init()` to lazily load better-sqlite3 and open the DB.
|
|
39
42
|
*
|
|
40
43
|
* @param name - Database name or path. Supports the following formats:
|
|
41
44
|
* - ":memory:" - In-memory database
|
|
42
45
|
* - "app.db" - Filename only, stored under {dataDir}/db/
|
|
43
46
|
* - "sub/app.db" - Relative path, relative to {dataDir}/db/
|
|
44
47
|
* - "/tmp/app.db" - Absolute path
|
|
45
|
-
* @param opt - better-sqlite3 database options, defaults merged with timeout: 5000
|
|
46
48
|
* @throws Asserts error if name is empty or contains ".." path traversal characters
|
|
47
49
|
*/
|
|
48
|
-
constructor(name: string
|
|
50
|
+
constructor(name: string);
|
|
49
51
|
/**
|
|
50
52
|
* Initialize the database connection
|
|
51
53
|
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
* - file mode: Uses getFilePath() to get the full database file path
|
|
54
|
+
* Lazily imports better-sqlite3 via dynamic import() and opens the database.
|
|
55
|
+
* This is the only point where the native .node binding is loaded.
|
|
55
56
|
*
|
|
56
|
-
*
|
|
57
|
-
* If the file does not exist, throws an assertion error (possibly due to insufficient directory permissions, etc.).
|
|
57
|
+
* Must be called after construction before using `this.db`.
|
|
58
58
|
*
|
|
59
59
|
* @param opt - better-sqlite3 database options, defaults merged with { timeout: 5000 }
|
|
60
|
-
* @returns
|
|
60
|
+
* @returns this SqliteStorage instance (for chaining: `const db = await new SqliteStorage('app.db').init()`)
|
|
61
61
|
* @throws Asserts error if the database file was not created successfully in file mode
|
|
62
62
|
*/
|
|
63
|
-
|
|
63
|
+
init(opt?: Record<string, unknown>): Promise<this>;
|
|
64
64
|
/**
|
|
65
65
|
* Extract the database filename
|
|
66
66
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sqliteStorage.d.ts","sourceRoot":"","sources":["../../../src/storage/sqliteStorage.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"sqliteStorage.d.ts","sourceRoot":"","sources":["../../../src/storage/sqliteStorage.ts"],"names":[],"mappings":"AA2CA,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAC;AAE3C;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,qBAAa,aAAa;IACxB,2CAA2C;IAC3C,IAAI,EAAE,MAAM,CAAC;IAEb,4DAA4D;IAC5D,IAAI,EAAE,MAAM,CAAC;IAEb,kEAAkE;IAClE,KAAK,EAAE,MAAM,CAAC;IAEd,sEAAsE;IACtE,QAAQ,EAAE,MAAM,CAAC;IAEjB,0DAA0D;IAC1D,EAAE,EAAG,QAAQ,CAAC,QAAQ,CAAC;IAEvB;;;;;;;;;;;;;OAaG;gBACS,IAAI,EAAE,MAAM;IAWxB;;;;;;;;;;;OAWG;IACG,IAAI,CAAC,GAAG,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAwB5D;;;;;;;;OAQG;IACH,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM;IAOrC;;;;;;;;;;;OAWG;IACH,kBAAkB,IAAI,MAAM;IAc5B;;;;;;;;;;;;OAYG;IACH,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM;IAe7B;;;;OAIG;IACH,QAAQ,IAAI,MAAM;IAIlB;;;;;;;OAOG;IACH,WAAW,IAAI,MAAM;CAGtB"}
|
|
@@ -1,4 +1,37 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
2
35
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
36
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
37
|
};
|
|
@@ -15,25 +48,32 @@ exports.SqliteStorage = void 0;
|
|
|
15
48
|
* - Prevent path traversal attacks (validates that ".." is not present in the name during construction)
|
|
16
49
|
* - Provide a unified interface for obtaining database file paths
|
|
17
50
|
*
|
|
51
|
+
* better-sqlite3 is lazily imported — only loaded when `init()` is called to open the database.
|
|
52
|
+
* This avoids loading the native .node binding at module import time, which would happen unconditionally
|
|
53
|
+
* even if the consumer never uses SQLite.
|
|
54
|
+
*
|
|
18
55
|
* Usage examples:
|
|
19
56
|
* ```ts
|
|
20
57
|
* // Name-only mode: file stored at {dataDir}/db/{name}.db
|
|
21
58
|
* const db1 = new SqliteStorage('myapp.db');
|
|
59
|
+
* await db1.init();
|
|
22
60
|
*
|
|
23
61
|
* // Relative path mode: file stored at {dataDir}/db/sub/myapp.db
|
|
24
62
|
* const db2 = new SqliteStorage('sub/myapp.db');
|
|
63
|
+
* await db2.init();
|
|
25
64
|
*
|
|
26
65
|
* // Absolute path mode: file stored at the specified path
|
|
27
66
|
* const db3 = new SqliteStorage('/tmp/data/myapp.db');
|
|
67
|
+
* await db3.init();
|
|
28
68
|
*
|
|
29
69
|
* // In-memory mode: not persisted, data is lost when the process exits
|
|
30
70
|
* const db4 = new SqliteStorage(':memory:');
|
|
71
|
+
* await db4.init();
|
|
31
72
|
* ```
|
|
32
73
|
*/
|
|
33
74
|
const assert_1 = __importDefault(require("assert"));
|
|
34
75
|
const fs_1 = __importDefault(require("fs"));
|
|
35
76
|
const path_1 = __importDefault(require("path"));
|
|
36
|
-
const better_sqlite3_1 = __importDefault(require("better-sqlite3"));
|
|
37
77
|
const helper_js_1 = require("../utils/helper.js");
|
|
38
78
|
const index_js_1 = require("../ps/index.js");
|
|
39
79
|
/**
|
|
@@ -52,27 +92,29 @@ const index_js_1 = require("../ps/index.js");
|
|
|
52
92
|
* Security:
|
|
53
93
|
* The constructor validates the database name for path traversal, disallowing ".." in the name
|
|
54
94
|
* to prevent malicious input from accessing files outside the intended directory.
|
|
95
|
+
*
|
|
96
|
+
* Lazy loading:
|
|
97
|
+
* better-sqlite3 is a native module that requires electron-rebuild for Electron.
|
|
98
|
+
* The constructor only computes paths (synchronous). The `init()` method lazily imports
|
|
99
|
+
* better-sqlite3 and opens the database connection. Projects that don't use SQLite
|
|
100
|
+
* never load the native binding.
|
|
55
101
|
*/
|
|
56
102
|
class SqliteStorage {
|
|
57
103
|
/**
|
|
58
|
-
* Create a SqliteStorage instance
|
|
104
|
+
* Create a SqliteStorage instance (path computation only, DB not yet opened)
|
|
59
105
|
*
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
* 3. Create the database file directory (_createDatabaseDir)
|
|
64
|
-
* 4. Extract the filename part (_formatFileName)
|
|
65
|
-
* 5. Open the database connection (_initDB)
|
|
106
|
+
* The constructor only validates the name, infers the storage mode, computes paths,
|
|
107
|
+
* and creates the database directory. It does NOT open the database connection —
|
|
108
|
+
* call `init()` to lazily load better-sqlite3 and open the DB.
|
|
66
109
|
*
|
|
67
110
|
* @param name - Database name or path. Supports the following formats:
|
|
68
111
|
* - ":memory:" - In-memory database
|
|
69
112
|
* - "app.db" - Filename only, stored under {dataDir}/db/
|
|
70
113
|
* - "sub/app.db" - Relative path, relative to {dataDir}/db/
|
|
71
114
|
* - "/tmp/app.db" - Absolute path
|
|
72
|
-
* @param opt - better-sqlite3 database options, defaults merged with timeout: 5000
|
|
73
115
|
* @throws Asserts error if name is empty or contains ".." path traversal characters
|
|
74
116
|
*/
|
|
75
|
-
constructor(name
|
|
117
|
+
constructor(name) {
|
|
76
118
|
(0, assert_1.default)(name, `db name ${name} Cannot be empty`);
|
|
77
119
|
// Prevent path traversal attacks: disallow ".." in the name to avoid directory backtracking
|
|
78
120
|
(0, assert_1.default)(!name.includes('..'), `db name ${name} contains path traversal`);
|
|
@@ -80,35 +122,36 @@ class SqliteStorage {
|
|
|
80
122
|
this.mode = this.getMode(name);
|
|
81
123
|
this.dbDir = this._createDatabaseDir();
|
|
82
124
|
this.fileName = this._formatFileName(name);
|
|
83
|
-
this.db = this._initDB(opt);
|
|
84
125
|
}
|
|
85
126
|
/**
|
|
86
127
|
* Initialize the database connection
|
|
87
128
|
*
|
|
88
|
-
*
|
|
89
|
-
*
|
|
90
|
-
* - file mode: Uses getFilePath() to get the full database file path
|
|
129
|
+
* Lazily imports better-sqlite3 via dynamic import() and opens the database.
|
|
130
|
+
* This is the only point where the native .node binding is loaded.
|
|
91
131
|
*
|
|
92
|
-
*
|
|
93
|
-
* If the file does not exist, throws an assertion error (possibly due to insufficient directory permissions, etc.).
|
|
132
|
+
* Must be called after construction before using `this.db`.
|
|
94
133
|
*
|
|
95
134
|
* @param opt - better-sqlite3 database options, defaults merged with { timeout: 5000 }
|
|
96
|
-
* @returns
|
|
135
|
+
* @returns this SqliteStorage instance (for chaining: `const db = await new SqliteStorage('app.db').init()`)
|
|
97
136
|
* @throws Asserts error if the database file was not created successfully in file mode
|
|
98
137
|
*/
|
|
99
|
-
|
|
138
|
+
async init(opt = {}) {
|
|
139
|
+
// Lazy import: better-sqlite3 is only loaded when actually needed.
|
|
140
|
+
// This avoids loading the native .node binding at module import time.
|
|
141
|
+
// better-sqlite3 is a CJS package; dynamic import() resolves it correctly.
|
|
142
|
+
const { default: Database } = await Promise.resolve().then(() => __importStar(require('better-sqlite3')));
|
|
100
143
|
const options = Object.assign({ timeout: 5000 }, opt);
|
|
101
144
|
// Storage type: db file, in-memory (:memory:)
|
|
102
145
|
let dbPath = this.name;
|
|
103
146
|
if (this.mode !== 'memory') {
|
|
104
147
|
dbPath = this.getFilePath();
|
|
105
148
|
}
|
|
106
|
-
|
|
149
|
+
this.db = new Database(dbPath, options);
|
|
107
150
|
// For file mode, check if the file was created successfully
|
|
108
151
|
if (this.mode !== 'memory') {
|
|
109
152
|
(0, assert_1.default)(fs_1.default.existsSync(dbPath), `error: db ${dbPath} not exists`);
|
|
110
153
|
}
|
|
111
|
-
return
|
|
154
|
+
return this;
|
|
112
155
|
}
|
|
113
156
|
/**
|
|
114
157
|
* Extract the database filename
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sqliteStorage.js","sourceRoot":"","sources":["../../../src/storage/sqliteStorage.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"sqliteStorage.js","sourceRoot":"","sources":["../../../src/storage/sqliteStorage.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,2 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module storage
|
|
3
|
+
* @description Data storage module entry point. Provides SqliteStorage for SQLite database operations.
|
|
4
|
+
*
|
|
5
|
+
* SqliteStorage class is exported statically — loading this module only imports built-in
|
|
6
|
+
* Node.js modules (assert, fs, path), NOT better-sqlite3. The native better-sqlite3 binding
|
|
7
|
+
* is only loaded when `SqliteStorage.init()` is called, via dynamic import().
|
|
8
|
+
*
|
|
9
|
+
* Usage:
|
|
10
|
+
* ```ts
|
|
11
|
+
* import { SqliteStorage } from 'ee-core/storage';
|
|
12
|
+
* const db = new SqliteStorage('myapp.db');
|
|
13
|
+
* await db.init(); // ← here better-sqlite3 is actually loaded
|
|
14
|
+
* ```
|
|
15
|
+
*/
|
|
1
16
|
export { SqliteStorage } from './sqliteStorage.js';
|
|
2
17
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/storage/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/storage/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC"}
|
|
@@ -1,6 +1,21 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.SqliteStorage = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* @module storage
|
|
6
|
+
* @description Data storage module entry point. Provides SqliteStorage for SQLite database operations.
|
|
7
|
+
*
|
|
8
|
+
* SqliteStorage class is exported statically — loading this module only imports built-in
|
|
9
|
+
* Node.js modules (assert, fs, path), NOT better-sqlite3. The native better-sqlite3 binding
|
|
10
|
+
* is only loaded when `SqliteStorage.init()` is called, via dynamic import().
|
|
11
|
+
*
|
|
12
|
+
* Usage:
|
|
13
|
+
* ```ts
|
|
14
|
+
* import { SqliteStorage } from 'ee-core/storage';
|
|
15
|
+
* const db = new SqliteStorage('myapp.db');
|
|
16
|
+
* await db.init(); // ← here better-sqlite3 is actually loaded
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
4
19
|
var sqliteStorage_js_1 = require("./sqliteStorage.js");
|
|
5
20
|
Object.defineProperty(exports, "SqliteStorage", { enumerable: true, get: function () { return sqliteStorage_js_1.SqliteStorage; } });
|
|
6
21
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/storage/index.ts"],"names":[],"mappings":";;;AAAA,uDAAmD;AAA1C,iHAAA,aAAa,OAAA"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/storage/index.ts"],"names":[],"mappings":";;;AAAA;;;;;;;;;;;;;;GAcG;AACH,uDAAmD;AAA1C,iHAAA,aAAa,OAAA"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import Database from 'better-sqlite3';
|
|
1
|
+
import type Database from 'better-sqlite3';
|
|
2
2
|
/**
|
|
3
3
|
* SqliteStorage - SQLite database storage class
|
|
4
4
|
*
|
|
@@ -15,6 +15,12 @@ import Database from 'better-sqlite3';
|
|
|
15
15
|
* Security:
|
|
16
16
|
* The constructor validates the database name for path traversal, disallowing ".." in the name
|
|
17
17
|
* to prevent malicious input from accessing files outside the intended directory.
|
|
18
|
+
*
|
|
19
|
+
* Lazy loading:
|
|
20
|
+
* better-sqlite3 is a native module that requires electron-rebuild for Electron.
|
|
21
|
+
* The constructor only computes paths (synchronous). The `init()` method lazily imports
|
|
22
|
+
* better-sqlite3 and opens the database connection. Projects that don't use SQLite
|
|
23
|
+
* never load the native binding.
|
|
18
24
|
*/
|
|
19
25
|
export declare class SqliteStorage {
|
|
20
26
|
/** Database name (original input value) */
|
|
@@ -25,42 +31,36 @@ export declare class SqliteStorage {
|
|
|
25
31
|
dbDir: string;
|
|
26
32
|
/** Database filename (only the filename part, excluding directory) */
|
|
27
33
|
fileName: string;
|
|
28
|
-
/** better-sqlite3 database instance */
|
|
34
|
+
/** better-sqlite3 database instance (set after init()) */
|
|
29
35
|
db: Database.Database;
|
|
30
36
|
/**
|
|
31
|
-
* Create a SqliteStorage instance
|
|
37
|
+
* Create a SqliteStorage instance (path computation only, DB not yet opened)
|
|
32
38
|
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
* 3. Create the database file directory (_createDatabaseDir)
|
|
37
|
-
* 4. Extract the filename part (_formatFileName)
|
|
38
|
-
* 5. Open the database connection (_initDB)
|
|
39
|
+
* The constructor only validates the name, infers the storage mode, computes paths,
|
|
40
|
+
* and creates the database directory. It does NOT open the database connection —
|
|
41
|
+
* call `init()` to lazily load better-sqlite3 and open the DB.
|
|
39
42
|
*
|
|
40
43
|
* @param name - Database name or path. Supports the following formats:
|
|
41
44
|
* - ":memory:" - In-memory database
|
|
42
45
|
* - "app.db" - Filename only, stored under {dataDir}/db/
|
|
43
46
|
* - "sub/app.db" - Relative path, relative to {dataDir}/db/
|
|
44
47
|
* - "/tmp/app.db" - Absolute path
|
|
45
|
-
* @param opt - better-sqlite3 database options, defaults merged with timeout: 5000
|
|
46
48
|
* @throws Asserts error if name is empty or contains ".." path traversal characters
|
|
47
49
|
*/
|
|
48
|
-
constructor(name: string
|
|
50
|
+
constructor(name: string);
|
|
49
51
|
/**
|
|
50
52
|
* Initialize the database connection
|
|
51
53
|
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
* - file mode: Uses getFilePath() to get the full database file path
|
|
54
|
+
* Lazily imports better-sqlite3 via dynamic import() and opens the database.
|
|
55
|
+
* This is the only point where the native .node binding is loaded.
|
|
55
56
|
*
|
|
56
|
-
*
|
|
57
|
-
* If the file does not exist, throws an assertion error (possibly due to insufficient directory permissions, etc.).
|
|
57
|
+
* Must be called after construction before using `this.db`.
|
|
58
58
|
*
|
|
59
59
|
* @param opt - better-sqlite3 database options, defaults merged with { timeout: 5000 }
|
|
60
|
-
* @returns
|
|
60
|
+
* @returns this SqliteStorage instance (for chaining: `const db = await new SqliteStorage('app.db').init()`)
|
|
61
61
|
* @throws Asserts error if the database file was not created successfully in file mode
|
|
62
62
|
*/
|
|
63
|
-
|
|
63
|
+
init(opt?: Record<string, unknown>): Promise<this>;
|
|
64
64
|
/**
|
|
65
65
|
* Extract the database filename
|
|
66
66
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sqliteStorage.d.ts","sourceRoot":"","sources":["../../../src/storage/sqliteStorage.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"sqliteStorage.d.ts","sourceRoot":"","sources":["../../../src/storage/sqliteStorage.ts"],"names":[],"mappings":"AA2CA,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAC;AAE3C;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,qBAAa,aAAa;IACxB,2CAA2C;IAC3C,IAAI,EAAE,MAAM,CAAC;IAEb,4DAA4D;IAC5D,IAAI,EAAE,MAAM,CAAC;IAEb,kEAAkE;IAClE,KAAK,EAAE,MAAM,CAAC;IAEd,sEAAsE;IACtE,QAAQ,EAAE,MAAM,CAAC;IAEjB,0DAA0D;IAC1D,EAAE,EAAG,QAAQ,CAAC,QAAQ,CAAC;IAEvB;;;;;;;;;;;;;OAaG;gBACS,IAAI,EAAE,MAAM;IAWxB;;;;;;;;;;;OAWG;IACG,IAAI,CAAC,GAAG,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAwB5D;;;;;;;;OAQG;IACH,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM;IAOrC;;;;;;;;;;;OAWG;IACH,kBAAkB,IAAI,MAAM;IAc5B;;;;;;;;;;;;OAYG;IACH,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM;IAe7B;;;;OAIG;IACH,QAAQ,IAAI,MAAM;IAIlB;;;;;;;OAOG;IACH,WAAW,IAAI,MAAM;CAGtB"}
|
|
@@ -15,25 +15,32 @@ exports.SqliteStorage = void 0;
|
|
|
15
15
|
* - Prevent path traversal attacks (validates that ".." is not present in the name during construction)
|
|
16
16
|
* - Provide a unified interface for obtaining database file paths
|
|
17
17
|
*
|
|
18
|
+
* better-sqlite3 is lazily imported — only loaded when `init()` is called to open the database.
|
|
19
|
+
* This avoids loading the native .node binding at module import time, which would happen unconditionally
|
|
20
|
+
* even if the consumer never uses SQLite.
|
|
21
|
+
*
|
|
18
22
|
* Usage examples:
|
|
19
23
|
* ```ts
|
|
20
24
|
* // Name-only mode: file stored at {dataDir}/db/{name}.db
|
|
21
25
|
* const db1 = new SqliteStorage('myapp.db');
|
|
26
|
+
* await db1.init();
|
|
22
27
|
*
|
|
23
28
|
* // Relative path mode: file stored at {dataDir}/db/sub/myapp.db
|
|
24
29
|
* const db2 = new SqliteStorage('sub/myapp.db');
|
|
30
|
+
* await db2.init();
|
|
25
31
|
*
|
|
26
32
|
* // Absolute path mode: file stored at the specified path
|
|
27
33
|
* const db3 = new SqliteStorage('/tmp/data/myapp.db');
|
|
34
|
+
* await db3.init();
|
|
28
35
|
*
|
|
29
36
|
* // In-memory mode: not persisted, data is lost when the process exits
|
|
30
37
|
* const db4 = new SqliteStorage(':memory:');
|
|
38
|
+
* await db4.init();
|
|
31
39
|
* ```
|
|
32
40
|
*/
|
|
33
41
|
const assert_1 = __importDefault(require("assert"));
|
|
34
42
|
const fs_1 = __importDefault(require("fs"));
|
|
35
43
|
const path_1 = __importDefault(require("path"));
|
|
36
|
-
const better_sqlite3_1 = __importDefault(require("better-sqlite3"));
|
|
37
44
|
const helper_js_1 = require("../utils/helper.js");
|
|
38
45
|
const index_js_1 = require("../ps/index.js");
|
|
39
46
|
/**
|
|
@@ -52,27 +59,29 @@ const index_js_1 = require("../ps/index.js");
|
|
|
52
59
|
* Security:
|
|
53
60
|
* The constructor validates the database name for path traversal, disallowing ".." in the name
|
|
54
61
|
* to prevent malicious input from accessing files outside the intended directory.
|
|
62
|
+
*
|
|
63
|
+
* Lazy loading:
|
|
64
|
+
* better-sqlite3 is a native module that requires electron-rebuild for Electron.
|
|
65
|
+
* The constructor only computes paths (synchronous). The `init()` method lazily imports
|
|
66
|
+
* better-sqlite3 and opens the database connection. Projects that don't use SQLite
|
|
67
|
+
* never load the native binding.
|
|
55
68
|
*/
|
|
56
69
|
class SqliteStorage {
|
|
57
70
|
/**
|
|
58
|
-
* Create a SqliteStorage instance
|
|
71
|
+
* Create a SqliteStorage instance (path computation only, DB not yet opened)
|
|
59
72
|
*
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
* 3. Create the database file directory (_createDatabaseDir)
|
|
64
|
-
* 4. Extract the filename part (_formatFileName)
|
|
65
|
-
* 5. Open the database connection (_initDB)
|
|
73
|
+
* The constructor only validates the name, infers the storage mode, computes paths,
|
|
74
|
+
* and creates the database directory. It does NOT open the database connection —
|
|
75
|
+
* call `init()` to lazily load better-sqlite3 and open the DB.
|
|
66
76
|
*
|
|
67
77
|
* @param name - Database name or path. Supports the following formats:
|
|
68
78
|
* - ":memory:" - In-memory database
|
|
69
79
|
* - "app.db" - Filename only, stored under {dataDir}/db/
|
|
70
80
|
* - "sub/app.db" - Relative path, relative to {dataDir}/db/
|
|
71
81
|
* - "/tmp/app.db" - Absolute path
|
|
72
|
-
* @param opt - better-sqlite3 database options, defaults merged with timeout: 5000
|
|
73
82
|
* @throws Asserts error if name is empty or contains ".." path traversal characters
|
|
74
83
|
*/
|
|
75
|
-
constructor(name
|
|
84
|
+
constructor(name) {
|
|
76
85
|
(0, assert_1.default)(name, `db name ${name} Cannot be empty`);
|
|
77
86
|
// Prevent path traversal attacks: disallow ".." in the name to avoid directory backtracking
|
|
78
87
|
(0, assert_1.default)(!name.includes('..'), `db name ${name} contains path traversal`);
|
|
@@ -80,35 +89,36 @@ class SqliteStorage {
|
|
|
80
89
|
this.mode = this.getMode(name);
|
|
81
90
|
this.dbDir = this._createDatabaseDir();
|
|
82
91
|
this.fileName = this._formatFileName(name);
|
|
83
|
-
this.db = this._initDB(opt);
|
|
84
92
|
}
|
|
85
93
|
/**
|
|
86
94
|
* Initialize the database connection
|
|
87
95
|
*
|
|
88
|
-
*
|
|
89
|
-
*
|
|
90
|
-
* - file mode: Uses getFilePath() to get the full database file path
|
|
96
|
+
* Lazily imports better-sqlite3 via dynamic import() and opens the database.
|
|
97
|
+
* This is the only point where the native .node binding is loaded.
|
|
91
98
|
*
|
|
92
|
-
*
|
|
93
|
-
* If the file does not exist, throws an assertion error (possibly due to insufficient directory permissions, etc.).
|
|
99
|
+
* Must be called after construction before using `this.db`.
|
|
94
100
|
*
|
|
95
101
|
* @param opt - better-sqlite3 database options, defaults merged with { timeout: 5000 }
|
|
96
|
-
* @returns
|
|
102
|
+
* @returns this SqliteStorage instance (for chaining: `const db = await new SqliteStorage('app.db').init()`)
|
|
97
103
|
* @throws Asserts error if the database file was not created successfully in file mode
|
|
98
104
|
*/
|
|
99
|
-
|
|
105
|
+
async init(opt = {}) {
|
|
106
|
+
// Lazy import: better-sqlite3 is only loaded when actually needed.
|
|
107
|
+
// This avoids loading the native .node binding at module import time.
|
|
108
|
+
// better-sqlite3 is a CJS package; dynamic import() resolves it correctly.
|
|
109
|
+
const { default: Database } = await import('better-sqlite3');
|
|
100
110
|
const options = Object.assign({ timeout: 5000 }, opt);
|
|
101
111
|
// Storage type: db file, in-memory (:memory:)
|
|
102
112
|
let dbPath = this.name;
|
|
103
113
|
if (this.mode !== 'memory') {
|
|
104
114
|
dbPath = this.getFilePath();
|
|
105
115
|
}
|
|
106
|
-
|
|
116
|
+
this.db = new Database(dbPath, options);
|
|
107
117
|
// For file mode, check if the file was created successfully
|
|
108
118
|
if (this.mode !== 'memory') {
|
|
109
119
|
(0, assert_1.default)(fs_1.default.existsSync(dbPath), `error: db ${dbPath} not exists`);
|
|
110
120
|
}
|
|
111
|
-
return
|
|
121
|
+
return this;
|
|
112
122
|
}
|
|
113
123
|
/**
|
|
114
124
|
* Extract the database filename
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sqliteStorage.js","sourceRoot":"","sources":["../../../src/storage/sqliteStorage.ts"],"names":[],"mappings":";;;;;;AAAA
|
|
1
|
+
{"version":3,"file":"sqliteStorage.js","sourceRoot":"","sources":["../../../src/storage/sqliteStorage.ts"],"names":[],"mappings":"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"}
|