weflayr 0.23.0 → 0.26.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.
Files changed (225) hide show
  1. package/README.md +2 -2
  2. package/dist/{api → src/api}/generated/facade.gen.d.ts +163 -12
  3. package/dist/{api → src/api}/generated/facade.gen.js +10 -2
  4. package/dist/src/api/generated/index.d.ts +2 -0
  5. package/dist/{api → src/api}/generated/index.js +9 -1
  6. package/dist/{api → src/api}/generated/sdk.gen.d.ts +35 -3
  7. package/dist/{api → src/api}/generated/sdk.gen.js +88 -4
  8. package/{src/api/generated/types.gen.ts → dist/src/api/generated/types.gen.d.ts} +692 -116
  9. package/dist/{api → src/api}/makeEndpoint.js +1 -3
  10. package/dist/src/auto-instrument.d.ts +42 -0
  11. package/dist/src/auto-instrument.js +156 -0
  12. package/dist/src/index.d.ts +9 -0
  13. package/dist/src/index.js +49 -0
  14. package/dist/src/instrumentation/anthropic/get_cache_and_reasoning_telemetry.d.ts +20 -0
  15. package/dist/src/instrumentation/anthropic/get_cache_and_reasoning_telemetry.js +114 -0
  16. package/dist/src/instrumentation/anthropic/get_cache_config_telemetry.d.ts +28 -0
  17. package/dist/src/instrumentation/anthropic/get_cache_config_telemetry.js +92 -0
  18. package/dist/src/instrumentation/anthropic/get_streaming_accumulation_telemetry.d.ts +25 -0
  19. package/dist/src/instrumentation/anthropic/get_streaming_accumulation_telemetry.js +141 -0
  20. package/dist/src/instrumentation/anthropic/get_structured_output_schema_telemetry.d.ts +9 -0
  21. package/dist/src/instrumentation/anthropic/get_structured_output_schema_telemetry.js +17 -0
  22. package/dist/src/instrumentation/anthropic/get_tool_definitions_telemetry.d.ts +15 -0
  23. package/dist/src/instrumentation/anthropic/get_tool_definitions_telemetry.js +50 -0
  24. package/dist/src/instrumentation/anthropic/set_telemetry_attributes.d.ts +15 -0
  25. package/dist/src/instrumentation/anthropic/set_telemetry_attributes.js +39 -0
  26. package/dist/src/instrumentation/bedrock/get_cache_config_telemetry.d.ts +22 -0
  27. package/dist/src/instrumentation/bedrock/get_cache_config_telemetry.js +97 -0
  28. package/dist/src/instrumentation/bedrock/get_cache_telemetry.d.ts +22 -0
  29. package/dist/src/instrumentation/bedrock/get_cache_telemetry.js +102 -0
  30. package/dist/src/instrumentation/bedrock/get_input_message_content_telemetry.d.ts +15 -0
  31. package/dist/src/instrumentation/bedrock/get_input_message_content_telemetry.js +91 -0
  32. package/dist/src/instrumentation/bedrock/get_model_id_telemetry.d.ts +21 -0
  33. package/dist/src/instrumentation/bedrock/get_model_id_telemetry.js +54 -0
  34. package/dist/src/instrumentation/bedrock/get_reasoning_output_telemetry.d.ts +19 -0
  35. package/dist/src/instrumentation/bedrock/get_reasoning_output_telemetry.js +81 -0
  36. package/dist/src/instrumentation/bedrock/get_streaming_output_message_telemetry.d.ts +27 -0
  37. package/dist/src/instrumentation/bedrock/get_streaming_output_message_telemetry.js +126 -0
  38. package/dist/src/instrumentation/bedrock/get_tool_call_output_telemetry.d.ts +17 -0
  39. package/dist/src/instrumentation/bedrock/get_tool_call_output_telemetry.js +85 -0
  40. package/dist/src/instrumentation/bedrock/get_tool_definitions_telemetry.d.ts +15 -0
  41. package/dist/src/instrumentation/bedrock/get_tool_definitions_telemetry.js +68 -0
  42. package/dist/src/instrumentation/bedrock/set_telemetry_attributes.d.ts +18 -0
  43. package/dist/src/instrumentation/bedrock/set_telemetry_attributes.js +47 -0
  44. package/dist/src/instrumentation/cohere/get_chat_telemetry.d.ts +23 -0
  45. package/dist/src/instrumentation/cohere/get_chat_telemetry.js +77 -0
  46. package/dist/src/instrumentation/cohere/get_embeddings_telemetry.d.ts +18 -0
  47. package/dist/src/instrumentation/cohere/get_embeddings_telemetry.js +88 -0
  48. package/dist/src/instrumentation/cohere/get_structured_output_schema_telemetry.d.ts +12 -0
  49. package/dist/src/instrumentation/cohere/get_structured_output_schema_telemetry.js +18 -0
  50. package/dist/src/instrumentation/cohere/get_tool_call_output_telemetry.d.ts +31 -0
  51. package/dist/src/instrumentation/cohere/get_tool_call_output_telemetry.js +106 -0
  52. package/dist/src/instrumentation/cohere/get_tool_definitions_telemetry.d.ts +27 -0
  53. package/dist/src/instrumentation/cohere/get_tool_definitions_telemetry.js +75 -0
  54. package/dist/src/instrumentation/cohere/set_telemetry_attributes.d.ts +17 -0
  55. package/dist/src/instrumentation/cohere/set_telemetry_attributes.js +44 -0
  56. package/dist/src/instrumentation/elevenlabs/full_instrumentation.d.ts +28 -0
  57. package/dist/src/instrumentation/elevenlabs/full_instrumentation.js +209 -0
  58. package/dist/src/instrumentation/google/get_audio_telemetry.d.ts +9 -0
  59. package/dist/src/instrumentation/google/get_audio_telemetry.js +28 -0
  60. package/dist/src/instrumentation/google/get_cache_operations_telemetry.d.ts +23 -0
  61. package/dist/src/instrumentation/google/get_cache_operations_telemetry.js +154 -0
  62. package/dist/src/instrumentation/google/get_cache_telemetry.d.ts +16 -0
  63. package/dist/src/instrumentation/google/get_cache_telemetry.js +38 -0
  64. package/dist/src/instrumentation/google/get_embeddings_telemetry.d.ts +12 -0
  65. package/dist/src/instrumentation/google/get_embeddings_telemetry.js +85 -0
  66. package/dist/src/instrumentation/google/get_provider_name.d.ts +11 -0
  67. package/{src → dist/src}/instrumentation/google/get_provider_name.js +11 -12
  68. package/{src/instrumentation/google/get_reasoning_telemetry.js → dist/src/instrumentation/google/get_reasoning_telemetry.d.ts} +1 -11
  69. package/dist/src/instrumentation/google/get_reasoning_telemetry.js +21 -0
  70. package/dist/src/instrumentation/google/get_structured_output_schema_telemetry.d.ts +12 -0
  71. package/dist/src/instrumentation/google/get_structured_output_schema_telemetry.js +19 -0
  72. package/dist/src/instrumentation/google/set_telemetry_attributes.d.ts +18 -0
  73. package/dist/src/instrumentation/google/set_telemetry_attributes.js +98 -0
  74. package/dist/src/instrumentation/openai/get_audio_telemetry.d.ts +25 -0
  75. package/dist/src/instrumentation/openai/get_audio_telemetry.js +123 -0
  76. package/dist/src/instrumentation/openai/get_bedrock_mantle_telemetry.d.ts +35 -0
  77. package/dist/src/instrumentation/openai/get_bedrock_mantle_telemetry.js +148 -0
  78. package/dist/src/instrumentation/openai/get_cache_and_reasoning_telemetry.d.ts +27 -0
  79. package/dist/src/instrumentation/openai/get_cache_and_reasoning_telemetry.js +72 -0
  80. package/dist/src/instrumentation/openai/get_cache_config_telemetry.d.ts +28 -0
  81. package/dist/src/instrumentation/openai/get_cache_config_telemetry.js +91 -0
  82. package/dist/src/instrumentation/openai/get_embeddings_telemetry.d.ts +18 -0
  83. package/dist/src/instrumentation/openai/get_embeddings_telemetry.js +86 -0
  84. package/dist/src/instrumentation/openai/get_streaming_usage.d.ts +21 -0
  85. package/dist/src/instrumentation/openai/get_streaming_usage.js +74 -0
  86. package/dist/src/instrumentation/openai/get_structured_output_schema_telemetry.d.ts +12 -0
  87. package/dist/src/instrumentation/openai/get_structured_output_schema_telemetry.js +20 -0
  88. package/dist/src/instrumentation/openai/set_telemetry_attributes.d.ts +17 -0
  89. package/dist/src/instrumentation/openai/set_telemetry_attributes.js +47 -0
  90. package/dist/src/instrumentation/shared/get_structured_output_schema_telemetry.d.ts +35 -0
  91. package/dist/src/instrumentation/shared/get_structured_output_schema_telemetry.js +76 -0
  92. package/dist/src/instrumentation/vercel/full_instrumentation.d.ts +3 -0
  93. package/dist/src/instrumentation/vercel/full_instrumentation.js +54 -0
  94. package/dist/src/instrumentation/vercel/get_cache_creation_telemetry.d.ts +22 -0
  95. package/dist/src/instrumentation/vercel/get_cache_creation_telemetry.js +66 -0
  96. package/dist/src/instrumentation/vercel/get_structured_output_schema_telemetry.d.ts +24 -0
  97. package/dist/src/instrumentation/vercel/get_structured_output_schema_telemetry.js +84 -0
  98. package/dist/src/logger.d.ts +1 -0
  99. package/dist/src/logger.js +6 -0
  100. package/dist/src/otel/exporter.d.ts +19 -0
  101. package/dist/src/otel/exporter.js +94 -0
  102. package/dist/src/otel/propagation.d.ts +46 -0
  103. package/dist/src/otel/propagation.js +110 -0
  104. package/dist/src/otel/span-filter.d.ts +3 -0
  105. package/dist/src/otel/span-filter.js +61 -0
  106. package/dist/src/otel/span-processor.d.ts +55 -0
  107. package/dist/src/otel/span-processor.js +169 -0
  108. package/dist/src/otel/vercel-ai-sdk-span-filter.d.ts +12 -0
  109. package/dist/src/otel/vercel-ai-sdk-span-filter.js +88 -0
  110. package/dist/src/setup.d.ts +50 -0
  111. package/dist/src/setup.js +155 -0
  112. package/package.json +11 -5
  113. package/.prettierignore +0 -2
  114. package/.prettierrc.json +0 -7
  115. package/LICENSE-MIT +0 -21
  116. package/NOTICE +0 -12
  117. package/dist/api/generated/index.d.ts +0 -2
  118. package/dist/api/generated/types.gen.d.ts +0 -749
  119. package/eslint.config.mjs +0 -36
  120. package/index.d.ts +0 -167
  121. package/openapi-ts.config.mjs +0 -9
  122. package/scripts/generate_facade.mjs +0 -86
  123. package/src/api/caseConversion.ts +0 -58
  124. package/src/api/generated/client/client.gen.ts +0 -277
  125. package/src/api/generated/client/index.ts +0 -27
  126. package/src/api/generated/client/types.gen.ts +0 -218
  127. package/src/api/generated/client/utils.gen.ts +0 -316
  128. package/src/api/generated/client.gen.ts +0 -16
  129. package/src/api/generated/core/auth.gen.ts +0 -48
  130. package/src/api/generated/core/bodySerializer.gen.ts +0 -82
  131. package/src/api/generated/core/params.gen.ts +0 -178
  132. package/src/api/generated/core/pathSerializer.gen.ts +0 -171
  133. package/src/api/generated/core/queryKeySerializer.gen.ts +0 -117
  134. package/src/api/generated/core/serverSentEvents.gen.ts +0 -242
  135. package/src/api/generated/core/types.gen.ts +0 -110
  136. package/src/api/generated/core/utils.gen.ts +0 -140
  137. package/src/api/generated/facade.gen.ts +0 -68
  138. package/src/api/generated/index.ts +0 -4
  139. package/src/api/generated/sdk.gen.ts +0 -121
  140. package/src/api/index.ts +0 -45
  141. package/src/api/makeEndpoint.ts +0 -54
  142. package/src/auto-instrument.js +0 -171
  143. package/src/index.js +0 -19
  144. package/src/instrumentation/anthropic/get_cache_and_reasoning_telemetry.js +0 -107
  145. package/src/instrumentation/anthropic/get_streaming_stop_reason_telemetry.js +0 -121
  146. package/src/instrumentation/anthropic/get_tool_definitions_telemetry.js +0 -50
  147. package/src/instrumentation/anthropic/set_telemetry_attributes.js +0 -37
  148. package/src/instrumentation/bedrock/get_cache_telemetry.js +0 -104
  149. package/src/instrumentation/bedrock/get_input_message_content_telemetry.js +0 -94
  150. package/src/instrumentation/bedrock/get_streaming_output_message_telemetry.js +0 -87
  151. package/src/instrumentation/bedrock/set_telemetry_attributes.js +0 -36
  152. package/src/instrumentation/cohere/get_chat_telemetry.js +0 -88
  153. package/src/instrumentation/cohere/get_embeddings_telemetry.js +0 -107
  154. package/src/instrumentation/cohere/set_telemetry_attributes.js +0 -32
  155. package/src/instrumentation/elevenlabs/full_instrumentation.js +0 -236
  156. package/src/instrumentation/google/get_audio_telemetry.js +0 -30
  157. package/src/instrumentation/google/get_cache_telemetry.js +0 -25
  158. package/src/instrumentation/google/get_embeddings_telemetry.js +0 -99
  159. package/src/instrumentation/google/set_telemetry_attributes.js +0 -98
  160. package/src/instrumentation/openai/get_audio_telemetry.js +0 -149
  161. package/src/instrumentation/openai/get_cache_and_reasoning_telemetry.js +0 -71
  162. package/src/instrumentation/openai/get_embeddings_telemetry.js +0 -107
  163. package/src/instrumentation/openai/get_streaming_usage.js +0 -81
  164. package/src/instrumentation/openai/set_telemetry_attributes.js +0 -39
  165. package/src/instrumentation/vercel/full_instrumentation.js +0 -44
  166. package/src/logger.js +0 -8
  167. package/src/otel/exporter.js +0 -108
  168. package/src/otel/propagation.js +0 -133
  169. package/src/otel/span-filter.js +0 -69
  170. package/src/otel/span-processor.js +0 -162
  171. package/src/otel/vercel-ai-sdk-span-filter.js +0 -79
  172. package/src/setup.js +0 -167
  173. package/tests/api/index.test.js +0 -437
  174. package/tests/index.test.js +0 -824
  175. package/tests/instrumentation/anthropic/get_cache_and_reasoning_telemetry.test.js +0 -158
  176. package/tests/instrumentation/anthropic/get_streaming_stop_reason_telemetry.test.js +0 -117
  177. package/tests/instrumentation/anthropic/get_tool_definitions_telemetry.test.js +0 -82
  178. package/tests/instrumentation/bedrock/get_cache_telemetry.test.js +0 -102
  179. package/tests/instrumentation/bedrock/get_input_message_content_telemetry.test.js +0 -89
  180. package/tests/instrumentation/bedrock/get_streaming_output_message_telemetry.test.js +0 -87
  181. package/tests/instrumentation/cohere/get_chat_telemetry.test.js +0 -148
  182. package/tests/instrumentation/cohere/get_embeddings_telemetry.test.js +0 -132
  183. package/tests/instrumentation/elevenlabs/full_instrumentation.test.js +0 -258
  184. package/tests/instrumentation/google/get_embeddings_telemetry.test.js +0 -128
  185. package/tests/instrumentation/google/get_reasoning_telemetry.test.js +0 -41
  186. package/tests/instrumentation/google/set_telemetry_attributes.test.js +0 -219
  187. package/tests/instrumentation/openai/get_audio_telemetry.test.js +0 -170
  188. package/tests/instrumentation/openai/get_cache_and_reasoning_telemetry.test.js +0 -83
  189. package/tests/instrumentation/openai/get_embeddings_telemetry.test.js +0 -145
  190. package/tests/instrumentation/openai/get_streaming_usage.test.js +0 -105
  191. package/tests/instrumentation/vercel/full_instrumentation.test.js +0 -16
  192. package/tests/otel/vercel-ai-sdk-span-filter.test.js +0 -159
  193. package/tsconfig.json +0 -18
  194. /package/dist/{api → src/api}/caseConversion.d.ts +0 -0
  195. /package/dist/{api → src/api}/caseConversion.js +0 -0
  196. /package/dist/{api → src/api}/generated/client/client.gen.d.ts +0 -0
  197. /package/dist/{api → src/api}/generated/client/client.gen.js +0 -0
  198. /package/dist/{api → src/api}/generated/client/index.d.ts +0 -0
  199. /package/dist/{api → src/api}/generated/client/index.js +0 -0
  200. /package/dist/{api → src/api}/generated/client/types.gen.d.ts +0 -0
  201. /package/dist/{api → src/api}/generated/client/types.gen.js +0 -0
  202. /package/dist/{api → src/api}/generated/client/utils.gen.d.ts +0 -0
  203. /package/dist/{api → src/api}/generated/client/utils.gen.js +0 -0
  204. /package/dist/{api → src/api}/generated/client.gen.d.ts +0 -0
  205. /package/dist/{api → src/api}/generated/client.gen.js +0 -0
  206. /package/dist/{api → src/api}/generated/core/auth.gen.d.ts +0 -0
  207. /package/dist/{api → src/api}/generated/core/auth.gen.js +0 -0
  208. /package/dist/{api → src/api}/generated/core/bodySerializer.gen.d.ts +0 -0
  209. /package/dist/{api → src/api}/generated/core/bodySerializer.gen.js +0 -0
  210. /package/dist/{api → src/api}/generated/core/params.gen.d.ts +0 -0
  211. /package/dist/{api → src/api}/generated/core/params.gen.js +0 -0
  212. /package/dist/{api → src/api}/generated/core/pathSerializer.gen.d.ts +0 -0
  213. /package/dist/{api → src/api}/generated/core/pathSerializer.gen.js +0 -0
  214. /package/dist/{api → src/api}/generated/core/queryKeySerializer.gen.d.ts +0 -0
  215. /package/dist/{api → src/api}/generated/core/queryKeySerializer.gen.js +0 -0
  216. /package/dist/{api → src/api}/generated/core/serverSentEvents.gen.d.ts +0 -0
  217. /package/dist/{api → src/api}/generated/core/serverSentEvents.gen.js +0 -0
  218. /package/dist/{api → src/api}/generated/core/types.gen.d.ts +0 -0
  219. /package/dist/{api → src/api}/generated/core/types.gen.js +0 -0
  220. /package/dist/{api → src/api}/generated/core/utils.gen.d.ts +0 -0
  221. /package/dist/{api → src/api}/generated/core/utils.gen.js +0 -0
  222. /package/dist/{api → src/api}/generated/types.gen.js +0 -0
  223. /package/dist/{api → src/api}/index.d.ts +0 -0
  224. /package/dist/{api → src/api}/index.js +0 -0
  225. /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
- });