weflayr 0.24.0 → 0.26.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 (235) hide show
  1. package/README.md +2 -2
  2. package/dist/package.json +51 -0
  3. package/dist/{api → src/api}/generated/facade.gen.d.ts +86 -12
  4. package/dist/{api → src/api}/generated/facade.gen.js +4 -2
  5. package/dist/src/api/generated/index.d.ts +2 -0
  6. package/dist/{api → src/api}/generated/index.js +3 -1
  7. package/dist/{api → src/api}/generated/sdk.gen.d.ts +11 -3
  8. package/dist/{api → src/api}/generated/sdk.gen.js +34 -4
  9. package/{src/api/generated/types.gen.ts → dist/src/api/generated/types.gen.d.ts} +436 -153
  10. package/dist/{api → src/api}/makeEndpoint.js +1 -3
  11. package/dist/src/auto-instrument.d.ts +42 -0
  12. package/dist/src/auto-instrument.js +156 -0
  13. package/dist/src/index.d.ts +9 -0
  14. package/dist/src/index.js +49 -0
  15. package/dist/src/instrumentation/anthropic/get_cache_and_reasoning_telemetry.d.ts +20 -0
  16. package/dist/src/instrumentation/anthropic/get_cache_and_reasoning_telemetry.js +114 -0
  17. package/dist/src/instrumentation/anthropic/get_cache_config_telemetry.d.ts +28 -0
  18. package/dist/src/instrumentation/anthropic/get_cache_config_telemetry.js +92 -0
  19. package/dist/src/instrumentation/anthropic/get_streaming_accumulation_telemetry.d.ts +25 -0
  20. package/dist/src/instrumentation/anthropic/get_streaming_accumulation_telemetry.js +141 -0
  21. package/dist/src/instrumentation/anthropic/get_structured_output_schema_telemetry.d.ts +9 -0
  22. package/dist/src/instrumentation/anthropic/get_structured_output_schema_telemetry.js +17 -0
  23. package/dist/src/instrumentation/anthropic/get_tool_definitions_telemetry.d.ts +15 -0
  24. package/dist/src/instrumentation/anthropic/get_tool_definitions_telemetry.js +50 -0
  25. package/dist/src/instrumentation/anthropic/set_telemetry_attributes.d.ts +15 -0
  26. package/dist/src/instrumentation/anthropic/set_telemetry_attributes.js +39 -0
  27. package/dist/src/instrumentation/bedrock/get_cache_config_telemetry.d.ts +22 -0
  28. package/dist/src/instrumentation/bedrock/get_cache_config_telemetry.js +97 -0
  29. package/dist/src/instrumentation/bedrock/get_cache_telemetry.d.ts +22 -0
  30. package/dist/src/instrumentation/bedrock/get_cache_telemetry.js +102 -0
  31. package/dist/src/instrumentation/bedrock/get_input_message_content_telemetry.d.ts +15 -0
  32. package/dist/src/instrumentation/bedrock/get_input_message_content_telemetry.js +91 -0
  33. package/dist/src/instrumentation/bedrock/get_model_id_telemetry.d.ts +21 -0
  34. package/dist/src/instrumentation/bedrock/get_model_id_telemetry.js +54 -0
  35. package/dist/src/instrumentation/bedrock/get_reasoning_output_telemetry.d.ts +19 -0
  36. package/dist/src/instrumentation/bedrock/get_reasoning_output_telemetry.js +81 -0
  37. package/dist/src/instrumentation/bedrock/get_streaming_output_message_telemetry.d.ts +27 -0
  38. package/dist/src/instrumentation/bedrock/get_streaming_output_message_telemetry.js +126 -0
  39. package/dist/src/instrumentation/bedrock/get_tool_call_output_telemetry.d.ts +17 -0
  40. package/dist/src/instrumentation/bedrock/get_tool_call_output_telemetry.js +85 -0
  41. package/dist/src/instrumentation/bedrock/get_tool_definitions_telemetry.d.ts +15 -0
  42. package/dist/src/instrumentation/bedrock/get_tool_definitions_telemetry.js +68 -0
  43. package/dist/src/instrumentation/bedrock/set_telemetry_attributes.d.ts +18 -0
  44. package/dist/src/instrumentation/bedrock/set_telemetry_attributes.js +47 -0
  45. package/dist/src/instrumentation/cohere/get_chat_telemetry.d.ts +23 -0
  46. package/dist/src/instrumentation/cohere/get_chat_telemetry.js +77 -0
  47. package/dist/src/instrumentation/cohere/get_embeddings_telemetry.d.ts +18 -0
  48. package/dist/src/instrumentation/cohere/get_embeddings_telemetry.js +88 -0
  49. package/dist/src/instrumentation/cohere/get_structured_output_schema_telemetry.d.ts +12 -0
  50. package/dist/src/instrumentation/cohere/get_structured_output_schema_telemetry.js +18 -0
  51. package/dist/src/instrumentation/cohere/get_tool_call_output_telemetry.d.ts +31 -0
  52. package/dist/src/instrumentation/cohere/get_tool_call_output_telemetry.js +106 -0
  53. package/dist/src/instrumentation/cohere/get_tool_definitions_telemetry.d.ts +27 -0
  54. package/dist/src/instrumentation/cohere/get_tool_definitions_telemetry.js +75 -0
  55. package/dist/src/instrumentation/cohere/set_telemetry_attributes.d.ts +17 -0
  56. package/dist/src/instrumentation/cohere/set_telemetry_attributes.js +44 -0
  57. package/dist/src/instrumentation/elevenlabs/full_instrumentation.d.ts +28 -0
  58. package/dist/src/instrumentation/elevenlabs/full_instrumentation.js +209 -0
  59. package/dist/src/instrumentation/google/get_audio_telemetry.d.ts +9 -0
  60. package/dist/src/instrumentation/google/get_audio_telemetry.js +28 -0
  61. package/dist/src/instrumentation/google/get_cache_operations_telemetry.d.ts +23 -0
  62. package/dist/src/instrumentation/google/get_cache_operations_telemetry.js +154 -0
  63. package/dist/src/instrumentation/google/get_cache_telemetry.d.ts +16 -0
  64. package/{src → dist/src}/instrumentation/google/get_cache_telemetry.js +18 -19
  65. package/dist/src/instrumentation/google/get_embeddings_telemetry.d.ts +12 -0
  66. package/dist/src/instrumentation/google/get_embeddings_telemetry.js +85 -0
  67. package/dist/src/instrumentation/google/get_provider_name.d.ts +11 -0
  68. package/{src → dist/src}/instrumentation/google/get_provider_name.js +11 -12
  69. package/{src/instrumentation/google/get_reasoning_telemetry.js → dist/src/instrumentation/google/get_reasoning_telemetry.d.ts} +1 -11
  70. package/dist/src/instrumentation/google/get_reasoning_telemetry.js +21 -0
  71. package/dist/src/instrumentation/google/get_structured_output_schema_telemetry.d.ts +12 -0
  72. package/dist/src/instrumentation/google/get_structured_output_schema_telemetry.js +19 -0
  73. package/dist/src/instrumentation/google/set_telemetry_attributes.d.ts +18 -0
  74. package/dist/src/instrumentation/google/set_telemetry_attributes.js +98 -0
  75. package/dist/src/instrumentation/openai/get_audio_telemetry.d.ts +25 -0
  76. package/dist/src/instrumentation/openai/get_audio_telemetry.js +123 -0
  77. package/dist/src/instrumentation/openai/get_bedrock_mantle_telemetry.d.ts +35 -0
  78. package/dist/src/instrumentation/openai/get_bedrock_mantle_telemetry.js +148 -0
  79. package/dist/src/instrumentation/openai/get_cache_and_reasoning_telemetry.d.ts +27 -0
  80. package/dist/src/instrumentation/openai/get_cache_and_reasoning_telemetry.js +72 -0
  81. package/dist/src/instrumentation/openai/get_cache_config_telemetry.d.ts +28 -0
  82. package/dist/src/instrumentation/openai/get_cache_config_telemetry.js +91 -0
  83. package/dist/src/instrumentation/openai/get_embeddings_telemetry.d.ts +18 -0
  84. package/dist/src/instrumentation/openai/get_embeddings_telemetry.js +86 -0
  85. package/dist/src/instrumentation/openai/get_streaming_usage.d.ts +21 -0
  86. package/dist/src/instrumentation/openai/get_streaming_usage.js +74 -0
  87. package/dist/src/instrumentation/openai/get_structured_output_schema_telemetry.d.ts +12 -0
  88. package/dist/src/instrumentation/openai/get_structured_output_schema_telemetry.js +20 -0
  89. package/dist/src/instrumentation/openai/set_telemetry_attributes.d.ts +17 -0
  90. package/dist/src/instrumentation/openai/set_telemetry_attributes.js +47 -0
  91. package/dist/src/instrumentation/shared/get_structured_output_schema_telemetry.d.ts +35 -0
  92. package/dist/src/instrumentation/shared/get_structured_output_schema_telemetry.js +76 -0
  93. package/dist/src/instrumentation/vercel/full_instrumentation.d.ts +3 -0
  94. package/dist/src/instrumentation/vercel/full_instrumentation.js +54 -0
  95. package/dist/src/instrumentation/vercel/get_cache_creation_telemetry.d.ts +22 -0
  96. package/dist/src/instrumentation/vercel/get_cache_creation_telemetry.js +66 -0
  97. package/dist/src/instrumentation/vercel/get_structured_output_schema_telemetry.d.ts +24 -0
  98. package/dist/src/instrumentation/vercel/get_structured_output_schema_telemetry.js +84 -0
  99. package/dist/src/logger.d.ts +1 -0
  100. package/dist/src/logger.js +6 -0
  101. package/dist/src/otel/exporter.d.ts +19 -0
  102. package/dist/src/otel/exporter.js +94 -0
  103. package/dist/src/otel/propagation.d.ts +46 -0
  104. package/dist/src/otel/propagation.js +110 -0
  105. package/dist/src/otel/span-filter.d.ts +3 -0
  106. package/dist/src/otel/span-filter.js +61 -0
  107. package/dist/src/otel/span-processor.d.ts +55 -0
  108. package/dist/src/otel/span-processor.js +169 -0
  109. package/dist/src/otel/vercel-ai-sdk-span-filter.d.ts +12 -0
  110. package/dist/src/otel/vercel-ai-sdk-span-filter.js +88 -0
  111. package/dist/src/setup.d.ts +50 -0
  112. package/dist/src/setup.js +155 -0
  113. package/package.json +12 -5
  114. package/.prettierignore +0 -2
  115. package/.prettierrc.json +0 -7
  116. package/LICENSE-MIT +0 -21
  117. package/NOTICE +0 -12
  118. package/dist/api/generated/index.d.ts +0 -2
  119. package/dist/api/generated/types.gen.d.ts +0 -1005
  120. package/eslint.config.mjs +0 -36
  121. package/index.d.ts +0 -170
  122. package/openapi-ts.config.mjs +0 -9
  123. package/scripts/generate_facade.mjs +0 -86
  124. package/src/api/caseConversion.ts +0 -58
  125. package/src/api/generated/client/client.gen.ts +0 -277
  126. package/src/api/generated/client/index.ts +0 -27
  127. package/src/api/generated/client/types.gen.ts +0 -218
  128. package/src/api/generated/client/utils.gen.ts +0 -316
  129. package/src/api/generated/client.gen.ts +0 -16
  130. package/src/api/generated/core/auth.gen.ts +0 -48
  131. package/src/api/generated/core/bodySerializer.gen.ts +0 -82
  132. package/src/api/generated/core/params.gen.ts +0 -178
  133. package/src/api/generated/core/pathSerializer.gen.ts +0 -171
  134. package/src/api/generated/core/queryKeySerializer.gen.ts +0 -117
  135. package/src/api/generated/core/serverSentEvents.gen.ts +0 -242
  136. package/src/api/generated/core/types.gen.ts +0 -110
  137. package/src/api/generated/core/utils.gen.ts +0 -140
  138. package/src/api/generated/facade.gen.ts +0 -104
  139. package/src/api/generated/index.ts +0 -4
  140. package/src/api/generated/sdk.gen.ts +0 -175
  141. package/src/api/index.ts +0 -45
  142. package/src/api/makeEndpoint.ts +0 -54
  143. package/src/auto-instrument.js +0 -172
  144. package/src/index.js +0 -19
  145. package/src/instrumentation/anthropic/get_cache_and_reasoning_telemetry.js +0 -118
  146. package/src/instrumentation/anthropic/get_cache_config_telemetry.js +0 -90
  147. package/src/instrumentation/anthropic/get_streaming_stop_reason_telemetry.js +0 -121
  148. package/src/instrumentation/anthropic/get_tool_definitions_telemetry.js +0 -55
  149. package/src/instrumentation/anthropic/set_telemetry_attributes.js +0 -39
  150. package/src/instrumentation/bedrock/get_cache_config_telemetry.js +0 -106
  151. package/src/instrumentation/bedrock/get_cache_telemetry.js +0 -109
  152. package/src/instrumentation/bedrock/get_input_message_content_telemetry.js +0 -94
  153. package/src/instrumentation/bedrock/get_streaming_output_message_telemetry.js +0 -87
  154. package/src/instrumentation/bedrock/set_telemetry_attributes.js +0 -40
  155. package/src/instrumentation/cohere/get_chat_telemetry.js +0 -88
  156. package/src/instrumentation/cohere/get_embeddings_telemetry.js +0 -107
  157. package/src/instrumentation/cohere/set_telemetry_attributes.js +0 -32
  158. package/src/instrumentation/elevenlabs/full_instrumentation.js +0 -236
  159. package/src/instrumentation/google/get_audio_telemetry.js +0 -30
  160. package/src/instrumentation/google/get_cache_operations_telemetry.js +0 -171
  161. package/src/instrumentation/google/get_embeddings_telemetry.js +0 -99
  162. package/src/instrumentation/google/set_telemetry_attributes.js +0 -104
  163. package/src/instrumentation/openai/get_audio_telemetry.js +0 -149
  164. package/src/instrumentation/openai/get_cache_and_reasoning_telemetry.js +0 -71
  165. package/src/instrumentation/openai/get_cache_config_telemetry.js +0 -63
  166. package/src/instrumentation/openai/get_embeddings_telemetry.js +0 -107
  167. package/src/instrumentation/openai/get_streaming_usage.js +0 -81
  168. package/src/instrumentation/openai/set_telemetry_attributes.js +0 -41
  169. package/src/instrumentation/vercel/full_instrumentation.js +0 -52
  170. package/src/instrumentation/vercel/get_cache_creation_telemetry.js +0 -70
  171. package/src/logger.js +0 -8
  172. package/src/otel/exporter.js +0 -108
  173. package/src/otel/propagation.js +0 -146
  174. package/src/otel/span-filter.js +0 -69
  175. package/src/otel/span-processor.js +0 -170
  176. package/src/otel/vercel-ai-sdk-span-filter.js +0 -79
  177. package/src/setup.js +0 -167
  178. package/tests/api/index.test.js +0 -437
  179. package/tests/index.test.js +0 -884
  180. package/tests/instrumentation/anthropic/get_cache_and_reasoning_telemetry.test.js +0 -181
  181. package/tests/instrumentation/anthropic/get_cache_config_telemetry.test.js +0 -92
  182. package/tests/instrumentation/anthropic/get_streaming_stop_reason_telemetry.test.js +0 -117
  183. package/tests/instrumentation/anthropic/get_tool_definitions_telemetry.test.js +0 -97
  184. package/tests/instrumentation/bedrock/get_cache_config_telemetry.test.js +0 -107
  185. package/tests/instrumentation/bedrock/get_cache_telemetry.test.js +0 -118
  186. package/tests/instrumentation/bedrock/get_input_message_content_telemetry.test.js +0 -89
  187. package/tests/instrumentation/bedrock/get_streaming_output_message_telemetry.test.js +0 -87
  188. package/tests/instrumentation/cohere/get_chat_telemetry.test.js +0 -148
  189. package/tests/instrumentation/cohere/get_embeddings_telemetry.test.js +0 -132
  190. package/tests/instrumentation/elevenlabs/full_instrumentation.test.js +0 -258
  191. package/tests/instrumentation/google/get_cache_operations_telemetry.test.js +0 -130
  192. package/tests/instrumentation/google/get_embeddings_telemetry.test.js +0 -128
  193. package/tests/instrumentation/google/get_reasoning_telemetry.test.js +0 -41
  194. package/tests/instrumentation/google/set_telemetry_attributes.test.js +0 -219
  195. package/tests/instrumentation/openai/get_audio_telemetry.test.js +0 -170
  196. package/tests/instrumentation/openai/get_cache_and_reasoning_telemetry.test.js +0 -83
  197. package/tests/instrumentation/openai/get_cache_config_telemetry.test.js +0 -83
  198. package/tests/instrumentation/openai/get_embeddings_telemetry.test.js +0 -145
  199. package/tests/instrumentation/openai/get_streaming_usage.test.js +0 -105
  200. package/tests/instrumentation/vercel/full_instrumentation.test.js +0 -16
  201. package/tests/instrumentation/vercel/get_cache_creation_telemetry.test.js +0 -96
  202. package/tests/otel/vercel-ai-sdk-span-filter.test.js +0 -159
  203. package/tsconfig.json +0 -18
  204. /package/dist/{api → src/api}/caseConversion.d.ts +0 -0
  205. /package/dist/{api → src/api}/caseConversion.js +0 -0
  206. /package/dist/{api → src/api}/generated/client/client.gen.d.ts +0 -0
  207. /package/dist/{api → src/api}/generated/client/client.gen.js +0 -0
  208. /package/dist/{api → src/api}/generated/client/index.d.ts +0 -0
  209. /package/dist/{api → src/api}/generated/client/index.js +0 -0
  210. /package/dist/{api → src/api}/generated/client/types.gen.d.ts +0 -0
  211. /package/dist/{api → src/api}/generated/client/types.gen.js +0 -0
  212. /package/dist/{api → src/api}/generated/client/utils.gen.d.ts +0 -0
  213. /package/dist/{api → src/api}/generated/client/utils.gen.js +0 -0
  214. /package/dist/{api → src/api}/generated/client.gen.d.ts +0 -0
  215. /package/dist/{api → src/api}/generated/client.gen.js +0 -0
  216. /package/dist/{api → src/api}/generated/core/auth.gen.d.ts +0 -0
  217. /package/dist/{api → src/api}/generated/core/auth.gen.js +0 -0
  218. /package/dist/{api → src/api}/generated/core/bodySerializer.gen.d.ts +0 -0
  219. /package/dist/{api → src/api}/generated/core/bodySerializer.gen.js +0 -0
  220. /package/dist/{api → src/api}/generated/core/params.gen.d.ts +0 -0
  221. /package/dist/{api → src/api}/generated/core/params.gen.js +0 -0
  222. /package/dist/{api → src/api}/generated/core/pathSerializer.gen.d.ts +0 -0
  223. /package/dist/{api → src/api}/generated/core/pathSerializer.gen.js +0 -0
  224. /package/dist/{api → src/api}/generated/core/queryKeySerializer.gen.d.ts +0 -0
  225. /package/dist/{api → src/api}/generated/core/queryKeySerializer.gen.js +0 -0
  226. /package/dist/{api → src/api}/generated/core/serverSentEvents.gen.d.ts +0 -0
  227. /package/dist/{api → src/api}/generated/core/serverSentEvents.gen.js +0 -0
  228. /package/dist/{api → src/api}/generated/core/types.gen.d.ts +0 -0
  229. /package/dist/{api → src/api}/generated/core/types.gen.js +0 -0
  230. /package/dist/{api → src/api}/generated/core/utils.gen.d.ts +0 -0
  231. /package/dist/{api → src/api}/generated/core/utils.gen.js +0 -0
  232. /package/dist/{api → src/api}/generated/types.gen.js +0 -0
  233. /package/dist/{api → src/api}/index.d.ts +0 -0
  234. /package/dist/{api → src/api}/index.js +0 -0
  235. /package/dist/{api → src/api}/makeEndpoint.d.ts +0 -0
