@buenojs/bueno 0.8.4 → 0.8.6

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 (234) hide show
  1. package/README.md +264 -17
  2. package/dist/cli/{index.js → bin.js} +413 -332
  3. package/dist/container/index.js +273 -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/graphql/index.js +2156 -0
  8. package/dist/health/index.js +364 -0
  9. package/dist/i18n/index.js +345 -0
  10. package/dist/index.js +9694 -5047
  11. package/dist/jobs/index.js +819 -0
  12. package/dist/lock/index.js +367 -0
  13. package/dist/logger/index.js +281 -0
  14. package/dist/metrics/index.js +289 -0
  15. package/dist/middleware/index.js +77 -0
  16. package/dist/migrations/index.js +571 -0
  17. package/dist/modules/index.js +3411 -0
  18. package/dist/notification/index.js +484 -0
  19. package/dist/observability/index.js +331 -0
  20. package/dist/openapi/index.js +795 -0
  21. package/dist/orm/index.js +1356 -0
  22. package/dist/router/index.js +886 -0
  23. package/dist/rpc/index.js +691 -0
  24. package/dist/schema/index.js +400 -0
  25. package/dist/telemetry/index.js +595 -0
  26. package/dist/template/index.js +640 -0
  27. package/dist/templates/index.js +640 -0
  28. package/dist/testing/index.js +1111 -0
  29. package/dist/types/index.js +60 -0
  30. package/llms.txt +231 -0
  31. package/package.json +125 -27
  32. package/src/cache/index.ts +2 -1
  33. package/src/cli/ARCHITECTURE.md +3 -3
  34. package/src/cli/bin.ts +2 -2
  35. package/src/cli/commands/build.ts +183 -165
  36. package/src/cli/commands/dev.ts +96 -89
  37. package/src/cli/commands/generate.ts +142 -111
  38. package/src/cli/commands/help.ts +20 -16
  39. package/src/cli/commands/index.ts +3 -6
  40. package/src/cli/commands/migration.ts +124 -105
  41. package/src/cli/commands/new.ts +294 -232
  42. package/src/cli/commands/start.ts +81 -79
  43. package/src/cli/core/args.ts +68 -50
  44. package/src/cli/core/console.ts +89 -95
  45. package/src/cli/core/index.ts +4 -4
  46. package/src/cli/core/prompt.ts +65 -62
  47. package/src/cli/core/spinner.ts +23 -20
  48. package/src/cli/index.ts +46 -38
  49. package/src/cli/templates/database/index.ts +37 -18
  50. package/src/cli/templates/database/mysql.ts +3 -3
  51. package/src/cli/templates/database/none.ts +2 -2
  52. package/src/cli/templates/database/postgresql.ts +3 -3
  53. package/src/cli/templates/database/sqlite.ts +3 -3
  54. package/src/cli/templates/deploy.ts +29 -26
  55. package/src/cli/templates/docker.ts +41 -30
  56. package/src/cli/templates/frontend/index.ts +33 -15
  57. package/src/cli/templates/frontend/none.ts +2 -2
  58. package/src/cli/templates/frontend/react.ts +18 -18
  59. package/src/cli/templates/frontend/solid.ts +15 -15
  60. package/src/cli/templates/frontend/svelte.ts +17 -17
  61. package/src/cli/templates/frontend/vue.ts +15 -15
  62. package/src/cli/templates/generators/index.ts +29 -29
  63. package/src/cli/templates/generators/types.ts +21 -21
  64. package/src/cli/templates/index.ts +6 -6
  65. package/src/cli/templates/project/api.ts +37 -36
  66. package/src/cli/templates/project/default.ts +25 -25
  67. package/src/cli/templates/project/fullstack.ts +28 -26
  68. package/src/cli/templates/project/index.ts +55 -16
  69. package/src/cli/templates/project/minimal.ts +17 -12
  70. package/src/cli/templates/project/types.ts +10 -5
  71. package/src/cli/templates/project/website.ts +15 -15
  72. package/src/cli/utils/fs.ts +55 -41
  73. package/src/cli/utils/index.ts +3 -3
  74. package/src/cli/utils/strings.ts +47 -33
  75. package/src/cli/utils/version.ts +14 -8
  76. package/src/config/env-validation.ts +100 -0
  77. package/src/config/env.ts +169 -41
  78. package/src/config/index.ts +28 -20
  79. package/src/config/loader.ts +25 -16
  80. package/src/config/merge.ts +21 -10
  81. package/src/config/types.ts +566 -25
  82. package/src/config/validation.ts +215 -7
  83. package/src/container/forward-ref.ts +22 -22
  84. package/src/container/index.ts +34 -12
  85. package/src/context/index.ts +11 -1
  86. package/src/database/index.ts +7 -190
  87. package/src/database/orm/builder.ts +457 -0
  88. package/src/database/orm/casts/index.ts +130 -0
  89. package/src/database/orm/casts/types.ts +25 -0
  90. package/src/database/orm/compiler.ts +304 -0
  91. package/src/database/orm/hooks/index.ts +114 -0
  92. package/src/database/orm/index.ts +61 -0
  93. package/src/database/orm/model-registry.ts +59 -0
  94. package/src/database/orm/model.ts +821 -0
  95. package/src/database/orm/relationships/base.ts +146 -0
  96. package/src/database/orm/relationships/belongs-to-many.ts +179 -0
  97. package/src/database/orm/relationships/belongs-to.ts +56 -0
  98. package/src/database/orm/relationships/has-many.ts +45 -0
  99. package/src/database/orm/relationships/has-one.ts +41 -0
  100. package/src/database/orm/relationships/index.ts +11 -0
  101. package/src/database/orm/scopes/index.ts +55 -0
  102. package/src/events/__tests__/event-system.test.ts +235 -0
  103. package/src/events/config.ts +238 -0
  104. package/src/events/example-usage.ts +185 -0
  105. package/src/events/index.ts +278 -0
  106. package/src/events/manager.ts +385 -0
  107. package/src/events/registry.ts +182 -0
  108. package/src/events/types.ts +124 -0
  109. package/src/frontend/api-routes.ts +65 -23
  110. package/src/frontend/bundler.ts +76 -34
  111. package/src/frontend/console-client.ts +2 -2
  112. package/src/frontend/console-stream.ts +94 -38
  113. package/src/frontend/dev-server.ts +94 -46
  114. package/src/frontend/file-router.ts +61 -19
  115. package/src/frontend/frameworks/index.ts +37 -10
  116. package/src/frontend/frameworks/react.ts +10 -8
  117. package/src/frontend/frameworks/solid.ts +11 -9
  118. package/src/frontend/frameworks/svelte.ts +15 -9
  119. package/src/frontend/frameworks/vue.ts +13 -11
  120. package/src/frontend/hmr-client.ts +12 -10
  121. package/src/frontend/hmr.ts +146 -103
  122. package/src/frontend/index.ts +14 -5
  123. package/src/frontend/islands.ts +41 -22
  124. package/src/frontend/isr.ts +59 -37
  125. package/src/frontend/layout.ts +36 -21
  126. package/src/frontend/ssr/react.ts +74 -27
  127. package/src/frontend/ssr/solid.ts +54 -20
  128. package/src/frontend/ssr/svelte.ts +48 -14
  129. package/src/frontend/ssr/vue.ts +50 -18
  130. package/src/frontend/ssr.ts +83 -39
  131. package/src/frontend/types.ts +91 -56
  132. package/src/graphql/built-in-engine.ts +598 -0
  133. package/src/graphql/context-builder.ts +110 -0
  134. package/src/graphql/decorators.ts +358 -0
  135. package/src/graphql/execution-pipeline.ts +227 -0
  136. package/src/graphql/graphql-module.ts +563 -0
  137. package/src/graphql/index.ts +101 -0
  138. package/src/graphql/metadata.ts +237 -0
  139. package/src/graphql/schema-builder.ts +319 -0
  140. package/src/graphql/subscription-handler.ts +283 -0
  141. package/src/graphql/types.ts +324 -0
  142. package/src/health/index.ts +21 -9
  143. package/src/i18n/engine.ts +305 -0
  144. package/src/i18n/index.ts +38 -0
  145. package/src/i18n/loader.ts +218 -0
  146. package/src/i18n/middleware.ts +164 -0
  147. package/src/i18n/negotiator.ts +162 -0
  148. package/src/i18n/types.ts +158 -0
  149. package/src/index.ts +182 -27
  150. package/src/jobs/drivers/memory.ts +315 -0
  151. package/src/jobs/drivers/redis.ts +459 -0
  152. package/src/jobs/index.ts +30 -0
  153. package/src/jobs/queue.ts +281 -0
  154. package/src/jobs/types.ts +295 -0
  155. package/src/jobs/worker.ts +380 -0
  156. package/src/logger/index.ts +1 -3
  157. package/src/logger/transports/index.ts +62 -22
  158. package/src/metrics/index.ts +25 -16
  159. package/src/migrations/index.ts +9 -0
  160. package/src/modules/filters.ts +13 -17
  161. package/src/modules/guards.ts +49 -26
  162. package/src/modules/index.ts +457 -299
  163. package/src/modules/interceptors.ts +58 -20
  164. package/src/modules/lazy.ts +11 -19
  165. package/src/modules/lifecycle.ts +15 -7
  166. package/src/modules/metadata.ts +15 -5
  167. package/src/modules/pipes.ts +94 -72
  168. package/src/notification/channels/base.ts +68 -0
  169. package/src/notification/channels/email.ts +105 -0
  170. package/src/notification/channels/push.ts +104 -0
  171. package/src/notification/channels/sms.ts +105 -0
  172. package/src/notification/channels/whatsapp.ts +104 -0
  173. package/src/notification/index.ts +48 -0
  174. package/src/notification/service.ts +354 -0
  175. package/src/notification/types.ts +344 -0
  176. package/src/observability/__tests__/observability.test.ts +483 -0
  177. package/src/observability/breadcrumbs.ts +114 -0
  178. package/src/observability/index.ts +136 -0
  179. package/src/observability/interceptor.ts +85 -0
  180. package/src/observability/service.ts +303 -0
  181. package/src/observability/trace.ts +37 -0
  182. package/src/observability/types.ts +196 -0
  183. package/src/openapi/__tests__/decorators.test.ts +335 -0
  184. package/src/openapi/__tests__/document-builder.test.ts +285 -0
  185. package/src/openapi/__tests__/route-scanner.test.ts +334 -0
  186. package/src/openapi/__tests__/schema-generator.test.ts +275 -0
  187. package/src/openapi/decorators.ts +328 -0
  188. package/src/openapi/document-builder.ts +274 -0
  189. package/src/openapi/index.ts +112 -0
  190. package/src/openapi/metadata.ts +112 -0
  191. package/src/openapi/route-scanner.ts +289 -0
  192. package/src/openapi/schema-generator.ts +256 -0
  193. package/src/openapi/swagger-module.ts +166 -0
  194. package/src/openapi/types.ts +398 -0
  195. package/src/orm/index.ts +10 -0
  196. package/src/rpc/index.ts +3 -1
  197. package/src/schema/index.ts +9 -0
  198. package/src/security/index.ts +15 -6
  199. package/src/ssg/index.ts +9 -8
  200. package/src/telemetry/index.ts +76 -22
  201. package/src/template/index.ts +7 -0
  202. package/src/templates/engine.ts +224 -0
  203. package/src/templates/index.ts +9 -0
  204. package/src/templates/loader.ts +331 -0
  205. package/src/templates/renderers/markdown.ts +212 -0
  206. package/src/templates/renderers/simple.ts +269 -0
  207. package/src/templates/types.ts +154 -0
  208. package/src/testing/index.ts +100 -27
  209. package/src/types/optional-deps.d.ts +347 -187
  210. package/src/validation/index.ts +92 -2
  211. package/src/validation/schemas.ts +536 -0
  212. package/tests/integration/cli.test.ts +19 -19
  213. package/tests/integration/fullstack.test.ts +4 -4
  214. package/tests/unit/cli.test.ts +1 -1
  215. package/tests/unit/database.test.ts +2 -72
  216. package/tests/unit/env-validation.test.ts +166 -0
  217. package/tests/unit/events.test.ts +910 -0
  218. package/tests/unit/graphql.test.ts +991 -0
  219. package/tests/unit/i18n.test.ts +455 -0
  220. package/tests/unit/jobs.test.ts +493 -0
  221. package/tests/unit/notification.test.ts +988 -0
  222. package/tests/unit/observability.test.ts +453 -0
  223. package/tests/unit/orm/builder.test.ts +323 -0
  224. package/tests/unit/orm/casts.test.ts +179 -0
  225. package/tests/unit/orm/compiler.test.ts +220 -0
  226. package/tests/unit/orm/eager-loading.test.ts +285 -0
  227. package/tests/unit/orm/hooks.test.ts +191 -0
  228. package/tests/unit/orm/model.test.ts +373 -0
  229. package/tests/unit/orm/relationships.test.ts +303 -0
  230. package/tests/unit/orm/scopes.test.ts +74 -0
  231. package/tests/unit/templates-simple.test.ts +53 -0
  232. package/tests/unit/templates.test.ts +454 -0
  233. package/tests/unit/validation.test.ts +18 -24
  234. 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
+ };