ee-core 5.0.0-beta.8 → 5.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +214 -40
- package/dist/cjs/jobs/child/jobProcess.js +1 -1
- package/dist/esm/jobs/child/jobProcess.js +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
|