imean-service-engine 1.0.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,9 +1,7 @@
1
- # Microservice Framework for Deno
1
+ # Microservice Framework
2
2
 
3
3
  一个轻量级的 TypeScript 微服务框架。提供了类型安全、自动客户端生成、请求重试等特性。
4
4
 
5
- [![JSR](https://jsr.io/badges/@imean/service-engine)](https://jsr.io/@imean/service-engine)
6
-
7
5
  ## 特性
8
6
 
9
7
  - 📝 完全的 TypeScript 支持
@@ -14,7 +12,6 @@
14
12
  - 🌟 优雅的装饰器 API
15
13
  - 🚦 优雅停机支持
16
14
  - 📡 生成基于 fetch 的客户端代码,可以在 Deno 、Node.js、Bun 以及浏览器中使用
17
- - 🌟 服务间调用可以利用 Deno 的分布式模块引入快速集成生成的客户端
18
15
  - 🌟 支持 Stream 流传输,客户端使用 AsyncIterator 迭代
19
16
  - 🌟 服务引擎支持通过 WebSocket 进行实时通信,相比 HTTP 请求具有以下优势:
20
17
  - 保持长连接,减少连接建立的开销
@@ -30,7 +27,7 @@
30
27
  ## 安装
31
28
 
32
29
  ```typescript
33
- import { Action, Microservice, Module } from "jsr:@imean/microservice";
30
+ import { Action, Microservice, Module } from "imean-service-engine";
34
31
  ```
35
32
 
36
33
  ## 快速开始
@@ -126,7 +123,7 @@ TypeScript 客户端代码。
126
123
 
127
124
  ```typescript
128
125
  const client = new MicroserviceClient({
129
- baseUrl: "http://localhost:3000/client.ts?cache=v1",
126
+ baseUrl: "http://localhost:3000",
130
127
  });
131
128
  // 创建用户
132
129
  const user = await client.users.createUser("张三", 25);
@@ -230,7 +227,7 @@ interface ClientOptions {
230
227
 
231
228
  ```typescript
232
229
  // main.ts
233
- import { startCheck } from "jsr:@imean/microservice";
230
+ import { startCheck } from "imean-service-engine";
234
231
 
235
232
  // 数据库连接检查
236
233
  async function checkDatabase() {
@@ -326,17 +323,17 @@ your-service/
326
323
  // config/index.ts
327
324
  export const config = {
328
325
  database: {
329
- host: Deno.env.get("DB_HOST") || "localhost",
330
- port: parseInt(Deno.env.get("DB_PORT") || "5432"),
326
+ host: process.env.DB_HOST || "localhost",
327
+ port: parseInt(process.env.DB_PORT || "5432"),
331
328
  // ...
332
329
  },
333
330
  redis: {
334
- url: Deno.env.get("REDIS_URL") || "redis://localhost:6379",
331
+ url: process.env.REDIS_URL || "redis://localhost:6379",
335
332
  // ...
336
333
  },
337
334
  service: {
338
- port: parseInt(Deno.env.get("PORT") || "3000"),
339
- prefix: Deno.env.get("API_PREFIX") || "/api",
335
+ port: parseInt(process.env.PORT || "3000"),
336
+ prefix: process.env.API_PREFIX || "/api",
340
337
  },
341
338
  };
342
339
 
@@ -550,7 +547,7 @@ const client = new MicroserviceClient({
550
547
 
551
548
  ### Node.js 环境使用 WebSocket
552
549
 
553
- Node.js 环境下,可以使用 `isomorphic-ws` 包来提供 WebSocket 实现:
550
+ 最新Node.js已经提供了 WebSocket 实现,可以直接使用。如果在较低 Node.js 环境下,可以使用 `isomorphic-ws` 包来提供 WebSocket 实现:
554
551
 
555
552
  ```typescript
556
553
  import WebSocket from "isomorphic-ws";
package/dist/mod.cjs CHANGED
@@ -7,9 +7,9 @@ var etcd3 = require('etcd3');
7
7
  var fs = require('fs-extra');
8
8
  var hono = require('hono');
9
9
  var lruCache = require('lru-cache');
10
- var winston = require('winston');
11
10
  var api = require('@opentelemetry/api');
12
- var apiLogs = require('@opentelemetry/api-logs');
11
+ var winston = require('winston');
12
+ var prettier = require('prettier');
13
13
  var crypto2 = require('crypto');
14
14
  var zlib = require('zlib');
15
15
  var nodeWs = require('@hono/node-ws');
@@ -20,6 +20,7 @@ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
20
20
  var ejson3__default = /*#__PURE__*/_interopDefault(ejson3);
21
21
  var fs__default = /*#__PURE__*/_interopDefault(fs);
22
22
  var winston__default = /*#__PURE__*/_interopDefault(winston);
23
+ var prettier__default = /*#__PURE__*/_interopDefault(prettier);
23
24
  var crypto2__default = /*#__PURE__*/_interopDefault(crypto2);
24
25
  var dayjs__default = /*#__PURE__*/_interopDefault(dayjs);
25
26
 
@@ -108,6 +109,7 @@ var logger = winston__default.default.createLogger({
108
109
  var logger_default = logger;
109
110
 
110
111
  // decorators/schedule.ts
112
+ var tracer = api.trace.getTracer("scheduler");
111
113
  var SCHEDULE_METADATA = Symbol("schedule:metadata");
112
114
  function Schedule(options) {
113
115
  return function(_originalMethod, context) {
@@ -160,20 +162,44 @@ var Scheduler = class {
160
162
  });
161
163
  campaign.on("elected", () => {
162
164
  this.isLeader.set(serviceId, true);
163
- this.startTimer(serviceId, metadata, method);
165
+ this.startTimer(serviceId, metadata, moduleName, method);
164
166
  logger_default.info(`become leader for ${moduleName}.${methodName}`);
165
167
  });
166
168
  }
167
169
  /**
168
170
  * 启动定时器
169
171
  */
170
- startTimer(serviceId, metadata, method) {
172
+ startTimer(serviceId, metadata, moduleName, method) {
171
173
  this.stopTimer(serviceId);
174
+ const wrappedMethod = async () => {
175
+ tracer.startActiveSpan(
176
+ `ScheduleTask ${moduleName}.${metadata.name}`,
177
+ { root: true },
178
+ async (span) => {
179
+ span.setAttribute("serviceId", serviceId);
180
+ span.setAttribute("methodName", metadata.name);
181
+ span.setAttribute("moduleName", moduleName);
182
+ span.setAttribute("interval", metadata.interval);
183
+ span.setAttribute("mode", metadata.mode);
184
+ try {
185
+ await method();
186
+ } catch (error) {
187
+ span.setStatus({
188
+ code: api.SpanStatusCode.ERROR,
189
+ message: error.message
190
+ });
191
+ } finally {
192
+ span.setStatus({ code: api.SpanStatusCode.OK });
193
+ span.end();
194
+ }
195
+ }
196
+ );
197
+ };
172
198
  if (metadata.mode === "FIXED_DELAY" /* FIXED_DELAY */) {
173
199
  const runTask = async () => {
174
200
  if (!this.isLeader.get(serviceId)) return;
175
201
  try {
176
- await method();
202
+ await wrappedMethod();
177
203
  } finally {
178
204
  this.timers.set(serviceId, setTimeout(runTask, metadata.interval));
179
205
  }
@@ -184,7 +210,7 @@ var Scheduler = class {
184
210
  serviceId,
185
211
  setInterval(async () => {
186
212
  if (!this.isLeader.get(serviceId)) return;
187
- await method();
213
+ await wrappedMethod();
188
214
  }, metadata.interval)
189
215
  );
190
216
  }
@@ -220,11 +246,9 @@ var Scheduler = class {
220
246
  }
221
247
  }
222
248
  };
223
-
224
- // utils/format.ts
225
249
  async function formatCode(code) {
226
250
  try {
227
- return code;
251
+ return prettier__default.default.format(code, { parser: "typescript" });
228
252
  } catch {
229
253
  return code;
230
254
  }
@@ -389,8 +413,7 @@ var brotli = {
389
413
  };
390
414
 
391
415
  // core/handler.ts
392
- var tracer = api.trace.getTracer("action-handler");
393
- var logger2 = apiLogs.logs.getLogger("action-handler");
416
+ var tracer2 = api.trace.getTracer("action-handler");
394
417
  var ActionHandler = class {
395
418
  constructor(moduleInstance, actionName, metadata, microservice, moduleName) {
396
419
  this.moduleInstance = moduleInstance;
@@ -400,47 +423,60 @@ var ActionHandler = class {
400
423
  this.moduleName = moduleName;
401
424
  }
402
425
  async handle(req) {
403
- const span = tracer.startSpan("handle");
404
- span.addEvent("logs");
405
- logger2.emit({
406
- attributes: { module: this.moduleName, action: this.actionName }
407
- });
408
- const startTime = Date.now();
409
- let args;
410
- if (typeof req === "string") {
411
- try {
412
- args = Object.values(ejson3__default.default.parse(req));
413
- } catch (error) {
414
- throw new Error(`Invalid request body: ${error.message}`);
426
+ return await tracer2.startActiveSpan(
427
+ `handle ${this.moduleName}.${this.actionName}`,
428
+ async (span) => {
429
+ span.setAttribute("module", this.moduleName);
430
+ span.setAttribute("action", this.actionName);
431
+ try {
432
+ return await this._handle(req);
433
+ } finally {
434
+ span.end();
435
+ }
415
436
  }
416
- } else {
417
- args = req;
418
- }
419
- if (this.metadata.params) {
420
- args = args.map((arg, index) => {
437
+ );
438
+ }
439
+ async _validate(req) {
440
+ const span = tracer2.startSpan("validate");
441
+ try {
442
+ let args;
443
+ if (typeof req === "string") {
421
444
  try {
422
- return this.metadata.params[index].parse(arg);
445
+ args = Object.values(ejson3__default.default.parse(req));
423
446
  } catch (error) {
424
- throw new Error(
425
- `Invalid argument ${index}: ${error.message}`
426
- );
447
+ throw new Error(`Invalid request body: ${error.message}`);
427
448
  }
428
- });
449
+ } else {
450
+ args = req;
451
+ }
452
+ if (this.metadata.params) {
453
+ args = args.map((arg, index) => {
454
+ try {
455
+ return this.metadata.params[index].parse(arg);
456
+ } catch (error) {
457
+ throw new Error(
458
+ `Invalid argument ${index}: ${error.message}`
459
+ );
460
+ }
461
+ });
462
+ }
463
+ const requestHash = hashText(ejson3__default.default.stringify(args));
464
+ span.setAttribute("requestHash", requestHash);
465
+ return { args, requestHash };
466
+ } finally {
467
+ span.end();
429
468
  }
430
- const requestHash = hashText(ejson3__default.default.stringify(args));
469
+ }
470
+ async _handle(req) {
471
+ const span = api.trace.getActiveSpan();
472
+ const { args, requestHash } = await this._validate(req);
431
473
  if (!this.metadata.stream && this.metadata.cache && !this.microservice.options.disableCache) {
432
474
  const cacheKey = `${this.moduleName}.${this.actionName}.${requestHash}`;
433
475
  const cached = await this.microservice.cache.get(cacheKey);
434
476
  const now = Date.now();
435
- if (cached.value !== null && (!this.metadata.ttl || cached.value?.expireAt > now)) {
436
- this.microservice.updateMethodStats(
437
- this.moduleName,
438
- this.actionName,
439
- 0,
440
- true,
441
- true
442
- );
443
- return cached.value.data;
477
+ if (cached !== null && (!this.metadata.ttl || cached?.expireAt > now)) {
478
+ span?.setAttribute("cacheHit", true);
479
+ return cached.data;
444
480
  }
445
481
  }
446
482
  try {
@@ -449,11 +485,11 @@ var ActionHandler = class {
449
485
  args
450
486
  );
451
487
  if (this.metadata.stream) {
488
+ span?.setAttribute("stream", true);
452
489
  if (!isAsyncIterable(result)) {
453
490
  throw new Error("Stream action must return AsyncIterator");
454
491
  }
455
492
  let count = 0;
456
- const self = this;
457
493
  return {
458
494
  [Symbol.asyncIterator]() {
459
495
  const iterator = result[Symbol.asyncIterator]();
@@ -461,24 +497,22 @@ var ActionHandler = class {
461
497
  async next() {
462
498
  try {
463
499
  const { value, done } = await iterator.next();
500
+ span?.addEvent("stream.next");
464
501
  if (!done) count++;
465
502
  if (done) {
466
- const responseTime = Date.now() - startTime;
467
- self.microservice.updateMethodStats(
468
- self.moduleName,
469
- self.actionName,
470
- responseTime / count,
471
- true
472
- );
503
+ span?.setAttribute("streamCount", count);
504
+ span?.addEvent("stream.end");
505
+ span?.end();
473
506
  }
474
507
  return { value, done };
475
508
  } catch (error) {
476
- self.microservice.updateMethodStats(
477
- self.moduleName,
478
- self.actionName,
479
- 0,
480
- false
481
- );
509
+ span?.addEvent("stream.error", { error: error.message });
510
+ span?.recordException(error);
511
+ span?.setStatus({
512
+ code: api.SpanStatusCode.ERROR,
513
+ message: error.message
514
+ });
515
+ span?.end();
482
516
  throw error;
483
517
  }
484
518
  }
@@ -497,41 +531,26 @@ var ActionHandler = class {
497
531
  if (this.metadata.cache && !this.microservice.options.disableCache) {
498
532
  const cacheKey = `${this.moduleName}.${this.actionName}.${requestHash}`;
499
533
  const now = Date.now();
500
- this.microservice.cache.set(cacheKey, {
501
- data: parsedResult,
502
- expireAt: this.metadata.ttl ? now + this.metadata.ttl : void 0
503
- });
534
+ this.microservice.cache.set(
535
+ cacheKey,
536
+ {
537
+ data: parsedResult,
538
+ expireAt: this.metadata.ttl ? now + this.metadata.ttl : void 0
539
+ },
540
+ { ttl: this.metadata.ttl }
541
+ );
504
542
  }
505
- this.microservice.updateMethodStats(
506
- this.moduleName,
507
- this.actionName,
508
- 0,
509
- true
510
- );
543
+ span?.setStatus({ code: api.SpanStatusCode.OK, message: "success" });
544
+ span?.end();
511
545
  return parsedResult;
512
546
  } catch (error) {
513
547
  if (this.metadata.printError !== false && this.microservice.options.printError !== false) {
514
548
  console.error(`Error in ${this.moduleName}.${this.actionName}:`, error);
515
549
  }
516
- if (this.microservice.options.events?.onError) {
517
- this.microservice.options.events.onError({
518
- requestHash,
519
- service: {
520
- id: this.microservice.serviceId,
521
- name: this.microservice.options.name,
522
- version: this.microservice.options.version,
523
- env: this.microservice.options.env
524
- },
525
- module: this.moduleName,
526
- action: this.actionName,
527
- params: args,
528
- time: Date.now(),
529
- error: error.message
530
- });
531
- }
550
+ span?.recordException(error);
551
+ span?.setStatus({ code: api.SpanStatusCode.ERROR, message: error.message });
552
+ span?.end();
532
553
  throw error;
533
- } finally {
534
- span.end();
535
554
  }
536
555
  }
537
556
  };
package/dist/mod.d.cts CHANGED
@@ -149,6 +149,8 @@ declare class ActionHandler {
149
149
  private moduleName;
150
150
  constructor(moduleInstance: any, actionName: string, metadata: ActionMetadata, microservice: Microservice, moduleName: string);
151
151
  handle(req: string | any[]): Promise<any>;
152
+ private _validate;
153
+ private _handle;
152
154
  }
153
155
 
154
156
  declare const ServiceContext: {
package/dist/mod.d.ts CHANGED
@@ -149,6 +149,8 @@ declare class ActionHandler {
149
149
  private moduleName;
150
150
  constructor(moduleInstance: any, actionName: string, metadata: ActionMetadata, microservice: Microservice, moduleName: string);
151
151
  handle(req: string | any[]): Promise<any>;
152
+ private _validate;
153
+ private _handle;
152
154
  }
153
155
 
154
156
  declare const ServiceContext: {
package/dist/mod.js CHANGED
@@ -6,9 +6,9 @@ import { Etcd3 } from 'etcd3';
6
6
  import fs from 'fs-extra';
7
7
  import { Hono } from 'hono';
8
8
  import { LRUCache } from 'lru-cache';
9
+ import { trace, SpanStatusCode } from '@opentelemetry/api';
9
10
  import winston, { format } from 'winston';
10
- import { trace } from '@opentelemetry/api';
11
- import { logs } from '@opentelemetry/api-logs';
11
+ import prettier from 'prettier';
12
12
  import crypto2 from 'node:crypto';
13
13
  import { brotliDecompress } from 'node:zlib';
14
14
  import { brotliCompress, constants } from 'zlib';
@@ -100,6 +100,7 @@ var logger = winston.createLogger({
100
100
  var logger_default = logger;
101
101
 
102
102
  // decorators/schedule.ts
103
+ var tracer = trace.getTracer("scheduler");
103
104
  var SCHEDULE_METADATA = Symbol("schedule:metadata");
104
105
  function Schedule(options) {
105
106
  return function(_originalMethod, context) {
@@ -152,20 +153,44 @@ var Scheduler = class {
152
153
  });
153
154
  campaign.on("elected", () => {
154
155
  this.isLeader.set(serviceId, true);
155
- this.startTimer(serviceId, metadata, method);
156
+ this.startTimer(serviceId, metadata, moduleName, method);
156
157
  logger_default.info(`become leader for ${moduleName}.${methodName}`);
157
158
  });
158
159
  }
159
160
  /**
160
161
  * 启动定时器
161
162
  */
162
- startTimer(serviceId, metadata, method) {
163
+ startTimer(serviceId, metadata, moduleName, method) {
163
164
  this.stopTimer(serviceId);
165
+ const wrappedMethod = async () => {
166
+ tracer.startActiveSpan(
167
+ `ScheduleTask ${moduleName}.${metadata.name}`,
168
+ { root: true },
169
+ async (span) => {
170
+ span.setAttribute("serviceId", serviceId);
171
+ span.setAttribute("methodName", metadata.name);
172
+ span.setAttribute("moduleName", moduleName);
173
+ span.setAttribute("interval", metadata.interval);
174
+ span.setAttribute("mode", metadata.mode);
175
+ try {
176
+ await method();
177
+ } catch (error) {
178
+ span.setStatus({
179
+ code: SpanStatusCode.ERROR,
180
+ message: error.message
181
+ });
182
+ } finally {
183
+ span.setStatus({ code: SpanStatusCode.OK });
184
+ span.end();
185
+ }
186
+ }
187
+ );
188
+ };
164
189
  if (metadata.mode === "FIXED_DELAY" /* FIXED_DELAY */) {
165
190
  const runTask = async () => {
166
191
  if (!this.isLeader.get(serviceId)) return;
167
192
  try {
168
- await method();
193
+ await wrappedMethod();
169
194
  } finally {
170
195
  this.timers.set(serviceId, setTimeout(runTask, metadata.interval));
171
196
  }
@@ -176,7 +201,7 @@ var Scheduler = class {
176
201
  serviceId,
177
202
  setInterval(async () => {
178
203
  if (!this.isLeader.get(serviceId)) return;
179
- await method();
204
+ await wrappedMethod();
180
205
  }, metadata.interval)
181
206
  );
182
207
  }
@@ -212,11 +237,9 @@ var Scheduler = class {
212
237
  }
213
238
  }
214
239
  };
215
-
216
- // utils/format.ts
217
240
  async function formatCode(code) {
218
241
  try {
219
- return code;
242
+ return prettier.format(code, { parser: "typescript" });
220
243
  } catch {
221
244
  return code;
222
245
  }
@@ -381,8 +404,7 @@ var brotli = {
381
404
  };
382
405
 
383
406
  // core/handler.ts
384
- var tracer = trace.getTracer("action-handler");
385
- var logger2 = logs.getLogger("action-handler");
407
+ var tracer2 = trace.getTracer("action-handler");
386
408
  var ActionHandler = class {
387
409
  constructor(moduleInstance, actionName, metadata, microservice, moduleName) {
388
410
  this.moduleInstance = moduleInstance;
@@ -392,47 +414,60 @@ var ActionHandler = class {
392
414
  this.moduleName = moduleName;
393
415
  }
394
416
  async handle(req) {
395
- const span = tracer.startSpan("handle");
396
- span.addEvent("logs");
397
- logger2.emit({
398
- attributes: { module: this.moduleName, action: this.actionName }
399
- });
400
- const startTime = Date.now();
401
- let args;
402
- if (typeof req === "string") {
403
- try {
404
- args = Object.values(ejson3.parse(req));
405
- } catch (error) {
406
- throw new Error(`Invalid request body: ${error.message}`);
417
+ return await tracer2.startActiveSpan(
418
+ `handle ${this.moduleName}.${this.actionName}`,
419
+ async (span) => {
420
+ span.setAttribute("module", this.moduleName);
421
+ span.setAttribute("action", this.actionName);
422
+ try {
423
+ return await this._handle(req);
424
+ } finally {
425
+ span.end();
426
+ }
407
427
  }
408
- } else {
409
- args = req;
410
- }
411
- if (this.metadata.params) {
412
- args = args.map((arg, index) => {
428
+ );
429
+ }
430
+ async _validate(req) {
431
+ const span = tracer2.startSpan("validate");
432
+ try {
433
+ let args;
434
+ if (typeof req === "string") {
413
435
  try {
414
- return this.metadata.params[index].parse(arg);
436
+ args = Object.values(ejson3.parse(req));
415
437
  } catch (error) {
416
- throw new Error(
417
- `Invalid argument ${index}: ${error.message}`
418
- );
438
+ throw new Error(`Invalid request body: ${error.message}`);
419
439
  }
420
- });
440
+ } else {
441
+ args = req;
442
+ }
443
+ if (this.metadata.params) {
444
+ args = args.map((arg, index) => {
445
+ try {
446
+ return this.metadata.params[index].parse(arg);
447
+ } catch (error) {
448
+ throw new Error(
449
+ `Invalid argument ${index}: ${error.message}`
450
+ );
451
+ }
452
+ });
453
+ }
454
+ const requestHash = hashText(ejson3.stringify(args));
455
+ span.setAttribute("requestHash", requestHash);
456
+ return { args, requestHash };
457
+ } finally {
458
+ span.end();
421
459
  }
422
- const requestHash = hashText(ejson3.stringify(args));
460
+ }
461
+ async _handle(req) {
462
+ const span = trace.getActiveSpan();
463
+ const { args, requestHash } = await this._validate(req);
423
464
  if (!this.metadata.stream && this.metadata.cache && !this.microservice.options.disableCache) {
424
465
  const cacheKey = `${this.moduleName}.${this.actionName}.${requestHash}`;
425
466
  const cached = await this.microservice.cache.get(cacheKey);
426
467
  const now = Date.now();
427
- if (cached.value !== null && (!this.metadata.ttl || cached.value?.expireAt > now)) {
428
- this.microservice.updateMethodStats(
429
- this.moduleName,
430
- this.actionName,
431
- 0,
432
- true,
433
- true
434
- );
435
- return cached.value.data;
468
+ if (cached !== null && (!this.metadata.ttl || cached?.expireAt > now)) {
469
+ span?.setAttribute("cacheHit", true);
470
+ return cached.data;
436
471
  }
437
472
  }
438
473
  try {
@@ -441,11 +476,11 @@ var ActionHandler = class {
441
476
  args
442
477
  );
443
478
  if (this.metadata.stream) {
479
+ span?.setAttribute("stream", true);
444
480
  if (!isAsyncIterable(result)) {
445
481
  throw new Error("Stream action must return AsyncIterator");
446
482
  }
447
483
  let count = 0;
448
- const self = this;
449
484
  return {
450
485
  [Symbol.asyncIterator]() {
451
486
  const iterator = result[Symbol.asyncIterator]();
@@ -453,24 +488,22 @@ var ActionHandler = class {
453
488
  async next() {
454
489
  try {
455
490
  const { value, done } = await iterator.next();
491
+ span?.addEvent("stream.next");
456
492
  if (!done) count++;
457
493
  if (done) {
458
- const responseTime = Date.now() - startTime;
459
- self.microservice.updateMethodStats(
460
- self.moduleName,
461
- self.actionName,
462
- responseTime / count,
463
- true
464
- );
494
+ span?.setAttribute("streamCount", count);
495
+ span?.addEvent("stream.end");
496
+ span?.end();
465
497
  }
466
498
  return { value, done };
467
499
  } catch (error) {
468
- self.microservice.updateMethodStats(
469
- self.moduleName,
470
- self.actionName,
471
- 0,
472
- false
473
- );
500
+ span?.addEvent("stream.error", { error: error.message });
501
+ span?.recordException(error);
502
+ span?.setStatus({
503
+ code: SpanStatusCode.ERROR,
504
+ message: error.message
505
+ });
506
+ span?.end();
474
507
  throw error;
475
508
  }
476
509
  }
@@ -489,41 +522,26 @@ var ActionHandler = class {
489
522
  if (this.metadata.cache && !this.microservice.options.disableCache) {
490
523
  const cacheKey = `${this.moduleName}.${this.actionName}.${requestHash}`;
491
524
  const now = Date.now();
492
- this.microservice.cache.set(cacheKey, {
493
- data: parsedResult,
494
- expireAt: this.metadata.ttl ? now + this.metadata.ttl : void 0
495
- });
525
+ this.microservice.cache.set(
526
+ cacheKey,
527
+ {
528
+ data: parsedResult,
529
+ expireAt: this.metadata.ttl ? now + this.metadata.ttl : void 0
530
+ },
531
+ { ttl: this.metadata.ttl }
532
+ );
496
533
  }
497
- this.microservice.updateMethodStats(
498
- this.moduleName,
499
- this.actionName,
500
- 0,
501
- true
502
- );
534
+ span?.setStatus({ code: SpanStatusCode.OK, message: "success" });
535
+ span?.end();
503
536
  return parsedResult;
504
537
  } catch (error) {
505
538
  if (this.metadata.printError !== false && this.microservice.options.printError !== false) {
506
539
  console.error(`Error in ${this.moduleName}.${this.actionName}:`, error);
507
540
  }
508
- if (this.microservice.options.events?.onError) {
509
- this.microservice.options.events.onError({
510
- requestHash,
511
- service: {
512
- id: this.microservice.serviceId,
513
- name: this.microservice.options.name,
514
- version: this.microservice.options.version,
515
- env: this.microservice.options.env
516
- },
517
- module: this.moduleName,
518
- action: this.actionName,
519
- params: args,
520
- time: Date.now(),
521
- error: error.message
522
- });
523
- }
541
+ span?.recordException(error);
542
+ span?.setStatus({ code: SpanStatusCode.ERROR, message: error.message });
543
+ span?.end();
524
544
  throw error;
525
- } finally {
526
- span.end();
527
545
  }
528
546
  }
529
547
  };
package/package.json CHANGED
@@ -1,74 +1,84 @@
1
- {
2
- "name": "imean-service-engine",
3
- "version": "1.0.0",
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 dev/index.ts",
35
- "build": "tsup",
36
- "lint": "deno lint",
37
- "fmt": "deno fmt",
38
- "test": "vitest",
39
- "coverage": "vitest --coverage",
40
- "prepublishOnly": "npm run build"
41
- },
42
- "dependencies": {
43
- "@hono/node-server": "^1.13.7",
44
- "@hono/node-ws": "^1.0.6",
45
- "@opentelemetry/api": "^1.9.0",
46
- "@opentelemetry/api-logs": "^0.57.1",
47
- "dayjs": "^1.11.13",
48
- "ejson": "^2.2.3",
49
- "etcd3": "^1.1.2",
50
- "fs-extra": "^11.3.0",
51
- "hono": "^4.6.17",
52
- "lru-cache": "^11.0.2",
53
- "winston": "^3.17.0",
54
- "zod": "^3.24.1"
55
- },
56
- "peerDependencies": {
57
- "isomorphic-ws": "^5.0.0"
58
- },
59
- "devDependencies": {
60
- "@types/ejson": "^2.2.2",
61
- "@types/fs-extra": "^11.0.4",
62
- "@types/node": "^20.0.0",
63
- "imean-service-client": "^1.4.1",
64
- "tslib": "^2.8.1",
65
- "tsup": "^8.0.1",
66
- "tsx": "^4.19.2",
67
- "typescript": "^5.3.3",
68
- "vite-tsconfig-paths": "^5.1.4",
69
- "vitest": "^3.0.3"
70
- },
71
- "engines": {
72
- "node": ">=23"
73
- }
74
- }
1
+ {
2
+ "name": "imean-service-engine",
3
+ "version": "1.2.0",
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 dev/index.ts",
35
+ "build": "tsup",
36
+ "lint": "deno lint",
37
+ "fmt": "deno fmt",
38
+ "test": "vitest",
39
+ "coverage": "vitest --coverage",
40
+ "prepublishOnly": "npm run build && npm run test"
41
+ },
42
+ "dependencies": {
43
+ "@hono/node-server": "^1.13.7",
44
+ "@hono/node-ws": "^1.0.6",
45
+ "dayjs": "^1.11.13",
46
+ "ejson": "^2.2.3",
47
+ "etcd3": "^1.1.2",
48
+ "fs-extra": "^11.3.0",
49
+ "hono": "^4.6.17",
50
+ "lru-cache": "^11.0.2",
51
+ "prettier": "^3.4.2",
52
+ "winston": "^3.17.0",
53
+ "zod": "^3.24.1"
54
+ },
55
+ "peerDependencies": {
56
+ "@opentelemetry/api": "^1.x"
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/node": "^20.0.0",
72
+ "imean-service-client": "^1.5.0",
73
+ "opentelemetry-instrumentation-fetch-node": "^1.2.3",
74
+ "tslib": "^2.8.1",
75
+ "tsup": "^8.0.1",
76
+ "tsx": "^4.19.2",
77
+ "typescript": "^5.3.3",
78
+ "vite-tsconfig-paths": "^5.1.4",
79
+ "vitest": "^3.0.3"
80
+ },
81
+ "engines": {
82
+ "node": ">=20"
83
+ }
84
+ }