@objectstack/observability 5.0.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.
package/dist/index.cjs ADDED
@@ -0,0 +1,457 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ ConsoleErrorReporter: () => ConsoleErrorReporter,
24
+ ConsoleLogger: () => ConsoleLogger,
25
+ ConsoleMetricsRegistry: () => ConsoleMetricsRegistry,
26
+ InMemoryErrorReporter: () => InMemoryErrorReporter,
27
+ InMemoryMetricsRegistry: () => InMemoryMetricsRegistry,
28
+ JsonLogger: () => JsonLogger,
29
+ LOG_LEVELS: () => LOG_LEVELS,
30
+ NoopErrorReporter: () => NoopErrorReporter,
31
+ NoopLogger: () => NoopLogger,
32
+ NoopMetricsRegistry: () => NoopMetricsRegistry,
33
+ OtlpHttpMetricsRegistry: () => OtlpHttpMetricsRegistry,
34
+ RUNTIME_METRICS: () => RUNTIME_METRICS,
35
+ SEMCONV: () => SEMCONV
36
+ });
37
+ module.exports = __toCommonJS(index_exports);
38
+
39
+ // src/semconv.ts
40
+ var SEMCONV = {
41
+ // ── HTTP — emitted by `@objectstack/runtime`'s instrumentRouteHandler ──
42
+ /** Counter, labels: `method`, `route`, `status`. */
43
+ httpRequestsTotal: "http_requests_total",
44
+ /** Histogram (ms), labels: `method`, `route`. */
45
+ httpRequestDurationMs: "http_request_duration_ms",
46
+ /**
47
+ * Counter, labels: `method`, `route`. Incremented when an
48
+ * in-flight handler throws after the response is sent.
49
+ */
50
+ httpRequestErrorsTotal: "http_request_errors_total",
51
+ // ── Storage — emitted by `@objectstack/service-storage` adapters ──
52
+ /** Counter, labels: `adapter` (`local`|`s3`|…), `op` (`get`|`put`|`delete`|`head`), `result` (`ok`|`error`). */
53
+ storageOperationsTotal: "storage_operations_total",
54
+ /** Histogram (ms), labels: `adapter`, `op`. */
55
+ storageOperationDurationMs: "storage_operation_duration_ms",
56
+ /** Counter, labels: `adapter`, `op`, `errorClass`. */
57
+ storageErrorsTotal: "storage_errors_total",
58
+ // ── Cache — emitted by `@objectstack/service-cache` adapters ──
59
+ /** Counter, labels: `adapter` (`memory`|`redis`), `result` (`hit`|`miss`). */
60
+ cacheLookupsTotal: "cache_lookups_total",
61
+ /** Counter, labels: `adapter`, `op` (`set`|`delete`|`clear`). */
62
+ cacheWritesTotal: "cache_writes_total",
63
+ /** Counter, labels: `adapter`, `op`, `errorClass`. */
64
+ cacheErrorsTotal: "cache_errors_total",
65
+ // ── Package / registry-reader — emitted by `@objectstack/service-package` ──
66
+ /** Counter, labels: `result` (`ok`|`miss`|`error`). */
67
+ registryLookupsTotal: "registry_lookups_total",
68
+ /** Histogram (ms). */
69
+ registryLookupDurationMs: "registry_lookup_duration_ms",
70
+ /** Counter, labels: `source` (`r2`|`http`|`local`), `result` (`hit`|`miss`|`error`). */
71
+ registrySourceFetchesTotal: "registry_source_fetches_total"
72
+ };
73
+ var RUNTIME_METRICS = {
74
+ httpRequestsTotal: SEMCONV.httpRequestsTotal,
75
+ httpRequestDurationMs: SEMCONV.httpRequestDurationMs,
76
+ httpRequestErrorsTotal: SEMCONV.httpRequestErrorsTotal
77
+ };
78
+
79
+ // src/metrics-exporters.ts
80
+ var NoopMetricsRegistry = class {
81
+ counter() {
82
+ }
83
+ histogram() {
84
+ }
85
+ gauge() {
86
+ }
87
+ };
88
+ var InMemoryMetricsRegistry = class {
89
+ constructor() {
90
+ this.samples = [];
91
+ }
92
+ counter(name, labels = {}, value = 1) {
93
+ this.samples.push({ name, kind: "counter", value, labels, at: Date.now() });
94
+ }
95
+ histogram(name, value, labels = {}) {
96
+ this.samples.push({ name, kind: "histogram", value, labels, at: Date.now() });
97
+ }
98
+ gauge(name, value, labels = {}) {
99
+ this.samples.push({ name, kind: "gauge", value, labels, at: Date.now() });
100
+ }
101
+ /** Sum of counter increments matching `name` and optionally a label subset. */
102
+ totalCounter(name, labelMatch = {}) {
103
+ return this.samples.filter((s) => s.kind === "counter" && s.name === name && matchesLabels(s.labels, labelMatch)).reduce((acc, s) => acc + s.value, 0);
104
+ }
105
+ /** Raw histogram observations matching `name` and optionally a label subset. */
106
+ histogramValues(name, labelMatch = {}) {
107
+ return this.samples.filter((s) => s.kind === "histogram" && s.name === name && matchesLabels(s.labels, labelMatch)).map((s) => s.value);
108
+ }
109
+ /** Last gauge value matching `name` and optionally a label subset, or undefined. */
110
+ lastGauge(name, labelMatch = {}) {
111
+ for (let i = this.samples.length - 1; i >= 0; i--) {
112
+ const s = this.samples[i];
113
+ if (s.kind === "gauge" && s.name === name && matchesLabels(s.labels, labelMatch)) {
114
+ return s.value;
115
+ }
116
+ }
117
+ return void 0;
118
+ }
119
+ /** Clear all recorded samples. */
120
+ reset() {
121
+ this.samples.length = 0;
122
+ }
123
+ };
124
+ function matchesLabels(actual, expected) {
125
+ for (const [k, v] of Object.entries(expected)) {
126
+ if (actual[k] !== v) return false;
127
+ }
128
+ return true;
129
+ }
130
+ var ConsoleMetricsRegistry = class {
131
+ constructor(opts = {}) {
132
+ this.opts = opts;
133
+ }
134
+ counter(name, labels = {}, value = 1) {
135
+ this.emit("counter", name, value, labels);
136
+ }
137
+ histogram(name, value, labels = {}) {
138
+ this.emit("histogram", name, value, labels);
139
+ }
140
+ gauge(name, value, labels = {}) {
141
+ this.emit("gauge", name, value, labels);
142
+ }
143
+ emit(kind, name, value, labels) {
144
+ try {
145
+ const sink = this.opts.sink ?? ((s) => {
146
+ console.log(s);
147
+ });
148
+ const prefix = this.opts.prefix ?? "[metric]";
149
+ const labelStr = Object.entries(labels).map(([k, v]) => `${k}=${JSON.stringify(v)}`).join(" ");
150
+ sink(`${prefix} ${kind} ${name} ${value}${labelStr ? " " + labelStr : ""}`);
151
+ } catch {
152
+ }
153
+ }
154
+ };
155
+ var OtlpHttpMetricsRegistry = class {
156
+ constructor(options) {
157
+ this.buffer = [];
158
+ this.endpoint = normaliseEndpoint(options.endpoint);
159
+ this.headers = options.headers ?? {};
160
+ this.resource = options.resource ?? {};
161
+ this.maxBufferSize = options.maxBufferSize ?? 1024;
162
+ this.fetchImpl = options.fetch ?? (typeof fetch !== "undefined" ? fetch.bind(globalThis) : noFetch);
163
+ this.onError = options.onError ?? (() => {
164
+ });
165
+ }
166
+ counter(name, labels = {}, value = 1) {
167
+ this.record({ name, kind: "counter", value, labels, at: Date.now() });
168
+ }
169
+ histogram(name, value, labels = {}) {
170
+ this.record({ name, kind: "histogram", value, labels, at: Date.now() });
171
+ }
172
+ gauge(name, value, labels = {}) {
173
+ this.record({ name, kind: "gauge", value, labels, at: Date.now() });
174
+ }
175
+ record(sample) {
176
+ this.buffer.push(sample);
177
+ if (this.buffer.length >= this.maxBufferSize) {
178
+ void this.flush().catch(this.onError);
179
+ }
180
+ }
181
+ /** Snapshot the current buffer (for tests). */
182
+ peek() {
183
+ return this.buffer.slice();
184
+ }
185
+ /**
186
+ * Send the buffered samples to the OTLP endpoint and clear the
187
+ * buffer. Safe to call concurrently — each invocation takes a
188
+ * snapshot before clearing.
189
+ */
190
+ async flush() {
191
+ if (this.buffer.length === 0) return;
192
+ const samples = this.buffer;
193
+ this.buffer = [];
194
+ try {
195
+ const body = JSON.stringify(serialiseToOtlp(samples, this.resource));
196
+ const res = await this.fetchImpl(this.endpoint, {
197
+ method: "POST",
198
+ headers: { "content-type": "application/json", ...this.headers },
199
+ body
200
+ });
201
+ if (!res.ok) {
202
+ this.onError(new Error(`OTLP export returned HTTP ${res.status}`));
203
+ }
204
+ } catch (err) {
205
+ this.onError(err);
206
+ }
207
+ }
208
+ };
209
+ function normaliseEndpoint(raw) {
210
+ const trimmed = raw.replace(/\/+$/, "");
211
+ if (/\/v1\/metrics$/.test(trimmed)) return trimmed;
212
+ return trimmed + "/v1/metrics";
213
+ }
214
+ function noFetch() {
215
+ throw new Error("OtlpHttpMetricsRegistry: no global fetch available; pass options.fetch");
216
+ }
217
+ function serialiseToOtlp(samples, resource) {
218
+ const byName = /* @__PURE__ */ new Map();
219
+ for (const s of samples) {
220
+ const existing = byName.get(s.name);
221
+ if (existing) existing.points.push(s);
222
+ else byName.set(s.name, { kind: s.kind, points: [s] });
223
+ }
224
+ const metrics = Array.from(byName.entries()).map(([name, { kind, points }]) => {
225
+ if (kind === "counter") {
226
+ return {
227
+ name,
228
+ sum: {
229
+ dataPoints: points.map((p) => toNumberPoint(p)),
230
+ aggregationTemporality: 2,
231
+ isMonotonic: true
232
+ }
233
+ };
234
+ }
235
+ if (kind === "gauge") {
236
+ return { name, gauge: { dataPoints: points.map((p) => toNumberPoint(p)) } };
237
+ }
238
+ return {
239
+ name,
240
+ histogram: {
241
+ aggregationTemporality: 2,
242
+ dataPoints: points.map((p) => ({
243
+ attributes: toAttributes(p.labels),
244
+ timeUnixNano: String(p.at) + "000000",
245
+ startTimeUnixNano: String(p.at) + "000000",
246
+ count: "1",
247
+ sum: p.value,
248
+ bucketCounts: ["0", "1"],
249
+ explicitBounds: [p.value]
250
+ }))
251
+ }
252
+ };
253
+ });
254
+ return {
255
+ resourceMetrics: [{
256
+ resource: { attributes: toAttributes(resource) },
257
+ scopeMetrics: [{
258
+ scope: { name: "@objectstack/observability", version: "0.1.0" },
259
+ metrics
260
+ }]
261
+ }]
262
+ };
263
+ }
264
+ function toNumberPoint(p) {
265
+ return {
266
+ attributes: toAttributes(p.labels),
267
+ timeUnixNano: String(p.at) + "000000",
268
+ startTimeUnixNano: String(p.at) + "000000",
269
+ asDouble: p.value
270
+ };
271
+ }
272
+ function toAttributes(labels) {
273
+ return Object.entries(labels).map(([key, value]) => ({
274
+ key,
275
+ value: { stringValue: value }
276
+ }));
277
+ }
278
+
279
+ // src/error-exporters.ts
280
+ var NoopErrorReporter = class {
281
+ captureException() {
282
+ }
283
+ };
284
+ var InMemoryErrorReporter = class {
285
+ constructor() {
286
+ this.captured = [];
287
+ }
288
+ captureException(error, context = {}) {
289
+ this.captured.push({ error, context, at: Date.now() });
290
+ }
291
+ reset() {
292
+ this.captured.length = 0;
293
+ }
294
+ };
295
+ var ConsoleErrorReporter = class {
296
+ constructor(opts = {}) {
297
+ this.opts = opts;
298
+ }
299
+ captureException(error, context = {}) {
300
+ try {
301
+ const sink = this.opts.sink ?? ((s) => {
302
+ console.error(s);
303
+ });
304
+ const record = {
305
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
306
+ level: "error",
307
+ msg: error instanceof Error ? error.message : String(error),
308
+ context
309
+ };
310
+ if (error instanceof Error && error.stack) {
311
+ record.stack = error.stack;
312
+ }
313
+ sink(JSON.stringify(record));
314
+ } catch {
315
+ }
316
+ }
317
+ };
318
+
319
+ // src/loggers.ts
320
+ var LOG_LEVELS = ["debug", "info", "warn", "error", "fatal"];
321
+ var LEVEL_PRIORITY = {
322
+ debug: 10,
323
+ info: 20,
324
+ warn: 30,
325
+ error: 40,
326
+ fatal: 50
327
+ };
328
+ var NoopLogger = class {
329
+ debug() {
330
+ }
331
+ info() {
332
+ }
333
+ warn() {
334
+ }
335
+ error() {
336
+ }
337
+ fatal() {
338
+ }
339
+ child() {
340
+ return this;
341
+ }
342
+ };
343
+ var ConsoleLogger = class _ConsoleLogger {
344
+ constructor(opts = {}) {
345
+ this.opts = opts;
346
+ }
347
+ get threshold() {
348
+ return LEVEL_PRIORITY[this.opts.level ?? "info"];
349
+ }
350
+ get sink() {
351
+ return this.opts.sink ?? { log: (s) => console.log(s), error: (s) => console.error(s) };
352
+ }
353
+ get context() {
354
+ return this.opts.context ?? {};
355
+ }
356
+ debug(message, meta) {
357
+ this.emit("debug", message, meta);
358
+ }
359
+ info(message, meta) {
360
+ this.emit("info", message, meta);
361
+ }
362
+ warn(message, meta) {
363
+ this.emit("warn", message, meta);
364
+ }
365
+ error(message, error, meta) {
366
+ this.emit("error", message, { ...meta ?? {}, ...error ? { error: error.message, stack: error.stack } : {} });
367
+ }
368
+ fatal(message, error, meta) {
369
+ this.emit("fatal", message, { ...meta ?? {}, ...error ? { error: error.message, stack: error.stack } : {} });
370
+ }
371
+ child(context) {
372
+ return new _ConsoleLogger({ ...this.opts, context: { ...this.context, ...context } });
373
+ }
374
+ emit(level, msg, meta) {
375
+ if (LEVEL_PRIORITY[level] < this.threshold) return;
376
+ try {
377
+ const merged = { ...this.context, ...meta ?? {} };
378
+ const tail = Object.keys(merged).length ? " " + JSON.stringify(merged) : "";
379
+ const line = `[${level}] ${msg}${tail}`;
380
+ if (level === "error" || level === "fatal") this.sink.error(line);
381
+ else this.sink.log(line);
382
+ } catch {
383
+ }
384
+ }
385
+ };
386
+ var JsonLogger = class _JsonLogger {
387
+ constructor(opts = {}) {
388
+ this.opts = opts;
389
+ }
390
+ get threshold() {
391
+ return LEVEL_PRIORITY[this.opts.level ?? "info"];
392
+ }
393
+ get sink() {
394
+ return this.opts.sink ?? { log: (s) => console.log(s), error: (s) => console.error(s) };
395
+ }
396
+ get context() {
397
+ return this.opts.context ?? {};
398
+ }
399
+ get base() {
400
+ return this.opts.base ?? {};
401
+ }
402
+ get now() {
403
+ return this.opts.now ?? (() => /* @__PURE__ */ new Date());
404
+ }
405
+ debug(message, meta) {
406
+ this.emit("debug", message, meta);
407
+ }
408
+ info(message, meta) {
409
+ this.emit("info", message, meta);
410
+ }
411
+ warn(message, meta) {
412
+ this.emit("warn", message, meta);
413
+ }
414
+ error(message, error, meta) {
415
+ this.emit("error", message, { ...meta ?? {}, ...error ? { error: error.message, stack: error.stack } : {} });
416
+ }
417
+ fatal(message, error, meta) {
418
+ this.emit("fatal", message, { ...meta ?? {}, ...error ? { error: error.message, stack: error.stack } : {} });
419
+ }
420
+ child(context) {
421
+ return new _JsonLogger({ ...this.opts, context: { ...this.context, ...context } });
422
+ }
423
+ emit(level, msg, meta) {
424
+ if (LEVEL_PRIORITY[level] < this.threshold) return;
425
+ try {
426
+ const record = {
427
+ ts: this.now().toISOString(),
428
+ level,
429
+ msg,
430
+ ...this.base,
431
+ ...this.context,
432
+ ...meta ?? {}
433
+ };
434
+ const line = JSON.stringify(record);
435
+ if (level === "error" || level === "fatal") this.sink.error(line);
436
+ else this.sink.log(line);
437
+ } catch {
438
+ }
439
+ }
440
+ };
441
+ // Annotate the CommonJS export names for ESM import in node:
442
+ 0 && (module.exports = {
443
+ ConsoleErrorReporter,
444
+ ConsoleLogger,
445
+ ConsoleMetricsRegistry,
446
+ InMemoryErrorReporter,
447
+ InMemoryMetricsRegistry,
448
+ JsonLogger,
449
+ LOG_LEVELS,
450
+ NoopErrorReporter,
451
+ NoopLogger,
452
+ NoopMetricsRegistry,
453
+ OtlpHttpMetricsRegistry,
454
+ RUNTIME_METRICS,
455
+ SEMCONV
456
+ });
457
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/semconv.ts","../src/metrics-exporters.ts","../src/error-exporters.ts","../src/loggers.ts"],"sourcesContent":["// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * `@objectstack/observability` — vendor-neutral contracts and exporters\n * for ObjectStack metrics, errors, and logs.\n *\n * @see {@link MetricsRegistry} {@link ErrorReporter} {@link Logger}\n */\n\n// Contracts\nexport type { MetricsRegistry, MetricSample, ErrorReporter, CapturedError, Logger } from './contracts.js';\n\n// Semantic conventions\nexport { SEMCONV, RUNTIME_METRICS } from './semconv.js';\n\n// Metric exporters\nexport {\n NoopMetricsRegistry,\n InMemoryMetricsRegistry,\n ConsoleMetricsRegistry,\n OtlpHttpMetricsRegistry,\n type OtlpHttpExporterOptions,\n} from './metrics-exporters.js';\n\n// Error reporters\nexport {\n NoopErrorReporter,\n InMemoryErrorReporter,\n ConsoleErrorReporter,\n} from './error-exporters.js';\n\n// Loggers\nexport {\n NoopLogger,\n ConsoleLogger,\n JsonLogger,\n LOG_LEVELS,\n type LogLevel,\n} from './loggers.js';\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Semantic conventions — canonical metric names emitted by the\n * framework. Listed here so hosts can wire alerts/dashboards against\n * a stable namespace and so call sites don't sprinkle string\n * literals through the code base.\n *\n * Naming follows Prometheus conventions:\n *\n * - snake_case identifiers.\n * - `_total` suffix for monotonic counters.\n * - `_ms`, `_seconds`, `_bytes` suffixes for histograms / gauges\n * with units.\n *\n * Groups roughly mirror the framework subsystems that emit them.\n * Cloud-specific metrics (DO restarts, Workers Analytics Engine\n * writes, …) do NOT belong here — they are deployment-specific and\n * stay in the deployment repo.\n */\nexport const SEMCONV = {\n // ── HTTP — emitted by `@objectstack/runtime`'s instrumentRouteHandler ──\n /** Counter, labels: `method`, `route`, `status`. */\n httpRequestsTotal: 'http_requests_total',\n /** Histogram (ms), labels: `method`, `route`. */\n httpRequestDurationMs: 'http_request_duration_ms',\n /**\n * Counter, labels: `method`, `route`. Incremented when an\n * in-flight handler throws after the response is sent.\n */\n httpRequestErrorsTotal: 'http_request_errors_total',\n\n // ── Storage — emitted by `@objectstack/service-storage` adapters ──\n /** Counter, labels: `adapter` (`local`|`s3`|…), `op` (`get`|`put`|`delete`|`head`), `result` (`ok`|`error`). */\n storageOperationsTotal: 'storage_operations_total',\n /** Histogram (ms), labels: `adapter`, `op`. */\n storageOperationDurationMs: 'storage_operation_duration_ms',\n /** Counter, labels: `adapter`, `op`, `errorClass`. */\n storageErrorsTotal: 'storage_errors_total',\n\n // ── Cache — emitted by `@objectstack/service-cache` adapters ──\n /** Counter, labels: `adapter` (`memory`|`redis`), `result` (`hit`|`miss`). */\n cacheLookupsTotal: 'cache_lookups_total',\n /** Counter, labels: `adapter`, `op` (`set`|`delete`|`clear`). */\n cacheWritesTotal: 'cache_writes_total',\n /** Counter, labels: `adapter`, `op`, `errorClass`. */\n cacheErrorsTotal: 'cache_errors_total',\n\n // ── Package / registry-reader — emitted by `@objectstack/service-package` ──\n /** Counter, labels: `result` (`ok`|`miss`|`error`). */\n registryLookupsTotal: 'registry_lookups_total',\n /** Histogram (ms). */\n registryLookupDurationMs: 'registry_lookup_duration_ms',\n /** Counter, labels: `source` (`r2`|`http`|`local`), `result` (`hit`|`miss`|`error`). */\n registrySourceFetchesTotal: 'registry_source_fetches_total',\n} as const;\n\n/**\n * Backwards-compat alias. `RUNTIME_METRICS` was the original (HTTP-only)\n * constant name shipped from `@objectstack/runtime`; we keep it here so\n * existing code reading `RUNTIME_METRICS.httpRequestsTotal` continues\n * to work after the constants moved into this package.\n */\nexport const RUNTIME_METRICS = {\n httpRequestsTotal: SEMCONV.httpRequestsTotal,\n httpRequestDurationMs: SEMCONV.httpRequestDurationMs,\n httpRequestErrorsTotal: SEMCONV.httpRequestErrorsTotal,\n} as const;\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { MetricsRegistry, MetricSample } from './contracts.js';\n\n// ─── Noop ─────────────────────────────────────────────────────────────\n\n/**\n * No-op metrics registry — the default. Discards every observation.\n * Production deployments should swap this for a real registry; tests\n * can use {@link InMemoryMetricsRegistry} to assert emissions.\n */\nexport class NoopMetricsRegistry implements MetricsRegistry {\n counter(): void { }\n histogram(): void { }\n gauge(): void { }\n}\n\n// ─── In-memory (tests + dev inspection) ───────────────────────────────\n\n/**\n * In-memory registry used for tests and local inspection. Stores\n * every observation in insertion order; query via the helpers below\n * or read {@link samples} directly.\n *\n * Not intended for production — unbounded growth.\n */\nexport class InMemoryMetricsRegistry implements MetricsRegistry {\n readonly samples: MetricSample[] = [];\n\n counter(name: string, labels: Record<string, string> = {}, value: number = 1): void {\n this.samples.push({ name, kind: 'counter', value, labels, at: Date.now() });\n }\n\n histogram(name: string, value: number, labels: Record<string, string> = {}): void {\n this.samples.push({ name, kind: 'histogram', value, labels, at: Date.now() });\n }\n\n gauge(name: string, value: number, labels: Record<string, string> = {}): void {\n this.samples.push({ name, kind: 'gauge', value, labels, at: Date.now() });\n }\n\n /** Sum of counter increments matching `name` and optionally a label subset. */\n totalCounter(name: string, labelMatch: Record<string, string> = {}): number {\n return this.samples\n .filter(s => s.kind === 'counter' && s.name === name && matchesLabels(s.labels, labelMatch))\n .reduce((acc, s) => acc + s.value, 0);\n }\n\n /** Raw histogram observations matching `name` and optionally a label subset. */\n histogramValues(name: string, labelMatch: Record<string, string> = {}): number[] {\n return this.samples\n .filter(s => s.kind === 'histogram' && s.name === name && matchesLabels(s.labels, labelMatch))\n .map(s => s.value);\n }\n\n /** Last gauge value matching `name` and optionally a label subset, or undefined. */\n lastGauge(name: string, labelMatch: Record<string, string> = {}): number | undefined {\n for (let i = this.samples.length - 1; i >= 0; i--) {\n const s = this.samples[i];\n if (s.kind === 'gauge' && s.name === name && matchesLabels(s.labels, labelMatch)) {\n return s.value;\n }\n }\n return undefined;\n }\n\n /** Clear all recorded samples. */\n reset(): void {\n this.samples.length = 0;\n }\n}\n\nfunction matchesLabels(actual: Record<string, string>, expected: Record<string, string>): boolean {\n for (const [k, v] of Object.entries(expected)) {\n if (actual[k] !== v) return false;\n }\n return true;\n}\n\n// ─── Console (development) ────────────────────────────────────────────\n\n/**\n * Console metrics registry — prints one line per observation. Useful\n * during local development to confirm that instrumentation is firing.\n *\n * Not intended for production: writing every observation to stdout\n * defeats Prometheus / OTLP pipelines and dominates request latency.\n */\nexport class ConsoleMetricsRegistry implements MetricsRegistry {\n constructor(private readonly opts: { sink?: (line: string) => void; prefix?: string } = {}) { }\n\n counter(name: string, labels: Record<string, string> = {}, value: number = 1): void {\n this.emit('counter', name, value, labels);\n }\n histogram(name: string, value: number, labels: Record<string, string> = {}): void {\n this.emit('histogram', name, value, labels);\n }\n gauge(name: string, value: number, labels: Record<string, string> = {}): void {\n this.emit('gauge', name, value, labels);\n }\n\n private emit(kind: string, name: string, value: number, labels: Record<string, string>): void {\n try {\n const sink = this.opts.sink ?? ((s) => { console.log(s); });\n const prefix = this.opts.prefix ?? '[metric]';\n const labelStr = Object.entries(labels)\n .map(([k, v]) => `${k}=${JSON.stringify(v)}`)\n .join(' ');\n sink(`${prefix} ${kind} ${name} ${value}${labelStr ? ' ' + labelStr : ''}`);\n } catch {\n // Per contract: never throw from a metric call site.\n }\n }\n}\n\n// ─── OTLP / HTTP (Prometheus via OTel Collector, Grafana Cloud, …) ────\n\n/**\n * Configuration for {@link OtlpHttpMetricsRegistry}.\n */\nexport interface OtlpHttpExporterOptions {\n /**\n * OTLP/HTTP metrics endpoint, e.g. `http://otel-collector:4318/v1/metrics`.\n * The path is appended automatically if missing.\n */\n endpoint: string;\n\n /** Optional headers (Authorization, x-tenant, …). */\n headers?: Record<string, string>;\n\n /**\n * Resource attributes — `service.name`, `service.namespace`,\n * `deployment.environment`, etc. Merged into the OTLP `resource`\n * block on every export.\n */\n resource?: Record<string, string>;\n\n /**\n * Custom fetch implementation. Defaults to the global `fetch`.\n * Allows Workers / undici / node-fetch substitution and test\n * doubles.\n */\n fetch?: typeof fetch;\n\n /**\n * Called when an export attempt fails. Default: silently swallow\n * (per the contract that metric emission must not throw / log\n * loudly on the hot path).\n */\n onError?: (error: unknown) => void;\n\n /**\n * Maximum number of samples buffered before {@link OtlpHttpMetricsRegistry.flush}\n * is called automatically. Defaults to 1024.\n */\n maxBufferSize?: number;\n}\n\n/**\n * OTLP/HTTP metrics exporter.\n *\n * Buffers samples in memory and serialises them to the OpenTelemetry\n * Protocol JSON encoding when {@link flush} is called (manually or\n * automatically once the buffer hits the configured size).\n *\n * Intentionally does **not** start an interval timer in the constructor:\n * (a) it makes the exporter usable on Cloudflare Workers where\n * `setInterval` is restricted, and (b) it keeps unit tests deterministic.\n * Long-running hosts should call `flush()` on a schedule\n * (e.g. `setInterval(() => reg.flush(), 10_000)` on Node, or\n * `ctx.waitUntil(reg.flush())` from a Workers fetch handler).\n *\n * Only counters, histograms, and gauges are emitted — no support for\n * exemplars or aggregation temporality switches (the Collector handles\n * those on the upstream side).\n */\nexport class OtlpHttpMetricsRegistry implements MetricsRegistry {\n private buffer: MetricSample[] = [];\n private readonly endpoint: string;\n private readonly headers: Record<string, string>;\n private readonly resource: Record<string, string>;\n private readonly maxBufferSize: number;\n private readonly fetchImpl: typeof fetch;\n private readonly onError: (error: unknown) => void;\n\n constructor(options: OtlpHttpExporterOptions) {\n this.endpoint = normaliseEndpoint(options.endpoint);\n this.headers = options.headers ?? {};\n this.resource = options.resource ?? {};\n this.maxBufferSize = options.maxBufferSize ?? 1024;\n this.fetchImpl = options.fetch ?? (typeof fetch !== 'undefined' ? fetch.bind(globalThis) : noFetch);\n this.onError = options.onError ?? (() => { });\n }\n\n counter(name: string, labels: Record<string, string> = {}, value: number = 1): void {\n this.record({ name, kind: 'counter', value, labels, at: Date.now() });\n }\n histogram(name: string, value: number, labels: Record<string, string> = {}): void {\n this.record({ name, kind: 'histogram', value, labels, at: Date.now() });\n }\n gauge(name: string, value: number, labels: Record<string, string> = {}): void {\n this.record({ name, kind: 'gauge', value, labels, at: Date.now() });\n }\n\n private record(sample: MetricSample): void {\n this.buffer.push(sample);\n if (this.buffer.length >= this.maxBufferSize) {\n // Fire-and-forget: do not block the call site.\n void this.flush().catch(this.onError);\n }\n }\n\n /** Snapshot the current buffer (for tests). */\n peek(): readonly MetricSample[] {\n return this.buffer.slice();\n }\n\n /**\n * Send the buffered samples to the OTLP endpoint and clear the\n * buffer. Safe to call concurrently — each invocation takes a\n * snapshot before clearing.\n */\n async flush(): Promise<void> {\n if (this.buffer.length === 0) return;\n const samples = this.buffer;\n this.buffer = [];\n try {\n const body = JSON.stringify(serialiseToOtlp(samples, this.resource));\n const res = await this.fetchImpl(this.endpoint, {\n method: 'POST',\n headers: { 'content-type': 'application/json', ...this.headers },\n body,\n });\n if (!res.ok) {\n this.onError(new Error(`OTLP export returned HTTP ${res.status}`));\n }\n } catch (err) {\n this.onError(err);\n }\n }\n}\n\nfunction normaliseEndpoint(raw: string): string {\n const trimmed = raw.replace(/\\/+$/, '');\n if (/\\/v1\\/metrics$/.test(trimmed)) return trimmed;\n return trimmed + '/v1/metrics';\n}\n\nfunction noFetch(): never {\n throw new Error('OtlpHttpMetricsRegistry: no global fetch available; pass options.fetch');\n}\n\n/**\n * Minimal OTLP/JSON metrics serialiser.\n *\n * Implements the subset of <https://opentelemetry.io/docs/specs/otlp/>\n * that we actually emit — sums for counters, gauges for gauges,\n * histograms encoded as bucketless \"summary\"-style histograms with\n * explicit per-sample data points. The Collector accepts this shape\n * and applies its own bucketing.\n *\n * Aggregation temporality is set to DELTA (2) because every flush\n * sends only the samples accumulated since the last flush.\n */\nfunction serialiseToOtlp(samples: MetricSample[], resource: Record<string, string>): unknown {\n const byName = new Map<string, { kind: MetricSample['kind']; points: MetricSample[] }>();\n for (const s of samples) {\n const existing = byName.get(s.name);\n if (existing) existing.points.push(s);\n else byName.set(s.name, { kind: s.kind, points: [s] });\n }\n\n const metrics = Array.from(byName.entries()).map(([name, { kind, points }]) => {\n if (kind === 'counter') {\n return {\n name,\n sum: {\n dataPoints: points.map(p => toNumberPoint(p)),\n aggregationTemporality: 2,\n isMonotonic: true,\n },\n };\n }\n if (kind === 'gauge') {\n return { name, gauge: { dataPoints: points.map(p => toNumberPoint(p)) } };\n }\n // histogram: emit a histogram with a single bucket boundary so the\n // Collector treats it as a recorded distribution.\n return {\n name,\n histogram: {\n aggregationTemporality: 2,\n dataPoints: points.map(p => ({\n attributes: toAttributes(p.labels),\n timeUnixNano: String(p.at) + '000000',\n startTimeUnixNano: String(p.at) + '000000',\n count: '1',\n sum: p.value,\n bucketCounts: ['0', '1'],\n explicitBounds: [p.value],\n })),\n },\n };\n });\n\n return {\n resourceMetrics: [{\n resource: { attributes: toAttributes(resource) },\n scopeMetrics: [{\n scope: { name: '@objectstack/observability', version: '0.1.0' },\n metrics,\n }],\n }],\n };\n}\n\nfunction toNumberPoint(p: MetricSample): unknown {\n return {\n attributes: toAttributes(p.labels),\n timeUnixNano: String(p.at) + '000000',\n startTimeUnixNano: String(p.at) + '000000',\n asDouble: p.value,\n };\n}\n\nfunction toAttributes(labels: Record<string, string>): unknown[] {\n return Object.entries(labels).map(([key, value]) => ({\n key,\n value: { stringValue: value },\n }));\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { ErrorReporter, CapturedError } from './contracts.js';\n\n/** No-op reporter — the default. */\nexport class NoopErrorReporter implements ErrorReporter {\n captureException(): void { }\n}\n\n/** In-memory reporter for tests. */\nexport class InMemoryErrorReporter implements ErrorReporter {\n readonly captured: CapturedError[] = [];\n\n captureException(error: unknown, context: Record<string, unknown> = {}): void {\n this.captured.push({ error, context, at: Date.now() });\n }\n\n reset(): void {\n this.captured.length = 0;\n }\n}\n\n/**\n * Console error reporter — writes a structured JSON line to stderr per\n * captured exception. Convenient default for local development and for\n * any deployment that ships stderr to a log aggregator (e.g. Loki,\n * fluent-bit) but does not have a dedicated APM.\n *\n * Stack traces are included when the captured value is an `Error`.\n */\nexport class ConsoleErrorReporter implements ErrorReporter {\n constructor(private readonly opts: { sink?: (line: string) => void } = {}) { }\n\n captureException(error: unknown, context: Record<string, unknown> = {}): void {\n try {\n const sink = this.opts.sink ?? ((s) => { console.error(s); });\n const record: Record<string, unknown> = {\n ts: new Date().toISOString(),\n level: 'error',\n msg: error instanceof Error ? error.message : String(error),\n context,\n };\n if (error instanceof Error && error.stack) {\n record.stack = error.stack;\n }\n sink(JSON.stringify(record));\n } catch {\n // Per contract: error reporting must never throw.\n }\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { Logger } from './contracts.js';\n\n/** Recognised log levels in increasing severity order. */\nexport const LOG_LEVELS = ['debug', 'info', 'warn', 'error', 'fatal'] as const;\nexport type LogLevel = (typeof LOG_LEVELS)[number];\n\nconst LEVEL_PRIORITY: Record<LogLevel, number> = {\n debug: 10,\n info: 20,\n warn: 30,\n error: 40,\n fatal: 50,\n};\n\n/** No-op logger — discards every message. */\nexport class NoopLogger implements Logger {\n debug(): void { }\n info(): void { }\n warn(): void { }\n error(): void { }\n fatal(): void { }\n child(): Logger { return this; }\n}\n\n/**\n * Console logger — pretty-printed messages for local development.\n *\n * Not suitable for production where structured JSON is required;\n * use {@link JsonLogger} there instead.\n */\nexport class ConsoleLogger implements Logger {\n constructor(\n private readonly opts: {\n level?: LogLevel;\n context?: Record<string, unknown>;\n sink?: { log: (s: string) => void; error: (s: string) => void };\n } = {},\n ) { }\n\n private get threshold(): number {\n return LEVEL_PRIORITY[this.opts.level ?? 'info'];\n }\n private get sink() {\n return this.opts.sink ?? { log: (s: string) => console.log(s), error: (s: string) => console.error(s) };\n }\n private get context(): Record<string, unknown> {\n return this.opts.context ?? {};\n }\n\n debug(message: string, meta?: Record<string, unknown>): void { this.emit('debug', message, meta); }\n info(message: string, meta?: Record<string, unknown>): void { this.emit('info', message, meta); }\n warn(message: string, meta?: Record<string, unknown>): void { this.emit('warn', message, meta); }\n error(message: string, error?: Error, meta?: Record<string, unknown>): void {\n this.emit('error', message, { ...(meta ?? {}), ...(error ? { error: error.message, stack: error.stack } : {}) });\n }\n fatal(message: string, error?: Error, meta?: Record<string, unknown>): void {\n this.emit('fatal', message, { ...(meta ?? {}), ...(error ? { error: error.message, stack: error.stack } : {}) });\n }\n child(context: Record<string, unknown>): Logger {\n return new ConsoleLogger({ ...this.opts, context: { ...this.context, ...context } });\n }\n\n private emit(level: LogLevel, msg: string, meta?: Record<string, unknown>): void {\n if (LEVEL_PRIORITY[level] < this.threshold) return;\n try {\n const merged = { ...this.context, ...(meta ?? {}) };\n const tail = Object.keys(merged).length ? ' ' + JSON.stringify(merged) : '';\n const line = `[${level}] ${msg}${tail}`;\n if (level === 'error' || level === 'fatal') this.sink.error(line);\n else this.sink.log(line);\n } catch {\n // Log emission must never throw.\n }\n }\n}\n\n/**\n * JSON logger — one JSON object per line on stdout (errors on stderr).\n *\n * Matches the shape that Loki / fluent-bit / Cloudflare Logpush ingest\n * by default. Every record contains `ts`, `level`, `msg`, and any\n * accumulated child-context plus per-call `meta`.\n *\n * Use this in production. Use {@link ConsoleLogger} during development\n * for human-friendly output.\n */\nexport class JsonLogger implements Logger {\n constructor(\n private readonly opts: {\n level?: LogLevel;\n context?: Record<string, unknown>;\n sink?: { log: (s: string) => void; error: (s: string) => void };\n /** Optional fields injected into every record (`service`, `env`, …). */\n base?: Record<string, unknown>;\n /** Wall clock for tests. */\n now?: () => Date;\n } = {},\n ) { }\n\n private get threshold(): number { return LEVEL_PRIORITY[this.opts.level ?? 'info']; }\n private get sink() {\n return this.opts.sink ?? { log: (s: string) => console.log(s), error: (s: string) => console.error(s) };\n }\n private get context(): Record<string, unknown> { return this.opts.context ?? {}; }\n private get base(): Record<string, unknown> { return this.opts.base ?? {}; }\n private get now(): () => Date { return this.opts.now ?? (() => new Date()); }\n\n debug(message: string, meta?: Record<string, unknown>): void { this.emit('debug', message, meta); }\n info(message: string, meta?: Record<string, unknown>): void { this.emit('info', message, meta); }\n warn(message: string, meta?: Record<string, unknown>): void { this.emit('warn', message, meta); }\n error(message: string, error?: Error, meta?: Record<string, unknown>): void {\n this.emit('error', message, { ...(meta ?? {}), ...(error ? { error: error.message, stack: error.stack } : {}) });\n }\n fatal(message: string, error?: Error, meta?: Record<string, unknown>): void {\n this.emit('fatal', message, { ...(meta ?? {}), ...(error ? { error: error.message, stack: error.stack } : {}) });\n }\n child(context: Record<string, unknown>): Logger {\n return new JsonLogger({ ...this.opts, context: { ...this.context, ...context } });\n }\n\n private emit(level: LogLevel, msg: string, meta?: Record<string, unknown>): void {\n if (LEVEL_PRIORITY[level] < this.threshold) return;\n try {\n const record = {\n ts: this.now().toISOString(),\n level,\n msg,\n ...this.base,\n ...this.context,\n ...(meta ?? {}),\n };\n const line = JSON.stringify(record);\n if (level === 'error' || level === 'fatal') this.sink.error(line);\n else this.sink.log(line);\n } catch {\n // Log emission must never throw.\n }\n }\n}\n"],"mappings":"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","names":[]}