autotel 4.1.0 → 4.2.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.
Files changed (253) hide show
  1. package/dist/auto.cjs +5 -3
  2. package/dist/auto.cjs.map +1 -1
  3. package/dist/auto.js +3 -3
  4. package/dist/auto.js.map +1 -1
  5. package/dist/chunk-C_NdSu1c.cjs +34 -0
  6. package/dist/correlation-id.cjs +1 -1
  7. package/dist/correlation-id.d.cts.map +1 -1
  8. package/dist/correlation-id.d.ts.map +1 -1
  9. package/dist/correlation-id.js +1 -1
  10. package/dist/decorators.cjs +1 -1
  11. package/dist/decorators.js +1 -1
  12. package/dist/{event-ByBTV9M2.js → event-531asIM6.js} +4 -4
  13. package/dist/{event-ByBTV9M2.js.map → event-531asIM6.js.map} +1 -1
  14. package/dist/{event-BhHREDJk.cjs → event-CcZYwp50.cjs} +4 -4
  15. package/dist/{event-BhHREDJk.cjs.map → event-CcZYwp50.cjs.map} +1 -1
  16. package/dist/event.cjs +1 -1
  17. package/dist/event.js +1 -1
  18. package/dist/{functional-zpzNLhky.cjs → functional-C8B0Qa7o.cjs} +10 -7
  19. package/dist/functional-C8B0Qa7o.cjs.map +1 -0
  20. package/dist/{functional-DtI0u4vx.js → functional-r-AUIRy_.js} +9 -9
  21. package/dist/functional-r-AUIRy_.js.map +1 -0
  22. package/dist/functional.cjs +1 -1
  23. package/dist/functional.js +1 -1
  24. package/dist/http.cjs +1 -1
  25. package/dist/http.js +1 -1
  26. package/dist/index.cjs +15 -13
  27. package/dist/index.cjs.map +1 -1
  28. package/dist/index.d.cts.map +1 -1
  29. package/dist/index.d.ts.map +1 -1
  30. package/dist/index.js +14 -14
  31. package/dist/index.js.map +1 -1
  32. package/dist/{init-D-jnNMix.js → init-BS2JVkrL.js} +2 -2
  33. package/dist/{init-D-jnNMix.js.map → init-BS2JVkrL.js.map} +1 -1
  34. package/dist/{init-BX7AmFRl.cjs → init-BXiuPK6j.cjs} +3 -3
  35. package/dist/{init-BX7AmFRl.cjs.map → init-BXiuPK6j.cjs.map} +1 -1
  36. package/dist/instrumentation.cjs +2 -2
  37. package/dist/instrumentation.js +2 -2
  38. package/dist/logger.cjs +236 -8
  39. package/dist/logger.cjs.map +1 -0
  40. package/dist/messaging.cjs +1 -1
  41. package/dist/messaging.js +1 -1
  42. package/dist/{node-require-DF5QBX6z.cjs → node-require-CZ_PU448.cjs} +6 -4
  43. package/dist/node-require-CZ_PU448.cjs.map +1 -0
  44. package/dist/{node-require-Db1oDpLj.js → node-require-vROmTeJ8.js} +5 -5
  45. package/dist/node-require-vROmTeJ8.js.map +1 -0
  46. package/dist/{operation-context-C-2hmmtP.js → operation-context-CKBoA4Qy.js} +3 -3
  47. package/dist/operation-context-CKBoA4Qy.js.map +1 -0
  48. package/dist/{operation-context-n4_obUwq.cjs → operation-context-D6LDf4W_.cjs} +3 -1
  49. package/dist/operation-context-D6LDf4W_.cjs.map +1 -0
  50. package/dist/register.cjs +3 -1
  51. package/dist/register.cjs.map +1 -1
  52. package/dist/register.js +2 -2
  53. package/dist/register.js.map +1 -1
  54. package/dist/semantic-helpers.cjs +1 -1
  55. package/dist/semantic-helpers.js +1 -1
  56. package/dist/{stable-hash-Cg5cT34Q.js → stable-hash-ChFBIhNt.js} +3 -3
  57. package/dist/stable-hash-ChFBIhNt.js.map +1 -0
  58. package/dist/{stable-hash-BNTMrmdB.cjs → stable-hash-brKISGf1.cjs} +4 -2
  59. package/dist/stable-hash-brKISGf1.cjs.map +1 -0
  60. package/dist/trace-context-Cijqoi6e.d.cts.map +1 -1
  61. package/dist/trace-context-Cijqoi6e.d.ts.map +1 -1
  62. package/dist/trace-helpers.cjs +1 -1
  63. package/dist/trace-helpers.js +1 -1
  64. package/dist/{track-wc0HafS_.js → track-COUuU48p.js} +5 -5
  65. package/dist/track-COUuU48p.js.map +1 -0
  66. package/dist/{track-D59FfpL0.cjs → track-Cb3Q4QmS.cjs} +4 -2
  67. package/dist/track-Cb3Q4QmS.cjs.map +1 -0
  68. package/dist/validate.cjs +1 -1
  69. package/dist/validate.js +1 -1
  70. package/dist/webhook.cjs +1 -1
  71. package/dist/webhook.js +1 -1
  72. package/dist/workflow-distributed.cjs +1 -1
  73. package/dist/workflow-distributed.js +1 -1
  74. package/dist/workflow.cjs +3 -1
  75. package/dist/workflow.cjs.map +1 -1
  76. package/dist/workflow.d.cts.map +1 -1
  77. package/dist/workflow.d.ts.map +1 -1
  78. package/dist/workflow.js +3 -3
  79. package/dist/workflow.js.map +1 -1
  80. package/dist/yaml-config.cjs +233 -4
  81. package/dist/yaml-config.cjs.map +1 -0
  82. package/dist/yaml-config.d.cts.map +1 -1
  83. package/dist/yaml-config.d.ts.map +1 -1
  84. package/dist/yaml-config.js +8 -7
  85. package/dist/yaml-config.js.map +1 -1
  86. package/package.json +1 -2
  87. package/dist/functional-DtI0u4vx.js.map +0 -1
  88. package/dist/functional-zpzNLhky.cjs.map +0 -1
  89. package/dist/logger-thMPLpOG.cjs +0 -487
  90. package/dist/logger-thMPLpOG.cjs.map +0 -1
  91. package/dist/node-require-DF5QBX6z.cjs.map +0 -1
  92. package/dist/node-require-Db1oDpLj.js.map +0 -1
  93. package/dist/operation-context-C-2hmmtP.js.map +0 -1
  94. package/dist/operation-context-n4_obUwq.cjs.map +0 -1
  95. package/dist/stable-hash-BNTMrmdB.cjs.map +0 -1
  96. package/dist/stable-hash-Cg5cT34Q.js.map +0 -1
  97. package/dist/track-D59FfpL0.cjs.map +0 -1
  98. package/dist/track-wc0HafS_.js.map +0 -1
  99. package/dist/yaml-config-Ck2uB0Dp.cjs +0 -273
  100. package/dist/yaml-config-Ck2uB0Dp.cjs.map +0 -1
  101. package/src/attribute-redacting-processor.test.ts +0 -763
  102. package/src/attribute-redacting-processor.ts +0 -621
  103. package/src/attributes/attachers.ts +0 -161
  104. package/src/attributes/builders.ts +0 -529
  105. package/src/attributes/domains.ts +0 -42
  106. package/src/attributes/index.ts +0 -81
  107. package/src/attributes/registry.ts +0 -323
  108. package/src/attributes/types.ts +0 -211
  109. package/src/attributes/utils.ts +0 -64
  110. package/src/attributes/validators.ts +0 -266
  111. package/src/attributes.test.ts +0 -292
  112. package/src/auto.ts +0 -67
  113. package/src/autotel-logger.test.ts +0 -548
  114. package/src/autotel-logger.ts +0 -364
  115. package/src/baggage-span-processor.test.ts +0 -202
  116. package/src/baggage-span-processor.ts +0 -100
  117. package/src/business-baggage.test.ts +0 -500
  118. package/src/business-baggage.ts +0 -669
  119. package/src/circuit-breaker.test.ts +0 -341
  120. package/src/circuit-breaker.ts +0 -184
  121. package/src/config.test.ts +0 -94
  122. package/src/config.ts +0 -172
  123. package/src/correlated-events.test.ts +0 -151
  124. package/src/correlated-events.ts +0 -47
  125. package/src/correlation-id.test.ts +0 -163
  126. package/src/correlation-id.ts +0 -206
  127. package/src/db.test.ts +0 -252
  128. package/src/db.ts +0 -447
  129. package/src/decorators.test.ts +0 -153
  130. package/src/decorators.ts +0 -188
  131. package/src/define-event.test.ts +0 -41
  132. package/src/define-event.ts +0 -58
  133. package/src/devtools.ts +0 -60
  134. package/src/drain-pipeline.test.ts +0 -68
  135. package/src/drain-pipeline.ts +0 -199
  136. package/src/drain-toolkit.test.ts +0 -113
  137. package/src/drain-toolkit.ts +0 -129
  138. package/src/enricher-toolkit.test.ts +0 -67
  139. package/src/enricher-toolkit.ts +0 -79
  140. package/src/enrichers.test.ts +0 -150
  141. package/src/enrichers.ts +0 -145
  142. package/src/env-config.test.ts +0 -323
  143. package/src/env-config.ts +0 -309
  144. package/src/error-catalog.test.ts +0 -133
  145. package/src/error-catalog.ts +0 -262
  146. package/src/event-queue.test.ts +0 -864
  147. package/src/event-queue.ts +0 -699
  148. package/src/event-subscriber.ts +0 -262
  149. package/src/event-testing.ts +0 -197
  150. package/src/event.test.ts +0 -1104
  151. package/src/event.ts +0 -988
  152. package/src/events-config.ts +0 -235
  153. package/src/exporters.ts +0 -165
  154. package/src/filtering-span-processor.test.ts +0 -281
  155. package/src/filtering-span-processor.ts +0 -111
  156. package/src/flatten-attributes.test.ts +0 -76
  157. package/src/flatten-attributes.ts +0 -80
  158. package/src/functional.strict-types.typecheck.ts +0 -53
  159. package/src/functional.test.ts +0 -1464
  160. package/src/functional.ts +0 -2539
  161. package/src/functional.types.test.ts +0 -135
  162. package/src/hook.mjs +0 -15
  163. package/src/http.test.ts +0 -485
  164. package/src/http.ts +0 -424
  165. package/src/index.ts +0 -433
  166. package/src/init-auto-redactor.test.ts +0 -53
  167. package/src/init-redactor.test.ts +0 -8
  168. package/src/init.customization.test.ts +0 -665
  169. package/src/init.integrations.test.ts +0 -399
  170. package/src/init.openllmetry.test.ts +0 -194
  171. package/src/init.protocol.test.ts +0 -215
  172. package/src/init.ts +0 -2439
  173. package/src/instrumentation.test.ts +0 -108
  174. package/src/instrumentation.ts +0 -319
  175. package/src/logger.test.ts +0 -125
  176. package/src/logger.ts +0 -341
  177. package/src/messaging-adapters.test.ts +0 -595
  178. package/src/messaging-adapters.ts +0 -583
  179. package/src/messaging-testing.test.ts +0 -573
  180. package/src/messaging-testing.ts +0 -935
  181. package/src/messaging.test.ts +0 -1646
  182. package/src/messaging.ts +0 -2245
  183. package/src/metric-helpers.ts +0 -47
  184. package/src/metric-testing.ts +0 -197
  185. package/src/metric.ts +0 -446
  186. package/src/metrics.test.ts +0 -241
  187. package/src/node-require.ts +0 -123
  188. package/src/operation-context.ts +0 -93
  189. package/src/parse-error.test.ts +0 -73
  190. package/src/parse-error.ts +0 -112
  191. package/src/posthog-logs.test.ts +0 -115
  192. package/src/posthog-logs.ts +0 -77
  193. package/src/pretty-console-exporter.test.ts +0 -545
  194. package/src/pretty-console-exporter.ts +0 -413
  195. package/src/pretty-log-formatter.test.ts +0 -123
  196. package/src/pretty-log-formatter.ts +0 -210
  197. package/src/processors/canonical-log-line-processor.test.ts +0 -523
  198. package/src/processors/canonical-log-line-processor.ts +0 -396
  199. package/src/processors.ts +0 -152
  200. package/src/rate-limiter.test.ts +0 -199
  201. package/src/rate-limiter.ts +0 -98
  202. package/src/redact-values.test.ts +0 -90
  203. package/src/redact-values.ts +0 -34
  204. package/src/register.ts +0 -37
  205. package/src/request-logger.test.ts +0 -545
  206. package/src/request-logger.ts +0 -342
  207. package/src/sampling.test.ts +0 -1060
  208. package/src/sampling.ts +0 -737
  209. package/src/security-schema.test.ts +0 -45
  210. package/src/security-schema.ts +0 -107
  211. package/src/semantic-conventions.ts +0 -15
  212. package/src/semantic-helpers.test.ts +0 -226
  213. package/src/semantic-helpers.ts +0 -438
  214. package/src/shutdown.test.ts +0 -364
  215. package/src/shutdown.ts +0 -246
  216. package/src/span-name-normalizer.test.ts +0 -377
  217. package/src/span-name-normalizer.ts +0 -213
  218. package/src/stable-hash.ts +0 -27
  219. package/src/structured-error.test.ts +0 -191
  220. package/src/structured-error.ts +0 -157
  221. package/src/stub.integration.test.ts +0 -361
  222. package/src/tail-sampling-processor.test.ts +0 -230
  223. package/src/tail-sampling-processor.ts +0 -55
  224. package/src/test-span-collector.test.ts +0 -234
  225. package/src/test-span-collector.ts +0 -150
  226. package/src/testing.ts +0 -705
  227. package/src/trace-context.test.ts +0 -73
  228. package/src/trace-context.ts +0 -567
  229. package/src/trace-helpers.new.test.ts +0 -278
  230. package/src/trace-helpers.test.ts +0 -290
  231. package/src/trace-helpers.ts +0 -710
  232. package/src/trace-hybrid.test.ts +0 -42
  233. package/src/trace-hybrid.ts +0 -37
  234. package/src/tracer-provider.test.ts +0 -183
  235. package/src/tracer-provider.ts +0 -266
  236. package/src/track.test.ts +0 -154
  237. package/src/track.ts +0 -216
  238. package/src/validate.test.ts +0 -287
  239. package/src/validate.ts +0 -307
  240. package/src/validation-attributes.ts +0 -43
  241. package/src/validation.test.ts +0 -330
  242. package/src/validation.ts +0 -246
  243. package/src/variable-name-inference.test.ts +0 -178
  244. package/src/variable-name-inference.ts +0 -242
  245. package/src/webhook.test.ts +0 -649
  246. package/src/webhook.ts +0 -637
  247. package/src/workflow-distributed.test.ts +0 -786
  248. package/src/workflow-distributed.ts +0 -916
  249. package/src/workflow.async-safety.integration.test.ts +0 -345
  250. package/src/workflow.test.ts +0 -647
  251. package/src/workflow.ts +0 -810
  252. package/src/yaml-config.test.ts +0 -373
  253. package/src/yaml-config.ts +0 -351
