oox 0.3.3 → 0.3.5

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 CHANGED
@@ -27,3 +27,16 @@ npm install oox
27
27
  ```bash
28
28
  npx oox --help
29
29
  ```
30
+
31
+ ### Examples
32
+ - [basic-example](./examples/basic-example)
33
+ - [routing-example](./examples/routing-example)
34
+ - [rpc-example](./examples/rpc-example)
35
+ - [distributed-example](./examples/distributed-example)
36
+ - [load-balancing-example](./examples/load-balancing-example)
37
+ - [tracing-example](./examples/tracing-example)
38
+ - [service-discovery-example](./examples/service-discovery-example)
39
+
40
+
41
+ ### Docs
42
+ [config](./docs/config.md)
package/app.js CHANGED
@@ -2,7 +2,7 @@ import { EventEmitter } from 'node:events';
2
2
  import { AsyncLocalStorage } from 'node:async_hooks';
3
3
  import { genKVMethods } from './utils.js';
4
4
  export * as logger from './logger.js';
5
- export class Context {
5
+ export class AppContext {
6
6
  // 请求溯源ID
7
7
  traceId = '';
8
8
  }
package/bin/cli.js CHANGED
@@ -16,7 +16,7 @@ if (execFilename.endsWith('oox') || fileURLToPath(import.meta.url) === execFilen
16
16
  if (!command || ['help', '-h', '-help', '--help', 'version', '-v', '-version', '--version'].includes(command)) {
17
17
  isStartup = false;
18
18
  console.log();
19
- console.log('OOX Service Engine');
19
+ console.log(pkg.description);
20
20
  console.log(chalk.bold('version'), chalk.bold.green(`${pkg.version}`));
21
21
  console.log(chalk.underline(`${pkg.homepage}`));
22
22
  console.log();
@@ -27,23 +27,56 @@ if (execFilename.endsWith('oox') || fileURLToPath(import.meta.url) === execFilen
27
27
  console.log();
28
28
  console.log(' oox entry.js port=8080');
29
29
  console.log();
30
- console.log(' oox app/entry/index.js group=app/ env=envs/entry.js ignore=core');
30
+ console.log(' oox entry.js group=app/ registry=:6000');
31
+ console.log(' oox app/entry/index.js group=app/ env=.env/prod.js ignore=core');
31
32
  console.log();
32
33
  console.log(chalk.bold('Params:'));
33
- const params = [
34
- [' default-env [ file ] ', '.js or .json file, merge to oox.config'],
35
- [' env [ file ] ', '.js or .json file, merge to oox.config', chalk.bold('(after default-env)')],
36
- [' port [ int ] ', 'set', chalk.bold('0'), 'for random port, or any integer > 0'],
37
- [' group [ dir ] ', 'service group directory, all LocalCall transform to RPC'],
38
- [' ignore [ name ] ', 'set a name for LocalCall do not transform to RPC, support string | array<string>'],
39
- [' http [ json ] ', 'HTTP server options, support flat name, ex: http.path=/api'],
40
- [' socketio [ json ] ', 'SocketIO server options, support flat name'],
41
- [' registry [ urls ] ', 'registry service url, support string | array<string>'],
42
- [' origin [ urls ] ', 'set', chalk.bold('*'), 'allow any connections <Access-Control-Allow-Origin>'],
43
- [' errorStack [ bool ] ', 'set no to hidden error stack return'],
44
- [' ... ', 'set params as', chalk.bold('foo=bar') + ',', 'usage as', chalk.bold('oox.config.foo')]
34
+ const params_base = [
35
+ ['default-env', 'file', '.js or .json file, merge to oox.config'],
36
+ ['env', 'file', `.js or .json file, merge to oox.config, ${chalk.bold('(after default-env)')}`],
37
+ ['port', 'int', `default is ${chalk.bold('0')}, for random port, or any integer > 0`],
38
+ ['group', 'dir', 'service group directory, all LocalCall transform to RPC'],
39
+ ['ignore', 'name', 'set a name for LocalCall do not transform to RPC, support string | array<string>'],
40
+ ['errorStack', 'bool', 'set no to hidden error stack return'],
45
41
  ];
46
- params.forEach(row => console.log(...row, '\n'));
42
+ const params_registry = [
43
+ ['isRegistry', 'bool', `set yes to enable service registry`],
44
+ ['registryAdapter', 'name', 'set service registry adapter name'],
45
+ ['registryToken', 'token', 'set token for service registry'],
46
+ ['registry', 'urls', 'registry service url, support string | array<string>'],
47
+ ['registry.url', 'url', 'set service registry url'],
48
+ ['registry.token', 'token', 'set service registry token'],
49
+ ];
50
+ const params_auth = [
51
+ ['token', 'token', 'set token for connection auth'],
52
+ ['allow.untrusted', 'bool', `set yes to allow untrusted caller`],
53
+ ['allow.ip', 'ip', 'set allow IP, support string | array<string>'],
54
+ ['allow.caller', 'name', 'set allow caller, support string | array<string>'],
55
+ ['origin', 'urls', `set ${chalk.bold('*')}, allow any connections <Access-Control-Allow-Origin>`],
56
+ ];
57
+ const params_modules = [
58
+ ['http', 'json/bool', 'HTTP server options, support flat name, ex: http.path=/api, or set no to disable HTTP server'],
59
+ ['http.port', 'int server port'],
60
+ ['http.path', 'string', 'set HTTP server path'],
61
+ ['http.origin', 'urls', 'set HTTP server origin, support string | array<string>'],
62
+ ['http.ssl', 'json', 'set yes to enable HTTPS'],
63
+ ['http.ssl.cert', 'file', 'set HTTPS certificate path'],
64
+ ['http.ssl.key', 'file', 'set HTTPS private key path'],
65
+ ['http.ssl.ca', 'file', 'set HTTPS CA path'],
66
+ ['socketio', 'json/bool', 'SocketIO server options, support flat name, or set no to disable SocketIO server'],
67
+ ['socketio.port', 'int server port'],
68
+ ['socketio.path', 'string', 'set SocketIO server path'],
69
+ ['socketio.origin', 'urls', 'set SocketIO server origin, support string | array<string>'],
70
+ ['socketio.ssl', 'json', 'like http.ssl'],
71
+ ];
72
+ console.log(mergeCommandParams(params_base));
73
+ console.log(chalk.bold('Registry:'));
74
+ console.log(mergeCommandParams(params_registry));
75
+ console.log(chalk.bold('Auth:'));
76
+ console.log(mergeCommandParams(params_auth));
77
+ console.log(chalk.bold('Modules:'));
78
+ console.log(mergeCommandParams(params_modules));
79
+ console.log(' ...', 'set params as', chalk.bold('foo=bar') + ',', 'usage as', chalk.bold('oox.config.foo'));
47
80
  console.log();
48
81
  }
49
82
  if (isStartup) {
@@ -55,3 +88,30 @@ if (execFilename.endsWith('oox') || fileURLToPath(import.meta.url) === execFilen
55
88
  await startup();
56
89
  }
57
90
  }
91
+ function mergeCommandParams(params) {
92
+ let result = '';
93
+ const perLinePrefix = ' '.repeat(2);
94
+ const nameMaxLength = 12;
95
+ const typeMaxLength = 6;
96
+ for (const param of params) {
97
+ const name = param[0];
98
+ const type = param[1];
99
+ const desc = param.slice(2).join(' ');
100
+ result += perLinePrefix + name;
101
+ if (name.length > nameMaxLength) {
102
+ result += '\n' + perLinePrefix;
103
+ result += ' '.repeat(nameMaxLength);
104
+ }
105
+ else {
106
+ result += ' '.repeat(Math.max(0, nameMaxLength - name.length));
107
+ }
108
+ const typeLeftSpaceLength = Math.max(0, Math.floor((typeMaxLength - type.length) / 2));
109
+ const typeLeftSpaces = ' '.repeat(typeLeftSpaceLength);
110
+ const typeRightSpaces = ' '.repeat(Math.max(0, typeMaxLength - type.length - typeLeftSpaceLength));
111
+ result += '[' + typeLeftSpaces + type + typeRightSpaces + ']';
112
+ result += ' ';
113
+ result += desc;
114
+ result += '\n';
115
+ }
116
+ return result;
117
+ }
package/bin/configurer.js CHANGED
@@ -12,7 +12,11 @@ function mergeFlatEnv(env) {
12
12
  const valueKey = subKeys.pop() || '';
13
13
  let tmpEnv = env;
14
14
  for (const subKey of subKeys) {
15
- if (subKey in tmpEnv) { }
15
+ if (subKey in tmpEnv) {
16
+ if ('object' !== typeof tmpEnv[subKey] || null === tmpEnv[subKey]) {
17
+ tmpEnv[subKey] = {};
18
+ }
19
+ }
16
20
  else {
17
21
  tmpEnv[subKey] = {};
18
22
  }
@@ -42,20 +46,13 @@ async function readEnvFile(filePath) {
42
46
  return env;
43
47
  }
44
48
  export async function buildConfig(mergeEnv) {
45
- const env = Object.create(null);
49
+ const env = {};
46
50
  const defaultEnvPath = argv.getEnvArg('default-env');
47
51
  const targetEnvPath = argv.getEnvArg('env');
48
52
  const defaultEnv = defaultEnvPath ? await readEnvFile(defaultEnvPath) : {};
49
53
  const targetEnv = targetEnvPath ? await readEnvFile(targetEnvPath) : {};
50
54
  Object.assign(env, defaultEnv, targetEnv, argv.getAllEnvArgs());
51
55
  mergeFlatEnv(env);
52
- if ('string' === typeof env.ignore)
53
- env.ignore = env.ignore.split(',');
54
- if ('string' === typeof env.registry)
55
- env.registry = env.registry.split(',');
56
- if ('string' === typeof env.origin && env.origin.includes(',')) {
57
- env.origin = env.origin.split(',');
58
- }
59
56
  if (mergeEnv)
60
57
  Object.assign(env, mergeEnv);
61
58
  return env;
package/bin/register.js CHANGED
@@ -1,32 +1,65 @@
1
1
  import chalk from 'chalk';
2
2
  import * as oox from '../index.js';
3
+ import { randomUUID } from 'node:crypto';
3
4
  const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms));
4
- async function connect(url, prevError = null) {
5
- const adapter = oox.rpcKeepAliveConnectionAdapters.get(oox.config.registryAdapter);
5
+ async function connect(identify, prevError = null) {
6
+ let url = '';
7
+ let connectionData;
8
+ if (typeof identify === 'string') {
9
+ url = identify;
10
+ connectionData = {
11
+ adapter: oox.config.registryAdapter,
12
+ url,
13
+ ip: '',
14
+ name: 'anonymous',
15
+ id: randomUUID(),
16
+ token: oox.config.registryToken,
17
+ };
18
+ }
19
+ else {
20
+ url = String(identify.url || '');
21
+ connectionData = identify;
22
+ }
23
+ if (!connectionData.name)
24
+ connectionData.name = 'anonymous';
25
+ if (!connectionData.id)
26
+ connectionData.id = randomUUID();
27
+ if (!connectionData.token)
28
+ connectionData.token = oox.config.registryToken;
29
+ if (!connectionData.adapter)
30
+ connectionData.adapter = oox.config.registryAdapter;
31
+ const adapter = oox.keepAliveConnectionAdapters.get(connectionData.adapter);
32
+ if (!adapter) {
33
+ console.error(chalk.red('[Registry]'), `Registry Adapter ${chalk.bold(connectionData.adapter)} is not found`);
34
+ return;
35
+ }
36
+ if (!url) {
37
+ console.error(chalk.red('[Registry]'), `Registry URL ${chalk.bold(url)} is empty`);
38
+ return;
39
+ }
6
40
  try {
7
- const connection = await adapter.open(url);
8
- onConnection(connection, url);
41
+ const connection = await adapter.open(connectionData);
42
+ connection.isRegistry = true;
43
+ onConnection(connection);
9
44
  }
10
45
  catch (error) {
11
46
  if (!prevError)
12
- console.log(chalk.red('[Registry]'), chalk.underline.red(`${url}`), 'error.');
47
+ console.error(chalk.red('[Registry]'), chalk.underline.red(`${url}`), 'error.');
13
48
  await delay(2000);
14
- connect(url, error);
49
+ connect(identify, error);
15
50
  }
16
51
  }
17
- async function onConnection(connection, url) {
52
+ async function onConnection(connection) {
18
53
  const { name } = connection.data;
19
54
  connection.once('disconnect', async () => {
20
- console.log(chalk.red('[Registry]'), `Service<${name}>`, chalk.underline.red(`${connection.data.url || url}`), 'disconnected.');
55
+ console.error(chalk.red('[Registry]'), `Service<${name}>`, chalk.underline.red(`${connection.data.url || name}`), 'disconnected.');
21
56
  await delay(1000);
22
- connect(url);
57
+ connect(connection.data);
23
58
  });
24
- console.log(chalk.green('[Registry]'), `Service<${name}>`, chalk.underline.green(`${connection.data.url || url}`), 'connected.');
59
+ console.info(chalk.green('[Registry]'), `Service<${name}>`, chalk.underline.green(`${connection.data.url || name}`), 'connected.');
25
60
  }
26
- export async function registry(urls) {
27
- if ('string' === typeof urls)
28
- urls = [urls];
29
- for (const url of urls) {
30
- connect(url);
61
+ export async function registry(identifies) {
62
+ for (const identify of identifies) {
63
+ connect(identify);
31
64
  }
32
65
  }
package/bin/starter.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import * as path from 'node:path';
2
2
  import chalk from 'chalk';
3
+ import { formatters as configFormatters } from '../config.js';
3
4
  import * as oox from '../index.js';
4
5
  import { buildConfig } from './configurer.js';
5
6
  import { registry } from './register.js';
@@ -17,6 +18,8 @@ function getEntryInfo(env, entryFilename) {
17
18
  const directory = fullDirectory.split(path.sep).pop();
18
19
  const groupFullDirectory = env.group ? path.resolve(env.group) : '';
19
20
  const entryMatch = filename.match(entryMatchRegExp);
21
+ if (!entryMatch)
22
+ throw new Error('Invalid entry file name: ' + entryFilename);
20
23
  const entryFilenameWithoutExtension = entryMatch[1];
21
24
  const name = entryFilenameWithoutExtension === 'index' && groupFullDirectory !== fullDirectory ? directory : entryFilenameWithoutExtension;
22
25
  return { name, path: fullPath, group: groupFullDirectory };
@@ -37,6 +40,11 @@ export async function configure(env, entryFilename) {
37
40
  path: entryInfo.path.replace(/\\/g, '/'),
38
41
  group: entryInfo.group.replace(/\\/g, '/'),
39
42
  };
43
+ for (const key in oox.config) {
44
+ if (key in configFormatters) {
45
+ oox.config[key] = configFormatters[key](oox.config[key]);
46
+ }
47
+ }
40
48
  // 模块配置
41
49
  oox.modules.setConfig(oox.config);
42
50
  oox.emit('app:configured');
package/config.js ADDED
@@ -0,0 +1,116 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { getIPAddress } from "./utils.js";
3
+ export class Config {
4
+ // 服务ID
5
+ id = randomUUID();
6
+ // 服务名称
7
+ name = 'local';
8
+ // 服务列表路径
9
+ group = '';
10
+ // 非服务模块
11
+ ignore = [];
12
+ // 启动文件
13
+ entryInfo = {
14
+ // 启动文件路径
15
+ path: '',
16
+ // 服务列表路径
17
+ group: '',
18
+ };
19
+ // 主机地址
20
+ host = getIPAddress(4)[0];
21
+ // 默认监听端口
22
+ port = 0;
23
+ // 默认跨域设置
24
+ origin = '';
25
+ // 默认返回错误调用栈信息信息
26
+ errorStack = true;
27
+ // 是否开启服务注册功能
28
+ isRegistry = false;
29
+ // 服务注册列表
30
+ registry = [];
31
+ // 注册服务令牌
32
+ registryToken = '';
33
+ // 服务注册适配器
34
+ registryAdapter = 'socketio';
35
+ // 当前服务的令牌,验证成功则忽略allow配置
36
+ token = randomUUID();
37
+ allow = {
38
+ // 是否允许未验证令牌连接
39
+ untrusted: false,
40
+ // 是否允许指定IP连接
41
+ ip: [],
42
+ // 是否允许指定名称连接
43
+ caller: [],
44
+ };
45
+ }
46
+ export const formatters = {
47
+ id: function (value) {
48
+ return String(value || randomUUID());
49
+ },
50
+ name: function (value) {
51
+ return String(value || 'local');
52
+ },
53
+ group: function (value) {
54
+ return String(value || '');
55
+ },
56
+ ignore: function (value) {
57
+ if (Array.isArray(value))
58
+ return value;
59
+ if (typeof value === 'string')
60
+ return value.split(',');
61
+ return [];
62
+ },
63
+ entryInfo: function (value) {
64
+ return value;
65
+ },
66
+ host: function (value) {
67
+ return String(value || getIPAddress(4)[0]);
68
+ },
69
+ port: function (value) {
70
+ return Number(value) || 0;
71
+ },
72
+ origin: function (value) {
73
+ if (value === '*')
74
+ return value;
75
+ if (Array.isArray(value))
76
+ return value;
77
+ if (typeof value === 'string')
78
+ return value.split(',');
79
+ return '';
80
+ },
81
+ errorStack: function (value) {
82
+ return Boolean(value);
83
+ },
84
+ isRegistry: function (value) {
85
+ return Boolean(value);
86
+ },
87
+ registry: function (value) {
88
+ if ('string' === typeof value)
89
+ value = value.split(',');
90
+ if (value && !Array.isArray(value))
91
+ value = [value];
92
+ return value;
93
+ },
94
+ registryToken: function (value) {
95
+ return String(value || '');
96
+ },
97
+ registryAdapter: function (value) {
98
+ return String(value || '');
99
+ },
100
+ token: function (value) {
101
+ return String(value || '');
102
+ },
103
+ allow: function (value) {
104
+ value = value || {
105
+ untrusted: false,
106
+ ip: [],
107
+ caller: [],
108
+ };
109
+ const { ip, caller } = value;
110
+ if ('string' === typeof ip)
111
+ value.ip = ip.split(',');
112
+ if ('string' === typeof caller)
113
+ value.caller = caller.split(',');
114
+ return value;
115
+ }
116
+ };
package/index.js CHANGED
@@ -1,14 +1,10 @@
1
1
  import { randomUUID } from 'node:crypto';
2
- import * as app from './app.js';
3
- import { getIPAddress } from './utils.js';
4
- import Module, { ModuleConfig } from './modules/module.js';
5
- import Modules from './modules/index.js';
6
- import { RPCKeepAliveConnection, rpcKeepAliveConnectionAdapters, keepAliveConnections, enabledKeepAliveConnections } from './rpc-keepalive-connection.js';
7
- export { RPCKeepAliveConnection, rpcKeepAliveConnectionAdapters, keepAliveConnections, enabledKeepAliveConnections };
8
- export { Module, ModuleConfig };
9
- export const modules = new Modules;
10
- export const { asyncStore, setMethods, getMethods, kvMethods, sourceKVMethods, call, execute, logger, on, once, off, emit, } = app;
11
- export class Context extends app.Context {
2
+ import { asyncStore, AppContext, } from './app.js';
3
+ import * as registry from './registry.js';
4
+ import { Config } from './config.js';
5
+ import Module from './modules/module.js';
6
+ import { KeepAliveConnection, keepAliveConnections, enabledKeepAliveConnections, KeepAliveConnectionError, } from './keepalive-connection.js';
7
+ export class Context extends AppContext {
12
8
  // 请求溯源IP
13
9
  sourceIP = '';
14
10
  // 请求者IP
@@ -19,41 +15,21 @@ export class Context extends app.Context {
19
15
  callerId = '';
20
16
  // 请求者连接把柄
21
17
  connection;
18
+ constructor(context) {
19
+ super();
20
+ if (context) {
21
+ if ('toJSON' in context && typeof context.toJSON !== 'function') {
22
+ throw new Error('Context.toJSON must be a function');
23
+ }
24
+ Object.assign(this, context);
25
+ }
26
+ }
22
27
  toJSON() {
23
28
  const context = Object.assign({}, this);
24
29
  delete context.connection;
25
30
  return context;
26
31
  }
27
32
  }
28
- export class Config {
29
- // 服务ID
30
- id = randomUUID();
31
- // 服务名称
32
- name = 'local';
33
- // 服务列表路径
34
- group = '';
35
- // 非服务模块
36
- ignore = [];
37
- // 启动文件
38
- entryInfo = {
39
- // 启动文件路径
40
- path: '',
41
- // 服务列表路径
42
- group: '',
43
- };
44
- // 主机地址
45
- host = getIPAddress(4)[0];
46
- // 默认监听端口
47
- port = 0;
48
- // 默认跨域设置
49
- origin = '';
50
- // 默认返回错误调用栈信息信息
51
- errorStack = true;
52
- // 服务注册列表
53
- registry = [];
54
- // 服务注册适配器
55
- registryAdapter = 'socketio';
56
- }
57
33
  export const config = new Config();
58
34
  let genTraceIdFunction = () => randomUUID();
59
35
  export function setGenTraceIdFunction(fn) {
@@ -69,7 +45,7 @@ export function genTraceId() {
69
45
  * 获取链路跟踪上下文
70
46
  */
71
47
  export function genContext(context) {
72
- const newContext = Object.assign(new Context(), context);
48
+ const newContext = new Context(context);
73
49
  if (!newContext.traceId)
74
50
  newContext.traceId = genTraceId();
75
51
  if (!newContext.sourceIP)
@@ -82,19 +58,62 @@ export function getContext() {
82
58
  }
83
59
  export async function serve() {
84
60
  await modules.serve();
61
+ registry.startBroadcastTimer();
85
62
  }
86
63
  export async function stop() {
87
64
  await modules.stop();
65
+ registry.stopBroadcastTimer();
88
66
  }
67
+ /**
68
+ * 检查连接是否被允许
69
+ * @param ip 连接主机 IP
70
+ * @param caller 连接服务名称
71
+ * @param token 令牌
72
+ * @returns 是否被允许
73
+ */
74
+ export function checkAllow(ip, caller, token) {
75
+ // check trusted token
76
+ const trusted = !config.token || token === config.token;
77
+ if (!trusted) {
78
+ const { allow } = config;
79
+ if (!allow.untrusted) {
80
+ return { allow: false, reason: 'untrusted' };
81
+ }
82
+ if (Array.isArray(allow.ip) && allow.ip.length) {
83
+ let allowed = allow.ip.some(item => item === '*' || new RegExp(item).test(ip));
84
+ if (!allowed)
85
+ return { allow: false, reason: 'not allow ip=' + ip };
86
+ }
87
+ if (Array.isArray(allow.caller) && allow.caller.length) {
88
+ let allowed = allow.caller.some(item => item === '*' || new RegExp(item).test(caller));
89
+ if (!allowed)
90
+ return { allow: false, reason: 'not allow caller=' + caller };
91
+ }
92
+ }
93
+ return { allow: true };
94
+ }
95
+ /**
96
+ * 添加长连接
97
+ * @throws {KeepAliveConnectionError} 连接已存在
98
+ * @param connection
99
+ */
89
100
  export function addKeepAliveConnection(connection) {
101
+ if (keepAliveConnections.has(connection.data.name, connection.data.id)) {
102
+ connection.disconnect();
103
+ throw new KeepAliveConnectionError('Connection already exists', connection);
104
+ }
90
105
  keepAliveConnections.add(connection);
91
106
  }
92
107
  export function removeKeepAliveConnection(arg1, arg2) {
93
108
  if (typeof arg1 === 'string') {
109
+ const connection = keepAliveConnections.get(arg1, arg2);
110
+ if (connection)
111
+ connection.disconnect();
94
112
  keepAliveConnections.remove(arg1, arg2);
95
113
  enabledKeepAliveConnections.remove(arg1, arg2);
96
114
  }
97
115
  else {
116
+ arg1.disconnect();
98
117
  keepAliveConnections.remove(arg1);
99
118
  enabledKeepAliveConnections.remove(arg1);
100
119
  }
@@ -119,16 +138,23 @@ export async function rpc(arg1, action, params, context) {
119
138
  if (!context || !context.traceId) {
120
139
  context = getContext();
121
140
  }
122
- let connection;
123
- if (arg1 instanceof RPCKeepAliveConnection) {
141
+ let connection = null;
142
+ if (arg1 instanceof KeepAliveConnection) {
124
143
  connection = arg1;
125
144
  }
126
145
  else if ('string' === typeof arg1) {
127
146
  connection = loadBalancePolicy(arg1);
128
- if (!connection)
129
- throw new Error(`Connection<${arg1}> not found`);
130
147
  }
131
148
  else
132
149
  throw new Error(`Unknown rpc arg1<${arg1}>`);
150
+ if (!connection)
151
+ throw new Error(`Connection<${arg1}> not found`);
133
152
  return connection.adapter.rpc(connection, action, params, context);
134
153
  }
154
+ export * from './app.js';
155
+ export * from './keepalive-connection.js';
156
+ export { Module };
157
+ export { registry };
158
+ // 放在最后导入,避免循环依赖问题
159
+ import * as modules from './modules/index.js';
160
+ export { modules };
@@ -1,5 +1,14 @@
1
1
  import EventEmitter from 'node:events';
2
- export class RPCKeepAliveConnection extends EventEmitter {
2
+ export class KeepAliveConnectionError extends Error {
3
+ connection;
4
+ constructor(message, connection) {
5
+ super(message);
6
+ this.connection = connection;
7
+ }
8
+ }
9
+ export class KeepAliveConnection extends EventEmitter {
10
+ // 是否为注册服务连接
11
+ isRegistry = false;
3
12
  // 连接是否可用
4
13
  #enabled = false;
5
14
  data;
@@ -9,8 +18,14 @@ export class RPCKeepAliveConnection extends EventEmitter {
9
18
  super();
10
19
  this.adapter = adapter;
11
20
  this.nativeConnection = nativeConnection;
12
- this.data = data;
21
+ this.data = Object.assign(data, {
22
+ adapter: adapter.name,
23
+ });
13
24
  }
25
+ /**
26
+ * 设置连接是否可用,并自动触发enabled或disabled事件
27
+ * @param enabled 是否可用
28
+ */
14
29
  set enabled(enabled) {
15
30
  if (enabled !== this.#enabled) {
16
31
  this.#enabled = enabled;
@@ -26,21 +41,26 @@ export class RPCKeepAliveConnection extends EventEmitter {
26
41
  get enabled() {
27
42
  return this.#enabled;
28
43
  }
29
- updateId(newId) {
44
+ /**
45
+ * enable & mount connection
46
+ * @param params
47
+ * @returns
48
+ */
49
+ ready(params) {
50
+ // 连接已就绪,不允许重复就绪
51
+ if (this.enabled)
52
+ return;
53
+ const { id, name } = params;
30
54
  keepAliveConnections.remove(this);
31
- this.data.id = newId;
55
+ // 更新连接数据
56
+ this.data.id = id;
57
+ this.data.name = name;
32
58
  keepAliveConnections.add(this);
59
+ this.enabled = true;
33
60
  }
34
- updateName(newName) {
35
- keepAliveConnections.remove(this);
36
- this.data.name = newName;
37
- keepAliveConnections.add(this);
38
- }
39
- updateIdAndName(newId, newName) {
40
- keepAliveConnections.remove(this);
41
- this.data.id = newId;
42
- this.data.name = newName;
43
- keepAliveConnections.add(this);
61
+ disconnect() {
62
+ this.enabled = false;
63
+ this.nativeConnection.disconnect();
44
64
  }
45
65
  }
46
66
  export class KeepAliveConnectionStore {
@@ -74,7 +94,7 @@ export class KeepAliveConnectionStore {
74
94
  }
75
95
  }
76
96
  remove(name, id) {
77
- if (name instanceof RPCKeepAliveConnection) {
97
+ if (name instanceof KeepAliveConnection) {
78
98
  id = name.data.id;
79
99
  name = name.data.name;
80
100
  }
@@ -84,6 +104,6 @@ export class KeepAliveConnectionStore {
84
104
  }
85
105
  }
86
106
  }
87
- export const rpcKeepAliveConnectionAdapters = new Map();
107
+ export const keepAliveConnectionAdapters = new Map();
88
108
  export const keepAliveConnections = new KeepAliveConnectionStore();
89
109
  export const enabledKeepAliveConnections = new KeepAliveConnectionStore();