@buenojs/bueno 0.8.3 → 0.8.5

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 (218) hide show
  1. package/README.md +136 -16
  2. package/dist/cli/{index.js → bin.js} +3036 -1421
  3. package/dist/container/index.js +250 -0
  4. package/dist/context/index.js +219 -0
  5. package/dist/database/index.js +493 -0
  6. package/dist/frontend/index.js +7697 -0
  7. package/dist/health/index.js +364 -0
  8. package/dist/i18n/index.js +345 -0
  9. package/dist/index.js +11043 -6482
  10. package/dist/jobs/index.js +819 -0
  11. package/dist/lock/index.js +367 -0
  12. package/dist/logger/index.js +281 -0
  13. package/dist/metrics/index.js +289 -0
  14. package/dist/middleware/index.js +77 -0
  15. package/dist/migrations/index.js +571 -0
  16. package/dist/modules/index.js +3346 -0
  17. package/dist/notification/index.js +484 -0
  18. package/dist/observability/index.js +331 -0
  19. package/dist/openapi/index.js +776 -0
  20. package/dist/orm/index.js +1356 -0
  21. package/dist/router/index.js +886 -0
  22. package/dist/rpc/index.js +691 -0
  23. package/dist/schema/index.js +400 -0
  24. package/dist/telemetry/index.js +595 -0
  25. package/dist/template/index.js +640 -0
  26. package/dist/templates/index.js +640 -0
  27. package/dist/testing/index.js +1111 -0
  28. package/dist/types/index.js +60 -0
  29. package/package.json +121 -27
  30. package/src/cache/index.ts +2 -1
  31. package/src/cli/bin.ts +2 -2
  32. package/src/cli/commands/build.ts +183 -165
  33. package/src/cli/commands/dev.ts +96 -89
  34. package/src/cli/commands/generate.ts +142 -111
  35. package/src/cli/commands/help.ts +20 -16
  36. package/src/cli/commands/index.ts +3 -6
  37. package/src/cli/commands/migration.ts +124 -105
  38. package/src/cli/commands/new.ts +392 -438
  39. package/src/cli/commands/start.ts +81 -79
  40. package/src/cli/core/args.ts +68 -50
  41. package/src/cli/core/console.ts +89 -95
  42. package/src/cli/core/index.ts +4 -4
  43. package/src/cli/core/prompt.ts +65 -62
  44. package/src/cli/core/spinner.ts +23 -20
  45. package/src/cli/index.ts +46 -38
  46. package/src/cli/templates/database/index.ts +61 -0
  47. package/src/cli/templates/database/mysql.ts +14 -0
  48. package/src/cli/templates/database/none.ts +16 -0
  49. package/src/cli/templates/database/postgresql.ts +14 -0
  50. package/src/cli/templates/database/sqlite.ts +14 -0
  51. package/src/cli/templates/deploy.ts +29 -26
  52. package/src/cli/templates/docker.ts +41 -30
  53. package/src/cli/templates/frontend/index.ts +63 -0
  54. package/src/cli/templates/frontend/none.ts +17 -0
  55. package/src/cli/templates/frontend/react.ts +140 -0
  56. package/src/cli/templates/frontend/solid.ts +134 -0
  57. package/src/cli/templates/frontend/svelte.ts +131 -0
  58. package/src/cli/templates/frontend/vue.ts +130 -0
  59. package/src/cli/templates/generators/index.ts +339 -0
  60. package/src/cli/templates/generators/types.ts +56 -0
  61. package/src/cli/templates/index.ts +35 -2
  62. package/src/cli/templates/project/api.ts +81 -0
  63. package/src/cli/templates/project/default.ts +140 -0
  64. package/src/cli/templates/project/fullstack.ts +111 -0
  65. package/src/cli/templates/project/index.ts +95 -0
  66. package/src/cli/templates/project/minimal.ts +45 -0
  67. package/src/cli/templates/project/types.ts +94 -0
  68. package/src/cli/templates/project/website.ts +263 -0
  69. package/src/cli/utils/fs.ts +55 -41
  70. package/src/cli/utils/index.ts +3 -2
  71. package/src/cli/utils/strings.ts +47 -33
  72. package/src/cli/utils/version.ts +47 -0
  73. package/src/config/env-validation.ts +100 -0
  74. package/src/config/env.ts +169 -41
  75. package/src/config/index.ts +28 -20
  76. package/src/config/loader.ts +25 -16
  77. package/src/config/merge.ts +21 -10
  78. package/src/config/types.ts +545 -25
  79. package/src/config/validation.ts +215 -7
  80. package/src/container/forward-ref.ts +22 -22
  81. package/src/container/index.ts +34 -12
  82. package/src/context/index.ts +11 -1
  83. package/src/database/index.ts +7 -190
  84. package/src/database/orm/builder.ts +457 -0
  85. package/src/database/orm/casts/index.ts +130 -0
  86. package/src/database/orm/casts/types.ts +25 -0
  87. package/src/database/orm/compiler.ts +304 -0
  88. package/src/database/orm/hooks/index.ts +114 -0
  89. package/src/database/orm/index.ts +61 -0
  90. package/src/database/orm/model-registry.ts +59 -0
  91. package/src/database/orm/model.ts +821 -0
  92. package/src/database/orm/relationships/base.ts +146 -0
  93. package/src/database/orm/relationships/belongs-to-many.ts +179 -0
  94. package/src/database/orm/relationships/belongs-to.ts +56 -0
  95. package/src/database/orm/relationships/has-many.ts +45 -0
  96. package/src/database/orm/relationships/has-one.ts +41 -0
  97. package/src/database/orm/relationships/index.ts +11 -0
  98. package/src/database/orm/scopes/index.ts +55 -0
  99. package/src/events/__tests__/event-system.test.ts +235 -0
  100. package/src/events/config.ts +238 -0
  101. package/src/events/example-usage.ts +185 -0
  102. package/src/events/index.ts +278 -0
  103. package/src/events/manager.ts +385 -0
  104. package/src/events/registry.ts +182 -0
  105. package/src/events/types.ts +124 -0
  106. package/src/frontend/api-routes.ts +65 -23
  107. package/src/frontend/bundler.ts +76 -34
  108. package/src/frontend/console-client.ts +2 -2
  109. package/src/frontend/console-stream.ts +94 -38
  110. package/src/frontend/dev-server.ts +94 -46
  111. package/src/frontend/file-router.ts +61 -19
  112. package/src/frontend/frameworks/index.ts +37 -10
  113. package/src/frontend/frameworks/react.ts +10 -8
  114. package/src/frontend/frameworks/solid.ts +11 -9
  115. package/src/frontend/frameworks/svelte.ts +15 -9
  116. package/src/frontend/frameworks/vue.ts +13 -11
  117. package/src/frontend/hmr-client.ts +12 -10
  118. package/src/frontend/hmr.ts +146 -103
  119. package/src/frontend/index.ts +14 -5
  120. package/src/frontend/islands.ts +41 -22
  121. package/src/frontend/isr.ts +59 -37
  122. package/src/frontend/layout.ts +36 -21
  123. package/src/frontend/ssr/react.ts +74 -27
  124. package/src/frontend/ssr/solid.ts +54 -20
  125. package/src/frontend/ssr/svelte.ts +48 -14
  126. package/src/frontend/ssr/vue.ts +50 -18
  127. package/src/frontend/ssr.ts +83 -39
  128. package/src/frontend/types.ts +91 -56
  129. package/src/health/index.ts +21 -9
  130. package/src/i18n/engine.ts +305 -0
  131. package/src/i18n/index.ts +38 -0
  132. package/src/i18n/loader.ts +218 -0
  133. package/src/i18n/middleware.ts +164 -0
  134. package/src/i18n/negotiator.ts +162 -0
  135. package/src/i18n/types.ts +158 -0
  136. package/src/index.ts +179 -27
  137. package/src/jobs/drivers/memory.ts +315 -0
  138. package/src/jobs/drivers/redis.ts +459 -0
  139. package/src/jobs/index.ts +30 -0
  140. package/src/jobs/queue.ts +281 -0
  141. package/src/jobs/types.ts +295 -0
  142. package/src/jobs/worker.ts +380 -0
  143. package/src/logger/index.ts +1 -3
  144. package/src/logger/transports/index.ts +62 -22
  145. package/src/metrics/index.ts +25 -16
  146. package/src/migrations/index.ts +9 -0
  147. package/src/modules/filters.ts +13 -17
  148. package/src/modules/guards.ts +49 -26
  149. package/src/modules/index.ts +409 -298
  150. package/src/modules/interceptors.ts +58 -20
  151. package/src/modules/lazy.ts +11 -19
  152. package/src/modules/lifecycle.ts +15 -7
  153. package/src/modules/metadata.ts +15 -5
  154. package/src/modules/pipes.ts +94 -72
  155. package/src/notification/channels/base.ts +68 -0
  156. package/src/notification/channels/email.ts +105 -0
  157. package/src/notification/channels/push.ts +104 -0
  158. package/src/notification/channels/sms.ts +105 -0
  159. package/src/notification/channels/whatsapp.ts +104 -0
  160. package/src/notification/index.ts +48 -0
  161. package/src/notification/service.ts +354 -0
  162. package/src/notification/types.ts +344 -0
  163. package/src/observability/__tests__/observability.test.ts +483 -0
  164. package/src/observability/breadcrumbs.ts +114 -0
  165. package/src/observability/index.ts +136 -0
  166. package/src/observability/interceptor.ts +85 -0
  167. package/src/observability/service.ts +303 -0
  168. package/src/observability/trace.ts +37 -0
  169. package/src/observability/types.ts +196 -0
  170. package/src/openapi/__tests__/decorators.test.ts +335 -0
  171. package/src/openapi/__tests__/document-builder.test.ts +285 -0
  172. package/src/openapi/__tests__/route-scanner.test.ts +334 -0
  173. package/src/openapi/__tests__/schema-generator.test.ts +275 -0
  174. package/src/openapi/decorators.ts +328 -0
  175. package/src/openapi/document-builder.ts +274 -0
  176. package/src/openapi/index.ts +112 -0
  177. package/src/openapi/metadata.ts +112 -0
  178. package/src/openapi/route-scanner.ts +289 -0
  179. package/src/openapi/schema-generator.ts +256 -0
  180. package/src/openapi/swagger-module.ts +166 -0
  181. package/src/openapi/types.ts +398 -0
  182. package/src/orm/index.ts +10 -0
  183. package/src/rpc/index.ts +3 -1
  184. package/src/schema/index.ts +9 -0
  185. package/src/security/index.ts +15 -6
  186. package/src/ssg/index.ts +9 -8
  187. package/src/telemetry/index.ts +76 -22
  188. package/src/template/index.ts +7 -0
  189. package/src/templates/engine.ts +224 -0
  190. package/src/templates/index.ts +9 -0
  191. package/src/templates/loader.ts +331 -0
  192. package/src/templates/renderers/markdown.ts +212 -0
  193. package/src/templates/renderers/simple.ts +269 -0
  194. package/src/templates/types.ts +154 -0
  195. package/src/testing/index.ts +100 -27
  196. package/src/types/optional-deps.d.ts +347 -187
  197. package/src/validation/index.ts +92 -2
  198. package/src/validation/schemas.ts +536 -0
  199. package/tests/integration/fullstack.test.ts +4 -4
  200. package/tests/unit/database.test.ts +2 -72
  201. package/tests/unit/env-validation.test.ts +166 -0
  202. package/tests/unit/events.test.ts +910 -0
  203. package/tests/unit/i18n.test.ts +455 -0
  204. package/tests/unit/jobs.test.ts +493 -0
  205. package/tests/unit/notification.test.ts +988 -0
  206. package/tests/unit/observability.test.ts +453 -0
  207. package/tests/unit/orm/builder.test.ts +323 -0
  208. package/tests/unit/orm/casts.test.ts +179 -0
  209. package/tests/unit/orm/compiler.test.ts +220 -0
  210. package/tests/unit/orm/eager-loading.test.ts +285 -0
  211. package/tests/unit/orm/hooks.test.ts +191 -0
  212. package/tests/unit/orm/model.test.ts +373 -0
  213. package/tests/unit/orm/relationships.test.ts +303 -0
  214. package/tests/unit/orm/scopes.test.ts +74 -0
  215. package/tests/unit/templates-simple.test.ts +53 -0
  216. package/tests/unit/templates.test.ts +454 -0
  217. package/tests/unit/validation.test.ts +18 -24
  218. package/tsconfig.json +11 -3