package/src/decorators.ts DELETED
@@ -1,188 +0,0 @@
1
- /**
2
- * TypeScript 5+ Decorators for autotel
3
- *
4
- * Provides @Trace decorator for class-based code.
5
- *
6
- * **Requires TypeScript 5.0+**
7
- *
8
- * @example Method decorator
9
- * ```typescript
10
- * import { Trace } from 'autotel/decorators'
11
- *
12
- * class OrderService {
13
- * @Trace('order.create', { withMetrics: true })
14
- * async createOrder(data: OrderData) {
15
- * return await db.orders.create(data)
16
- * }
17
- *
18
- * @Trace() // Uses method name as span name
19
- * async processPayment(orderId: string) {
20
- * return await stripe.charge(orderId)
21
- * }
22
- * }
23
- * ```
24
- */
25
-
26
- import type { TracingOptions, TraceContext } from './functional';
27
- import { getConfig } from './config';
28
- import { SpanStatusCode } from '@opentelemetry/api';
29
- import { createTraceContext } from './trace-context';
30
-
31
- /**
32
- * Options for @Trace method decorator
33
- */
34
- export interface TraceDecoratorOptions extends Omit<TracingOptions, 'name'> {
35
- /**
36
- * Custom span name. If not provided, uses the method name.
37
- */
38
- name?: string;
39
- }
40
-
41
- /**
42
- * @Trace - Method decorator for fine-grained tracing
43
- *
44
- * Wraps a class method with automatic tracing. Supports both patterns:
45
- * - Simple: method doesn't use ctx
46
- * - Advanced: method accesses ctx via this.ctx
47
- *
48
- * @example Simple usage (no ctx)
49
- * ```typescript
50
- * class OrderService {
51
- * @Trace()
52
- * async createOrder(data: OrderData) {
53
- * return await db.orders.create(data)
54
- * }
55
- * }
56
- * ```
57
- *
58
- * @example With custom name and options
59
- * ```typescript
60
- * class PaymentService {
61
- * @Trace('payment.charge', { withMetrics: true })
62
- * async chargeCard(amount: number) {
63
- * return await stripe.charges.create({ amount })
64
- * }
65
- * }
66
- * ```
67
- *
68
- * @example Accessing ctx
69
- * ```typescript
70
- * interface WithTraceContext {
71
- * ctx?: TraceContext
72
- * }
73
- *
74
- * class UserService {
75
- * @Trace()
76
- * async createUser(data: UserData) {
77
- * // Access ctx via this.ctx (available during execution)
78
- * const ctx = (this as unknown as WithTraceContext).ctx
79
- * if (ctx) {
80
- * ctx.setAttribute('user.id', data.id)
81
- * }
82
- * return await db.users.create(data)
83
- * }
84
- * }
85
- * ```
86
- */
87
- export function Trace(
88
- options?: TraceDecoratorOptions,
89
- ): <T extends (...args: unknown[]) => Promise<unknown>>(
90
- originalMethod: T,
91
- context: ClassMethodDecoratorContext,
92
- ) => T;
93
- export function Trace(
94
- name?: string,
95
- options?: TraceDecoratorOptions,
96
- ): <T extends (...args: unknown[]) => Promise<unknown>>(
97
- originalMethod: T,
98
- context: ClassMethodDecoratorContext,
99
- ) => T;
100
- export function Trace(
101
- nameOrOptions?: string | TraceDecoratorOptions,
102
- maybeOptions?: TraceDecoratorOptions,
103
- ): <T extends (...args: unknown[]) => Promise<unknown>>(
104
- originalMethod: T,
105
- context: ClassMethodDecoratorContext,
106
- ) => T {
107
- // Parse arguments
108
- const name =
109
- typeof nameOrOptions === 'string' ? nameOrOptions : nameOrOptions?.name;
110
- // Options are used in the returned decorator function, not here
111
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
112
- const _options: TraceDecoratorOptions =
113
- typeof nameOrOptions === 'string'
114
- ? maybeOptions || {}
115
- : nameOrOptions || {};
116
-
117
- // TypeScript 5+ decorator signature
118
- return function <T extends (...args: unknown[]) => Promise<unknown>>(
119
- originalMethod: T,
120
- context: ClassMethodDecoratorContext,
121
- ): T {
122
- const methodName = String(context.name);
123
-
124
- // Skip if not an async function
125
- // Check multiple ways to detect async functions (for different transpilation environments)
126
- // TypeScript decorators run at class definition time, so we need robust detection
127
- const methodStr = originalMethod?.toString() || '';
128
- const isAsync =
129
- originalMethod &&
130
- (originalMethod.constructor?.name === 'AsyncFunction' ||
131
- methodStr.trim().startsWith('async ') ||
132
- (methodStr.includes('[native code]') && methodStr.includes('async')) ||
133
- // Fallback: if function has async in its string representation
134
- /async\s+/.test(methodStr));
135
-
136
- if (!isAsync) {
137
- // Not an async function, return as-is
138
- return originalMethod;
139
- }
140
-
141
- const spanName = name || methodName;
142
-
143
- return async function <This>(
144
- this: This,
145
- ...args: unknown[]
146
- ): Promise<unknown> {
147
- const config = getConfig();
148
- const tracer = config.tracer;
149
-
150
- return tracer.startActiveSpan(spanName, async (span) => {
151
- try {
152
- // Make ctx available via this.ctx for methods that need it
153
- const ctx: TraceContext = createTraceContext(span);
154
-
155
- const originalCtx = (this as { ctx?: TraceContext }).ctx;
156
- try {
157
- (this as { ctx?: TraceContext }).ctx = ctx;
158
- const result = await originalMethod.apply(this, args as []);
159
- span.setStatus({ code: SpanStatusCode.OK });
160
- return result;
161
- } finally {
162
- // Restore original ctx
163
- if (originalCtx === undefined) {
164
- delete (this as { ctx?: TraceContext }).ctx;
165
- } else {
166
- (this as { ctx?: TraceContext }).ctx = originalCtx;
167
- }
168
- }
169
- } catch (error) {
170
- span.setStatus({
171
- code: SpanStatusCode.ERROR,
172
- message: error instanceof Error ? error.message : 'Unknown error',
173
- });
174
- span.recordException(
175
- error instanceof Error ? error : new Error(String(error)),
176
- );
177
- throw error;
178
- } finally {
179
- span.end();
180
- }
181
- });
182
- } as T;
183
- };
184
- }
185
-
186
- // Re-export types for convenience
187
-
188
- export { type TraceContext, type TracingOptions } from './functional';
@@ -1,41 +0,0 @@
1
- import { describe, expect, it } from 'vitest';
2
- import { defineEvent } from './define-event';
3
-
4
- describe('defineEvent', () => {
5
- it('validates payload and exposes schema metadata when provided', () => {
6
- const event = defineEvent(
7
- 'order.placed',
8
- {
9
- safeParse(input: unknown) {
10
- if (
11
- typeof input === 'object' &&
12
- input !== null &&
13
- 'orderId' in input &&
14
- typeof (input as Record<string, unknown>).orderId === 'string'
15
- ) {
16
- return {
17
- success: true as const,
18
- data: input as { orderId: string },
19
- };
20
- }
21
- return { success: false as const, error: new Error('invalid') };
22
- },
23
- },
24
- {
25
- toJsonSchema: () => ({
26
- type: 'object',
27
- properties: { orderId: { type: 'string' } },
28
- required: ['orderId'],
29
- }),
30
- },
31
- );
32
-
33
- expect(event.name).toBe('order.placed');
34
- expect(event.schemaMetadata?.source).toBe('zod');
35
- expect(event.schemaMetadata?.hash).toMatch(/^[a-f0-9]{64}$/);
36
- expect(() => event.track({ orderId: 'o-1' })).not.toThrow();
37
- expect(() => event.track({} as { orderId: string })).toThrow(
38
- /Schema validation failed/,
39
- );
40
- });
41
- });
@@ -1,58 +0,0 @@
1
- import { track } from './track';
2
- import { hashJson } from './stable-hash';
3
- import type { EventSchemaMetadata } from './event-subscriber';
4
-
5
- type SafeParseResult<T> =
6
- | { success: true; data: T }
7
- | { success: false; error: unknown };
8
-
9
- export interface SchemaLike<T> {
10
- safeParse(input: unknown): SafeParseResult<T>;
11
- }
12
-
13
- export interface DefineEventOptions<S> {
14
- toJsonSchema?: (schema: S) => unknown;
15
- }
16
-
17
- export interface DefinedEvent<Name extends string, Payload> {
18
- readonly name: Name;
19
- readonly schemaMetadata?: EventSchemaMetadata;
20
- track(payload: Payload): void;
21
- }
22
-
23
- export function defineEvent<
24
- Name extends string,
25
- Payload,
26
- S extends SchemaLike<Payload>,
27
- >(
28
- name: Name,
29
- schema: S,
30
- options: DefineEventOptions<S> = {},
31
- ): DefinedEvent<Name, Payload> {
32
- const jsonSchema = options.toJsonSchema?.(schema);
33
- const schemaMetadata = jsonSchema
34
- ? {
35
- source: 'zod' as const,
36
- jsonSchema,
37
- hash: hashJson(jsonSchema),
38
- }
39
- : undefined;
40
-
41
- return {
42
- name,
43
- schemaMetadata,
44
- track(payload: Payload) {
45
- const parsed = schema.safeParse(payload);
46
- if (!parsed.success) {
47
- throw new Error(
48
- `Invalid payload for event "${name}". Schema validation failed.`,
49
- );
50
- }
51
- track(
52
- name,
53
- parsed.data,
54
- schemaMetadata ? { schema: schemaMetadata } : undefined,
55
- );
56
- },
57
- };
58
- }
package/src/devtools.ts DELETED
@@ -1,60 +0,0 @@
1
- export interface AutotelDevtoolsConfig {
2
- enabled?: boolean;
3
- endpoint?: string;
4
- embedded?: boolean;
5
- host?: string;
6
- port?: number;
7
- verbose?: boolean;
8
- }
9
-
10
- export interface ResolvedAutotelDevtoolsConfig {
11
- enabled: boolean;
12
- endpoint?: string;
13
- embedded: boolean;
14
- host: string;
15
- port: number;
16
- verbose: boolean;
17
- }
18
-
19
- const defaultHost = '127.0.0.1';
20
- const defaultPort = 4318;
21
-
22
- export function resolveDevtoolsConfig(
23
- config: boolean | AutotelDevtoolsConfig | undefined,
24
- ): ResolvedAutotelDevtoolsConfig {
25
- if (!config) {
26
- return {
27
- enabled: false,
28
- endpoint: undefined,
29
- embedded: false,
30
- host: defaultHost,
31
- port: defaultPort,
32
- verbose: false,
33
- };
34
- }
35
-
36
- if (config === true) {
37
- return {
38
- enabled: true,
39
- endpoint: `http://${defaultHost}:${defaultPort}`,
40
- embedded: false,
41
- host: defaultHost,
42
- port: defaultPort,
43
- verbose: false,
44
- };
45
- }
46
-
47
- const enabled = config.enabled ?? true;
48
- const host = config.host ?? defaultHost;
49
- const port = config.port ?? defaultPort;
50
- const endpoint = config.endpoint ?? `http://${host}:${port}`;
51
-
52
- return {
53
- enabled,
54
- endpoint: enabled ? endpoint : undefined,
55
- embedded: enabled && (config.embedded ?? false),
56
- host,
57
- port,
58
- verbose: config.verbose ?? false,
59
- };
60
- }
@@ -1,68 +0,0 @@
1
- import { describe, expect, it, vi } from 'vitest';
2
- import { createDrainPipeline } from './drain-pipeline';
3
-
4
- describe('createDrainPipeline', () => {
5
- it('batches by size and sends to drain', async () => {
6
- const batchDrain = vi.fn(async () => {});
7
- const pipeline = createDrainPipeline<number>({
8
- batch: { size: 2, intervalMs: 1000 },
9
- });
10
- const drain = pipeline(batchDrain);
11
-
12
- drain(1);
13
- drain(2);
14
- await new Promise((resolve) => setImmediate(resolve));
15
-
16
- expect(batchDrain).toHaveBeenCalledTimes(1);
17
- expect(batchDrain).toHaveBeenCalledWith([1, 2]);
18
- expect(drain.pending).toBe(0);
19
- });
20
-
21
- it('retries failed batches and eventually succeeds', async () => {
22
- let attempts = 0;
23
- const batchDrain = vi.fn(async () => {
24
- attempts++;
25
- if (attempts < 2) throw new Error('temporary');
26
- });
27
-
28
- const pipeline = createDrainPipeline<number>({
29
- batch: { size: 1, intervalMs: 1000 },
30
- retry: {
31
- maxAttempts: 3,
32
- initialDelayMs: 1,
33
- maxDelayMs: 2,
34
- backoff: 'fixed',
35
- jitter: false,
36
- },
37
- });
38
- const drain = pipeline(batchDrain);
39
-
40
- drain(42);
41
- await drain.flush();
42
-
43
- expect(batchDrain).toHaveBeenCalledTimes(2);
44
- expect(drain.pending).toBe(0);
45
- });
46
-
47
- it('drops overflowed events based on policy', async () => {
48
- const dropped: number[] = [];
49
- const batchDrain = vi.fn(async () => {});
50
- const pipeline = createDrainPipeline<number>({
51
- batch: { size: 10, intervalMs: 1000 },
52
- maxBufferSize: 2,
53
- dropPolicy: 'oldest',
54
- onDropped: (events) => dropped.push(...events),
55
- });
56
- const drain = pipeline(batchDrain);
57
-
58
- drain(1);
59
- drain(2);
60
- drain(3); // drops 1
61
-
62
- expect(dropped).toEqual([1]);
63
- expect(drain.pending).toBe(2);
64
-
65
- await drain.flush();
66
- expect(batchDrain).toHaveBeenCalledWith([2, 3]);
67
- });
68
- });
@@ -1,199 +0,0 @@
1
- export interface DrainPipelineOptions<T = unknown> {
2
- batch?: {
3
- /** Maximum events per batch. @default 50 */
4
- size?: number;
5
- /** Max time an event can stay buffered before flush. @default 5000 */
6
- intervalMs?: number;
7
- };
8
- retry?: {
9
- /** Total attempts including first try. @default 3 */
10
- maxAttempts?: number;
11
- /** Delay strategy between attempts. @default 'exponential' */
12
- backoff?: 'exponential' | 'linear' | 'fixed';
13
- /** Base delay for first retry. @default 1000 */
14
- initialDelayMs?: number;
15
- /** Max delay cap. @default 30000 */
16
- maxDelayMs?: number;
17
- /** Add random jitter to delays. @default true */
18
- jitter?: boolean;
19
- };
20
- /** Max buffered events before dropping. @default 1000 */
21
- maxBufferSize?: number;
22
- /** Overflow policy. @default 'oldest' */
23
- dropPolicy?: 'oldest' | 'newest';
24
- /** Called when events are dropped from overflow or exhausted retries. */
25
- onDropped?: (events: T[], error?: Error) => void;
26
- }
27
-
28
- export interface PipelineDrainFn<T> {
29
- (ctx: T): void;
30
- /** Flush all buffered events. */
31
- flush: () => Promise<void>;
32
- /** Flush and stop scheduling future timer work. */
33
- shutdown: () => Promise<void>;
34
- readonly pending: number;
35
- }
36
-
37
- function wait(ms: number): Promise<void> {
38
- return new Promise((resolve) => {
39
- const timer = setTimeout(resolve, ms);
40
- timer.unref?.();
41
- });
42
- }
43
-
44
- export function createDrainPipeline<T = unknown>(
45
- options?: DrainPipelineOptions<T>,
46
- ): (drain: (batch: T[]) => void | Promise<void>) => PipelineDrainFn<T> {
47
- const batchSize = options?.batch?.size ?? 50;
48
- const intervalMs = options?.batch?.intervalMs ?? 5000;
49
- const maxBufferSize = options?.maxBufferSize ?? 1000;
50
- const maxAttempts = options?.retry?.maxAttempts ?? 3;
51
- const backoff = options?.retry?.backoff ?? 'exponential';
52
- const initialDelayMs = options?.retry?.initialDelayMs ?? 1000;
53
- const maxDelayMs = options?.retry?.maxDelayMs ?? 30_000;
54
- const jitter = options?.retry?.jitter ?? true;
55
- const dropPolicy = options?.dropPolicy ?? 'oldest';
56
- const onDropped = options?.onDropped;
57
-
58
- if (!Number.isFinite(batchSize) || batchSize <= 0) {
59
- throw new Error(
60
- `[autotel/drain-pipeline] batch.size must be a positive finite number, got: ${batchSize}`,
61
- );
62
- }
63
- if (!Number.isFinite(intervalMs) || intervalMs <= 0) {
64
- throw new Error(
65
- `[autotel/drain-pipeline] batch.intervalMs must be a positive finite number, got: ${intervalMs}`,
66
- );
67
- }
68
- if (!Number.isFinite(maxBufferSize) || maxBufferSize <= 0) {
69
- throw new Error(
70
- `[autotel/drain-pipeline] maxBufferSize must be a positive finite number, got: ${maxBufferSize}`,
71
- );
72
- }
73
- if (!Number.isFinite(maxAttempts) || maxAttempts <= 0) {
74
- throw new Error(
75
- `[autotel/drain-pipeline] retry.maxAttempts must be a positive finite number, got: ${maxAttempts}`,
76
- );
77
- }
78
-
79
- return (drain: (batch: T[]) => void | Promise<void>): PipelineDrainFn<T> => {
80
- const buffer: T[] = [];
81
- let timer: ReturnType<typeof setTimeout> | null = null;
82
- let activeFlush: Promise<void> | null = null;
83
- let isShutdown = false;
84
-
85
- const clearTimer = () => {
86
- if (timer) {
87
- clearTimeout(timer);
88
- timer = null;
89
- }
90
- };
91
-
92
- const computeDelay = (attempt: number): number => {
93
- const base =
94
- backoff === 'fixed'
95
- ? initialDelayMs
96
- : backoff === 'linear'
97
- ? initialDelayMs * attempt
98
- : initialDelayMs * 2 ** (attempt - 1);
99
-
100
- const bounded = Math.min(base, maxDelayMs);
101
- if (!jitter || bounded <= 0) return bounded;
102
- const factor = 0.5 + Math.random(); // [0.5, 1.5)
103
- return Math.max(0, Math.round(bounded * factor));
104
- };
105
-
106
- const sendWithRetry = async (batch: T[]): Promise<void> => {
107
- let lastError: Error | undefined;
108
- for (let attempt = 1; attempt <= maxAttempts; attempt++) {
109
- try {
110
- await drain(batch);
111
- return;
112
- } catch (error) {
113
- lastError = error instanceof Error ? error : new Error(String(error));
114
- if (attempt < maxAttempts) {
115
- await wait(computeDelay(attempt));
116
- }
117
- }
118
- }
119
- onDropped?.(batch, lastError);
120
- };
121
-
122
- const drainBuffer = async (): Promise<void> => {
123
- while (buffer.length > 0) {
124
- const batch = buffer.splice(0, batchSize);
125
- await sendWithRetry(batch);
126
- }
127
- };
128
-
129
- const scheduleFlush = () => {
130
- if (isShutdown || timer || activeFlush) return;
131
- timer = setTimeout(() => {
132
- timer = null;
133
- startFlush();
134
- }, intervalMs);
135
- timer.unref?.();
136
- };
137
-
138
- const startFlush = () => {
139
- if (activeFlush || isShutdown) return;
140
- activeFlush = drainBuffer().finally(() => {
141
- activeFlush = null;
142
- if (isShutdown) return;
143
- if (buffer.length >= batchSize) {
144
- startFlush();
145
- } else if (buffer.length > 0) {
146
- scheduleFlush();
147
- }
148
- });
149
- };
150
-
151
- const push = (ctx: T) => {
152
- if (isShutdown) return;
153
-
154
- if (buffer.length >= maxBufferSize) {
155
- if (dropPolicy === 'newest') {
156
- onDropped?.([ctx]);
157
- return;
158
- }
159
- const dropped = buffer.splice(0, 1);
160
- onDropped?.(dropped);
161
- }
162
-
163
- buffer.push(ctx);
164
- if (buffer.length >= batchSize) {
165
- clearTimer();
166
- startFlush();
167
- } else {
168
- scheduleFlush();
169
- }
170
- };
171
-
172
- const flush = async (): Promise<void> => {
173
- clearTimer();
174
- if (activeFlush) await activeFlush;
175
-
176
- const snapshot = buffer.length;
177
- if (snapshot <= 0) return;
178
- const toFlush = buffer.splice(0, snapshot);
179
- while (toFlush.length > 0) {
180
- const batch = toFlush.splice(0, batchSize);
181
- await sendWithRetry(batch);
182
- }
183
- };
184
-
185
- const shutdown = async (): Promise<void> => {
186
- isShutdown = true;
187
- await flush();
188
- };
189
-
190
- const fn = push as PipelineDrainFn<T>;
191
- fn.flush = flush;
192
- fn.shutdown = shutdown;
193
- Object.defineProperty(fn, 'pending', {
194
- enumerable: true,
195
- get: () => buffer.length,
196
- });
197
- return fn;
198
- };
199
- }