package/src/setup.js DELETED
@@ -1,167 +0,0 @@
1
- 'use strict';
2
-
3
- const { trace } = require('@opentelemetry/api');
4
- const { NodeTracerProvider } = require('@opentelemetry/sdk-trace-node');
5
- const { resourceFromAttributes } = require('@opentelemetry/resources');
6
-
7
- const { buildExporter } = require('./otel/exporter');
8
- const { CONTENT_CAPTURE_ENV, WeflayrSpanProcessor } = require('./otel/span-processor');
9
- const { debug } = require('./logger');
10
- const { version: SDK_VERSION } = require('../package.json');
11
-
12
- const SDK_LANGUAGE = 'javascript';
13
- const DEFAULT_BASE_URL = 'https://api.weflayr.com';
14
-
15
- const _state = {
16
- tracerProvider: null,
17
- processor: null,
18
- beforeExitListener: null,
19
- };
20
-
21
- // `trace.getTracerProvider()` returns a ProxyTracerProvider that forwards to
22
- // whoever called `.register()` last. Unwrap it to reach the real provider.
23
- function _unwrapTracerProvider(p) {
24
- if (p && typeof p.getDelegate === 'function') {
25
- return p.getDelegate();
26
- }
27
- return p;
28
- }
29
-
30
- // `NodeTracerProvider` 2.x dropped the public `addSpanProcessor` API —
31
- // processors are construction-time only. To attach to a provider somebody
32
- // else owns, we splice into the internal MultiSpanProcessor. This matches
33
- // the actual public OTel JS guidance for composing post-construction.
34
- function _attachProcessorToExistingProvider(provider, processor) {
35
- if (!provider) return false;
36
- if (typeof provider.addSpanProcessor === 'function') {
37
- provider.addSpanProcessor(processor);
38
- return true;
39
- }
40
- const multi = provider._activeSpanProcessor;
41
- if (multi && Array.isArray(multi._spanProcessors)) {
42
- multi._spanProcessors.push(processor);
43
- return true;
44
- }
45
- return false;
46
- }
47
-
48
- /**
49
- * Configure the Weflayr SDK.
50
- *
51
- * Call once per process, before `autoInstrument`. For per-request or
52
- * per-tenant data in a long-running server use `propagateMetadata(...)` —
53
- * it stores metadata in OTel's `Context` (built on AsyncLocalStorage) and
54
- * isolates correctly across concurrent requests.
55
- *
56
- * Calling `setup` again is supported but replaces the previous
57
- * configuration: the prior processor is shut down and detached so we never
58
- * stack exporters (which would otherwise double-export every span and leak
59
- * HTTP pools).
60
- */
61
- function setup(options) {
62
- const opts = options || {};
63
- const apiKey = opts.apiKey || process.env.WEFLAYR_API_KEY;
64
- if (typeof apiKey !== 'string' || !apiKey) {
65
- throw new Error(
66
- 'weflayr.setup: `apiKey` is required (pass it explicitly or set WEFLAYR_API_KEY)'
67
- );
68
- }
69
- options = { ...opts, apiKey };
70
-
71
- if (options.captureMessageContent === false) {
72
- process.env[CONTENT_CAPTURE_ENV] = 'false';
73
- } else if (options.captureMessageContent === true) {
74
- process.env[CONTENT_CAPTURE_ENV] = 'true';
75
- }
76
-
77
- _detachPreviousWeflayrProcessor();
78
-
79
- const baseUrl = options.baseUrl || process.env.WEFLAYR_BASE_URL || DEFAULT_BASE_URL;
80
-
81
- const exporter = buildExporter({
82
- baseUrl,
83
- apiKey: options.apiKey,
84
- warnOnExportError: options.warnOnExportError !== false,
85
- });
86
-
87
- const processor = new WeflayrSpanProcessor({
88
- exporter,
89
- exportMode: options.exportMode || 'batch',
90
- defaultTags: options.defaultTags || {},
91
- mask: options.mask,
92
- });
93
-
94
- // Adopt an existing TracerProvider when there is one — e.g. the user already
95
- // configured Datadog/Honeycomb/OTel, or called weflayr.setup() before.
96
- // provider.register() silently refuses to override the proxy if another
97
- // provider is already wired in, so naively creating a new provider would
98
- // leave our processor attached to a dead one and we'd drop every LLM span.
99
- const currentTracerProvider = _unwrapTracerProvider(trace.getTracerProvider());
100
- let tracerProvider;
101
- if (_attachProcessorToExistingProvider(currentTracerProvider, processor)) {
102
- tracerProvider = currentTracerProvider;
103
- } else {
104
- tracerProvider = new NodeTracerProvider({
105
- resource: resourceFromAttributes({
106
- sdk_version: SDK_VERSION,
107
- sdk_language: SDK_LANGUAGE,
108
- }),
109
- spanProcessors: [processor],
110
- });
111
- tracerProvider.register();
112
- }
113
-
114
- // beforeExit fires when the event loop empties on a clean exit, giving us a
115
- // last async tick to drain buffered spans. It does NOT fire on process.exit()
116
- // or uncaught fatals — callers handling those paths still need to call flush
117
- // explicitly (e.g. in a finally) for the OTLP HTTP request to complete.
118
- if (!_state.beforeExitListener) {
119
- _state.beforeExitListener = async () => {
120
- try {
121
- await flush();
122
- } catch {
123
- // best-effort: process is already winding down.
124
- }
125
- };
126
- process.on('beforeExit', _state.beforeExitListener);
127
- }
128
-
129
- _state.tracerProvider = tracerProvider;
130
- _state.processor = processor;
131
- debug(
132
- 'setup ready: baseUrl=%s exportMode=%s defaultTags=%d',
133
- baseUrl,
134
- options.exportMode || 'batch',
135
- Object.keys(options.defaultTags || {}).length
136
- );
137
- }
138
-
139
- async function flush() {
140
- if (!_state.processor) return;
141
- debug('flush: forcing exporter flush');
142
- await _state.processor.forceFlush();
143
- }
144
-
145
- // Shut down + detach the prior weflayr processor — idempotency for setup().
146
- // Without this, calling setup() twice (key rotation, hot reload, tests that
147
- // reconfigure) would leave the first processor wired in alongside the new
148
- // one, double-exporting every span and leaking exporter HTTP pools.
149
- function _detachPreviousWeflayrProcessor() {
150
- const previousProcessor = _state.processor;
151
- if (!previousProcessor) return;
152
- const tp = _state.tracerProvider;
153
- const multi = tp && tp._activeSpanProcessor;
154
- if (multi && Array.isArray(multi._spanProcessors)) {
155
- multi._spanProcessors = multi._spanProcessors.filter(
156
- (processor) => processor !== previousProcessor
157
- );
158
- }
159
- try {
160
- previousProcessor.shutdown();
161
- } catch {
162
- // best-effort cleanup.
163
- }
164
- _state.processor = null;
165
- }
166
-
167
- module.exports = { setup, flush, _state };
@@ -1,437 +0,0 @@
1
- 'use strict';
2
-
3
- // Tests for the public-API client: auth and base-URL wiring of the
4
- // client facade, plus the request/response wiring of every generated
5
- // endpoint (server behavior is covered by the dashboard's app_api tests).
6
-
7
- const { test } = require('node:test');
8
- const assert = require('node:assert/strict');
9
- const http = require('node:http');
10
-
11
- const weflayr = require('../../src/index');
12
- const { api } = weflayr;
13
-
14
- // A client whose server records requests and returns a canned response.
15
- async function withClientReturning(responseBody, statusCode, run, clientOptions = {}) {
16
- const seenRequests = [];
17
- const server = http.createServer((request, response) => {
18
- let requestBody = '';
19
- request.on('data', (chunk) => {
20
- requestBody += chunk;
21
- });
22
- request.on('end', () => {
23
- seenRequests.push({
24
- method: request.method,
25
- url: request.url,
26
- authorization: request.headers.authorization,
27
- body: requestBody === '' ? null : JSON.parse(requestBody),
28
- });
29
- response.statusCode = statusCode;
30
- response.setHeader('content-type', 'application/json');
31
- response.end(JSON.stringify(responseBody));
32
- });
33
- });
34
- await new Promise((resolve) => server.listen(0, resolve));
35
- const client = api.client({
36
- apiKey: 'wf-test-key',
37
- ...clientOptions,
38
- baseUrl: `http://127.0.0.1:${server.address().port}/api`,
39
- });
40
- try {
41
- await run(client, seenRequests);
42
- } finally {
43
- server.close();
44
- }
45
- }
46
-
47
- // ── client facade ─────────────────────────────────────────────────────────
48
-
49
- test('client sends the bearer key to the configured host', async () => {
50
- await withClientReturning({ rows: [] }, 200, async (client, seenRequests) => {
51
- const { data } = await api.getRevenue({ client, throwOnError: true });
52
- assert.deepEqual(data, { rows: [] });
53
- assert.deepEqual(seenRequests, [
54
- { method: 'GET', url: '/api/revenue/', authorization: 'Bearer wf-test-key', body: null },
55
- ]);
56
- });
57
- });
58
-
59
- test('weflayr.client is the same function as api.client', () => {
60
- assert.equal(weflayr.client, api.client);
61
- });
62
-
63
- test('client defaults to the production API host', () => {
64
- const client = api.client({ apiKey: 'wf-test-key' });
65
- assert.equal(client.getConfig().baseUrl, 'https://app.weflayr.com/api');
66
- });
67
-
68
- test('client throws without apiKey', () => {
69
- const saved = process.env.WEFLAYR_API_KEY;
70
- delete process.env.WEFLAYR_API_KEY;
71
- try {
72
- assert.throws(() => api.client(), /apiKey/);
73
- } finally {
74
- if (saved !== undefined) process.env.WEFLAYR_API_KEY = saved;
75
- }
76
- });
77
-
78
- test('client picks up apiKey from WEFLAYR_API_KEY env var', async () => {
79
- process.env.WEFLAYR_API_KEY = 'env-key';
80
- try {
81
- await withClientReturning(
82
- { rows: [] },
83
- 200,
84
- async (client, seenRequests) => {
85
- await api.getRevenue({ client, throwOnError: true });
86
- assert.equal(seenRequests[0].authorization, 'Bearer env-key');
87
- },
88
- { apiKey: undefined }
89
- );
90
- } finally {
91
- delete process.env.WEFLAYR_API_KEY;
92
- }
93
- });
94
-
95
- // ── Revenue ───────────────────────────────────────────────────────────────────
96
-
97
- test('setRevenue posts the rows in snake_case and parses the booked count', async () => {
98
- await withClientReturning({ ok: true, rows_inserted: 2 }, 200, async (client, seenRequests) => {
99
- const rows = [
100
- { customerName: 'c_1', amount: 1200.5, month: '2026-06' },
101
- { customerName: 'c_2', amount: 800, month: '2026-06' },
102
- ];
103
- const { data } = await api.setRevenue({ client, throwOnError: true, body: { rows } });
104
- assert.deepEqual(data, { ok: true, rowsInserted: 2 });
105
- const request = seenRequests[0];
106
- assert.equal(request.method, 'POST');
107
- assert.equal(request.url, '/api/revenue/');
108
- assert.deepEqual(request.body, {
109
- rows: [
110
- { customer_name: 'c_1', amount: 1200.5, month: '2026-06' },
111
- { customer_name: 'c_2', amount: 800, month: '2026-06' },
112
- ],
113
- });
114
- });
115
- });
116
-
117
- test('setRevenue posts the revenueId in snake_case and the upsert flag', async () => {
118
- await withClientReturning({ ok: true, rows_inserted: 1 }, 200, async (client, seenRequests) => {
119
- const rows = [
120
- {
121
- revenueId: 'b3f2b160-6e12-4f1a-9c3d-0a2e7c9b5d41',
122
- customerName: 'c_1',
123
- amount: 1300,
124
- month: '2026-06',
125
- },
126
- ];
127
- const { data } = await api.setRevenue({
128
- client,
129
- throwOnError: true,
130
- body: { upsert: true, rows },
131
- });
132
- assert.deepEqual(data, { ok: true, rowsInserted: 1 });
133
- assert.deepEqual(seenRequests[0].body, {
134
- upsert: true,
135
- rows: [
136
- {
137
- revenue_id: 'b3f2b160-6e12-4f1a-9c3d-0a2e7c9b5d41',
138
- customer_name: 'c_1',
139
- amount: 1300,
140
- month: '2026-06',
141
- },
142
- ],
143
- });
144
- });
145
- });
146
-
147
- test('getRevenue parses the returned rows into camelCase', async () => {
148
- const wireRow = {
149
- customer_name: 'c_1',
150
- metric_name: 'revenue',
151
- month: '2026-06',
152
- amount: 1200.5,
153
- source: 'api',
154
- };
155
- await withClientReturning({ rows: [wireRow] }, 200, async (client) => {
156
- const { data } = await api.getRevenue({ client, throwOnError: true });
157
- assert.deepEqual(data.rows, [
158
- {
159
- customerName: 'c_1',
160
- metricName: 'revenue',
161
- month: '2026-06',
162
- amount: 1200.5,
163
- source: 'api',
164
- },
165
- ]);
166
- });
167
- });
168
-
169
- test('getRevenue sends the customerNames filter as repeated params', async () => {
170
- await withClientReturning({ rows: [] }, 200, async (client, seenRequests) => {
171
- const { data } = await api.getRevenue({
172
- client,
173
- throwOnError: true,
174
- query: { customerNames: ['c_1', 'c_2'] },
175
- });
176
- assert.deepEqual(data, { rows: [] });
177
- const query = new URL(seenRequests[0].url, 'http://x').searchParams;
178
- assert.deepEqual(query.getAll('customer_names'), ['c_1', 'c_2']);
179
- });
180
- });
181
-
182
- test('deleteRevenue sends the customerNames filter and parses the deleted count', async () => {
183
- await withClientReturning({ ok: true, rows_deleted: 2 }, 200, async (client, seenRequests) => {
184
- const { data } = await api.deleteRevenue({
185
- client,
186
- throwOnError: true,
187
- query: { customerNames: ['c_1', 'c_2'] },
188
- });
189
- assert.deepEqual(data, { ok: true, rowsDeleted: 2 });
190
- const request = seenRequests[0];
191
- assert.equal(request.method, 'DELETE');
192
- const url = new URL(request.url, 'http://x');
193
- assert.equal(url.pathname, '/api/revenue/');
194
- assert.deepEqual(url.searchParams.getAll('customer_names'), ['c_1', 'c_2']);
195
- });
196
- });
197
-
198
- // ── Key metrics ───────────────────────────────────────────────────────────────
199
-
200
- test('setKeyMetrics posts the rows in snake_case and parses the booked count', async () => {
201
- await withClientReturning({ ok: true, rows_inserted: 1 }, 200, async (client, seenRequests) => {
202
- const rows = [{ userName: 'u_1', metricName: 'Meetings booked', amount: 3, month: '2026-06' }];
203
- const { data } = await api.setKeyMetrics({ client, throwOnError: true, body: { rows } });
204
- assert.deepEqual(data, { ok: true, rowsInserted: 1 });
205
- const request = seenRequests[0];
206
- assert.equal(request.method, 'POST');
207
- assert.equal(request.url, '/api/key-metrics/');
208
- assert.deepEqual(request.body, {
209
- rows: [{ user_name: 'u_1', metric_name: 'Meetings booked', amount: 3, month: '2026-06' }],
210
- });
211
- });
212
- });
213
-
214
- test('getKeyMetrics parses the returned rows into camelCase', async () => {
215
- const wireRow = {
216
- user_name: 'u_1',
217
- metric_name: 'Meetings booked',
218
- month: '2026-06',
219
- amount: 3.0,
220
- source: 'api',
221
- };
222
- await withClientReturning({ rows: [wireRow] }, 200, async (client) => {
223
- const { data } = await api.getKeyMetrics({ client, throwOnError: true });
224
- assert.deepEqual(data.rows, [
225
- {
226
- userName: 'u_1',
227
- metricName: 'Meetings booked',
228
- month: '2026-06',
229
- amount: 3.0,
230
- source: 'api',
231
- },
232
- ]);
233
- });
234
- });
235
-
236
- test('getKeyMetrics sends the userNames filter as repeated params', async () => {
237
- await withClientReturning({ rows: [] }, 200, async (client, seenRequests) => {
238
- const { data } = await api.getKeyMetrics({
239
- client,
240
- throwOnError: true,
241
- query: { userNames: ['u_1', 'u_2'] },
242
- });
243
- assert.deepEqual(data, { rows: [] });
244
- const query = new URL(seenRequests[0].url, 'http://x').searchParams;
245
- assert.deepEqual(query.getAll('user_names'), ['u_1', 'u_2']);
246
- });
247
- });
248
-
249
- test('setKeyMetrics surfaces a 422 and its details', async () => {
250
- const rejection = {
251
- error: 'Invalid payload.',
252
- detail: [{ field: 'rows.0.metric_name', error: 'Unknown metric.' }],
253
- };
254
- await withClientReturning(rejection, 422, async (client) => {
255
- const { error, response } = await api.setKeyMetrics({ client, body: { rows: [] } });
256
- assert.equal(response.status, 422);
257
- assert.deepEqual(error, rejection);
258
- });
259
- });
260
-
261
- test('deleteKeyMetrics sends the userNames filter and parses the deleted count', async () => {
262
- await withClientReturning({ ok: true, rows_deleted: 2 }, 200, async (client, seenRequests) => {
263
- const { data } = await api.deleteKeyMetrics({
264
- client,
265
- throwOnError: true,
266
- query: { userNames: ['u_1', 'u_2'] },
267
- });
268
- assert.deepEqual(data, { ok: true, rowsDeleted: 2 });
269
- const request = seenRequests[0];
270
- assert.equal(request.method, 'DELETE');
271
- const url = new URL(request.url, 'http://x');
272
- assert.equal(url.pathname, '/api/key-metrics/');
273
- assert.deepEqual(url.searchParams.getAll('user_names'), ['u_1', 'u_2']);
274
- });
275
- });
276
-
277
- // ── Margin ────────────────────────────────────────────────────────────────────
278
-
279
- test('getMargin parses the returned rows into camelCase', async () => {
280
- const wireRow = {
281
- customer_name: 'c_1',
282
- revenue: 1000.0,
283
- cost: 200.0,
284
- margin: 800.0,
285
- margin_pct: 80.0,
286
- profitability_status: 'healthy',
287
- };
288
- await withClientReturning(
289
- { rows: [wireRow], next_cursor: null, has_more: false },
290
- 200,
291
- async (client) => {
292
- const { data } = await api.getMargin({ client, throwOnError: true });
293
- assert.deepEqual(data.rows, [
294
- {
295
- customerName: 'c_1',
296
- revenue: 1000.0,
297
- cost: 200.0,
298
- margin: 800.0,
299
- marginPct: 80.0,
300
- profitabilityStatus: 'healthy',
301
- },
302
- ]);
303
- }
304
- );
305
- });
306
-
307
- test('getMargin sends the cursor and limit query params', async () => {
308
- await withClientReturning(
309
- { rows: [], next_cursor: 'Y19h', has_more: true },
310
- 200,
311
- async (client, seenRequests) => {
312
- const { data } = await api.getMargin({
313
- client,
314
- throwOnError: true,
315
- query: { cursor: 'abc123', limit: 2 },
316
- });
317
- assert.equal(data.nextCursor, 'Y19h');
318
- assert.equal(data.hasMore, true);
319
- const query = new URL(seenRequests[0].url, 'http://x').searchParams;
320
- assert.equal(query.get('cursor'), 'abc123');
321
- assert.equal(query.get('limit'), '2');
322
- }
323
- );
324
- });
325
-
326
- test('getMargin sends the customerNames filter as repeated params', async () => {
327
- await withClientReturning({ rows: [] }, 200, async (client, seenRequests) => {
328
- const { data } = await api.getMargin({
329
- client,
330
- throwOnError: true,
331
- query: { customerNames: ['c_1', 'c_2'] },
332
- });
333
- assert.deepEqual(data, { rows: [] });
334
- const query = new URL(seenRequests[0].url, 'http://x').searchParams;
335
- assert.deepEqual(query.getAll('customer_names'), ['c_1', 'c_2']);
336
- });
337
- });
338
-
339
- test('getMargin sends the dateFrom/dateTo/applyCredits filters in snake_case', async () => {
340
- await withClientReturning({ rows: [] }, 200, async (client, seenRequests) => {
341
- await api.getMargin({
342
- client,
343
- throwOnError: true,
344
- query: { dateFrom: '2026-06-01', dateTo: '2026-06-30', applyCredits: true },
345
- });
346
- const query = new URL(seenRequests[0].url, 'http://x').searchParams;
347
- assert.equal(query.get('date_from'), '2026-06-01');
348
- assert.equal(query.get('date_to'), '2026-06-30');
349
- assert.equal(query.get('apply_credits'), 'true');
350
- });
351
- });
352
-
353
- // ── Customer tags ─────────────────────────────────────────────────────────────
354
-
355
- test('setCustomersTags posts the customers in snake_case and parses the updated count', async () => {
356
- await withClientReturning(
357
- { ok: true, customers_updated: 1 },
358
- 200,
359
- async (client, seenRequests) => {
360
- const customers = [{ customerName: 'c_1', tags: { plan: 'pro' } }];
361
- const { data } = await api.setCustomersTags({
362
- client,
363
- throwOnError: true,
364
- body: { customers },
365
- });
366
- assert.deepEqual(data, { ok: true, customersUpdated: 1 });
367
- const request = seenRequests[0];
368
- assert.equal(request.method, 'POST');
369
- assert.equal(request.url, '/api/customer-tags/');
370
- assert.deepEqual(request.body, {
371
- customers: [{ customer_name: 'c_1', tags: { plan: 'pro' } }],
372
- });
373
- }
374
- );
375
- });
376
-
377
- test('setCustomersTags leaves tag keys untouched, even when they contain underscores', async () => {
378
- await withClientReturning(
379
- { ok: true, customers_updated: 1 },
380
- 200,
381
- async (client, seenRequests) => {
382
- const customers = [{ customerName: 'c_1', tags: { utm_source: 'ads', plan_tier: 'pro' } }];
383
- await api.setCustomersTags({ client, throwOnError: true, body: { customers } });
384
- assert.deepEqual(seenRequests[0].body, {
385
- customers: [{ customer_name: 'c_1', tags: { utm_source: 'ads', plan_tier: 'pro' } }],
386
- });
387
- }
388
- );
389
- });
390
-
391
- test('getCustomersTags parses the returned customers into camelCase', async () => {
392
- const wireCustomer = { customer_name: 'c_1', tags: { plan: 'pro' } };
393
- await withClientReturning(
394
- { customers: [wireCustomer], next_cursor: null, has_more: false },
395
- 200,
396
- async (client) => {
397
- const { data } = await api.getCustomersTags({ client, throwOnError: true });
398
- assert.deepEqual(data.customers, [{ customerName: 'c_1', tags: { plan: 'pro' } }]);
399
- }
400
- );
401
- });
402
-
403
- test('getCustomersTags sends the customerNames filter, leaving tag keys untouched', async () => {
404
- const wireCustomer = { customer_name: 'c_1', tags: { plan: 'pro', utm_source: 'ads' } };
405
- await withClientReturning(
406
- { customers: [wireCustomer], next_cursor: null, has_more: false },
407
- 200,
408
- async (client, seenRequests) => {
409
- const { data } = await api.getCustomersTags({
410
- client,
411
- throwOnError: true,
412
- query: { customerNames: ['c_1', 'c_2'] },
413
- });
414
- assert.deepEqual(data.customers, [
415
- { customerName: 'c_1', tags: { plan: 'pro', utm_source: 'ads' } },
416
- ]);
417
- const query = new URL(seenRequests[0].url, 'http://x').searchParams;
418
- assert.deepEqual(query.getAll('customer_names'), ['c_1', 'c_2']);
419
- }
420
- );
421
- });
422
-
423
- test('deleteCustomersTags sends the customerNames filter and parses the deleted count', async () => {
424
- await withClientReturning({ ok: true, tags_deleted: 3 }, 200, async (client, seenRequests) => {
425
- const { data } = await api.deleteCustomersTags({
426
- client,
427
- throwOnError: true,
428
- query: { customerNames: ['c_1', 'c_2'] },
429
- });
430
- assert.deepEqual(data, { ok: true, tagsDeleted: 3 });
431
- const request = seenRequests[0];
432
- assert.equal(request.method, 'DELETE');
433
- const url = new URL(request.url, 'http://x');
434
- assert.equal(url.pathname, '/api/customer-tags/');
435
- assert.deepEqual(url.searchParams.getAll('customer_names'), ['c_1', 'c_2']);
436
- });
437
- });