@dangao/bun-server 1.2.0 → 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/README.md +15 -0
- package/dist/config/config-module.d.ts +17 -0
- package/dist/config/config-module.d.ts.map +1 -1
- package/dist/config/service.d.ts +18 -1
- package/dist/config/service.d.ts.map +1 -1
- package/dist/config/types.d.ts +25 -0
- package/dist/config/types.d.ts.map +1 -1
- package/dist/controller/controller.d.ts +5 -0
- package/dist/controller/controller.d.ts.map +1 -1
- package/dist/controller/decorators.d.ts +30 -1
- package/dist/controller/decorators.d.ts.map +1 -1
- package/dist/controller/index.d.ts +2 -2
- package/dist/controller/index.d.ts.map +1 -1
- package/dist/controller/param-binder.d.ts +12 -0
- package/dist/controller/param-binder.d.ts.map +1 -1
- package/dist/core/application.d.ts +15 -0
- package/dist/core/application.d.ts.map +1 -1
- package/dist/core/context.d.ts +1 -0
- package/dist/core/context.d.ts.map +1 -1
- package/dist/core/server.d.ts +8 -0
- package/dist/core/server.d.ts.map +1 -1
- package/dist/index.d.ts +8 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6487 -4178
- package/dist/microservice/config-center/config-center-module.d.ts +43 -0
- package/dist/microservice/config-center/config-center-module.d.ts.map +1 -0
- package/dist/microservice/config-center/decorators.d.ts +58 -0
- package/dist/microservice/config-center/decorators.d.ts.map +1 -0
- package/dist/microservice/config-center/index.d.ts +9 -0
- package/dist/microservice/config-center/index.d.ts.map +1 -0
- package/dist/microservice/config-center/nacos-config-center.d.ts +37 -0
- package/dist/microservice/config-center/nacos-config-center.d.ts.map +1 -0
- package/dist/microservice/config-center/nacos-decorators.d.ts +24 -0
- package/dist/microservice/config-center/nacos-decorators.d.ts.map +1 -0
- package/dist/microservice/config-center/types.d.ts +63 -0
- package/dist/microservice/config-center/types.d.ts.map +1 -0
- package/dist/microservice/governance/circuit-breaker.d.ts +54 -0
- package/dist/microservice/governance/circuit-breaker.d.ts.map +1 -0
- package/dist/microservice/governance/decorators.d.ts +51 -0
- package/dist/microservice/governance/decorators.d.ts.map +1 -0
- package/dist/microservice/governance/index.d.ts +9 -0
- package/dist/microservice/governance/index.d.ts.map +1 -0
- package/dist/microservice/governance/rate-limiter.d.ts +26 -0
- package/dist/microservice/governance/rate-limiter.d.ts.map +1 -0
- package/dist/microservice/governance/redis-rate-limiter.d.ts +76 -0
- package/dist/microservice/governance/redis-rate-limiter.d.ts.map +1 -0
- package/dist/microservice/governance/retry-strategy.d.ts +21 -0
- package/dist/microservice/governance/retry-strategy.d.ts.map +1 -0
- package/dist/microservice/governance/types.d.ts +212 -0
- package/dist/microservice/governance/types.d.ts.map +1 -0
- package/dist/microservice/index.d.ts +10 -0
- package/dist/microservice/index.d.ts.map +1 -0
- package/dist/microservice/monitoring/index.d.ts +4 -0
- package/dist/microservice/monitoring/index.d.ts.map +1 -0
- package/dist/microservice/monitoring/metrics-collector.d.ts +54 -0
- package/dist/microservice/monitoring/metrics-collector.d.ts.map +1 -0
- package/dist/microservice/monitoring/metrics-integration.d.ts +24 -0
- package/dist/microservice/monitoring/metrics-integration.d.ts.map +1 -0
- package/dist/microservice/monitoring/types.d.ts +99 -0
- package/dist/microservice/monitoring/types.d.ts.map +1 -0
- package/dist/microservice/service-client/call-decorators.d.ts +52 -0
- package/dist/microservice/service-client/call-decorators.d.ts.map +1 -0
- package/dist/microservice/service-client/decorators.d.ts +35 -0
- package/dist/microservice/service-client/decorators.d.ts.map +1 -0
- package/dist/microservice/service-client/index.d.ts +7 -0
- package/dist/microservice/service-client/index.d.ts.map +1 -0
- package/dist/microservice/service-client/interceptors.d.ts +96 -0
- package/dist/microservice/service-client/interceptors.d.ts.map +1 -0
- package/dist/microservice/service-client/load-balancer.d.ts +59 -0
- package/dist/microservice/service-client/load-balancer.d.ts.map +1 -0
- package/dist/microservice/service-client/service-client.d.ts +74 -0
- package/dist/microservice/service-client/service-client.d.ts.map +1 -0
- package/dist/microservice/service-client/types.d.ts +155 -0
- package/dist/microservice/service-client/types.d.ts.map +1 -0
- package/dist/microservice/service-registry/decorators.d.ts +84 -0
- package/dist/microservice/service-registry/decorators.d.ts.map +1 -0
- package/dist/microservice/service-registry/discovery-decorators.d.ts +58 -0
- package/dist/microservice/service-registry/discovery-decorators.d.ts.map +1 -0
- package/dist/microservice/service-registry/health-integration.d.ts +32 -0
- package/dist/microservice/service-registry/health-integration.d.ts.map +1 -0
- package/dist/microservice/service-registry/index.d.ts +10 -0
- package/dist/microservice/service-registry/index.d.ts.map +1 -0
- package/dist/microservice/service-registry/nacos-service-registry.d.ts +68 -0
- package/dist/microservice/service-registry/nacos-service-registry.d.ts.map +1 -0
- package/dist/microservice/service-registry/service-registry-module.d.ts +48 -0
- package/dist/microservice/service-registry/service-registry-module.d.ts.map +1 -0
- package/dist/microservice/service-registry/types.d.ts +121 -0
- package/dist/microservice/service-registry/types.d.ts.map +1 -0
- package/dist/microservice/tracing/collectors.d.ts +27 -0
- package/dist/microservice/tracing/collectors.d.ts.map +1 -0
- package/dist/microservice/tracing/index.d.ts +4 -0
- package/dist/microservice/tracing/index.d.ts.map +1 -0
- package/dist/microservice/tracing/tracer.d.ts +59 -0
- package/dist/microservice/tracing/tracer.d.ts.map +1 -0
- package/dist/microservice/tracing/types.d.ts +179 -0
- package/dist/microservice/tracing/types.d.ts.map +1 -0
- package/dist/request/request.d.ts +1 -0
- package/dist/request/request.d.ts.map +1 -1
- package/docs/microservice-config-center.md +258 -0
- package/docs/microservice-nacos.md +346 -0
- package/docs/microservice-service-registry.md +306 -0
- package/docs/microservice.md +680 -0
- package/docs/troubleshooting.md +41 -0
- package/docs/zh/troubleshooting.md +41 -0
- package/package.json +5 -4
- package/src/config/config-module.ts +210 -0
- package/src/config/service.ts +52 -1
- package/src/config/types.ts +31 -0
- package/src/controller/controller.ts +8 -0
- package/src/controller/decorators.ts +55 -0
- package/src/controller/index.ts +16 -2
- package/src/controller/param-binder.ts +87 -1
- package/src/core/application.ts +100 -2
- package/src/core/context.ts +1 -0
- package/src/core/server.ts +14 -0
- package/src/index.ts +98 -2
- package/src/microservice/config-center/config-center-module.ts +98 -0
- package/src/microservice/config-center/decorators.ts +159 -0
- package/src/microservice/config-center/index.ts +13 -0
- package/src/microservice/config-center/nacos-config-center.ts +126 -0
- package/src/microservice/config-center/nacos-decorators.ts +34 -0
- package/src/microservice/config-center/types.ts +80 -0
- package/src/microservice/governance/circuit-breaker.ts +229 -0
- package/src/microservice/governance/decorators.ts +113 -0
- package/src/microservice/governance/index.ts +18 -0
- package/src/microservice/governance/rate-limiter.ts +72 -0
- package/src/microservice/governance/redis-rate-limiter.ts +154 -0
- package/src/microservice/governance/retry-strategy.ts +74 -0
- package/src/microservice/governance/types.ts +247 -0
- package/src/microservice/index.ts +12 -0
- package/src/microservice/monitoring/index.ts +8 -0
- package/src/microservice/monitoring/metrics-collector.ts +223 -0
- package/src/microservice/monitoring/metrics-integration.ts +154 -0
- package/src/microservice/monitoring/types.ts +118 -0
- package/src/microservice/service-client/call-decorators.ts +107 -0
- package/src/microservice/service-client/decorators.ts +87 -0
- package/src/microservice/service-client/index.ts +37 -0
- package/src/microservice/service-client/interceptors.ts +182 -0
- package/src/microservice/service-client/load-balancer.ts +205 -0
- package/src/microservice/service-client/service-client.ts +488 -0
- package/src/microservice/service-client/types.ts +186 -0
- package/src/microservice/service-registry/decorators.ts +238 -0
- package/src/microservice/service-registry/discovery-decorators.ts +156 -0
- package/src/microservice/service-registry/health-integration.ts +146 -0
- package/src/microservice/service-registry/index.ts +20 -0
- package/src/microservice/service-registry/nacos-service-registry.ts +259 -0
- package/src/microservice/service-registry/service-registry-module.ts +105 -0
- package/src/microservice/service-registry/types.ts +149 -0
- package/src/microservice/tracing/collectors.ts +50 -0
- package/src/microservice/tracing/index.ts +15 -0
- package/src/microservice/tracing/tracer.ts +293 -0
- package/src/microservice/tracing/types.ts +213 -0
- package/src/request/request.ts +1 -0
- package/tests/config/set-value-by-path.test.ts +53 -0
- package/tests/controller/param-map.test.ts +237 -0
- package/tests/microservice/config-center.test.ts +77 -0
- package/tests/microservice/governance.test.ts +157 -0
- package/tests/microservice/monitoring.test.ts +75 -0
- package/tests/microservice/service-client.test.ts +136 -0
- package/tests/microservice/service-registry.test.ts +80 -0
- package/tests/microservice/tracing.test.ts +143 -0
- package/tests/utils/test-port.ts +29 -19
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
import {
|
|
2
|
+
SpanKind,
|
|
3
|
+
SpanStatus,
|
|
4
|
+
type TraceId,
|
|
5
|
+
type SpanId,
|
|
6
|
+
type SpanContext,
|
|
7
|
+
type Span,
|
|
8
|
+
type SpanTags,
|
|
9
|
+
type SpanEvent,
|
|
10
|
+
type TracingOptions,
|
|
11
|
+
type TraceCollector,
|
|
12
|
+
} from './types';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Trace ID 生成器(默认实现)
|
|
16
|
+
*/
|
|
17
|
+
function generateTraceId(): TraceId {
|
|
18
|
+
// 生成 32 位十六进制字符串(类似 OpenTelemetry)
|
|
19
|
+
const chars = '0123456789abcdef';
|
|
20
|
+
let result = '';
|
|
21
|
+
for (let i = 0; i < 32; i++) {
|
|
22
|
+
result += chars[Math.floor(Math.random() * 16)]!;
|
|
23
|
+
}
|
|
24
|
+
return result;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Span ID 生成器(默认实现)
|
|
29
|
+
*/
|
|
30
|
+
function generateSpanId(): SpanId {
|
|
31
|
+
// 生成 16 位十六进制字符串
|
|
32
|
+
const chars = '0123456789abcdef';
|
|
33
|
+
let result = '';
|
|
34
|
+
for (let i = 0; i < 16; i++) {
|
|
35
|
+
result += chars[Math.floor(Math.random() * 16)]!;
|
|
36
|
+
}
|
|
37
|
+
return result;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* 分布式追踪器
|
|
42
|
+
*/
|
|
43
|
+
export class Tracer {
|
|
44
|
+
private readonly options: Required<Pick<TracingOptions, 'samplingRate' | 'enabled'>> & {
|
|
45
|
+
traceIdGenerator: () => TraceId;
|
|
46
|
+
spanIdGenerator: () => SpanId;
|
|
47
|
+
};
|
|
48
|
+
private readonly collectors: TraceCollector[] = [];
|
|
49
|
+
private readonly activeSpans: Map<SpanId, Span> = new Map();
|
|
50
|
+
|
|
51
|
+
public constructor(options: TracingOptions = {}) {
|
|
52
|
+
this.options = {
|
|
53
|
+
samplingRate: options.samplingRate ?? 1.0,
|
|
54
|
+
enabled: options.enabled ?? true,
|
|
55
|
+
traceIdGenerator: options.traceIdGenerator ?? generateTraceId,
|
|
56
|
+
spanIdGenerator: options.spanIdGenerator ?? generateSpanId,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* 添加追踪数据收集器
|
|
62
|
+
*/
|
|
63
|
+
public addCollector(collector: TraceCollector): void {
|
|
64
|
+
this.collectors.push(collector);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* 开始新的 Span
|
|
69
|
+
*/
|
|
70
|
+
public startSpan(
|
|
71
|
+
name: string,
|
|
72
|
+
kind: SpanKind = SpanKind.INTERNAL,
|
|
73
|
+
parentContext?: SpanContext,
|
|
74
|
+
): Span {
|
|
75
|
+
if (!this.options.enabled) {
|
|
76
|
+
// 如果未启用,返回一个空的 Span
|
|
77
|
+
return this.createEmptySpan(name, kind, parentContext);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// 决定是否采样
|
|
81
|
+
const sampled = Math.random() < this.options.samplingRate;
|
|
82
|
+
|
|
83
|
+
const traceId = parentContext?.traceId ?? this.options.traceIdGenerator();
|
|
84
|
+
const spanId = this.options.spanIdGenerator();
|
|
85
|
+
|
|
86
|
+
const span: Span = {
|
|
87
|
+
context: {
|
|
88
|
+
traceId,
|
|
89
|
+
spanId,
|
|
90
|
+
parentSpanId: parentContext?.spanId,
|
|
91
|
+
sampled,
|
|
92
|
+
},
|
|
93
|
+
name,
|
|
94
|
+
kind,
|
|
95
|
+
startTime: Date.now(),
|
|
96
|
+
status: SpanStatus.UNSET,
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
// 如果有父 Span,将其添加到父 Span 的 children 中
|
|
100
|
+
if (parentContext?.spanId) {
|
|
101
|
+
const parentSpan = this.activeSpans.get(parentContext.spanId);
|
|
102
|
+
if (parentSpan) {
|
|
103
|
+
if (!parentSpan.children) {
|
|
104
|
+
parentSpan.children = [];
|
|
105
|
+
}
|
|
106
|
+
parentSpan.children.push(span);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
this.activeSpans.set(spanId, span);
|
|
111
|
+
|
|
112
|
+
return span;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* 结束 Span
|
|
117
|
+
*/
|
|
118
|
+
public endSpan(spanId: SpanId, status: SpanStatus = SpanStatus.OK, error?: Error): void {
|
|
119
|
+
const span = this.activeSpans.get(spanId);
|
|
120
|
+
if (!span) {
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
span.endTime = Date.now();
|
|
125
|
+
span.duration = span.endTime - span.startTime;
|
|
126
|
+
span.status = status;
|
|
127
|
+
if (error) {
|
|
128
|
+
span.error = error;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// 收集 Span 数据
|
|
132
|
+
if (span.context.sampled) {
|
|
133
|
+
for (const collector of this.collectors) {
|
|
134
|
+
try {
|
|
135
|
+
collector.collect(span);
|
|
136
|
+
} catch (err) {
|
|
137
|
+
console.error('[Tracer] Failed to collect span:', err);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// 从活跃 Span 列表中移除
|
|
143
|
+
this.activeSpans.delete(spanId);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* 设置 Span 标签
|
|
148
|
+
*/
|
|
149
|
+
public setSpanTag(spanId: SpanId, key: string, value: string | number | boolean): void {
|
|
150
|
+
const span = this.activeSpans.get(spanId);
|
|
151
|
+
if (span) {
|
|
152
|
+
if (!span.tags) {
|
|
153
|
+
span.tags = {};
|
|
154
|
+
}
|
|
155
|
+
span.tags[key] = value;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* 设置 Span 标签(批量)
|
|
161
|
+
*/
|
|
162
|
+
public setSpanTags(spanId: SpanId, tags: SpanTags): void {
|
|
163
|
+
const span = this.activeSpans.get(spanId);
|
|
164
|
+
if (span) {
|
|
165
|
+
if (!span.tags) {
|
|
166
|
+
span.tags = {};
|
|
167
|
+
}
|
|
168
|
+
Object.assign(span.tags, tags);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* 添加 Span 事件
|
|
174
|
+
*/
|
|
175
|
+
public addSpanEvent(spanId: SpanId, event: Omit<SpanEvent, 'timestamp'>): void {
|
|
176
|
+
const span = this.activeSpans.get(spanId);
|
|
177
|
+
if (span) {
|
|
178
|
+
if (!span.events) {
|
|
179
|
+
span.events = [];
|
|
180
|
+
}
|
|
181
|
+
span.events.push({
|
|
182
|
+
...event,
|
|
183
|
+
timestamp: Date.now(),
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* 获取当前 Span 上下文(用于传播)
|
|
190
|
+
*/
|
|
191
|
+
public getCurrentSpanContext(spanId: SpanId): SpanContext | undefined {
|
|
192
|
+
const span = this.activeSpans.get(spanId);
|
|
193
|
+
return span?.context;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* 从 HTTP 头中提取 Span 上下文
|
|
198
|
+
*/
|
|
199
|
+
public extractFromHeaders(headers: Record<string, string>): SpanContext | undefined {
|
|
200
|
+
const traceId = headers['x-trace-id'] || headers['X-Trace-Id'];
|
|
201
|
+
const spanId = headers['x-span-id'] || headers['X-Span-Id'];
|
|
202
|
+
const parentSpanId = headers['x-parent-span-id'] || headers['X-Parent-Span-Id'];
|
|
203
|
+
const sampled = headers['x-sampled'] || headers['X-Sampled'];
|
|
204
|
+
|
|
205
|
+
if (traceId && spanId) {
|
|
206
|
+
return {
|
|
207
|
+
traceId,
|
|
208
|
+
spanId,
|
|
209
|
+
parentSpanId,
|
|
210
|
+
sampled: sampled === 'true' || sampled === '1',
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
return undefined;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* 将 Span 上下文注入到 HTTP 头中
|
|
219
|
+
*/
|
|
220
|
+
public injectToHeaders(context: SpanContext): Record<string, string> {
|
|
221
|
+
const headers: Record<string, string> = {
|
|
222
|
+
'X-Trace-Id': context.traceId,
|
|
223
|
+
'X-Span-Id': context.spanId,
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
if (context.parentSpanId) {
|
|
227
|
+
headers['X-Parent-Span-Id'] = context.parentSpanId;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
if (context.sampled !== undefined) {
|
|
231
|
+
headers['X-Sampled'] = context.sampled ? '1' : '0';
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
return headers;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* 创建空的 Span(未启用追踪时使用)
|
|
239
|
+
*/
|
|
240
|
+
private createEmptySpan(
|
|
241
|
+
name: string,
|
|
242
|
+
kind: SpanKind,
|
|
243
|
+
parentContext?: SpanContext,
|
|
244
|
+
): Span {
|
|
245
|
+
return {
|
|
246
|
+
context: {
|
|
247
|
+
traceId: parentContext?.traceId ?? '',
|
|
248
|
+
spanId: '',
|
|
249
|
+
parentSpanId: parentContext?.spanId,
|
|
250
|
+
sampled: false,
|
|
251
|
+
},
|
|
252
|
+
name,
|
|
253
|
+
kind,
|
|
254
|
+
startTime: Date.now(),
|
|
255
|
+
status: SpanStatus.UNSET,
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* 刷新所有收集器
|
|
261
|
+
*/
|
|
262
|
+
public async flush(): Promise<void> {
|
|
263
|
+
for (const collector of this.collectors) {
|
|
264
|
+
if (collector.flush) {
|
|
265
|
+
try {
|
|
266
|
+
await Promise.resolve(collector.flush());
|
|
267
|
+
} catch (err) {
|
|
268
|
+
console.error('[Tracer] Failed to flush collector:', err);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* 关闭追踪器
|
|
276
|
+
*/
|
|
277
|
+
public async close(): Promise<void> {
|
|
278
|
+
await this.flush();
|
|
279
|
+
|
|
280
|
+
for (const collector of this.collectors) {
|
|
281
|
+
if (collector.close) {
|
|
282
|
+
try {
|
|
283
|
+
await Promise.resolve(collector.close());
|
|
284
|
+
} catch (err) {
|
|
285
|
+
console.error('[Tracer] Failed to close collector:', err);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
this.activeSpans.clear();
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Trace ID 类型
|
|
3
|
+
*/
|
|
4
|
+
export type TraceId = string;
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Span ID 类型
|
|
8
|
+
*/
|
|
9
|
+
export type SpanId = string;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Span 状态
|
|
13
|
+
*/
|
|
14
|
+
export enum SpanStatus {
|
|
15
|
+
/**
|
|
16
|
+
* 未设置
|
|
17
|
+
*/
|
|
18
|
+
UNSET = 'UNSET',
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* 成功
|
|
22
|
+
*/
|
|
23
|
+
OK = 'OK',
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* 错误
|
|
27
|
+
*/
|
|
28
|
+
ERROR = 'ERROR',
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Span 类型
|
|
33
|
+
*/
|
|
34
|
+
export enum SpanKind {
|
|
35
|
+
/**
|
|
36
|
+
* 客户端(发起请求)
|
|
37
|
+
*/
|
|
38
|
+
CLIENT = 'CLIENT',
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* 服务端(处理请求)
|
|
42
|
+
*/
|
|
43
|
+
SERVER = 'SERVER',
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* 内部(内部操作)
|
|
47
|
+
*/
|
|
48
|
+
INTERNAL = 'INTERNAL',
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Span 上下文
|
|
53
|
+
*/
|
|
54
|
+
export interface SpanContext {
|
|
55
|
+
/**
|
|
56
|
+
* Trace ID
|
|
57
|
+
*/
|
|
58
|
+
traceId: TraceId;
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Span ID
|
|
62
|
+
*/
|
|
63
|
+
spanId: SpanId;
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* 父 Span ID(可选)
|
|
67
|
+
*/
|
|
68
|
+
parentSpanId?: SpanId;
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* 是否采样
|
|
72
|
+
*/
|
|
73
|
+
sampled?: boolean;
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* 标志位(用于传播)
|
|
77
|
+
*/
|
|
78
|
+
flags?: number;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Span 标签
|
|
83
|
+
*/
|
|
84
|
+
export type SpanTags = Record<string, string | number | boolean>;
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Span 事件
|
|
88
|
+
*/
|
|
89
|
+
export interface SpanEvent {
|
|
90
|
+
/**
|
|
91
|
+
* 事件名称
|
|
92
|
+
*/
|
|
93
|
+
name: string;
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* 时间戳(毫秒)
|
|
97
|
+
*/
|
|
98
|
+
timestamp: number;
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* 事件属性
|
|
102
|
+
*/
|
|
103
|
+
attributes?: Record<string, string | number | boolean>;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Span 数据
|
|
108
|
+
*/
|
|
109
|
+
export interface Span {
|
|
110
|
+
/**
|
|
111
|
+
* Span 上下文
|
|
112
|
+
*/
|
|
113
|
+
context: SpanContext;
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Span 名称
|
|
117
|
+
*/
|
|
118
|
+
name: string;
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Span 类型
|
|
122
|
+
*/
|
|
123
|
+
kind: SpanKind;
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* 开始时间(毫秒)
|
|
127
|
+
*/
|
|
128
|
+
startTime: number;
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* 结束时间(毫秒,可选)
|
|
132
|
+
*/
|
|
133
|
+
endTime?: number;
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* 持续时间(毫秒)
|
|
137
|
+
*/
|
|
138
|
+
duration?: number;
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* 状态
|
|
142
|
+
*/
|
|
143
|
+
status: SpanStatus;
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* 错误信息(可选)
|
|
147
|
+
*/
|
|
148
|
+
error?: Error;
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* 标签
|
|
152
|
+
*/
|
|
153
|
+
tags?: SpanTags;
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* 事件列表
|
|
157
|
+
*/
|
|
158
|
+
events?: SpanEvent[];
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* 子 Span 列表
|
|
162
|
+
*/
|
|
163
|
+
children?: Span[];
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* 追踪配置选项
|
|
168
|
+
*/
|
|
169
|
+
export interface TracingOptions {
|
|
170
|
+
/**
|
|
171
|
+
* 采样率(0-1)
|
|
172
|
+
* @default 1.0
|
|
173
|
+
*/
|
|
174
|
+
samplingRate?: number;
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* 是否启用追踪
|
|
178
|
+
* @default true
|
|
179
|
+
*/
|
|
180
|
+
enabled?: boolean;
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Trace ID 生成器
|
|
184
|
+
*/
|
|
185
|
+
traceIdGenerator?: () => TraceId;
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Span ID 生成器
|
|
189
|
+
*/
|
|
190
|
+
spanIdGenerator?: () => SpanId;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* 追踪数据收集器接口
|
|
195
|
+
*/
|
|
196
|
+
export interface TraceCollector {
|
|
197
|
+
/**
|
|
198
|
+
* 收集 Span 数据
|
|
199
|
+
* @param span - Span 数据
|
|
200
|
+
*/
|
|
201
|
+
collect(span: Span): void | Promise<void>;
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* 刷新数据(可选)
|
|
205
|
+
*/
|
|
206
|
+
flush?(): void | Promise<void>;
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* 关闭收集器
|
|
210
|
+
*/
|
|
211
|
+
close?(): void | Promise<void>;
|
|
212
|
+
}
|
|
213
|
+
|
package/src/request/request.ts
CHANGED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { describe, expect, test } from 'bun:test';
|
|
2
|
+
import { ConfigModule } from '../../src/config/config-module';
|
|
3
|
+
|
|
4
|
+
describe('ConfigModule.setValueByPath', () => {
|
|
5
|
+
test('should set value at root level', () => {
|
|
6
|
+
const obj: Record<string, unknown> = {};
|
|
7
|
+
// 使用反射访问私有方法进行测试
|
|
8
|
+
const setValueByPath = (ConfigModule as any).setValueByPath.bind(ConfigModule);
|
|
9
|
+
setValueByPath(obj, 'key', 'value');
|
|
10
|
+
expect(obj.key).toBe('value');
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
test('should set value at nested level', () => {
|
|
14
|
+
const obj: Record<string, unknown> = {};
|
|
15
|
+
const setValueByPath = (ConfigModule as any).setValueByPath.bind(ConfigModule);
|
|
16
|
+
setValueByPath(obj, 'a.b.c', 'value');
|
|
17
|
+
expect((obj.a as any).b.c).toBe('value');
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
test('should handle null values correctly', () => {
|
|
21
|
+
const obj: Record<string, unknown> = {
|
|
22
|
+
a: null,
|
|
23
|
+
};
|
|
24
|
+
const setValueByPath = (ConfigModule as any).setValueByPath.bind(ConfigModule);
|
|
25
|
+
// 当 a 是 null 时,应该创建新对象而不是尝试使用 null
|
|
26
|
+
setValueByPath(obj, 'a.b', 'value');
|
|
27
|
+
expect((obj.a as any).b).toBe('value');
|
|
28
|
+
expect(typeof obj.a).toBe('object');
|
|
29
|
+
expect(obj.a).not.toBeNull();
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
test('should handle existing nested objects', () => {
|
|
33
|
+
const obj: Record<string, unknown> = {
|
|
34
|
+
a: {
|
|
35
|
+
b: 'existing',
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
const setValueByPath = (ConfigModule as any).setValueByPath.bind(ConfigModule);
|
|
39
|
+
setValueByPath(obj, 'a.c', 'new');
|
|
40
|
+
expect((obj.a as any).b).toBe('existing');
|
|
41
|
+
expect((obj.a as any).c).toBe('new');
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
test('should overwrite non-object values', () => {
|
|
45
|
+
const obj: Record<string, unknown> = {
|
|
46
|
+
a: 'string-value',
|
|
47
|
+
};
|
|
48
|
+
const setValueByPath = (ConfigModule as any).setValueByPath.bind(ConfigModule);
|
|
49
|
+
setValueByPath(obj, 'a.b', 'value');
|
|
50
|
+
expect((obj.a as any).b).toBe('value');
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
|