imean-service-engine 1.7.3 → 1.8.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/dist/mod.cjs CHANGED
@@ -14,10 +14,6 @@ var prettier = require('prettier');
14
14
  var crypto2 = require('crypto');
15
15
  var zlib = require('zlib');
16
16
  var nodeWs = require('@hono/node-ws');
17
- var mcp_js = require('@modelcontextprotocol/sdk/server/mcp.js');
18
- var types_js = require('@modelcontextprotocol/sdk/types.js');
19
- var streaming = require('hono/streaming');
20
- var ulid = require('ulid');
21
17
  var html = require('hono/html');
22
18
  var jsxRuntime = require('hono/jsx/jsx-runtime');
23
19
  var dayjs = require('dayjs');
@@ -92,8 +88,7 @@ function Action(options) {
92
88
  printError: options.printError ?? false,
93
89
  cache: options.cache,
94
90
  ttl: options.ttl,
95
- stream: options.stream,
96
- mcp: options.mcp
91
+ stream: options.stream
97
92
  };
98
93
  prototype[ACTION_METADATA] = existingMetadata;
99
94
  });
@@ -471,14 +466,14 @@ var ActionHandler = class {
471
466
  this.microservice = microservice;
472
467
  this.moduleName = moduleName;
473
468
  }
474
- async handle(req) {
469
+ async handle(req, ctx) {
475
470
  return await tracer2.startActiveSpan(
476
471
  `handle ${this.moduleName}.${this.actionName}`,
477
472
  async (span) => {
478
473
  span.setAttribute("module", this.moduleName);
479
474
  span.setAttribute("action", this.actionName);
480
475
  try {
481
- return await this._handle(req);
476
+ return await this._handle(req, ctx);
482
477
  } catch (error) {
483
478
  span.recordException(error);
484
479
  span.setStatus({
@@ -523,7 +518,7 @@ var ActionHandler = class {
523
518
  span.end();
524
519
  }
525
520
  }
526
- async _handle(req) {
521
+ async _handle(req, ctx) {
527
522
  const span = api.trace.getActiveSpan();
528
523
  const { args, requestHash } = await this._validate(req);
529
524
  const cacheHash = typeof this.metadata.cache === "function" ? this.metadata.cache(...args) : requestHash;
@@ -541,7 +536,7 @@ var ActionHandler = class {
541
536
  try {
542
537
  const result = await this.moduleInstance[this.actionName].apply(
543
538
  this.moduleInstance,
544
- args
539
+ args.concat(ctx)
545
540
  );
546
541
  if (this.metadata.stream) {
547
542
  span?.setAttribute("stream", true);
@@ -610,9 +605,10 @@ function Page(options) {
610
605
  existingMetadata[methodName] = {
611
606
  name: methodName,
612
607
  description: options.description || "",
613
- method: options.method,
608
+ method: options.method || "get",
614
609
  path: normalizePath(options.path),
615
- absolutePath: options.absolutePath ?? false
610
+ absolutePath: options.absolutePath ?? false,
611
+ middlewares: options.middlewares ?? []
616
612
  };
617
613
  prototype[PAGE_METADATA] = existingMetadata;
618
614
  });
@@ -667,8 +663,9 @@ var PageHandler = class {
667
663
  }
668
664
  };
669
665
  var WebSocketHandler = class {
670
- constructor(microservice, options) {
666
+ constructor(microservice, ctx, options) {
671
667
  this.microservice = microservice;
668
+ this.ctx = ctx;
672
669
  this.options = {
673
670
  timeout: options?.timeout || 3e4
674
671
  };
@@ -708,7 +705,7 @@ var WebSocketHandler = class {
708
705
  await this.sendMessage(ws, { type: "pong" });
709
706
  return;
710
707
  }
711
- const response = await this.handleRequest(ws, message);
708
+ const response = await this.handleRequest(ws, message, this.ctx);
712
709
  if (response) {
713
710
  await this.sendMessage(ws, response);
714
711
  }
@@ -735,7 +732,7 @@ var WebSocketHandler = class {
735
732
  onError(_error, ws) {
736
733
  this.onClose(ws);
737
734
  }
738
- async handleRequest(ws, message) {
735
+ async handleRequest(ws, message, ctx) {
739
736
  if (!message.id || !message.module || !message.action) {
740
737
  throw new Error("Invalid request message");
741
738
  }
@@ -771,7 +768,7 @@ var WebSocketHandler = class {
771
768
  message.action
772
769
  );
773
770
  const args = message.args ? Object.values(message.args) : [];
774
- const result = await handler.handle(args);
771
+ const result = await handler.handle(args, ctx);
775
772
  if (handler.metadata.stream) {
776
773
  try {
777
774
  for await (const value of result) {
@@ -887,7 +884,6 @@ var Microservice = class {
887
884
  };
888
885
  this.cache = this.options.cacheAdapter;
889
886
  this.fetch = this.app.request;
890
- this.waitingInitialization = this.initialize();
891
887
  ServiceContext.service = this;
892
888
  }
893
889
  async initialize() {
@@ -932,20 +928,20 @@ var Microservice = class {
932
928
  );
933
929
  this.actionHandlers.set(`${moduleName}.${actionName}`, handler);
934
930
  logger_default.info(
935
- `[ \u6CE8\u518C\u52A8\u4F5C ] ${moduleName}.${actionName} ${actionMetadata.description} ${actionMetadata.mcp ? "MCP:" + actionMetadata.mcp?.type : ""}`
931
+ `[ \u6CE8\u518C\u52A8\u4F5C ] ${moduleName}.${actionName} ${actionMetadata.description}`
936
932
  );
937
933
  }
938
934
  for (const [_, page] of Object.entries(pages)) {
939
- const handler = new PageHandler(
940
- moduleInstance,
941
- page,
942
- moduleName
943
- );
935
+ const handler = new PageHandler(moduleInstance, page, moduleName);
944
936
  this.pageHandlers.set(`${moduleName}.${page.name}`, handler);
945
937
  const paths = Array.isArray(page.path) ? page.path : [page.path];
946
938
  for (let path of paths) {
947
939
  path = page.absolutePath ? path : `${this.options.prefix}${path}`;
948
- this.app[page.method](path, (ctx) => handler.handle(ctx));
940
+ this.app[page.method](
941
+ path,
942
+ ...page.middlewares ?? [],
943
+ (ctx) => handler.handle(ctx)
944
+ );
949
945
  logger_default.info(
950
946
  `[ \u6CE8\u518C\u9875\u9762 ] ${moduleName}.${page.name} ${page.method.toUpperCase()} ${page.path} ${page.description}`
951
947
  );
@@ -1007,11 +1003,10 @@ var Microservice = class {
1007
1003
  });
1008
1004
  this.app.post(`${prefix}/:moduleName/:actionName`, this.handleRequest);
1009
1005
  if (this.options.websocket?.enabled) {
1010
- this.wsHandler = new WebSocketHandler(this);
1011
1006
  this.app.get(
1012
1007
  `${prefix}/ws`,
1013
- this.nodeWebSocket.upgradeWebSocket((_ctx) => {
1014
- const wsHandler = new WebSocketHandler(this, {
1008
+ this.nodeWebSocket.upgradeWebSocket((ctx) => {
1009
+ const wsHandler = new WebSocketHandler(this, ctx, {
1015
1010
  timeout: this.options.websocket?.timeout
1016
1011
  });
1017
1012
  return {
@@ -1118,7 +1113,7 @@ var Microservice = class {
1118
1113
  * 启动服务
1119
1114
  */
1120
1115
  async start(port = 3e3, silent = false) {
1121
- await this.waitingInitialization;
1116
+ await this.init();
1122
1117
  const prefix = this.options.prefix ?? "/api";
1123
1118
  this.abortController = new AbortController();
1124
1119
  !silent && console.log("");
@@ -1233,7 +1228,9 @@ Received SIGTERM signal`);
1233
1228
  async waitForActiveRequests() {
1234
1229
  const timeout = this.options.gracefulShutdown?.timeout || 3e4;
1235
1230
  const startTime = Date.now();
1236
- logger_default.info(`Waiting for ${this.activeRequests.size} active requests to complete...`);
1231
+ logger_default.info(
1232
+ `Waiting for ${this.activeRequests.size} active requests to complete...`
1233
+ );
1237
1234
  return new Promise((resolve) => {
1238
1235
  const checkInterval = setInterval(() => {
1239
1236
  const elapsed = Date.now() - startTime;
@@ -1243,10 +1240,14 @@ Received SIGTERM signal`);
1243
1240
  resolve();
1244
1241
  } else if (elapsed >= timeout) {
1245
1242
  clearInterval(checkInterval);
1246
- logger_default.warn(`Timeout waiting for requests to complete. ${this.activeRequests.size} requests still active`);
1243
+ logger_default.warn(
1244
+ `Timeout waiting for requests to complete. ${this.activeRequests.size} requests still active`
1245
+ );
1247
1246
  resolve();
1248
1247
  } else {
1249
- logger_default.info(`Still waiting for ${this.activeRequests.size} requests... (${elapsed}ms elapsed)`);
1248
+ logger_default.info(
1249
+ `Still waiting for ${this.activeRequests.size} requests... (${elapsed}ms elapsed)`
1250
+ );
1250
1251
  }
1251
1252
  }, 1e3);
1252
1253
  });
@@ -1268,7 +1269,10 @@ Received SIGTERM signal`);
1268
1269
  await Promise.race([
1269
1270
  Promise.resolve(hook.cleanup()),
1270
1271
  new Promise(
1271
- (_, reject) => setTimeout(() => reject(new Error(`Cleanup hook ${hook.name} timeout`)), timeout)
1272
+ (_, reject) => setTimeout(
1273
+ () => reject(new Error(`Cleanup hook ${hook.name} timeout`)),
1274
+ timeout
1275
+ )
1272
1276
  )
1273
1277
  ]);
1274
1278
  logger_default.info(`Cleanup hook ${hook.name} completed successfully`);
@@ -1349,10 +1353,13 @@ Received SIGTERM signal`);
1349
1353
  const requestId = crypto.randomUUID();
1350
1354
  const startTime = Date.now();
1351
1355
  if (this.status === "shutting_down") {
1352
- return ctx.json({
1353
- success: false,
1354
- error: "Service is shutting down"
1355
- }, 503);
1356
+ return ctx.json(
1357
+ {
1358
+ success: false,
1359
+ error: "Service is shutting down"
1360
+ },
1361
+ 503
1362
+ );
1356
1363
  }
1357
1364
  try {
1358
1365
  const paramsText = await ctx.req.text();
@@ -1363,7 +1370,7 @@ Received SIGTERM signal`);
1363
1370
  startTime,
1364
1371
  params: paramsText
1365
1372
  });
1366
- const result = await handler.handle(paramsText);
1373
+ const result = await handler.handle(paramsText, ctx);
1367
1374
  if (handler.metadata.stream) {
1368
1375
  const encoder = new TextEncoder();
1369
1376
  const microservice = this;
@@ -1419,124 +1426,10 @@ Received SIGTERM signal`);
1419
1426
  }
1420
1427
  }
1421
1428
  async init() {
1429
+ this.waitingInitialization = this.initialize();
1422
1430
  await this.waitingInitialization;
1423
1431
  }
1424
1432
  };
1425
- var HonoTransport = class {
1426
- constructor(url, stream, closeStream) {
1427
- this.url = url;
1428
- this.stream = stream;
1429
- this.closeStream = closeStream;
1430
- this.sessionId = ulid.ulid();
1431
- }
1432
- sessionId;
1433
- onclose;
1434
- onerror;
1435
- onmessage;
1436
- async start() {
1437
- await this.stream.writeSSE({
1438
- event: "endpoint",
1439
- data: `${encodeURI(this.url)}?sessionId=${this.sessionId}`
1440
- });
1441
- this.stream.onAbort(() => {
1442
- this.close();
1443
- });
1444
- }
1445
- async handleMessage(message) {
1446
- let parsedMessage;
1447
- try {
1448
- parsedMessage = types_js.JSONRPCMessageSchema.parse(message);
1449
- } catch (error) {
1450
- this.onerror?.(error);
1451
- throw error;
1452
- }
1453
- this.onmessage?.(parsedMessage);
1454
- }
1455
- async send(message) {
1456
- await this.stream.writeln(
1457
- `event: message
1458
- data: ${JSON.stringify(message)}
1459
-
1460
- `
1461
- );
1462
- }
1463
- async close() {
1464
- this.onclose?.();
1465
- this.closeStream();
1466
- }
1467
- };
1468
- var ModelContextProtocolPlugin = class extends Plugin {
1469
- mcpServer;
1470
- transports = {};
1471
- registerMcpTools(engine) {
1472
- const modules = engine.getModules(true);
1473
- for (const module of Object.values(modules)) {
1474
- for (const action of Object.values(module.actions)) {
1475
- if (action.mcp?.type !== "tool") {
1476
- continue;
1477
- }
1478
- const args = {};
1479
- const argsIndex = {};
1480
- for (const [index, param] of (action.params ?? []).entries()) {
1481
- args[param.description] = param;
1482
- argsIndex[param.description] = index;
1483
- }
1484
- this.mcpServer.tool(
1485
- `${module.name}.${action.name}`,
1486
- action.description ?? "",
1487
- args,
1488
- async (params) => {
1489
- const argsList = [];
1490
- for (const [key, value] of Object.entries(params)) {
1491
- argsList[argsIndex[key]] = value;
1492
- }
1493
- const result = await engine.getActionHandler(module.name, action.name).handle(argsList);
1494
- return {
1495
- content: [{ type: "text", text: JSON.stringify(result) }]
1496
- };
1497
- }
1498
- );
1499
- }
1500
- }
1501
- }
1502
- initialize = async (engine) => {
1503
- const app = engine.getApp();
1504
- this.mcpServer = new mcp_js.McpServer({
1505
- name: engine.options.name,
1506
- version: engine.options.version
1507
- });
1508
- this.registerMcpTools(engine);
1509
- app.get(`${engine.options.prefix}/mcp_sse`, async (ctx) => {
1510
- return streaming.streamSSE(ctx, async (stream) => {
1511
- return new Promise(async (resolve) => {
1512
- const transport = new HonoTransport(
1513
- `${engine.options.prefix}/mcp_messages`,
1514
- stream,
1515
- () => {
1516
- delete this.transports[transport.sessionId];
1517
- resolve();
1518
- }
1519
- );
1520
- this.transports[transport.sessionId] = transport;
1521
- await this.mcpServer.connect(transport);
1522
- });
1523
- });
1524
- });
1525
- app.post(`${engine.options.prefix}/mcp_messages`, async (ctx) => {
1526
- const sessionId = ctx.req.query("sessionId");
1527
- if (!sessionId) {
1528
- return ctx.text("No transport found for sessionId", 400);
1529
- }
1530
- const transport = this.transports[sessionId];
1531
- const message = await ctx.req.json();
1532
- await transport.handleMessage(message);
1533
- return ctx.text("Accepted", 202);
1534
- });
1535
- logger_default.info(
1536
- `ModelContextProtocolPlugin endpoint: ${engine.options.prefix}/mcp_sse`
1537
- );
1538
- };
1539
- };
1540
1433
  var DEFAULT_FAVICON = /* @__PURE__ */ jsxRuntime.jsx(
1541
1434
  "link",
1542
1435
  {
@@ -1749,7 +1642,6 @@ exports.CacheAdapter = CacheAdapter;
1749
1642
  exports.HtmxLayout = HtmxLayout;
1750
1643
  exports.MemoryCacheAdapter = MemoryCacheAdapter;
1751
1644
  exports.Microservice = Microservice;
1752
- exports.ModelContextProtocolPlugin = ModelContextProtocolPlugin;
1753
1645
  exports.Module = Module;
1754
1646
  exports.Page = Page;
1755
1647
  exports.PageRenderPlugin = PageRenderPlugin;
package/dist/mod.d.cts CHANGED
@@ -3,7 +3,7 @@ export * from 'zod';
3
3
  import { Redis, Cluster } from 'ioredis';
4
4
  import { LRUCache } from 'lru-cache';
5
5
  import { Lease, Etcd3 } from 'etcd3';
6
- import { Hono } from 'hono';
6
+ import { MiddlewareHandler, Context, Hono } from 'hono';
7
7
  export { default as dayjs } from 'dayjs';
8
8
  import winston from 'winston';
9
9
  import * as hono_utils_html from 'hono/utils/html';
@@ -27,9 +27,6 @@ declare class MemoryCacheAdapter extends CacheAdapter {
27
27
  }
28
28
 
29
29
  type CacheFn = (...args: any[]) => any;
30
- type McpOptions = {
31
- type: "tool";
32
- };
33
30
  interface ActionOptions {
34
31
  params?: z.ZodType<any>[];
35
32
  returns?: z.ZodType<any>;
@@ -39,16 +36,17 @@ interface ActionOptions {
39
36
  cache?: boolean | CacheFn;
40
37
  ttl?: number;
41
38
  stream?: boolean;
42
- mcp?: McpOptions;
43
39
  }
44
40
  interface PageOptions {
45
- method: "get" | "post" | "put" | "delete" | "patch" | "options";
41
+ method?: "get" | "post" | "put" | "delete" | "patch" | "options";
42
+ middlewares?: MiddlewareHandler[];
46
43
  path: string | string[];
47
44
  absolutePath?: boolean;
48
45
  description?: string;
49
46
  }
50
47
  interface PageMetadata extends PageOptions {
51
48
  name: string;
49
+ method: "get" | "post" | "put" | "delete" | "patch" | "options";
52
50
  }
53
51
  interface ActionMetadata extends ActionOptions {
54
52
  name: string;
@@ -213,7 +211,7 @@ declare class ActionHandler {
213
211
  private microservice;
214
212
  private moduleName;
215
213
  constructor(moduleInstance: any, actionName: string, metadata: ActionMetadata, microservice: Microservice, moduleName: string);
216
- handle(req: string | any[]): Promise<any>;
214
+ handle(req: string | any[], ctx: Context): Promise<any>;
217
215
  private _validate;
218
216
  private _handle;
219
217
  }
@@ -316,13 +314,6 @@ declare class Microservice {
316
314
  init(): Promise<void>;
317
315
  }
318
316
 
319
- declare class ModelContextProtocolPlugin extends Plugin {
320
- private mcpServer;
321
- private transports;
322
- private registerMcpTools;
323
- initialize: (engine: Microservice) => Promise<void>;
324
- }
325
-
326
317
  declare const BaseLayout: (props?: {
327
318
  children?: any;
328
319
  title?: string;
@@ -381,4 +372,4 @@ declare function Schedule(options: ScheduleOptions): Function;
381
372
 
382
373
  declare const logger: winston.Logger;
383
374
 
384
- export { Action, type ActionErrorEvent, type ActionMetadata, type ActionOptions, BaseLayout, CacheAdapter, type CacheFn, type CleanupHook, type EtcdConfig, type EventServiceInfo, HtmxLayout, type McpOptions, MemoryCacheAdapter, Microservice, type MicroserviceOptions, ModelContextProtocolPlugin, Module, type ModuleInfo, type ModuleMetadata, type ModuleOptions, Page, type PageMetadata, type PageOptions, PageRenderPlugin, type PageRenderPluginOptions, Plugin, type PreStartChecker, RedisCacheAdapter, type RequestInfo, Schedule, type ScheduleMetadata, ScheduleMode, type ScheduleOptions, ServiceContext, type ServiceInfo, ServiceInfoCards, type ServiceStats, ServiceStatusPage, type StatisticsEvent, type StreamResponse, logger, startCheck };
375
+ export { Action, type ActionErrorEvent, type ActionMetadata, type ActionOptions, BaseLayout, CacheAdapter, type CacheFn, type CleanupHook, type EtcdConfig, type EventServiceInfo, HtmxLayout, MemoryCacheAdapter, Microservice, type MicroserviceOptions, Module, type ModuleInfo, type ModuleMetadata, type ModuleOptions, Page, type PageMetadata, type PageOptions, PageRenderPlugin, type PageRenderPluginOptions, Plugin, type PreStartChecker, RedisCacheAdapter, type RequestInfo, Schedule, type ScheduleMetadata, ScheduleMode, type ScheduleOptions, ServiceContext, type ServiceInfo, ServiceInfoCards, type ServiceStats, ServiceStatusPage, type StatisticsEvent, type StreamResponse, logger, startCheck };
package/dist/mod.d.ts CHANGED
@@ -3,7 +3,7 @@ export * from 'zod';
3
3
  import { Redis, Cluster } from 'ioredis';
4
4
  import { LRUCache } from 'lru-cache';
5
5
  import { Lease, Etcd3 } from 'etcd3';
6
- import { Hono } from 'hono';
6
+ import { MiddlewareHandler, Context, Hono } from 'hono';
7
7
  export { default as dayjs } from 'dayjs';
8
8
  import winston from 'winston';
9
9
  import * as hono_utils_html from 'hono/utils/html';
@@ -27,9 +27,6 @@ declare class MemoryCacheAdapter extends CacheAdapter {
27
27
  }
28
28
 
29
29
  type CacheFn = (...args: any[]) => any;
30
- type McpOptions = {
31
- type: "tool";
32
- };
33
30
  interface ActionOptions {
34
31
  params?: z.ZodType<any>[];
35
32
  returns?: z.ZodType<any>;
@@ -39,16 +36,17 @@ interface ActionOptions {
39
36
  cache?: boolean | CacheFn;
40
37
  ttl?: number;
41
38
  stream?: boolean;
42
- mcp?: McpOptions;
43
39
  }
44
40
  interface PageOptions {
45
- method: "get" | "post" | "put" | "delete" | "patch" | "options";
41
+ method?: "get" | "post" | "put" | "delete" | "patch" | "options";
42
+ middlewares?: MiddlewareHandler[];
46
43
  path: string | string[];
47
44
  absolutePath?: boolean;
48
45
  description?: string;
49
46
  }
50
47
  interface PageMetadata extends PageOptions {
51
48
  name: string;
49
+ method: "get" | "post" | "put" | "delete" | "patch" | "options";
52
50
  }
53
51
  interface ActionMetadata extends ActionOptions {
54
52
  name: string;
@@ -213,7 +211,7 @@ declare class ActionHandler {
213
211
  private microservice;
214
212
  private moduleName;
215
213
  constructor(moduleInstance: any, actionName: string, metadata: ActionMetadata, microservice: Microservice, moduleName: string);
216
- handle(req: string | any[]): Promise<any>;
214
+ handle(req: string | any[], ctx: Context): Promise<any>;
217
215
  private _validate;
218
216
  private _handle;
219
217
  }
@@ -316,13 +314,6 @@ declare class Microservice {
316
314
  init(): Promise<void>;
317
315
  }
318
316
 
319
- declare class ModelContextProtocolPlugin extends Plugin {
320
- private mcpServer;
321
- private transports;
322
- private registerMcpTools;
323
- initialize: (engine: Microservice) => Promise<void>;
324
- }
325
-
326
317
  declare const BaseLayout: (props?: {
327
318
  children?: any;
328
319
  title?: string;
@@ -381,4 +372,4 @@ declare function Schedule(options: ScheduleOptions): Function;
381
372
 
382
373
  declare const logger: winston.Logger;
383
374
 
384
- export { Action, type ActionErrorEvent, type ActionMetadata, type ActionOptions, BaseLayout, CacheAdapter, type CacheFn, type CleanupHook, type EtcdConfig, type EventServiceInfo, HtmxLayout, type McpOptions, MemoryCacheAdapter, Microservice, type MicroserviceOptions, ModelContextProtocolPlugin, Module, type ModuleInfo, type ModuleMetadata, type ModuleOptions, Page, type PageMetadata, type PageOptions, PageRenderPlugin, type PageRenderPluginOptions, Plugin, type PreStartChecker, RedisCacheAdapter, type RequestInfo, Schedule, type ScheduleMetadata, ScheduleMode, type ScheduleOptions, ServiceContext, type ServiceInfo, ServiceInfoCards, type ServiceStats, ServiceStatusPage, type StatisticsEvent, type StreamResponse, logger, startCheck };
375
+ export { Action, type ActionErrorEvent, type ActionMetadata, type ActionOptions, BaseLayout, CacheAdapter, type CacheFn, type CleanupHook, type EtcdConfig, type EventServiceInfo, HtmxLayout, MemoryCacheAdapter, Microservice, type MicroserviceOptions, Module, type ModuleInfo, type ModuleMetadata, type ModuleOptions, Page, type PageMetadata, type PageOptions, PageRenderPlugin, type PageRenderPluginOptions, Plugin, type PreStartChecker, RedisCacheAdapter, type RequestInfo, Schedule, type ScheduleMetadata, ScheduleMode, type ScheduleOptions, ServiceContext, type ServiceInfo, ServiceInfoCards, type ServiceStats, ServiceStatusPage, type StatisticsEvent, type StreamResponse, logger, startCheck };
package/dist/mod.js CHANGED
@@ -14,10 +14,6 @@ import crypto2 from 'node:crypto';
14
14
  import { brotliDecompress } from 'node:zlib';
15
15
  import { brotliCompress, constants } from 'zlib';
16
16
  import { createNodeWebSocket } from '@hono/node-ws';
17
- import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
18
- import { JSONRPCMessageSchema } from '@modelcontextprotocol/sdk/types.js';
19
- import { streamSSE } from 'hono/streaming';
20
- import { ulid } from 'ulid';
21
17
  import { html } from 'hono/html';
22
18
  import { jsx, jsxs } from 'hono/jsx/jsx-runtime';
23
19
  export { default as dayjs } from 'dayjs';
@@ -83,8 +79,7 @@ function Action(options) {
83
79
  printError: options.printError ?? false,
84
80
  cache: options.cache,
85
81
  ttl: options.ttl,
86
- stream: options.stream,
87
- mcp: options.mcp
82
+ stream: options.stream
88
83
  };
89
84
  prototype[ACTION_METADATA] = existingMetadata;
90
85
  });
@@ -462,14 +457,14 @@ var ActionHandler = class {
462
457
  this.microservice = microservice;
463
458
  this.moduleName = moduleName;
464
459
  }
465
- async handle(req) {
460
+ async handle(req, ctx) {
466
461
  return await tracer2.startActiveSpan(
467
462
  `handle ${this.moduleName}.${this.actionName}`,
468
463
  async (span) => {
469
464
  span.setAttribute("module", this.moduleName);
470
465
  span.setAttribute("action", this.actionName);
471
466
  try {
472
- return await this._handle(req);
467
+ return await this._handle(req, ctx);
473
468
  } catch (error) {
474
469
  span.recordException(error);
475
470
  span.setStatus({
@@ -514,7 +509,7 @@ var ActionHandler = class {
514
509
  span.end();
515
510
  }
516
511
  }
517
- async _handle(req) {
512
+ async _handle(req, ctx) {
518
513
  const span = trace.getActiveSpan();
519
514
  const { args, requestHash } = await this._validate(req);
520
515
  const cacheHash = typeof this.metadata.cache === "function" ? this.metadata.cache(...args) : requestHash;
@@ -532,7 +527,7 @@ var ActionHandler = class {
532
527
  try {
533
528
  const result = await this.moduleInstance[this.actionName].apply(
534
529
  this.moduleInstance,
535
- args
530
+ args.concat(ctx)
536
531
  );
537
532
  if (this.metadata.stream) {
538
533
  span?.setAttribute("stream", true);
@@ -601,9 +596,10 @@ function Page(options) {
601
596
  existingMetadata[methodName] = {
602
597
  name: methodName,
603
598
  description: options.description || "",
604
- method: options.method,
599
+ method: options.method || "get",
605
600
  path: normalizePath(options.path),
606
- absolutePath: options.absolutePath ?? false
601
+ absolutePath: options.absolutePath ?? false,
602
+ middlewares: options.middlewares ?? []
607
603
  };
608
604
  prototype[PAGE_METADATA] = existingMetadata;
609
605
  });
@@ -658,8 +654,9 @@ var PageHandler = class {
658
654
  }
659
655
  };
660
656
  var WebSocketHandler = class {
661
- constructor(microservice, options) {
657
+ constructor(microservice, ctx, options) {
662
658
  this.microservice = microservice;
659
+ this.ctx = ctx;
663
660
  this.options = {
664
661
  timeout: options?.timeout || 3e4
665
662
  };
@@ -699,7 +696,7 @@ var WebSocketHandler = class {
699
696
  await this.sendMessage(ws, { type: "pong" });
700
697
  return;
701
698
  }
702
- const response = await this.handleRequest(ws, message);
699
+ const response = await this.handleRequest(ws, message, this.ctx);
703
700
  if (response) {
704
701
  await this.sendMessage(ws, response);
705
702
  }
@@ -726,7 +723,7 @@ var WebSocketHandler = class {
726
723
  onError(_error, ws) {
727
724
  this.onClose(ws);
728
725
  }
729
- async handleRequest(ws, message) {
726
+ async handleRequest(ws, message, ctx) {
730
727
  if (!message.id || !message.module || !message.action) {
731
728
  throw new Error("Invalid request message");
732
729
  }
@@ -762,7 +759,7 @@ var WebSocketHandler = class {
762
759
  message.action
763
760
  );
764
761
  const args = message.args ? Object.values(message.args) : [];
765
- const result = await handler.handle(args);
762
+ const result = await handler.handle(args, ctx);
766
763
  if (handler.metadata.stream) {
767
764
  try {
768
765
  for await (const value of result) {
@@ -878,7 +875,6 @@ var Microservice = class {
878
875
  };
879
876
  this.cache = this.options.cacheAdapter;
880
877
  this.fetch = this.app.request;
881
- this.waitingInitialization = this.initialize();
882
878
  ServiceContext.service = this;
883
879
  }
884
880
  async initialize() {
@@ -923,20 +919,20 @@ var Microservice = class {
923
919
  );
924
920
  this.actionHandlers.set(`${moduleName}.${actionName}`, handler);
925
921
  logger_default.info(
926
- `[ \u6CE8\u518C\u52A8\u4F5C ] ${moduleName}.${actionName} ${actionMetadata.description} ${actionMetadata.mcp ? "MCP:" + actionMetadata.mcp?.type : ""}`
922
+ `[ \u6CE8\u518C\u52A8\u4F5C ] ${moduleName}.${actionName} ${actionMetadata.description}`
927
923
  );
928
924
  }
929
925
  for (const [_, page] of Object.entries(pages)) {
930
- const handler = new PageHandler(
931
- moduleInstance,
932
- page,
933
- moduleName
934
- );
926
+ const handler = new PageHandler(moduleInstance, page, moduleName);
935
927
  this.pageHandlers.set(`${moduleName}.${page.name}`, handler);
936
928
  const paths = Array.isArray(page.path) ? page.path : [page.path];
937
929
  for (let path of paths) {
938
930
  path = page.absolutePath ? path : `${this.options.prefix}${path}`;
939
- this.app[page.method](path, (ctx) => handler.handle(ctx));
931
+ this.app[page.method](
932
+ path,
933
+ ...page.middlewares ?? [],
934
+ (ctx) => handler.handle(ctx)
935
+ );
940
936
  logger_default.info(
941
937
  `[ \u6CE8\u518C\u9875\u9762 ] ${moduleName}.${page.name} ${page.method.toUpperCase()} ${page.path} ${page.description}`
942
938
  );
@@ -998,11 +994,10 @@ var Microservice = class {
998
994
  });
999
995
  this.app.post(`${prefix}/:moduleName/:actionName`, this.handleRequest);
1000
996
  if (this.options.websocket?.enabled) {
1001
- this.wsHandler = new WebSocketHandler(this);
1002
997
  this.app.get(
1003
998
  `${prefix}/ws`,
1004
- this.nodeWebSocket.upgradeWebSocket((_ctx) => {
1005
- const wsHandler = new WebSocketHandler(this, {
999
+ this.nodeWebSocket.upgradeWebSocket((ctx) => {
1000
+ const wsHandler = new WebSocketHandler(this, ctx, {
1006
1001
  timeout: this.options.websocket?.timeout
1007
1002
  });
1008
1003
  return {
@@ -1109,7 +1104,7 @@ var Microservice = class {
1109
1104
  * 启动服务
1110
1105
  */
1111
1106
  async start(port = 3e3, silent = false) {
1112
- await this.waitingInitialization;
1107
+ await this.init();
1113
1108
  const prefix = this.options.prefix ?? "/api";
1114
1109
  this.abortController = new AbortController();
1115
1110
  !silent && console.log("");
@@ -1224,7 +1219,9 @@ Received SIGTERM signal`);
1224
1219
  async waitForActiveRequests() {
1225
1220
  const timeout = this.options.gracefulShutdown?.timeout || 3e4;
1226
1221
  const startTime = Date.now();
1227
- logger_default.info(`Waiting for ${this.activeRequests.size} active requests to complete...`);
1222
+ logger_default.info(
1223
+ `Waiting for ${this.activeRequests.size} active requests to complete...`
1224
+ );
1228
1225
  return new Promise((resolve) => {
1229
1226
  const checkInterval = setInterval(() => {
1230
1227
  const elapsed = Date.now() - startTime;
@@ -1234,10 +1231,14 @@ Received SIGTERM signal`);
1234
1231
  resolve();
1235
1232
  } else if (elapsed >= timeout) {
1236
1233
  clearInterval(checkInterval);
1237
- logger_default.warn(`Timeout waiting for requests to complete. ${this.activeRequests.size} requests still active`);
1234
+ logger_default.warn(
1235
+ `Timeout waiting for requests to complete. ${this.activeRequests.size} requests still active`
1236
+ );
1238
1237
  resolve();
1239
1238
  } else {
1240
- logger_default.info(`Still waiting for ${this.activeRequests.size} requests... (${elapsed}ms elapsed)`);
1239
+ logger_default.info(
1240
+ `Still waiting for ${this.activeRequests.size} requests... (${elapsed}ms elapsed)`
1241
+ );
1241
1242
  }
1242
1243
  }, 1e3);
1243
1244
  });
@@ -1259,7 +1260,10 @@ Received SIGTERM signal`);
1259
1260
  await Promise.race([
1260
1261
  Promise.resolve(hook.cleanup()),
1261
1262
  new Promise(
1262
- (_, reject) => setTimeout(() => reject(new Error(`Cleanup hook ${hook.name} timeout`)), timeout)
1263
+ (_, reject) => setTimeout(
1264
+ () => reject(new Error(`Cleanup hook ${hook.name} timeout`)),
1265
+ timeout
1266
+ )
1263
1267
  )
1264
1268
  ]);
1265
1269
  logger_default.info(`Cleanup hook ${hook.name} completed successfully`);
@@ -1340,10 +1344,13 @@ Received SIGTERM signal`);
1340
1344
  const requestId = crypto.randomUUID();
1341
1345
  const startTime = Date.now();
1342
1346
  if (this.status === "shutting_down") {
1343
- return ctx.json({
1344
- success: false,
1345
- error: "Service is shutting down"
1346
- }, 503);
1347
+ return ctx.json(
1348
+ {
1349
+ success: false,
1350
+ error: "Service is shutting down"
1351
+ },
1352
+ 503
1353
+ );
1347
1354
  }
1348
1355
  try {
1349
1356
  const paramsText = await ctx.req.text();
@@ -1354,7 +1361,7 @@ Received SIGTERM signal`);
1354
1361
  startTime,
1355
1362
  params: paramsText
1356
1363
  });
1357
- const result = await handler.handle(paramsText);
1364
+ const result = await handler.handle(paramsText, ctx);
1358
1365
  if (handler.metadata.stream) {
1359
1366
  const encoder = new TextEncoder();
1360
1367
  const microservice = this;
@@ -1410,124 +1417,10 @@ Received SIGTERM signal`);
1410
1417
  }
1411
1418
  }
1412
1419
  async init() {
1420
+ this.waitingInitialization = this.initialize();
1413
1421
  await this.waitingInitialization;
1414
1422
  }
1415
1423
  };
1416
- var HonoTransport = class {
1417
- constructor(url, stream, closeStream) {
1418
- this.url = url;
1419
- this.stream = stream;
1420
- this.closeStream = closeStream;
1421
- this.sessionId = ulid();
1422
- }
1423
- sessionId;
1424
- onclose;
1425
- onerror;
1426
- onmessage;
1427
- async start() {
1428
- await this.stream.writeSSE({
1429
- event: "endpoint",
1430
- data: `${encodeURI(this.url)}?sessionId=${this.sessionId}`
1431
- });
1432
- this.stream.onAbort(() => {
1433
- this.close();
1434
- });
1435
- }
1436
- async handleMessage(message) {
1437
- let parsedMessage;
1438
- try {
1439
- parsedMessage = JSONRPCMessageSchema.parse(message);
1440
- } catch (error) {
1441
- this.onerror?.(error);
1442
- throw error;
1443
- }
1444
- this.onmessage?.(parsedMessage);
1445
- }
1446
- async send(message) {
1447
- await this.stream.writeln(
1448
- `event: message
1449
- data: ${JSON.stringify(message)}
1450
-
1451
- `
1452
- );
1453
- }
1454
- async close() {
1455
- this.onclose?.();
1456
- this.closeStream();
1457
- }
1458
- };
1459
- var ModelContextProtocolPlugin = class extends Plugin {
1460
- mcpServer;
1461
- transports = {};
1462
- registerMcpTools(engine) {
1463
- const modules = engine.getModules(true);
1464
- for (const module of Object.values(modules)) {
1465
- for (const action of Object.values(module.actions)) {
1466
- if (action.mcp?.type !== "tool") {
1467
- continue;
1468
- }
1469
- const args = {};
1470
- const argsIndex = {};
1471
- for (const [index, param] of (action.params ?? []).entries()) {
1472
- args[param.description] = param;
1473
- argsIndex[param.description] = index;
1474
- }
1475
- this.mcpServer.tool(
1476
- `${module.name}.${action.name}`,
1477
- action.description ?? "",
1478
- args,
1479
- async (params) => {
1480
- const argsList = [];
1481
- for (const [key, value] of Object.entries(params)) {
1482
- argsList[argsIndex[key]] = value;
1483
- }
1484
- const result = await engine.getActionHandler(module.name, action.name).handle(argsList);
1485
- return {
1486
- content: [{ type: "text", text: JSON.stringify(result) }]
1487
- };
1488
- }
1489
- );
1490
- }
1491
- }
1492
- }
1493
- initialize = async (engine) => {
1494
- const app = engine.getApp();
1495
- this.mcpServer = new McpServer({
1496
- name: engine.options.name,
1497
- version: engine.options.version
1498
- });
1499
- this.registerMcpTools(engine);
1500
- app.get(`${engine.options.prefix}/mcp_sse`, async (ctx) => {
1501
- return streamSSE(ctx, async (stream) => {
1502
- return new Promise(async (resolve) => {
1503
- const transport = new HonoTransport(
1504
- `${engine.options.prefix}/mcp_messages`,
1505
- stream,
1506
- () => {
1507
- delete this.transports[transport.sessionId];
1508
- resolve();
1509
- }
1510
- );
1511
- this.transports[transport.sessionId] = transport;
1512
- await this.mcpServer.connect(transport);
1513
- });
1514
- });
1515
- });
1516
- app.post(`${engine.options.prefix}/mcp_messages`, async (ctx) => {
1517
- const sessionId = ctx.req.query("sessionId");
1518
- if (!sessionId) {
1519
- return ctx.text("No transport found for sessionId", 400);
1520
- }
1521
- const transport = this.transports[sessionId];
1522
- const message = await ctx.req.json();
1523
- await transport.handleMessage(message);
1524
- return ctx.text("Accepted", 202);
1525
- });
1526
- logger_default.info(
1527
- `ModelContextProtocolPlugin endpoint: ${engine.options.prefix}/mcp_sse`
1528
- );
1529
- };
1530
- };
1531
1424
  var DEFAULT_FAVICON = /* @__PURE__ */ jsx(
1532
1425
  "link",
1533
1426
  {
@@ -1730,4 +1623,4 @@ async function startCheck(checkers, pass) {
1730
1623
  if (pass) await pass();
1731
1624
  }
1732
1625
 
1733
- export { Action, BaseLayout, CacheAdapter, HtmxLayout, MemoryCacheAdapter, Microservice, ModelContextProtocolPlugin, Module, Page, PageRenderPlugin, Plugin, RedisCacheAdapter, Schedule, ScheduleMode, ServiceContext, ServiceInfoCards, ServiceStatusPage, logger_default as logger, startCheck };
1626
+ export { Action, BaseLayout, CacheAdapter, HtmxLayout, MemoryCacheAdapter, Microservice, Module, Page, PageRenderPlugin, Plugin, RedisCacheAdapter, Schedule, ScheduleMode, ServiceContext, ServiceInfoCards, ServiceStatusPage, logger_default as logger, startCheck };
package/package.json CHANGED
@@ -1,87 +1,86 @@
1
- {
2
- "name": "imean-service-engine",
3
- "version": "1.7.3",
4
- "description": "microservice engine",
5
- "keywords": [
6
- "microservice",
7
- "websocket",
8
- "http",
9
- "node"
10
- ],
11
- "author": "imean",
12
- "type": "module",
13
- "license": "MIT",
14
- "repository": {
15
- "type": "git",
16
- "url": "git+https://git.imean.tech/imean/imean-microservice-framework.git"
17
- },
18
- "main": "dist/mod.js",
19
- "module": "dist/mod.js",
20
- "types": "dist/mod.d.ts",
21
- "exports": {
22
- ".": {
23
- "types": "./dist/mod.d.ts",
24
- "import": "./dist/mod.js",
25
- "require": "./dist/mod.cjs"
26
- }
27
- },
28
- "files": [
29
- "dist",
30
- "README.md",
31
- "LICENSE"
32
- ],
33
- "scripts": {
34
- "dev": "tsx watch dev/index.ts",
35
- "build": "tsup",
36
- "test": "vitest run",
37
- "prepublishOnly": "npm run build && npm run test"
38
- },
39
- "dependencies": {
40
- "@hono/node-server": "^1.13.7",
41
- "@hono/node-ws": "^1.0.6",
42
- "@modelcontextprotocol/sdk": "^1.8.0",
43
- "dayjs": "^1.11.13",
44
- "ejson": "^2.2.3",
45
- "etcd3": "^1.1.2",
46
- "fs-extra": "^11.3.0",
47
- "hono": "^4.6.17",
48
- "lru-cache": "^11.0.2",
49
- "prettier": "^3.4.2",
50
- "ulid": "^3.0.0",
51
- "winston": "^3.17.0",
52
- "zod": "^3.24.1"
53
- },
54
- "peerDependencies": {
55
- "@opentelemetry/api": "^1.x",
56
- "ioredis": "^5.6.0"
57
- },
58
- "devDependencies": {
59
- "@opentelemetry/auto-instrumentations-node": "^0.55.3",
60
- "@opentelemetry/exporter-logs-otlp-proto": "^0.57.1",
61
- "@opentelemetry/exporter-metrics-otlp-proto": "^0.57.1",
62
- "@opentelemetry/exporter-trace-otlp-proto": "^0.57.1",
63
- "@opentelemetry/instrumentation-winston": "^0.44.0",
64
- "@opentelemetry/sdk-logs": "^0.57.1",
65
- "@opentelemetry/sdk-metrics": "^1.30.1",
66
- "@opentelemetry/sdk-node": "^0.57.1",
67
- "@opentelemetry/sdk-trace-node": "^1.30.1",
68
- "@opentelemetry/winston-transport": "^0.10.0",
69
- "@types/ejson": "^2.2.2",
70
- "@types/fs-extra": "^11.0.4",
71
- "@types/ioredis-mock": "^8.2.5",
72
- "@types/node": "^20.0.0",
73
- "@vitest/coverage-v8": "^3.0.4",
74
- "imean-service-client": "^1.5.0",
75
- "ioredis-mock": "^8.9.0",
76
- "opentelemetry-instrumentation-fetch-node": "^1.2.3",
77
- "tslib": "^2.8.1",
78
- "tsup": "^8.0.1",
79
- "tsx": "^4.19.2",
80
- "typescript": "^5.3.3",
81
- "vite-tsconfig-paths": "^5.1.4",
82
- "vitest": "^3.0.3"
83
- },
84
- "engines": {
85
- "node": ">=20"
86
- }
87
- }
1
+ {
2
+ "name": "imean-service-engine",
3
+ "version": "1.8.1",
4
+ "description": "microservice engine",
5
+ "keywords": [
6
+ "microservice",
7
+ "websocket",
8
+ "http",
9
+ "node"
10
+ ],
11
+ "author": "imean",
12
+ "type": "module",
13
+ "license": "MIT",
14
+ "repository": {
15
+ "type": "git",
16
+ "url": "git+https://git.imean.tech/imean/imean-microservice-framework.git"
17
+ },
18
+ "main": "dist/mod.js",
19
+ "module": "dist/mod.js",
20
+ "types": "dist/mod.d.ts",
21
+ "exports": {
22
+ ".": {
23
+ "types": "./dist/mod.d.ts",
24
+ "import": "./dist/mod.js",
25
+ "require": "./dist/mod.cjs"
26
+ }
27
+ },
28
+ "files": [
29
+ "dist",
30
+ "README.md",
31
+ "LICENSE"
32
+ ],
33
+ "scripts": {
34
+ "dev": "tsx watch dev/index.ts",
35
+ "build": "tsup",
36
+ "test": "vitest run",
37
+ "prepublishOnly": "npm run build && npm run test"
38
+ },
39
+ "dependencies": {
40
+ "@hono/node-server": "^1.13.7",
41
+ "@hono/node-ws": "^1.0.6",
42
+ "dayjs": "^1.11.13",
43
+ "ejson": "^2.2.3",
44
+ "etcd3": "^1.1.2",
45
+ "fs-extra": "^11.3.0",
46
+ "hono": "^4.6.17",
47
+ "lru-cache": "^11.0.2",
48
+ "prettier": "^3.4.2",
49
+ "ulid": "^3.0.0",
50
+ "winston": "^3.17.0",
51
+ "zod": "^3.24.1"
52
+ },
53
+ "peerDependencies": {
54
+ "@opentelemetry/api": "^1.x",
55
+ "ioredis": "^5.6.0"
56
+ },
57
+ "devDependencies": {
58
+ "@opentelemetry/auto-instrumentations-node": "^0.55.3",
59
+ "@opentelemetry/exporter-logs-otlp-proto": "^0.57.1",
60
+ "@opentelemetry/exporter-metrics-otlp-proto": "^0.57.1",
61
+ "@opentelemetry/exporter-trace-otlp-proto": "^0.57.1",
62
+ "@opentelemetry/instrumentation-winston": "^0.44.0",
63
+ "@opentelemetry/sdk-logs": "^0.57.1",
64
+ "@opentelemetry/sdk-metrics": "^1.30.1",
65
+ "@opentelemetry/sdk-node": "^0.57.1",
66
+ "@opentelemetry/sdk-trace-node": "^1.30.1",
67
+ "@opentelemetry/winston-transport": "^0.10.0",
68
+ "@types/ejson": "^2.2.2",
69
+ "@types/fs-extra": "^11.0.4",
70
+ "@types/ioredis-mock": "^8.2.5",
71
+ "@types/node": "^20.0.0",
72
+ "@vitest/coverage-v8": "^3.0.4",
73
+ "imean-service-client": "^1.5.0",
74
+ "ioredis-mock": "^8.9.0",
75
+ "opentelemetry-instrumentation-fetch-node": "^1.2.3",
76
+ "tslib": "^2.8.1",
77
+ "tsup": "^8.0.1",
78
+ "tsx": "^4.19.2",
79
+ "typescript": "^5.3.3",
80
+ "vite-tsconfig-paths": "^5.1.4",
81
+ "vitest": "^3.0.3"
82
+ },
83
+ "engines": {
84
+ "node": ">=20"
85
+ }
86
+ }