imean-service-engine 1.2.3 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/mod.cjs CHANGED
@@ -1,12 +1,12 @@
1
1
  'use strict';
2
2
 
3
3
  var zod = require('zod');
4
+ var ejson4 = require('ejson');
5
+ var lruCache = require('lru-cache');
4
6
  var nodeServer = require('@hono/node-server');
5
- var ejson3 = require('ejson');
6
7
  var etcd3 = require('etcd3');
7
8
  var fs = require('fs-extra');
8
9
  var hono = require('hono');
9
- var lruCache = require('lru-cache');
10
10
  var api = require('@opentelemetry/api');
11
11
  var winston = require('winston');
12
12
  var prettier = require('prettier');
@@ -17,7 +17,7 @@ var dayjs = require('dayjs');
17
17
 
18
18
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
19
19
 
20
- var ejson3__default = /*#__PURE__*/_interopDefault(ejson3);
20
+ var ejson4__default = /*#__PURE__*/_interopDefault(ejson4);
21
21
  var fs__default = /*#__PURE__*/_interopDefault(fs);
22
22
  var winston__default = /*#__PURE__*/_interopDefault(winston);
23
23
  var prettier__default = /*#__PURE__*/_interopDefault(prettier);
@@ -25,6 +25,38 @@ var crypto2__default = /*#__PURE__*/_interopDefault(crypto2);
25
25
  var dayjs__default = /*#__PURE__*/_interopDefault(dayjs);
26
26
 
27
27
  // mod.ts
28
+ var CacheAdapter = class {
29
+ };
30
+ var RedisCacheAdapter = class extends CacheAdapter {
31
+ constructor(redis) {
32
+ super();
33
+ this.redis = redis;
34
+ }
35
+ async get(key) {
36
+ const value = await this.redis.get(key);
37
+ return value ? ejson4__default.default.parse(value) : null;
38
+ }
39
+ async set(key, value, ttl) {
40
+ return this.redis.set(key, ejson4__default.default.stringify(value), "EX", ttl / 1e3);
41
+ }
42
+ };
43
+ var MemoryCacheAdapter = class extends CacheAdapter {
44
+ cache;
45
+ constructor(options) {
46
+ super();
47
+ this.cache = new lruCache.LRUCache({
48
+ max: 1e3,
49
+ ttl: 1e3 * 60 * 10,
50
+ ...options
51
+ });
52
+ }
53
+ async get(key) {
54
+ return this.cache.get(key);
55
+ }
56
+ async set(key, value, ttl) {
57
+ this.cache.set(key, value, { ttl });
58
+ }
59
+ };
28
60
  var ACTION_METADATA = Symbol("action:metadata");
29
61
  function Action(options) {
30
62
  options.params = options.params || [];
@@ -53,7 +85,8 @@ function Action(options) {
53
85
  printError: options.printError ?? false,
54
86
  cache: options.cache,
55
87
  ttl: options.ttl,
56
- stream: options.stream
88
+ stream: options.stream,
89
+ mcp: options.mcp
57
90
  };
58
91
  prototype[ACTION_METADATA] = existingMetadata;
59
92
  });
@@ -89,6 +122,8 @@ var ScheduleMode = /* @__PURE__ */ ((ScheduleMode2) => {
89
122
  ScheduleMode2["FIXED_DELAY"] = "FIXED_DELAY";
90
123
  return ScheduleMode2;
91
124
  })(ScheduleMode || {});