@@ -0,0 +1,595 @@
1
+ // @bun
2
+ var __defProp = Object.defineProperty;
3
+ var __export = (target, all) => {
4
+ for (var name in all)
5
+ __defProp(target, name, {
6
+ get: all[name],
7
+ enumerable: true,
8
+ configurable: true,
9
+ set: (newValue) => all[name] = () => newValue
10
+ });
11
+ };
12
+ var __legacyDecorateClassTS = function(decorators, target, key, desc) {
13
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
14
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function")
15
+ r = Reflect.decorate(decorators, target, key, desc);
16
+ else
17
+ for (var i = decorators.length - 1;i >= 0; i--)
18
+ if (d = decorators[i])
19
+ r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
20
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
21
+ };
22
+ var __legacyMetadataTS = (k, v) => {
23
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function")
24
+ return Reflect.metadata(k, v);
25
+ };
26
+ var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
27
+ var __require = import.meta.require;
28
+
29
+ // src/telemetry/index.ts
30
+ function generateTraceId() {
31
+ const bytes = new Uint8Array(16);
32
+ crypto.getRandomValues(bytes);
33
+ return Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
34
+ }
35
+ function generateSpanId() {
36
+ const bytes = new Uint8Array(8);
37
+ crypto.getRandomValues(bytes);
38
+ return Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
39
+ }
40
+ function hexToBase64(hex) {
41
+ const bytes = new Uint8Array(hex.length / 2);
42
+ for (let i = 0;i < hex.length; i += 2) {
43
+ bytes[i / 2] = Number.parseInt(hex.substring(i, i + 2), 16);
44
+ }
45
+ return btoa(String.fromCharCode(...bytes));
46
+ }
47
+ function nowNanoseconds() {
48
+ return Bun.nanoseconds();
49
+ }
50
+ function toOTLPAttribute(key, value) {
51
+ if (typeof value === "string") {
52
+ return { key, value: { stringValue: value } };
53
+ } else if (typeof value === "number") {
54
+ if (Number.isInteger(value)) {
55
+ return { key, value: { intValue: value } };
56
+ }
57
+ return { key, value: { doubleValue: value } };
58
+ } else if (typeof value === "boolean") {
59
+ return { key, value: { boolValue: value } };
60
+ }
61
+ return { key, value: { stringValue: String(value) } };
62
+ }
63
+ function spanKindToOTLP(kind) {
64
+ const kindMap = {
65
+ internal: 1,
66
+ server: 2,
67
+ client: 3,
68
+ producer: 4,
69
+ consumer: 5
70
+ };
71
+ return kindMap[kind] ?? 1;
72
+ }
73
+ function statusCodeToOTLP(code) {
74
+ const codeMap = {
75
+ unset: 0,
76
+ ok: 1,
77
+ error: 2
78
+ };
79
+ return codeMap[code];
80
+ }
81
+
82
+ class OTLPExporter {
83
+ endpoint;
84
+ headers;
85
+ exportInterval;
86
+ maxBatchSize;
87
+ maxRetries;
88
+ retryDelay;
89
+ timeout;
90
+ pendingSpans = [];
91
+ exportTimer = null;
92
+ isShuttingDown = false;
93
+ serviceName = "unknown-service";
94
+ resourceAttributes = {};
95
+ constructor(options) {
96
+ this.endpoint = options.endpoint;
97
+ this.headers = {
98
+ "Content-Type": "application/json",
99
+ ...options.headers
100
+ };
101
+ this.exportInterval = options.exportInterval ?? 5000;
102
+ this.maxBatchSize = options.maxBatchSize ?? 100;
103
+ this.maxRetries = options.maxRetries ?? 3;
104
+ this.retryDelay = options.retryDelay ?? 1000;
105
+ this.timeout = options.timeout ?? 30000;
106
+ }
107
+ setServiceName(name) {
108
+ this.serviceName = name;
109
+ }
110
+ setResourceAttributes(attributes) {
111
+ this.resourceAttributes = { ...attributes };
112
+ }
113
+ start() {
114
+ if (this.exportTimer !== null)
115
+ return;
116
+ this.exportTimer = setInterval(() => {
117
+ this.flush().catch(() => {});
118
+ }, this.exportInterval);
119
+ }
120
+ stop() {
121
+ if (this.exportTimer !== null) {
122
+ clearInterval(this.exportTimer);
123
+ this.exportTimer = null;
124
+ }
125
+ }
126
+ addSpan(span) {
127
+ if (this.isShuttingDown)
128
+ return;
129
+ this.pendingSpans.push(span);
130
+ if (this.pendingSpans.length >= this.maxBatchSize) {
131
+ this.flush().catch(() => {});
132
+ }
133
+ }
134
+ async export(spans) {
135
+ if (spans.length === 0)
136
+ return true;
137
+ const exportRequest = this.buildExportRequest(spans);
138
+ for (let attempt = 0;attempt < this.maxRetries; attempt++) {
139
+ try {
140
+ const controller = new AbortController;
141
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout);
142
+ const response = await fetch(this.endpoint, {
143
+ method: "POST",
144
+ headers: this.headers,
145
+ body: JSON.stringify(exportRequest),
146
+ signal: controller.signal
147
+ });
148
+ clearTimeout(timeoutId);
149
+ if (response.ok) {
150
+ return true;
151
+ }
152
+ if (response.status >= 400 && response.status < 500) {
153
+ console.error(`OTLP export failed with status ${response.status}`);
154
+ return false;
155
+ }
156
+ if (attempt < this.maxRetries - 1) {
157
+ await this.delay(this.retryDelay * Math.pow(2, attempt));
158
+ }
159
+ } catch (error) {
160
+ if (attempt < this.maxRetries - 1) {
161
+ await this.delay(this.retryDelay * Math.pow(2, attempt));
162
+ } else {
163
+ console.error("OTLP export failed:", error);
164
+ return false;
165
+ }
166
+ }
167
+ }
168
+ return false;
169
+ }
170
+ async flush() {
171
+ if (this.pendingSpans.length === 0)
172
+ return;
173
+ const spansToExport = [...this.pendingSpans];
174
+ this.pendingSpans = [];
175
+ await this.export(spansToExport);
176
+ }
177
+ async close() {
178
+ this.isShuttingDown = true;
179
+ this.stop();
180
+ await this.flush();
181
+ }
182
+ buildExportRequest(spans) {
183
+ const resourceAttributes = [
184
+ toOTLPAttribute("service.name", this.serviceName)
185
+ ];
186
+ for (const [key, value] of Object.entries(this.resourceAttributes)) {
187
+ resourceAttributes.push(toOTLPAttribute(key, value));
188
+ }
189
+ const otlpSpans = spans.map((span) => ({
190
+ traceId: hexToBase64(span.traceId),
191
+ spanId: hexToBase64(span.spanId),
192
+ parentSpanId: span.parentSpanId ? hexToBase64(span.parentSpanId) : undefined,
193
+ name: span.name,
194
+ kind: spanKindToOTLP(span.kind),
195
+ startTimeUnixNano: span.startTime,
196
+ endTimeUnixNano: span.endTime ?? span.startTime,
197
+ attributes: Object.entries(span.attributes).map(([k, v]) => toOTLPAttribute(k, v)),
198
+ events: span.events.map((event) => ({
199
+ timeUnixNano: event.timestamp,
200
+ name: event.name,
201
+ attributes: event.attributes ? Object.entries(event.attributes).map(([k, v]) => toOTLPAttribute(k, v)) : []
202
+ })),
203
+ status: {
204
+ code: statusCodeToOTLP(span.status.code),
205
+ message: span.status.message
206
+ }
207
+ }));
208
+ return {
209
+ resourceSpans: [
210
+ {
211
+ resource: {
212
+ attributes: resourceAttributes
213
+ },
214
+ scopeSpans: [
215
+ {
216
+ scope: { name: "bueno-tracer" },
217
+ spans: otlpSpans
218
+ }
219
+ ]
220
+ }
221
+ ]
222
+ };
223
+ }
224
+ delay(ms) {
225
+ return new Promise((resolve) => setTimeout(resolve, ms));
226
+ }
227
+ }
228
+
229
+ class Tracer {
230
+ serviceName;
231
+ exporter;
232
+ sampler;
233
+ probability;
234
+ resourceAttributes;
235
+ currentSpan = null;
236
+ spanStack = [];
237
+ constructor(options = {}) {
238
+ this.serviceName = options.serviceName ?? "unknown-service";
239
+ this.exporter = options.exporter;
240
+ this.sampler = options.sampler ?? "always";
241
+ this.probability = options.probability ?? 1;
242
+ this.resourceAttributes = options.resourceAttributes ?? {};
243
+ if (this.exporter) {
244
+ this.exporter.setServiceName(this.serviceName);
245
+ this.exporter.setResourceAttributes(this.resourceAttributes);
246
+ this.exporter.start();
247
+ }
248
+ }
249
+ shouldSample() {
250
+ switch (this.sampler) {
251
+ case "always":
252
+ return true;
253
+ case "never":
254
+ return false;
255
+ case "probabilistic":
256
+ return Math.random() < this.probability;
257
+ default:
258
+ return true;
259
+ }
260
+ }
261
+ startSpan(name, options = {}) {
262
+ if (!this.shouldSample()) {
263
+ return this.createNoopSpan(name, options);
264
+ }
265
+ const parent = options.parent ?? this.currentSpan;
266
+ const span = {
267
+ traceId: parent?.traceId ?? generateTraceId(),
268
+ spanId: generateSpanId(),
269
+ parentSpanId: parent?.spanId,
270
+ name,
271
+ kind: options.kind ?? "internal",
272
+ startTime: options.startTime ?? nowNanoseconds(),
273
+ attributes: { ...options.attributes },
274
+ events: [],
275
+ status: { code: "unset" },
276
+ ended: false
277
+ };
278
+ return span;
279
+ }
280
+ endSpan(span, endTime) {
281
+ if (span.ended)
282
+ return;
283
+ span.ended = true;
284
+ span.endTime = endTime ?? nowNanoseconds();
285
+ span.duration = span.endTime - span.startTime;
286
+ if (this.exporter) {
287
+ this.exporter.addSpan(span);
288
+ }
289
+ }
290
+ getCurrentSpan() {
291
+ return this.currentSpan;
292
+ }
293
+ async withSpan(name, fn, options = {}) {
294
+ const span = this.startSpan(name, options);
295
+ const previousSpan = this.currentSpan;
296
+ this.currentSpan = span;
297
+ this.spanStack.push(span);
298
+ try {
299
+ const result = await fn(span);
300
+ return result;
301
+ } catch (error) {
302
+ this.setError(span, error);
303
+ throw error;
304
+ } finally {
305
+ this.endSpan(span);
306
+ this.spanStack.pop();
307
+ this.currentSpan = previousSpan ?? this.spanStack[this.spanStack.length - 1] ?? null;
308
+ }
309
+ }
310
+ addEvent(span, name, attributes) {
311
+ if (span.ended)
312
+ return;
313
+ span.events.push({
314
+ name,
315
+ timestamp: nowNanoseconds(),
316
+ attributes
317
+ });
318
+ }
319
+ setAttribute(span, key, value) {
320
+ if (span.ended)
321
+ return;
322
+ span.attributes[key] = value;
323
+ }
324
+ setAttributes(span, attributes) {
325
+ if (span.ended)
326
+ return;
327
+ Object.assign(span.attributes, attributes);
328
+ }
329
+ setStatus(span, code, message) {
330
+ if (span.ended)
331
+ return;
332
+ span.status = { code, message };
333
+ }
334
+ setError(span, error) {
335
+ if (span.ended)
336
+ return;
337
+ span.status = {
338
+ code: "error",
339
+ message: error.message
340
+ };
341
+ span.events.push({
342
+ name: "exception",
343
+ timestamp: nowNanoseconds(),
344
+ attributes: {
345
+ "exception.type": error.name,
346
+ "exception.message": error.message,
347
+ "exception.stacktrace": error.stack ?? ""
348
+ }
349
+ });
350
+ }
351
+ updateName(span, name) {
352
+ if (span.ended)
353
+ return;
354
+ span.name = name;
355
+ }
356
+ injectContext(carrier, span) {
357
+ const activeSpan = span ?? this.currentSpan;
358
+ if (!activeSpan)
359
+ return;
360
+ const traceFlags = activeSpan.status.code === "error" ? 0 : 1;
361
+ const traceparent = `00-${activeSpan.traceId}-${activeSpan.spanId}-${traceFlags.toString(16).padStart(2, "0")}`;
362
+ carrier["traceparent"] = traceparent;
363
+ }
364
+ extractContext(carrier) {
365
+ const traceparent = carrier["traceparent"] ?? carrier["Traceparent"];
366
+ if (!traceparent)
367
+ return null;
368
+ const parts = traceparent.split("-");
369
+ if (parts.length !== 4)
370
+ return null;
371
+ const [version, traceId, spanId, flags] = parts;
372
+ if (version !== "00")
373
+ return null;
374
+ if (!/^[0-9a-f]{32}$/i.test(traceId))
375
+ return null;
376
+ if (!/^[0-9a-f]{16}$/i.test(spanId))
377
+ return null;
378
+ return {
379
+ traceId,
380
+ spanId,
381
+ traceFlags: Number.parseInt(flags, 16),
382
+ traceState: carrier["tracestate"] ?? carrier["Tracestate"]
383
+ };
384
+ }
385
+ startSpanFromContext(name, context, options = {}) {
386
+ return this.startSpan(name, {
387
+ ...options,
388
+ parent: {
389
+ traceId: context.traceId,
390
+ spanId: context.spanId
391
+ }
392
+ });
393
+ }
394
+ async flush() {
395
+ if (this.exporter) {
396
+ await this.exporter.flush();
397
+ }
398
+ }
399
+ async close() {
400
+ if (this.exporter) {
401
+ await this.exporter.close();
402
+ }
403
+ }
404
+ createNoopSpan(name, options) {
405
+ return {
406
+ traceId: generateTraceId(),
407
+ spanId: generateSpanId(),
408
+ parentSpanId: options.parent?.spanId,
409
+ name,
410
+ kind: options.kind ?? "internal",
411
+ startTime: options.startTime ?? nowNanoseconds(),
412
+ attributes: {},
413
+ events: [],
414
+ status: { code: "unset" },
415
+ ended: false
416
+ };
417
+ }
418
+ }
419
+ function createTracer(serviceName, options = {}) {
420
+ return new Tracer({
421
+ ...options,
422
+ serviceName
423
+ });
424
+ }
425
+ function traceMiddleware(tracer) {
426
+ return async (ctx, next) => {
427
+ const headers = {};
428
+ for (const [key, value] of Object.entries(ctx.headers)) {
429
+ headers[key.toLowerCase()] = value;
430
+ }
431
+ const parentContext = tracer.extractContext(headers);
432
+ const spanOptions = {
433
+ kind: "server",
434
+ attributes: {
435
+ "http.method": ctx.method,
436
+ "http.url": ctx.url?.toString() ?? ctx.path,
437
+ "http.route": ctx.path
438
+ }
439
+ };
440
+ let span;
441
+ if (parentContext) {
442
+ span = tracer.startSpanFromContext(`${ctx.method} ${ctx.path}`, parentContext, spanOptions);
443
+ } else {
444
+ span = tracer.startSpan(`${ctx.method} ${ctx.path}`, spanOptions);
445
+ }
446
+ const outgoingHeaders = {};
447
+ tracer.injectContext(outgoingHeaders, span);
448
+ for (const [key, value] of Object.entries(outgoingHeaders)) {
449
+ ctx.setHeader(key, value);
450
+ }
451
+ try {
452
+ const response = await next();
453
+ tracer.setAttribute(span, "http.status_code", response.status);
454
+ if (response.status >= 400) {
455
+ tracer.setStatus(span, "error");
456
+ } else {
457
+ tracer.setStatus(span, "ok");
458
+ }
459
+ return response;
460
+ } catch (error) {
461
+ tracer.setError(span, error);
462
+ throw error;
463
+ } finally {
464
+ tracer.endSpan(span);
465
+ }
466
+ };
467
+ }
468
+ function traceDatabase(tracer, db, system = "unknown") {
469
+ const tracedDb = { ...db };
470
+ if (typeof db.query === "function") {
471
+ const originalQuery = db.query.bind(db);
472
+ tracedDb.query = async (sql, params) => {
473
+ return tracer.withSpan(`db.query`, async (span) => {
474
+ tracer.setAttributes(span, {
475
+ "db.system": system,
476
+ "db.statement": sql,
477
+ "db.operation": extractOperation(sql)
478
+ });
479
+ if (params) {
480
+ tracer.setAttribute(span, "db.params", JSON.stringify(params));
481
+ }
482
+ const result = await originalQuery(sql, params);
483
+ tracer.setStatus(span, "ok");
484
+ return result;
485
+ }, { kind: "client" });
486
+ };
487
+ }
488
+ if (typeof db.execute === "function") {
489
+ const originalExecute = db.execute.bind(db);
490
+ tracedDb.execute = async (sql, params) => {
491
+ return tracer.withSpan(`db.execute`, async (span) => {
492
+ tracer.setAttributes(span, {
493
+ "db.system": system,
494
+ "db.statement": sql,
495
+ "db.operation": extractOperation(sql)
496
+ });
497
+ if (params) {
498
+ tracer.setAttribute(span, "db.params", JSON.stringify(params));
499
+ }
500
+ const result = await originalExecute(sql, params);
501
+ tracer.setStatus(span, "ok");
502
+ return result;
503
+ }, { kind: "client" });
504
+ };
505
+ }
506
+ return tracedDb;
507
+ }
508
+ function extractOperation(sql) {
509
+ const normalized = sql.trim().toUpperCase();
510
+ const match = normalized.match(/^(SELECT|INSERT|UPDATE|DELETE|CREATE|DROP|ALTER|TRUNCATE)/);
511
+ return match ? match[1] : "UNKNOWN";
512
+ }
513
+ function createTracedFetch(tracer) {
514
+ return async (url, options = {}) => {
515
+ const { parentSpan, attributes = {}, ...fetchOptions } = options;
516
+ const urlStr = url.toString();
517
+ return tracer.withSpan(`HTTP ${fetchOptions.method ?? "GET"}`, async (span) => {
518
+ tracer.setAttributes(span, {
519
+ "http.method": fetchOptions.method ?? "GET",
520
+ "http.url": urlStr,
521
+ ...attributes
522
+ });
523
+ const headers = new Headers(fetchOptions.headers);
524
+ const carrier = {};
525
+ tracer.injectContext(carrier, span);
526
+ for (const [key, value] of Object.entries(carrier)) {
527
+ headers.set(key, value);
528
+ }
529
+ try {
530
+ const response = await fetch(url, {
531
+ ...fetchOptions,
532
+ headers
533
+ });
534
+ tracer.setAttribute(span, "http.status_code", response.status);
535
+ tracer.setStatus(span, response.status >= 400 ? "error" : "ok");
536
+ return response;
537
+ } catch (error) {
538
+ tracer.setError(span, error);
539
+ throw error;
540
+ }
541
+ }, { kind: "client", parent: parentSpan });
542
+ };
543
+ }
544
+
545
+ class SpanBuilder {
546
+ span;
547
+ tracer;
548
+ constructor(tracer, name, options = {}) {
549
+ this.tracer = tracer;
550
+ this.span = tracer.startSpan(name, options);
551
+ }
552
+ setAttribute(key, value) {
553
+ this.tracer.setAttribute(this.span, key, value);
554
+ return this;
555
+ }
556
+ setAttributes(attributes) {
557
+ this.tracer.setAttributes(this.span, attributes);
558
+ return this;
559
+ }
560
+ addEvent(name, attributes) {
561
+ this.tracer.addEvent(this.span, name, attributes);
562
+ return this;
563
+ }
564
+ setStatus(code, message) {
565
+ this.tracer.setStatus(this.span, code, message);
566
+ return this;
567
+ }
568
+ setError(error) {
569
+ this.tracer.setError(this.span, error);
570
+ return this;
571
+ }
572
+ end() {
573
+ this.tracer.endSpan(this.span);
574
+ return this.span;
575
+ }
576
+ getSpan() {
577
+ return this.span;
578
+ }
579
+ }
580
+ function span(tracer, name, options = {}) {
581
+ return new SpanBuilder(tracer, name, options);
582
+ }
583
+ export {
584
+ traceMiddleware,
585
+ traceDatabase,
586
+ span,
587
+ nowNanoseconds,
588
+ generateTraceId,
589
+ generateSpanId,
590
+ createTracer,
591
+ createTracedFetch,
592
+ Tracer,
593
+ SpanBuilder,
594
+ OTLPExporter
595
+ };