125
+ var Plugin = class {
126
+ };
92
127
  var logger = winston__default.default.createLogger({
93
128
  level: "info",
94
129
  transports: [
@@ -297,9 +332,6 @@ function getZodTypeString(schema, defaultOptional = false) {
297
332
  }).join("; ");
298
333
  return `{ ${props} }`;
299
334
  }
300
- case "ZodEnum": {
301
- return def.values.map((opt) => `"${opt}"`).join(" | ");
302
- }
303
335
  case "ZodUnion": {
304
336
  return def.options.map((opt) => processType(opt)).join(" | ");
305
337
  }
@@ -328,8 +360,8 @@ function getZodTypeString(schema, defaultOptional = false) {
328
360
  case "ZodUnknown": {
329
361
  return "unknown";
330
362
  }
331
- case "ZodEnum ": {
332
- return def.values.map((opt) => `"${opt}"`).join(" | ");
363
+ case "ZodEnum": {
364
+ return "(" + def.values.map((opt) => `"${opt}"`).join(" | ") + ")";
333
365
  }
334
366
  case "ZodDefault": {
335
367
  return processType(def.innerType);
@@ -455,7 +487,7 @@ var ActionHandler = class {
455
487
  let args;
456
488
  if (typeof req === "string") {
457
489
  try {
458
- args = Object.values(ejson3__default.default.parse(req));
490
+ args = Object.values(ejson4__default.default.parse(req));
459
491
  } catch (error) {
460
492
  throw new Error(`Invalid request body: ${error.message}`);
461
493
  }
@@ -473,7 +505,7 @@ var ActionHandler = class {
473
505
  }
474
506
  });
475
507
  }
476
- const requestHash = hashText(ejson3__default.default.stringify(args));
508
+ const requestHash = hashText(ejson4__default.default.stringify(args));
477
509
  span.setAttribute("requestHash", requestHash);
478
510
  return { args, requestHash };
479
511
  } finally {
@@ -484,7 +516,7 @@ var ActionHandler = class {
484
516
  const span = api.trace.getActiveSpan();
485
517
  const { args, requestHash } = await this._validate(req);
486
518
  const cacheHash = typeof this.metadata.cache === "function" ? this.metadata.cache(...args) : requestHash;
487
- const cacheKey = `${this.moduleName}.${this.actionName}.${hashText(ejson3__default.default.stringify(cacheHash))}`;
519
+ const cacheKey = `${this.microservice.options.name}:cache:${this.moduleName}:${this.actionName}:${hashText(ejson4__default.default.stringify(cacheHash))}`;
488
520
  span?.setAttribute("args", JSON.stringify(args));
489
521
  if (!this.metadata.stream && this.metadata.cache && !this.microservice.options.disableCache) {
490
522
  const cached = await this.microservice.cache.get(cacheKey);
@@ -539,7 +571,7 @@ var ActionHandler = class {
539
571
  data: parsedResult,
540
572
  expireAt: this.metadata.ttl ? now + this.metadata.ttl : void 0
541
573
  },
542
- { ttl: this.metadata.ttl }
574
+ this.metadata.ttl ?? 1e3
543
575
  );
544
576
  }
545
577
  return parsedResult;
@@ -568,7 +600,7 @@ var WebSocketHandler = class {
568
600
  this.sockets.add(ws);
569
601
  }
570
602
  async encodeMessage(message) {
571
- const jsonStr = ejson3__default.default.stringify(message);
603
+ const jsonStr = ejson4__default.default.stringify(message);
572
604
  const data = new TextEncoder().encode(jsonStr);
573
605
  const compressed = await brotli.compress(data, 6);
574
606
  return compressed;
@@ -577,7 +609,7 @@ var WebSocketHandler = class {
577
609
  try {
578
610
  const decompressed = await brotli.decompress(new Uint8Array(data));
579
611
  const jsonStr = new TextDecoder().decode(decompressed);
580
- return ejson3__default.default.parse(jsonStr);
612
+ return ejson4__default.default.parse(jsonStr);
581
613
  } catch (error) {
582
614
  console.error("Error decoding message", error);
583
615
  throw error;
@@ -638,7 +670,7 @@ var WebSocketHandler = class {
638
670
  false
639
671
  );
640
672
  ws.send(
641
- ejson3__default.default.stringify({
673
+ ejson4__default.default.stringify({
642
674
  id: message.id,
643
675
  success: false,
644
676
  error: "Request timeout"
@@ -754,17 +786,24 @@ var Microservice = class {
754
786
  this.options = {
755
787
  prefix: "/api",
756
788
  name: "microservice",
789
+ version: "0.0.1",
790
+ env: "default",
791
+ printError: false,
792
+ disableCache: false,
793
+ generateClient: false,
794
+ etcd: false,
795
+ events: {},
796
+ websocket: { enabled: false },
797
+ cacheAdapter: new MemoryCacheAdapter(),
798
+ plugins: [],
757
799
  ...options
758
800
  };
801
+ this.cache = this.options.cacheAdapter;
759
802
  this.fetch = this.app.request;
760
803
  this.waitingInitialization = this.initialize();
761
804
  ServiceContext.service = this;
762
805
  }
763
806
  async initialize() {
764
- this.cache = new lruCache.LRUCache({
765
- max: 1e3,
766
- ttl: 1e3 * 60 * 10
767
- });
768
807
  if (this.options.etcd) {
769
808
  this.initEtcd(this.options.etcd);
770
809
  }
@@ -775,6 +814,7 @@ var Microservice = class {
775
814
  this.initStatsEventManager();
776
815
  }
777
816
  await this.registerService(true);
817
+ await this.initPlugins();
778
818
  }
779
819
  async initModules() {
780
820
  for (const ModuleClass of this.options.modules) {
@@ -799,7 +839,7 @@ var Microservice = class {
799
839
  );
800
840
  this.actionHandlers.set(`${moduleName}.${actionName}`, handler);
801
841
  logger_default.info(
802
- `[ \u6CE8\u518C\u52A8\u4F5C ] ${moduleName}.${actionName} ${actionMetadata.description}`
842
+ `[ \u6CE8\u518C\u52A8\u4F5C ] ${moduleName}.${actionName} ${actionMetadata.description} ${actionMetadata.mcp ? "MCP:" + actionMetadata.mcp?.type : ""}`
803
843
  );
804
844
  }
805
845
  const schedules = getScheduleMetadata(ModuleClass.prototype);
@@ -1128,12 +1168,12 @@ Received SIGINT signal`);
1128
1168
  try {
1129
1169
  for await (const value of result) {
1130
1170
  const response = { value, done: false };
1131
- const chunk = encoder.encode(ejson3__default.default.stringify(response) + "\n");
1171
+ const chunk = encoder.encode(ejson4__default.default.stringify(response) + "\n");
1132
1172
  controller.enqueue(chunk);
1133
1173
  }
1134
1174
  controller.enqueue(
1135
1175
  encoder.encode(
1136
- ejson3__default.default.stringify({ done: true }) + "\n"
1176
+ ejson4__default.default.stringify({ done: true }) + "\n"
1137
1177
  )
1138
1178
  );
1139
1179
  controller.close();
@@ -1143,7 +1183,7 @@ Received SIGINT signal`);
1143
1183
  done: true
1144
1184
  };
1145
1185
  controller.enqueue(
1146
- encoder.encode(ejson3__default.default.stringify(response) + "\n")
1186
+ encoder.encode(ejson4__default.default.stringify(response) + "\n")
1147
1187
  );
1148
1188
  controller.close();
1149
1189
  }
@@ -1157,37 +1197,21 @@ Received SIGINT signal`);
1157
1197
  }
1158
1198
  });
1159
1199
  }
1160
- return ctx.text(ejson3__default.default.stringify({ success: true, data: result }));
1200
+ return ctx.text(ejson4__default.default.stringify({ success: true, data: result }));
1161
1201
  } catch (error) {
1162
1202
  return ctx.json({ success: false, error: error.message });
1163
1203
  }
1164
1204
  };
1165
- async init() {
1166
- await this.waitingInitialization;
1167
- }
1168
- /**
1169
- * 获取所有注册的服务
1170
- */
1171
- async getServices() {
1172
- if (!this.etcdClient) {
1173
- throw new Error("etcd is not configured");
1205
+ async initPlugins() {
1206
+ for (const plugin of this.options.plugins) {
1207
+ logger_default.info(`[ \u521D\u59CB\u5316\u63D2\u4EF6 ] ${plugin.constructor.name}`);
1208
+ await plugin.initialize(this).catch((e) => {
1209
+ logger_default.error(`[ \u521D\u59CB\u5316\u63D2\u4EF6\u5931\u8D25 ] ${plugin.constructor.name}: ${e}`);
1210
+ });
1174
1211
  }
1175
- const namespace = this.options.etcd?.namespace ? `${this.options.etcd.namespace}/` : "";
1176
- const servicesKey = `${namespace}services`;
1177
- const services = await this.etcdClient.getAll().prefix(servicesKey);
1178
- return Object.values(services).map((value) => JSON.parse(value));
1179
1212
  }
1180
- /**
1181
- * 获取指定服务的实例
1182
- */
1183
- async getServiceInstances(serviceName) {
1184
- if (!this.etcdClient) {
1185
- throw new Error("etcd is not configured");
1186
- }
1187
- const namespace = this.options.etcd?.namespace ? `${this.options.etcd.namespace}/` : "";
1188
- const serviceKey = `${namespace}services/${serviceName}`;
1189
- const instances = await this.etcdClient.getAll().prefix(serviceKey);
1190
- return Object.values(instances).map((value) => JSON.parse(value));
1213
+ async init() {
1214
+ await this.waitingInitialization;
1191
1215
  }
1192
1216
  };
1193
1217
 
@@ -1218,8 +1242,12 @@ Object.defineProperty(exports, "dayjs", {
1218
1242
  get: function () { return dayjs__default.default; }
1219
1243
  });
1220
1244
  exports.Action = Action;
1245
+ exports.CacheAdapter = CacheAdapter;
1246
+ exports.MemoryCacheAdapter = MemoryCacheAdapter;
1221
1247
  exports.Microservice = Microservice;
1222
1248
  exports.Module = Module;
1249
+ exports.Plugin = Plugin;
1250
+ exports.RedisCacheAdapter = RedisCacheAdapter;
1223
1251
  exports.Schedule = Schedule;
1224
1252
  exports.ScheduleMode = ScheduleMode;
1225
1253
  exports.ServiceContext = ServiceContext;
package/dist/mod.d.cts CHANGED
@@ -1,12 +1,33 @@
1
1
  import { z } from 'zod';
2
2
  export * from 'zod';
3
+ import { Redis, Cluster } from 'ioredis';
4
+ import { LRUCache } from 'lru-cache';
3
5
  import { Lease, Etcd3 } from 'etcd3';
4
6
  import { Hono } from 'hono';
5
- import { LRUCache } from 'lru-cache';
6
7
  export { default as dayjs } from 'dayjs';
7
8
  import winston from 'winston';
8
9
 
10
+ declare abstract class CacheAdapter {
11
+ abstract get(key: string): Promise<any>;
12
+ abstract set(key: string, value: any, ttl: number): Promise<any>;
13
+ }
14
+ declare class RedisCacheAdapter extends CacheAdapter {
15
+ private readonly redis;
16
+ constructor(redis: Redis | Cluster);
17
+ get(key: string): Promise<any>;
18
+ set(key: string, value: string, ttl: number): Promise<"OK">;
19
+ }
20
+ declare class MemoryCacheAdapter extends CacheAdapter {
21
+ private cache;
22
+ constructor(options?: LRUCache.Options<string, any, any>);
23
+ get(key: string): Promise<any>;
24
+ set(key: string, value: any, ttl: number): Promise<void>;
25
+ }
26
+
9
27
  type CacheFn = (...args: any[]) => any;
28
+ type McpOptions = {
29
+ type: "tool";
30
+ };
10
31
  interface ActionOptions {
11
32
  params?: z.ZodType<any>[];
12
33
  returns?: z.ZodType<any>;
@@ -16,6 +37,7 @@ interface ActionOptions {
16
37
  cache?: boolean | CacheFn;
17
38
  ttl?: number;
18
39
  stream?: boolean;
40
+ mcp?: McpOptions;
19
41
  }
20
42
  interface ActionMetadata extends ActionOptions {
21
43
  name: string;
@@ -121,7 +143,7 @@ interface MicroserviceOptions {
121
143
  disableCache?: boolean;
122
144
  modules: (new () => any)[];
123
145
  generateClient?: URL | boolean;
124
- etcd?: EtcdConfig;
146
+ etcd?: EtcdConfig | false;
125
147
  events?: StatisticsEventOptions & {
126
148
  onStats?: (event: StatisticsEvent) => void | Promise<void>;
127
149
  onRegister?: (serviceInfo: ServiceInfo) => void | Promise<void>;
@@ -131,6 +153,8 @@ interface MicroserviceOptions {
131
153
  enabled?: boolean;
132
154
  timeout?: number;
133
155
  };
156
+ cacheAdapter?: CacheAdapter;
157
+ plugins?: Plugin[];
134
158
  }
135
159
  interface ModuleInfo extends ModuleOptions {
136
160
  actions: Record<string, ActionMetadata>;
@@ -141,6 +165,9 @@ interface StreamResponse<T = any> {
141
165
  done: boolean;
142
166
  error?: string;
143
167
  }
168
+ declare abstract class Plugin {
169
+ abstract initialize(engine: Microservice): Promise<void>;
170
+ }
144
171
 
145
172
  declare class ActionHandler {
146
173
  private moduleInstance;
@@ -176,8 +203,8 @@ declare class Microservice {
176
203
  private actionHandlers;
177
204
  modules: Map<string, ModuleInfo>;
178
205
  readonly fetch: typeof fetch;
179
- options: MicroserviceOptions;
180
- cache: LRUCache<string, any>;
206
+ options: Required<MicroserviceOptions>;
207
+ cache: CacheAdapter;
181
208
  serviceId: string;
182
209
  constructor(options: MicroserviceOptions);
183
210
  private initialize;
@@ -205,7 +232,7 @@ declare class Microservice {
205
232
  /**
206
233
  * 获取所有模块的元数据
207
234
  */
208
- private getModules;
235
+ getModules(withTypes?: boolean): Record<string, ModuleInfo>;
209
236
  /**
210
237
  * 停止服务
211
238
  */
@@ -222,15 +249,8 @@ declare class Microservice {
222
249
  private updateStats;
223
250
  getActionHandler(moduleName: string, actionName: string): ActionHandler;
224
251
  private handleRequest;
252
+ private initPlugins;
225
253
  init(): Promise<void>;
226
- /**
227
- * 获取所有注册的服务
228
- */
229
- getServices(): Promise<ServiceInfo[]>;
230
- /**
231
- * 获取指定服务的实例
232
- */
233
- getServiceInstances(serviceName: string): Promise<ServiceInfo[]>;
234
254
  }
235
255
 
236
256
  interface PreStartChecker {
@@ -259,4 +279,4 @@ declare function Schedule(options: ScheduleOptions): Function;
259
279
 
260
280
  declare const logger: winston.Logger;
261
281
 
262
- export { Action, type ActionErrorEvent, type ActionMetadata, type ActionOptions, type CacheFn, type EtcdConfig, type EventServiceInfo, Microservice, type MicroserviceOptions, Module, type ModuleInfo, type ModuleMetadata, type ModuleOptions, type PreStartChecker, Schedule, type ScheduleMetadata, ScheduleMode, type ScheduleOptions, ServiceContext, type ServiceInfo, type ServiceStats, type StatisticsEvent, type StreamResponse, logger, startCheck };
282
+ export { Action, type ActionErrorEvent, type ActionMetadata, type ActionOptions, CacheAdapter, type CacheFn, type EtcdConfig, type EventServiceInfo, type McpOptions, MemoryCacheAdapter, Microservice, type MicroserviceOptions, Module, type ModuleInfo, type ModuleMetadata, type ModuleOptions, Plugin, type PreStartChecker, RedisCacheAdapter, Schedule, type ScheduleMetadata, ScheduleMode, type ScheduleOptions, ServiceContext, type ServiceInfo, type ServiceStats, type StatisticsEvent, type StreamResponse, logger, startCheck };
package/dist/mod.d.ts CHANGED
@@ -1,12 +1,33 @@
1
1
  import { z } from 'zod';
2
2
  export * from 'zod';
3
+ import { Redis, Cluster } from 'ioredis';
4
+ import { LRUCache } from 'lru-cache';
3
5
  import { Lease, Etcd3 } from 'etcd3';
4
6
  import { Hono } from 'hono';
5
- import { LRUCache } from 'lru-cache';
6
7
  export { default as dayjs } from 'dayjs';
7
8
  import winston from 'winston';
8
9
 
10
+ declare abstract class CacheAdapter {
11
+ abstract get(key: string): Promise<any>;
12
+ abstract set(key: string, value: any, ttl: number): Promise<any>;
13
+ }
14
+ declare class RedisCacheAdapter extends CacheAdapter {
15
+ private readonly redis;
16
+ constructor(redis: Redis | Cluster);
17
+ get(key: string): Promise<any>;
18
+ set(key: string, value: string, ttl: number): Promise<"OK">;
19
+ }
20
+ declare class MemoryCacheAdapter extends CacheAdapter {
21
+ private cache;
22
+ constructor(options?: LRUCache.Options<string, any, any>);
23
+ get(key: string): Promise<any>;
24
+ set(key: string, value: any, ttl: number): Promise<void>;
25
+ }
26
+
9
27
  type CacheFn = (...args: any[]) => any;
28
+ type McpOptions = {
29
+ type: "tool";
30
+ };
10
31
  interface ActionOptions {
11
32
  params?: z.ZodType<any>[];
12
33
  returns?: z.ZodType<any>;
@@ -16,6 +37,7 @@ interface ActionOptions {
16
37
  cache?: boolean | CacheFn;
17
38
  ttl?: number;
18
39
  stream?: boolean;
40
+ mcp?: McpOptions;
19
41
  }
20
42
  interface ActionMetadata extends ActionOptions {
21
43
  name: string;
@@ -121,7 +143,7 @@ interface MicroserviceOptions {
121
143
  disableCache?: boolean;
122
144
  modules: (new () => any)[];
123
145
  generateClient?: URL | boolean;
124
- etcd?: EtcdConfig;
146
+ etcd?: EtcdConfig | false;
125
147
  events?: StatisticsEventOptions & {
126
148
  onStats?: (event: StatisticsEvent) => void | Promise<void>;
127
149
  onRegister?: (serviceInfo: ServiceInfo) => void | Promise<void>;
@@ -131,6 +153,8 @@ interface MicroserviceOptions {
131
153
  enabled?: boolean;
132
154
  timeout?: number;
133
155
  };
156
+ cacheAdapter?: CacheAdapter;
157
+ plugins?: Plugin[];
134
158
  }
135
159
  interface ModuleInfo extends ModuleOptions {
136
160
  actions: Record<string, ActionMetadata>;
@@ -141,6 +165,9 @@ interface StreamResponse<T = any> {
141
165
  done: boolean;
142
166
  error?: string;
143
167
  }
168
+ declare abstract class Plugin {
169
+ abstract initialize(engine: Microservice): Promise<void>;
170
+ }
144
171
 
145
172
  declare class ActionHandler {
146
173
  private moduleInstance;
@@ -176,8 +203,8 @@ declare class Microservice {
176
203
  private actionHandlers;
177
204
  modules: Map<string, ModuleInfo>;
178
205
  readonly fetch: typeof fetch;
179
- options: MicroserviceOptions;
180
- cache: LRUCache<string, any>;
206
+ options: Required<MicroserviceOptions>;
207
+ cache: CacheAdapter;
181
208
  serviceId: string;
182
209
  constructor(options: MicroserviceOptions);
183
210
  private initialize;
@@ -205,7 +232,7 @@ declare class Microservice {
205
232
  /**
206
233
  * 获取所有模块的元数据
207
234
  */
208
- private getModules;
235
+ getModules(withTypes?: boolean): Record<string, ModuleInfo>;
209
236
  /**
210
237
  * 停止服务
211
238
  */
@@ -222,15 +249,8 @@ declare class Microservice {
222
249
  private updateStats;
223
250
  getActionHandler(moduleName: string, actionName: string): ActionHandler;
224
251
  private handleRequest;
252
+ private initPlugins;
225
253
  init(): Promise<void>;
226
- /**
227
- * 获取所有注册的服务
228
- */
229
- getServices(): Promise<ServiceInfo[]>;
230
- /**
231
- * 获取指定服务的实例
232
- */
233
- getServiceInstances(serviceName: string): Promise<ServiceInfo[]>;
234
254
  }
235
255
 
236
256
  interface PreStartChecker {
@@ -259,4 +279,4 @@ declare function Schedule(options: ScheduleOptions): Function;
259
279
 
260
280
  declare const logger: winston.Logger;
261
281
 
262
- export { Action, type ActionErrorEvent, type ActionMetadata, type ActionOptions, type CacheFn, type EtcdConfig, type EventServiceInfo, Microservice, type MicroserviceOptions, Module, type ModuleInfo, type ModuleMetadata, type ModuleOptions, type PreStartChecker, Schedule, type ScheduleMetadata, ScheduleMode, type ScheduleOptions, ServiceContext, type ServiceInfo, type ServiceStats, type StatisticsEvent, type StreamResponse, logger, startCheck };
282
+ export { Action, type ActionErrorEvent, type ActionMetadata, type ActionOptions, CacheAdapter, type CacheFn, type EtcdConfig, type EventServiceInfo, type McpOptions, MemoryCacheAdapter, Microservice, type MicroserviceOptions, Module, type ModuleInfo, type ModuleMetadata, type ModuleOptions, Plugin, type PreStartChecker, RedisCacheAdapter, Schedule, type ScheduleMetadata, ScheduleMode, type ScheduleOptions, ServiceContext, type ServiceInfo, type ServiceStats, type StatisticsEvent, type StreamResponse, logger, startCheck };
package/dist/mod.js CHANGED
@@ -1,11 +1,11 @@
1
1
  import { z } from 'zod';
2
2
  export * from 'zod';
3
+ import ejson4 from 'ejson';
4
+ import { LRUCache } from 'lru-cache';
3
5
  import { serve } from '@hono/node-server';
4
- import ejson3 from 'ejson';
5
6
  import { Etcd3 } from 'etcd3';
6
7
  import fs from 'fs-extra';
7
8
  import { Hono } from 'hono';
8
- import { LRUCache } from 'lru-cache';
9
9
  import { trace, SpanStatusCode } from '@opentelemetry/api';
10
10
  import winston, { format } from 'winston';
11
11
  import prettier from 'prettier';
@@ -16,6 +16,38 @@ import { createNodeWebSocket } from '@hono/node-ws';
16
16
  export { default as dayjs } from 'dayjs';
17
17
 
18
18
  // mod.ts
19
+ var CacheAdapter = class {
20
+ };
21
+ var RedisCacheAdapter = class extends CacheAdapter {
22
+ constructor(redis) {
23
+ super();
24
+ this.redis = redis;
25
+ }
26
+ async get(key) {
27
+ const value = await this.redis.get(key);
28
+ return value ? ejson4.parse(value) : null;
29
+ }
30
+ async set(key, value, ttl) {
31
+ return this.redis.set(key, ejson4.stringify(value), "EX", ttl / 1e3);
32
+ }
33
+ };
34
+ var MemoryCacheAdapter = class extends CacheAdapter {
35
+ cache;
36
+ constructor(options) {
37
+ super();
38
+ this.cache = new LRUCache({
39
+ max: 1e3,
40
+ ttl: 1e3 * 60 * 10,
41
+ ...options
42
+ });
43
+ }
44
+ async get(key) {
45
+ return this.cache.get(key);
46
+ }
47
+ async set(key, value, ttl) {
48
+ this.cache.set(key, value, { ttl });
49
+ }
50
+ };
19
51
  var ACTION_METADATA = Symbol("action:metadata");
20
52
  function Action(options) {
21
53
  options.params = options.params || [];
@@ -44,7 +76,8 @@ function Action(options) {
44
76
  printError: options.printError ?? false,
45
77
  cache: options.cache,
46
78
  ttl: options.ttl,
47
- stream: options.stream
79
+ stream: options.stream,
80
+ mcp: options.mcp
48
81
  };
49
82
  prototype[ACTION_METADATA] = existingMetadata;
50
83
  });
@@ -80,6 +113,8 @@ var ScheduleMode = /* @__PURE__ */ ((ScheduleMode2) => {
80
113
  ScheduleMode2["FIXED_DELAY"] = "FIXED_DELAY";
81
114
  return ScheduleMode2;
82
115
  })(ScheduleMode || {});
116
+ var Plugin = class {
117
+ };
83
118
  var logger = winston.createLogger({
84
119
  level: "info",
85
120
  transports: [
@@ -288,9 +323,6 @@ function getZodTypeString(schema, defaultOptional = false) {
288
323
  }).join("; ");
289
324
  return `{ ${props} }`;
290
325
  }
291
- case "ZodEnum": {
292
- return def.values.map((opt) => `"${opt}"`).join(" | ");
293
- }
294
326
  case "ZodUnion": {
295
327
  return def.options.map((opt) => processType(opt)).join(" | ");
296
328
  }
@@ -319,8 +351,8 @@ function getZodTypeString(schema, defaultOptional = false) {
319
351
  case "ZodUnknown": {
320
352
  return "unknown";
321
353
  }
322
- case "ZodEnum ": {
323
- return def.values.map((opt) => `"${opt}"`).join(" | ");
354
+ case "ZodEnum": {
355
+ return "(" + def.values.map((opt) => `"${opt}"`).join(" | ") + ")";
324
356
  }
325
357
  case "ZodDefault": {
326
358
  return processType(def.innerType);
@@ -446,7 +478,7 @@ var ActionHandler = class {
446
478
  let args;
447
479
  if (typeof req === "string") {
448
480
  try {
449
- args = Object.values(ejson3.parse(req));
481
+ args = Object.values(ejson4.parse(req));
450
482
  } catch (error) {
451
483
  throw new Error(`Invalid request body: ${error.message}`);
452
484
  }
@@ -464,7 +496,7 @@ var ActionHandler = class {
464
496
  }
465
497
  });
466
498
  }
467
- const requestHash = hashText(ejson3.stringify(args));
499
+ const requestHash = hashText(ejson4.stringify(args));
468
500
  span.setAttribute("requestHash", requestHash);
469
501
  return { args, requestHash };
470
502
  } finally {
@@ -475,7 +507,7 @@ var ActionHandler = class {
475
507
  const span = trace.getActiveSpan();
476
508
  const { args, requestHash } = await this._validate(req);
477
509
  const cacheHash = typeof this.metadata.cache === "function" ? this.metadata.cache(...args) : requestHash;
478
- const cacheKey = `${this.moduleName}.${this.actionName}.${hashText(ejson3.stringify(cacheHash))}`;
510
+ const cacheKey = `${this.microservice.options.name}:cache:${this.moduleName}:${this.actionName}:${hashText(ejson4.stringify(cacheHash))}`;
479
511
  span?.setAttribute("args", JSON.stringify(args));
480
512
  if (!this.metadata.stream && this.metadata.cache && !this.microservice.options.disableCache) {
481
513
  const cached = await this.microservice.cache.get(cacheKey);
@@ -530,7 +562,7 @@ var ActionHandler = class {
530
562
  data: parsedResult,
531
563
  expireAt: this.metadata.ttl ? now + this.metadata.ttl : void 0
532
564
  },
533
- { ttl: this.metadata.ttl }
565
+ this.metadata.ttl ?? 1e3
534
566
  );
535
567
  }
536
568
  return parsedResult;
@@ -559,7 +591,7 @@ var WebSocketHandler = class {
559
591
  this.sockets.add(ws);
560
592
  }
561
593
  async encodeMessage(message) {
562
- const jsonStr = ejson3.stringify(message);
594
+ const jsonStr = ejson4.stringify(message);
563
595
  const data = new TextEncoder().encode(jsonStr);
564
596
  const compressed = await brotli.compress(data, 6);
565
597
  return compressed;
@@ -568,7 +600,7 @@ var WebSocketHandler = class {
568
600
  try {
569
601
  const decompressed = await brotli.decompress(new Uint8Array(data));
570
602
  const jsonStr = new TextDecoder().decode(decompressed);
571
- return ejson3.parse(jsonStr);
603
+ return ejson4.parse(jsonStr);
572
604
  } catch (error) {
573
605
  console.error("Error decoding message", error);
574
606
  throw error;
@@ -629,7 +661,7 @@ var WebSocketHandler = class {
629
661
  false
630
662
  );
631
663
  ws.send(
632
- ejson3.stringify({
664
+ ejson4.stringify({
633
665
  id: message.id,
634
666
  success: false,
635
667
  error: "Request timeout"
@@ -745,17 +777,24 @@ var Microservice = class {
745
777
  this.options = {
746
778
  prefix: "/api",
747
779
  name: "microservice",
780
+ version: "0.0.1",
781
+ env: "default",
782
+ printError: false,
783
+ disableCache: false,
784
+ generateClient: false,
785
+ etcd: false,
786
+ events: {},
787
+ websocket: { enabled: false },
788
+ cacheAdapter: new MemoryCacheAdapter(),
789
+ plugins: [],
748
790
  ...options
749
791
  };
792
+ this.cache = this.options.cacheAdapter;
750
793
  this.fetch = this.app.request;
751
794
  this.waitingInitialization = this.initialize();
752
795
  ServiceContext.service = this;
753
796
  }
754
797
  async initialize() {
755
- this.cache = new LRUCache({
756
- max: 1e3,
757
- ttl: 1e3 * 60 * 10
758
- });
759
798
  if (this.options.etcd) {
760
799
  this.initEtcd(this.options.etcd);
761
800
  }
@@ -766,6 +805,7 @@ var Microservice = class {
766
805
  this.initStatsEventManager();
767
806
  }
768
807
  await this.registerService(true);
808
+ await this.initPlugins();
769
809
  }
770
810
  async initModules() {
771
811
  for (const ModuleClass of this.options.modules) {
@@ -790,7 +830,7 @@ var Microservice = class {
790
830
  );
791
831
  this.actionHandlers.set(`${moduleName}.${actionName}`, handler);
792
832
  logger_default.info(
793
- `[ \u6CE8\u518C\u52A8\u4F5C ] ${moduleName}.${actionName} ${actionMetadata.description}`
833
+ `[ \u6CE8\u518C\u52A8\u4F5C ] ${moduleName}.${actionName} ${actionMetadata.description} ${actionMetadata.mcp ? "MCP:" + actionMetadata.mcp?.type : ""}`
794
834
  );
795
835
  }
796
836
  const schedules = getScheduleMetadata(ModuleClass.prototype);
@@ -1119,12 +1159,12 @@ Received SIGINT signal`);
1119
1159
  try {
1120
1160
  for await (const value of result) {
1121
1161
  const response = { value, done: false };
1122
- const chunk = encoder.encode(ejson3.stringify(response) + "\n");
1162
+ const chunk = encoder.encode(ejson4.stringify(response) + "\n");
1123
1163
  controller.enqueue(chunk);
1124
1164
  }
1125
1165
  controller.enqueue(
1126
1166
  encoder.encode(
1127
- ejson3.stringify({ done: true }) + "\n"
1167
+ ejson4.stringify({ done: true }) + "\n"
1128
1168
  )
1129
1169
  );
1130
1170
  controller.close();
@@ -1134,7 +1174,7 @@ Received SIGINT signal`);
1134
1174
  done: true
1135
1175
  };
1136
1176
  controller.enqueue(
1137
- encoder.encode(ejson3.stringify(response) + "\n")
1177
+ encoder.encode(ejson4.stringify(response) + "\n")
1138
1178
  );
1139
1179
  controller.close();
1140
1180
  }
@@ -1148,37 +1188,21 @@ Received SIGINT signal`);
1148
1188
  }
1149
1189
  });
1150
1190
  }
1151
- return ctx.text(ejson3.stringify({ success: true, data: result }));
1191
+ return ctx.text(ejson4.stringify({ success: true, data: result }));
1152
1192
  } catch (error) {
1153
1193
  return ctx.json({ success: false, error: error.message });
1154
1194
  }
1155
1195
  };
1156
- async init() {
1157
- await this.waitingInitialization;
1158
- }
1159
- /**
1160
- * 获取所有注册的服务
1161
- */
1162
- async getServices() {
1163
- if (!this.etcdClient) {
1164
- throw new Error("etcd is not configured");
1196
+ async initPlugins() {
1197
+ for (const plugin of this.options.plugins) {
1198
+ logger_default.info(`[ \u521D\u59CB\u5316\u63D2\u4EF6 ] ${plugin.constructor.name}`);
1199
+ await plugin.initialize(this).catch((e) => {
1200
+ logger_default.error(`[ \u521D\u59CB\u5316\u63D2\u4EF6\u5931\u8D25 ] ${plugin.constructor.name}: ${e}`);
1201
+ });
1165
1202
  }
1166
- const namespace = this.options.etcd?.namespace ? `${this.options.etcd.namespace}/` : "";
1167
- const servicesKey = `${namespace}services`;
1168
- const services = await this.etcdClient.getAll().prefix(servicesKey);
1169
- return Object.values(services).map((value) => JSON.parse(value));
1170
1203
  }
1171
- /**
1172
- * 获取指定服务的实例
1173
- */
1174
- async getServiceInstances(serviceName) {
1175
- if (!this.etcdClient) {
1176
- throw new Error("etcd is not configured");
1177
- }
1178
- const namespace = this.options.etcd?.namespace ? `${this.options.etcd.namespace}/` : "";
1179
- const serviceKey = `${namespace}services/${serviceName}`;
1180
- const instances = await this.etcdClient.getAll().prefix(serviceKey);
1181
- return Object.values(instances).map((value) => JSON.parse(value));
1204
+ async init() {
1205
+ await this.waitingInitialization;
1182
1206
  }
1183
1207
  };
1184
1208
 
@@ -1204,4 +1228,4 @@ async function startCheck(checkers, pass) {
1204
1228
  if (pass) await pass();
1205
1229
  }
1206
1230
 
1207
- export { Action, Microservice, Module, Schedule, ScheduleMode, ServiceContext, logger_default as logger, startCheck };
1231
+ export { Action, CacheAdapter, MemoryCacheAdapter, Microservice, Module, Plugin, RedisCacheAdapter, Schedule, ScheduleMode, ServiceContext, logger_default as logger, startCheck };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "imean-service-engine",
3
- "version": "1.2.3",
3
+ "version": "1.4.0",
4
4
  "description": "microservice engine",
5
5
  "keywords": [
6
6
  "microservice",
@@ -39,6 +39,7 @@
39
39
  "dependencies": {
40
40
  "@hono/node-server": "^1.13.7",
41
41
  "@hono/node-ws": "^1.0.6",
42
+ "@modelcontextprotocol/sdk": "^1.8.0",
42
43
  "dayjs": "^1.11.13",
43
44
  "ejson": "^2.2.3",
44
45
  "etcd3": "^1.1.2",
@@ -46,11 +47,13 @@
46
47
  "hono": "^4.6.17",
47
48
  "lru-cache": "^11.0.2",
48
49
  "prettier": "^3.4.2",
50
+ "ulid": "^3.0.0",
49
51
  "winston": "^3.17.0",
50
52
  "zod": "^3.24.1"
51
53
  },
52
54
  "peerDependencies": {
53
- "@opentelemetry/api": "^1.x"
55
+ "@opentelemetry/api": "^1.x",
56
+ "ioredis": "^5.6.0"
54
57
  },
55
58
  "devDependencies": {
56
59
  "@opentelemetry/auto-instrumentations-node": "^0.55.3",
@@ -65,9 +68,11 @@
65
68
  "@opentelemetry/winston-transport": "^0.10.0",
66
69
  "@types/ejson": "^2.2.2",
67
70
  "@types/fs-extra": "^11.0.4",
71
+ "@types/ioredis-mock": "^8.2.5",
68
72
  "@types/node": "^20.0.0",
69
73
  "@vitest/coverage-v8": "^3.0.4",
70
74
  "imean-service-client": "^1.5.0",
75
+ "ioredis-mock": "^8.9.0",
71
76
  "opentelemetry-instrumentation-fetch-node": "^1.2.3",
72
77
  "tslib": "^2.8.1",
73
78
  "tsup": "^8.0.1",