@oas-tools/oas-telemetry 0.7.1 → 0.8.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (194) hide show
  1. package/.env.example +17 -3
  2. package/README.md +1 -2
  3. package/dist/cjs/config/bootConfig.cjs +16 -14
  4. package/dist/cjs/config/config.cjs +120 -125
  5. package/dist/cjs/config/config.types.cjs +1 -4
  6. package/dist/cjs/docs/openapi.yaml +158 -4
  7. package/dist/cjs/index.cjs +27 -30
  8. package/dist/cjs/routesManager.cjs +62 -70
  9. package/dist/cjs/telemetry/custom-implementations/exporters/DiskLogExporter.cjs +121 -0
  10. package/dist/cjs/telemetry/custom-implementations/exporters/DiskMetricExporter.cjs +101 -0
  11. package/dist/cjs/telemetry/custom-implementations/exporters/DiskTraceExporter.cjs +103 -0
  12. package/dist/cjs/telemetry/custom-implementations/exporters/InMemoryDbLogExporter.cjs +194 -190
  13. package/dist/cjs/telemetry/custom-implementations/exporters/InMemoryDbMetricExporter.cjs +147 -99
  14. package/dist/cjs/telemetry/custom-implementations/exporters/InMemoryDbSpanExporter.cjs +143 -116
  15. package/dist/cjs/telemetry/custom-implementations/exporters/MultiMetricExporter.cjs +57 -0
  16. package/dist/cjs/telemetry/custom-implementations/instrumentations/logsInstrumentation.cjs +92 -0
  17. package/dist/cjs/telemetry/custom-implementations/metrics/tsdb/Chunk.cjs +159 -0
  18. package/dist/cjs/telemetry/custom-implementations/metrics/tsdb/Series.cjs +168 -0
  19. package/dist/cjs/telemetry/custom-implementations/metrics/tsdb/SeriesRegistry.cjs +392 -0
  20. package/dist/cjs/telemetry/custom-implementations/metrics/tsdb/types.cjs +2 -0
  21. package/dist/cjs/telemetry/custom-implementations/metrics/tsdb/utils.cjs +77 -0
  22. package/dist/cjs/telemetry/custom-implementations/processors/dynamicMultiLogProcessor.cjs +65 -63
  23. package/dist/cjs/telemetry/custom-implementations/processors/dynamicMultiSpanProcessor.cjs +63 -62
  24. package/dist/cjs/telemetry/custom-implementations/utils/circular.cjs +47 -47
  25. package/dist/cjs/telemetry/custom-implementations/wrappers.cjs +209 -138
  26. package/dist/cjs/telemetry/initializeTelemetry.cjs +35 -91
  27. package/dist/cjs/telemetry/persistence/DiskImporter.cjs +85 -0
  28. package/dist/cjs/telemetry/persistence/DiskUtils.cjs +61 -0
  29. package/dist/cjs/telemetry/persistence/DiskWriter.cjs +66 -0
  30. package/dist/cjs/telemetry/telemetryConfigurator.cjs +139 -72
  31. package/dist/cjs/telemetry/telemetryRegistry.cjs +45 -31
  32. package/dist/cjs/tlm-ai/agent.cjs +49 -64
  33. package/dist/cjs/tlm-ai/aiController.cjs +54 -76
  34. package/dist/cjs/tlm-ai/aiRoutes.cjs +17 -20
  35. package/dist/cjs/tlm-ai/aiService.cjs +91 -95
  36. package/dist/cjs/tlm-ai/tools.cjs +177 -174
  37. package/dist/cjs/tlm-auth/authController.cjs +80 -123
  38. package/dist/cjs/tlm-auth/authMiddleware.cjs +25 -30
  39. package/dist/cjs/tlm-auth/authRoutes.cjs +11 -14
  40. package/dist/cjs/tlm-log/logController.cjs +135 -116
  41. package/dist/cjs/tlm-log/logRoutes.cjs +19 -20
  42. package/dist/cjs/tlm-log/logService.cjs +29 -0
  43. package/dist/cjs/tlm-metric/metricsController.cjs +154 -122
  44. package/dist/cjs/tlm-metric/metricsRoutes.cjs +22 -20
  45. package/dist/cjs/tlm-metric/metricsService.cjs +26 -0
  46. package/dist/cjs/tlm-plugin/pluginController.cjs +128 -140
  47. package/dist/cjs/tlm-plugin/pluginProcess.cjs +89 -94
  48. package/dist/cjs/tlm-plugin/pluginRoutes.cjs +11 -14
  49. package/dist/cjs/tlm-plugin/pluginService.cjs +73 -74
  50. package/dist/cjs/tlm-trace/traceController.cjs +140 -123
  51. package/dist/cjs/tlm-trace/traceRoutes.cjs +19 -20
  52. package/dist/cjs/tlm-trace/traceService.cjs +29 -0
  53. package/dist/cjs/tlm-ui/uiRoutes.cjs +63 -32
  54. package/dist/cjs/tlm-util/utilController.cjs +68 -70
  55. package/dist/cjs/tlm-util/utilRoutes.cjs +51 -63
  56. package/dist/cjs/types/index.cjs +2 -5
  57. package/dist/cjs/utils/logger.cjs +38 -43
  58. package/dist/cjs/utils/regexUtils.cjs +22 -22
  59. package/dist/esm/config/bootConfig.js +5 -2
  60. package/dist/esm/config/config.js +9 -2
  61. package/dist/esm/docs/openapi.yaml +158 -4
  62. package/dist/esm/index.js +9 -8
  63. package/dist/esm/routesManager.js +6 -10
  64. package/dist/esm/telemetry/custom-implementations/exporters/DiskLogExporter.js +114 -0
  65. package/dist/esm/telemetry/custom-implementations/exporters/DiskMetricExporter.js +94 -0
  66. package/dist/esm/telemetry/custom-implementations/exporters/DiskTraceExporter.js +96 -0
  67. package/dist/esm/telemetry/custom-implementations/exporters/InMemoryDbLogExporter.js +38 -7
  68. package/dist/esm/telemetry/custom-implementations/exporters/InMemoryDbMetricExporter.js +107 -48
  69. package/dist/esm/telemetry/custom-implementations/exporters/InMemoryDbSpanExporter.js +60 -29
  70. package/dist/esm/telemetry/custom-implementations/exporters/MultiMetricExporter.js +53 -0
  71. package/dist/esm/telemetry/custom-implementations/instrumentations/logsInstrumentation.js +85 -0
  72. package/dist/esm/telemetry/custom-implementations/metrics/tsdb/Chunk.js +155 -0
  73. package/dist/esm/telemetry/custom-implementations/metrics/tsdb/Series.js +164 -0
  74. package/dist/esm/telemetry/custom-implementations/metrics/tsdb/SeriesRegistry.js +385 -0
  75. package/dist/esm/telemetry/custom-implementations/metrics/tsdb/types.js +1 -0
  76. package/dist/esm/telemetry/custom-implementations/metrics/tsdb/utils.js +74 -0
  77. package/dist/esm/telemetry/custom-implementations/processors/dynamicMultiLogProcessor.js +2 -1
  78. package/dist/esm/telemetry/custom-implementations/processors/dynamicMultiSpanProcessor.js +1 -1
  79. package/dist/esm/telemetry/custom-implementations/wrappers.js +77 -6
  80. package/dist/esm/telemetry/initializeTelemetry.js +27 -69
  81. package/dist/esm/telemetry/persistence/DiskImporter.js +78 -0
  82. package/dist/esm/telemetry/persistence/DiskUtils.js +51 -0
  83. package/dist/esm/telemetry/persistence/DiskWriter.js +59 -0
  84. package/dist/esm/telemetry/telemetryConfigurator.js +110 -39
  85. package/dist/esm/telemetry/telemetryRegistry.js +12 -1
  86. package/dist/esm/tlm-ai/agent.js +5 -3
  87. package/dist/esm/tlm-ai/aiController.js +3 -3
  88. package/dist/esm/tlm-ai/aiService.js +6 -2
  89. package/dist/esm/tlm-ai/tools.js +5 -9
  90. package/dist/esm/tlm-auth/authController.js +3 -2
  91. package/dist/esm/tlm-log/logController.js +62 -18
  92. package/dist/esm/tlm-log/logRoutes.js +3 -1
  93. package/dist/esm/tlm-log/logService.js +25 -0
  94. package/dist/esm/tlm-metric/metricsController.js +116 -50
  95. package/dist/esm/tlm-metric/metricsRoutes.js +8 -3
  96. package/dist/esm/tlm-metric/metricsService.js +22 -0
  97. package/dist/esm/tlm-plugin/pluginController.js +6 -11
  98. package/dist/esm/tlm-plugin/pluginService.js +2 -4
  99. package/dist/esm/tlm-trace/traceController.js +87 -36
  100. package/dist/esm/tlm-trace/traceRoutes.js +3 -1
  101. package/dist/esm/tlm-trace/traceService.js +25 -0
  102. package/dist/esm/tlm-ui/uiRoutes.js +5 -5
  103. package/dist/esm/tlm-util/utilController.js +3 -9
  104. package/dist/esm/tlm-util/utilRoutes.js +2 -2
  105. package/dist/types/config/bootConfig.d.ts +3 -0
  106. package/dist/types/config/config.d.ts +48 -7
  107. package/dist/types/config/config.types.d.ts +7 -0
  108. package/dist/types/index.d.ts +2 -3
  109. package/dist/types/telemetry/custom-implementations/exporters/DiskLogExporter.d.ts +24 -0
  110. package/dist/types/telemetry/custom-implementations/exporters/DiskMetricExporter.d.ts +23 -0
  111. package/dist/types/telemetry/custom-implementations/exporters/DiskTraceExporter.d.ts +23 -0
  112. package/dist/types/telemetry/custom-implementations/exporters/InMemoryDbLogExporter.d.ts +3 -1
  113. package/dist/types/telemetry/custom-implementations/exporters/InMemoryDbMetricExporter.d.ts +56 -15
  114. package/dist/types/telemetry/custom-implementations/exporters/InMemoryDbSpanExporter.d.ts +8 -4
  115. package/dist/types/telemetry/custom-implementations/exporters/MultiMetricExporter.d.ts +9 -0
  116. package/dist/types/telemetry/custom-implementations/instrumentations/logsInstrumentation.d.ts +23 -0
  117. package/dist/types/telemetry/custom-implementations/metrics/tsdb/Chunk.d.ts +49 -0
  118. package/dist/types/telemetry/custom-implementations/metrics/tsdb/Series.d.ts +67 -0
  119. package/dist/types/telemetry/custom-implementations/metrics/tsdb/SeriesRegistry.d.ts +69 -0
  120. package/dist/types/telemetry/custom-implementations/metrics/tsdb/types.d.ts +68 -0
  121. package/dist/types/telemetry/custom-implementations/metrics/tsdb/utils.d.ts +21 -0
  122. package/dist/types/telemetry/custom-implementations/processors/dynamicMultiLogProcessor.d.ts +2 -2
  123. package/dist/types/telemetry/custom-implementations/wrappers.d.ts +2 -1
  124. package/dist/types/telemetry/persistence/DiskImporter.d.ts +17 -0
  125. package/dist/types/telemetry/persistence/DiskUtils.d.ts +11 -0
  126. package/dist/types/telemetry/persistence/DiskWriter.d.ts +21 -0
  127. package/dist/types/telemetry/telemetryConfigurator.d.ts +1 -1
  128. package/dist/types/telemetry/telemetryRegistry.d.ts +8 -0
  129. package/dist/types/tlm-ai/agent.d.ts +1 -1
  130. package/dist/types/tlm-ai/aiService.d.ts +1 -1
  131. package/dist/types/tlm-log/logController.d.ts +2 -0
  132. package/dist/types/tlm-log/logService.d.ts +4 -0
  133. package/dist/types/tlm-metric/metricsController.d.ts +11 -2
  134. package/dist/types/tlm-metric/metricsService.d.ts +6 -0
  135. package/dist/types/tlm-trace/traceController.d.ts +9 -7
  136. package/dist/types/tlm-trace/traceService.d.ts +4 -0
  137. package/dist/types/types/index.d.ts +2 -2
  138. package/dist/ui/assets/{ApiDocsPage-C_VVPPHa.js → ApiDocsPage-DTCgVbW2.js} +2 -2
  139. package/dist/ui/assets/CollapsibleCard-lWgfsaAn.js +1 -0
  140. package/dist/ui/assets/DevToolsPage-DEhf8CBy.js +1 -0
  141. package/dist/ui/assets/LandingPage-CfEHCDxY.js +6 -0
  142. package/dist/ui/assets/LogsPage-DFDKRuGH.js +1 -0
  143. package/dist/ui/assets/{NotFoundPage-B3quk3P1.js → NotFoundPage-DCy0DcV7.js} +1 -1
  144. package/dist/ui/assets/PluginCreatePage-BawZ5_-h.js +50 -0
  145. package/dist/ui/assets/PluginPage-D3FmgU7d.js +27 -0
  146. package/dist/ui/assets/TraceSpansPage-D0_L45Rb.js +6 -0
  147. package/dist/ui/assets/VirtualizedListPanel-q605n9He.js +16 -0
  148. package/dist/ui/assets/alert-DBAFshSi.js +1133 -0
  149. package/dist/ui/assets/badge-DGNBtnxU.js +1 -0
  150. package/dist/ui/assets/{chevron-down-CPsvsmqj.js → chevron-down-CFEqYzGC.js} +1 -1
  151. package/dist/ui/assets/{chevron-up-Df9jMo1X.js → chevron-up-lDnFwAJq.js} +1 -1
  152. package/dist/ui/assets/{circle-alert-DOPQPvU8.js → circle-alert-BpYUuRs7.js} +1 -1
  153. package/dist/ui/assets/dialog-1dRyI6SC.js +15 -0
  154. package/dist/ui/assets/index-C7RfU6hR.js +1 -0
  155. package/dist/ui/assets/index-C9dDYIpd.js +305 -0
  156. package/dist/ui/assets/index-D6f1KjWV.css +1 -0
  157. package/dist/ui/assets/info-CuJQWoBU.js +6 -0
  158. package/dist/ui/assets/{input-Dzvg_ZEZ.js → input-BLXaar0X.js} +1 -1
  159. package/dist/ui/assets/label-DfAcltsl.js +1 -0
  160. package/dist/ui/assets/{loader-circle-CrvlRy5o.js → loader-circle-B7oLyPsi.js} +1 -1
  161. package/dist/ui/assets/{loginPage-qa4V-B70.js → loginPage-DswZvOJ-.js} +1 -1
  162. package/dist/ui/assets/metrics-page-BhtXrfUW.js +31 -0
  163. package/dist/ui/assets/metrics-page-D1GxaB_c.css +1 -0
  164. package/dist/ui/assets/popover-IDker85U.js +11 -0
  165. package/dist/ui/assets/select-B8y5IidE.js +6 -0
  166. package/dist/ui/assets/separator-B6EzrxYY.js +6 -0
  167. package/dist/ui/assets/severityOptions-DtCsaAZK.js +11 -0
  168. package/dist/ui/assets/square-pen-D_oecB1x.js +6 -0
  169. package/dist/ui/assets/switch-Dqo0XkRD.js +1 -0
  170. package/dist/ui/assets/trace-DJq1miYa.js +1 -0
  171. package/dist/ui/assets/upload-prIohEdY.js +11 -0
  172. package/dist/ui/assets/{utilService-DNyqzwj0.js → utilService-C8TJKLqs.js} +1 -1
  173. package/dist/ui/assets/wand-sparkles-OgXuzsSx.js +6 -0
  174. package/dist/ui/index.html +2 -2
  175. package/package.json +44 -49
  176. package/dist/ui/assets/CollapsibleCard-B3KR_8mL.js +0 -1
  177. package/dist/ui/assets/DevToolsPage-OyZcDcmw.js +0 -1
  178. package/dist/ui/assets/LandingPage-CppFBA6K.js +0 -6
  179. package/dist/ui/assets/LogsPage-9Fq8GArS.js +0 -26
  180. package/dist/ui/assets/PluginCreatePage-X_aCH4t4.js +0 -50
  181. package/dist/ui/assets/PluginPage-DMDSihrZ.js +0 -27
  182. package/dist/ui/assets/alert-jQ9HCPIf.js +0 -1133
  183. package/dist/ui/assets/badge-CNq0-mH5.js +0 -1
  184. package/dist/ui/assets/card-DFAwwhN3.js +0 -1
  185. package/dist/ui/assets/index-BkD6DijD.js +0 -15
  186. package/dist/ui/assets/index-CERGVYZK.js +0 -292
  187. package/dist/ui/assets/index-CSIPf9qw.css +0 -1
  188. package/dist/ui/assets/label-DuVnkZ4q.js +0 -1
  189. package/dist/ui/assets/select-DhS8YUtJ.js +0 -1
  190. package/dist/ui/assets/separator-isK4chBP.js +0 -6
  191. package/dist/ui/assets/severityOptions-O38dSOfk.js +0 -11
  192. package/dist/ui/assets/switch-Z3mImG9n.js +0 -1
  193. package/dist/ui/assets/tabs-_77MUUQe.js +0 -16
  194. package/dist/ui/assets/upload-C1LT4Gkb.js +0 -16
@@ -0,0 +1,168 @@
1
+ "use strict";
2
+ /**
3
+ * Series: Time-series storage with metadata and chunks
4
+ * One series per unique metric+labels combination
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.Series = void 0;
8
+ const Chunk_js_1 = require("./Chunk.cjs");
9
+ class Series {
10
+ labelSet;
11
+ metadata;
12
+ chunks = [];
13
+ maxChunks;
14
+ chunkSize;
15
+ isHistogram;
16
+ constructor(labelSet, metadata, chunkSize = 120, maxChunks = 60 // ~1 hour at 1min intervals with 120 samples/chunk
17
+ ) {
18
+ this.labelSet = labelSet;
19
+ this.metadata = metadata;
20
+ this.chunkSize = chunkSize;
21
+ this.maxChunks = maxChunks;
22
+ this.isHistogram = metadata.dataPointType === 0; // HISTOGRAM = 0
23
+ }
24
+ /**
25
+ * Append a sample to the series
26
+ */
27
+ append(startTime, endTime, value) {
28
+ // Get or create current chunk
29
+ let currentChunk = this.chunks[this.chunks.length - 1];
30
+ if (!currentChunk || currentChunk.isFull()) {
31
+ // Create new chunk
32
+ currentChunk = new Chunk_js_1.Chunk(this.chunkSize, this.isHistogram);
33
+ this.chunks.push(currentChunk);
34
+ // Enforce max chunks limit (circular buffer behavior)
35
+ if (this.chunks.length > this.maxChunks) {
36
+ this.chunks.shift(); // Remove oldest chunk
37
+ }
38
+ }
39
+ currentChunk.append(startTime, endTime, value);
40
+ }
41
+ /**
42
+ * Query slices with options object. Example:
43
+ * querySlices({ startTime, endTime, includeStartTimes: true })
44
+ * includeStartTimes defaults to false.
45
+ * If both startTime and endTime are undefined, returns all data (no time filtering).
46
+ */
47
+ querySlices(options) {
48
+ const start = options?.startTime ?? 0;
49
+ const end = options?.endTime ?? Number.MAX_VALUE;
50
+ const includeStartTimes = options?.includeStartTimes ?? false;
51
+ let totalLength = 0;
52
+ for (const chunk of this.chunks) {
53
+ if (!chunk.overlaps(start, end))
54
+ continue;
55
+ const slice = chunk.getSlices(start, end);
56
+ totalLength += slice.startTimes.length;
57
+ }
58
+ if (totalLength === 0) {
59
+ const empty = {
60
+ endTimes: new Float64Array(0),
61
+ values: this.isHistogram ? [] : new Float64Array(0),
62
+ };
63
+ if (includeStartTimes) {
64
+ empty.startTimes = new Float64Array(0);
65
+ }
66
+ return empty;
67
+ }
68
+ let resultStart;
69
+ if (includeStartTimes) {
70
+ resultStart = new Float64Array(totalLength);
71
+ }
72
+ const resultEnd = new Float64Array(totalLength);
73
+ const resultValues = this.isHistogram
74
+ ? new Array(totalLength)
75
+ : new Float64Array(totalLength);
76
+ let offset = 0;
77
+ for (const chunk of this.chunks) {
78
+ if (!chunk.overlaps(start, end))
79
+ continue;
80
+ const slice = chunk.getSlices(start, end);
81
+ const len = slice.startTimes.length;
82
+ if (len === 0)
83
+ continue;
84
+ // Copy start and end times
85
+ if (includeStartTimes && resultStart) {
86
+ resultStart.set(slice.startTimes, offset);
87
+ }
88
+ resultEnd.set(slice.endTimes, offset);
89
+ // Copy values (different type depending on metric)
90
+ if (this.isHistogram) {
91
+ resultValues.splice(offset, len, ...slice.values);
92
+ }
93
+ else {
94
+ resultValues.set(slice.values, offset);
95
+ }
96
+ offset += len;
97
+ }
98
+ const result = {
99
+ endTimes: resultEnd,
100
+ values: resultValues
101
+ };
102
+ if (includeStartTimes && resultStart) {
103
+ result.startTimes = resultStart;
104
+ }
105
+ return result;
106
+ }
107
+ /**
108
+ * Remove chunks older than threshold
109
+ */
110
+ evictOldChunks(thresholdTime) {
111
+ let evicted = 0;
112
+ while (this.chunks.length > 0 && this.chunks[0].getMaxTime() < thresholdTime) {
113
+ this.chunks.shift();
114
+ evicted++;
115
+ }
116
+ return evicted;
117
+ }
118
+ /**
119
+ * Get series metadata
120
+ */
121
+ getMetadata() {
122
+ return this.metadata;
123
+ }
124
+ getLabels() {
125
+ return this.labelSet.labels;
126
+ }
127
+ getOriginalAttributes() {
128
+ return this.labelSet.originalAttributes;
129
+ }
130
+ /**
131
+ * Get label hash
132
+ */
133
+ getLabelHash() {
134
+ return this.labelSet.hash;
135
+ }
136
+ getStats() {
137
+ const totalSamples = this.chunks.reduce((sum, chunk) => sum + chunk.size(), 0);
138
+ const memoryBytes = this.chunks.reduce((sum, chunk) => sum + chunk.getMemoryUsage(), 0);
139
+ return {
140
+ metricName: this.metadata?.descriptor?.name ?? 'unknown',
141
+ labels: this.labelSet?.labels ?? {},
142
+ chunks: this.chunks.length,
143
+ samples: totalSamples,
144
+ memoryBytes,
145
+ oldestTime: this.chunks[0]?.getMinTime() ?? 0,
146
+ newestTime: this.chunks[this.chunks.length - 1]?.getMaxTime() ?? 0
147
+ };
148
+ }
149
+ /**
150
+ * Check if series has any samples
151
+ */
152
+ isEmpty() {
153
+ return this.chunks.length === 0 || this.chunks.every(c => c.size() === 0);
154
+ }
155
+ /**
156
+ * Get time range covered by this series
157
+ */
158
+ getTimeRange() {
159
+ if (this.chunks.length === 0) {
160
+ return { min: 0, max: 0 };
161
+ }
162
+ return {
163
+ min: this.chunks[0].getMinTime(),
164
+ max: this.chunks[this.chunks.length - 1].getMaxTime()
165
+ };
166
+ }
167
+ }
168
+ exports.Series = Series;
@@ -0,0 +1,392 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.SeriesRegistry = void 0;
7
+ const Series_js_1 = require("./Series.cjs");
8
+ const Chunk_js_1 = require("./Chunk.cjs");
9
+ /*
10
+ OTEL export = Resource (unchanged) + SCOPE METRICS
11
+ scopeMetrics: {
12
+ scope: {name: library, version: 1.0.0},; //repeated for each metric
13
+ metrics: [
14
+ {
15
+ //Metadata (Repeated for each dataPoint)
16
+ descriptor: {name: metricName, unit, description, ...},
17
+ aggregationTemporality,
18
+ dataPointType,
19
+ // Actual dataPoints are stored in Series
20
+ dataPoints: []
21
+ }
22
+ ]
23
+ }
24
+ */
25
+ // Import fs at top level for disk operations
26
+ const fs_1 = __importDefault(require("fs"));
27
+ const logger_js_1 = __importDefault(require("../../../../utils/logger.cjs"));
28
+ class SeriesRegistry {
29
+ // Main storage
30
+ series = new Map(); // seriesId (scopeId:metricName:attributesId) -> Series
31
+ // Cached repeated data
32
+ scopes = new Map(); // scopeId -> scope
33
+ metricMetadataMap = new Map(); // "scopeId:metricName" -> MetricMetadata
34
+ // Faster lookup index
35
+ metricIdIndex = new Map(); // "scopeId:metricName" -> Set<seriesId>
36
+ chunkSize;
37
+ maxChunks;
38
+ constructor(chunkSize = 120, maxChunks = 60) {
39
+ this.chunkSize = chunkSize;
40
+ this.maxChunks = maxChunks;
41
+ }
42
+ /**
43
+ * Store entire ScopeMetrics array efficiently
44
+ * Replaces addSample - processes all metrics in one batch
45
+ */
46
+ storeScopeMetrics(scopeMetrics) {
47
+ for (const scopeMetric of scopeMetrics) {
48
+ const scope = scopeMetric.scope;
49
+ const scopeId = scopeToId(scope);
50
+ this.scopes.set(scopeId, scope);
51
+ for (const metricData of scopeMetric.metrics) {
52
+ const metricName = metricData.descriptor.name;
53
+ const metricId = makeMetricId(scopeId, metricName);
54
+ if (!this.metricMetadataMap.has(metricId)) {
55
+ const { dataPoints: _dataPoints, ...metadata } = metricData;
56
+ this.metricMetadataMap.set(metricId, metadata);
57
+ }
58
+ for (const dp of metricData.dataPoints) {
59
+ const startTime = dp.startTime[0] * 1_000_000_000 + dp.startTime[1];
60
+ const endTime = dp.endTime[0] * 1_000_000_000 + dp.endTime[1];
61
+ const attributes = dp.attributes;
62
+ const attributesId = attributesToId(attributes);
63
+ const seriesId = makeSeriesId(metricId, attributesId);
64
+ if (!this.series.has(seriesId)) {
65
+ this.series.set(seriesId, new Series_js_1.Series({ hash: 0, labels: attributes, originalAttributes: attributes }, this.metricMetadataMap.get(metricId), this.chunkSize, this.maxChunks));
66
+ (this.metricIdIndex.get(metricId) ?? this.metricIdIndex.set(metricId, new Set()).get(metricId)).add(seriesId);
67
+ }
68
+ this.series.get(seriesId).append(startTime, endTime, dp.value);
69
+ }
70
+ }
71
+ }
72
+ }
73
+ query(scopeMetrics, startTime, endTime) {
74
+ // If no queries provided, get all scopeMetrics
75
+ if (!scopeMetrics || scopeMetrics.length === 0) {
76
+ const queries = Array.from(this.metricIdIndex.keys()).map(indexKey => {
77
+ const [scopeId, metricName] = indexKey.split(':').slice(0, 2);
78
+ const scope = this.scopes.get(scopeId);
79
+ return {
80
+ scope,
81
+ descriptor: { name: metricName },
82
+ filters: undefined
83
+ };
84
+ });
85
+ return queries
86
+ .map(query => this._querySingle(query, startTime, endTime))
87
+ .filter((result) => result !== null);
88
+ }
89
+ return scopeMetrics
90
+ .map(query => this._querySingle(query, startTime, endTime))
91
+ .filter((result) => result !== null);
92
+ }
93
+ /**
94
+ * Query single metric with attribute filters
95
+ */
96
+ _querySingle(query, startTime, endTime) {
97
+ const scopeId = scopeToId(query.scope);
98
+ const indexKey = makeMetricId(scopeId, query.descriptor.name);
99
+ const seriesKeysInMetric = this.metricIdIndex.get(indexKey);
100
+ if (!seriesKeysInMetric || seriesKeysInMetric.size === 0) {
101
+ return null;
102
+ }
103
+ const scope = this.scopes.get(scopeId);
104
+ const metricData = this.metricMetadataMap.get(indexKey);
105
+ if (!scope || !metricData) {
106
+ return null;
107
+ }
108
+ // Filter by attributes if provided
109
+ const filteredSeriesKeys = query.filters
110
+ ? Array.from(seriesKeysInMetric).filter(key => {
111
+ const series = this.series.get(key);
112
+ return this._matchesAttributeFilters(series, query.filters);
113
+ })
114
+ : Array.from(seriesKeysInMetric);
115
+ if (filteredSeriesKeys.length === 0) {
116
+ return null;
117
+ }
118
+ const matchingSeries = filteredSeriesKeys
119
+ .map(key => {
120
+ const series = this.series.get(key);
121
+ const { startTimes, endTimes, values } = series.querySlices({ startTime, endTime, includeStartTimes: true });
122
+ return {
123
+ id: key,
124
+ attributes: series.getOriginalAttributes(),
125
+ startTimes: startTimes ? Array.from(startTimes) : undefined,
126
+ endTimes: Array.from(endTimes),
127
+ values: Array.isArray(values) ? values : Array.from(values)
128
+ };
129
+ })
130
+ .filter(s => s.endTimes.length > 0);
131
+ return {
132
+ scope,
133
+ descriptor: metricData.descriptor,
134
+ series: matchingSeries
135
+ };
136
+ }
137
+ /**
138
+ * Match series attributes against filters
139
+ * Supports exact match, negation (!), and regex (~)
140
+ */
141
+ _matchesAttributeFilters(series, filters) {
142
+ if (!filters || Object.keys(filters).length === 0) {
143
+ return true;
144
+ }
145
+ const attrs = series.getOriginalAttributes();
146
+ for (const [key, filterValue] of Object.entries(filters)) {
147
+ const attrValue = String(attrs[key] ?? '');
148
+ // Regex match: status=~4.*
149
+ if (filterValue.startsWith('~')) {
150
+ const pattern = filterValue.slice(1);
151
+ try {
152
+ const regex = new RegExp(pattern);
153
+ if (!regex.test(attrValue)) {
154
+ return false;
155
+ }
156
+ }
157
+ catch {
158
+ if (attrValue !== filterValue) {
159
+ return false;
160
+ }
161
+ }
162
+ }
163
+ // Negation: status=!200
164
+ else if (filterValue.startsWith('!')) {
165
+ const negatedValue = filterValue.slice(1);
166
+ if (attrValue === negatedValue) {
167
+ return false;
168
+ }
169
+ }
170
+ // Exact match: method=GET
171
+ else {
172
+ if (attrValue !== filterValue) {
173
+ return false;
174
+ }
175
+ }
176
+ }
177
+ return true;
178
+ }
179
+ evictOldData(retentionTimeNs) {
180
+ const thresholdTime = Date.now() * 1_000_000 - retentionTimeNs;
181
+ let evictedChunks = 0;
182
+ let evictedSeries = 0;
183
+ for (const [key, series] of this.series.entries()) {
184
+ evictedChunks += series.evictOldChunks(thresholdTime);
185
+ if (series.isEmpty()) {
186
+ this.series.delete(key);
187
+ evictedSeries++;
188
+ }
189
+ }
190
+ return { evictedChunks, evictedSeries };
191
+ }
192
+ getStats() {
193
+ const seriesStats = Array.from(this.series.values()).map(s => s.getStats());
194
+ const totalSamples = seriesStats.reduce((sum, s) => sum + s.samples, 0);
195
+ const totalMemory = seriesStats.reduce((sum, s) => sum + s.memoryBytes, 0);
196
+ return {
197
+ totalMetrics: this.metricMetadataMap.size,
198
+ totalScopes: this.scopes.size,
199
+ totalSeries: this.series.size,
200
+ totalSamples,
201
+ memoryUsageBytes: totalMemory
202
+ };
203
+ }
204
+ reset() {
205
+ this.series.clear();
206
+ this.scopes.clear();
207
+ this.metricMetadataMap.clear();
208
+ this.metricIdIndex.clear();
209
+ }
210
+ size() {
211
+ return this.series.size;
212
+ }
213
+ /**
214
+ * Serialize registry to line-delimited JSON format (newline-delimited JSON)
215
+ * Each line is a complete record: metadata, scope, metric, or data point
216
+ * Format:
217
+ * {"type":"header","version":1,"timestamp":"...","stats":{...}}
218
+ * {"type":"scope","id":"...","data":{...}}
219
+ * {"type":"metric","id":"...","data":{...}}
220
+ * {"type":"series","id":"...","labelSet":{...},"chunks":[...]}
221
+ */
222
+ serializeToLineDelimitedJson() {
223
+ const lines = [];
224
+ // Header
225
+ lines.push(JSON.stringify({
226
+ type: 'header',
227
+ version: 1,
228
+ timestamp: new Date().toISOString(),
229
+ stats: this.getStats()
230
+ }));
231
+ // Scopes
232
+ for (const [scopeId, scope] of this.scopes.entries()) {
233
+ lines.push(JSON.stringify({
234
+ type: 'scope',
235
+ id: scopeId,
236
+ data: scope
237
+ }));
238
+ }
239
+ // Metric metadata
240
+ for (const [metricId, metadata] of this.metricMetadataMap.entries()) {
241
+ lines.push(JSON.stringify({
242
+ type: 'metric',
243
+ id: metricId,
244
+ data: metadata
245
+ }));
246
+ }
247
+ // Series headers + chunks (one chunk per line for streaming efficiency)
248
+ const serializedSeriesHeaders = new Set();
249
+ for (const [seriesId, series] of this.series.entries()) {
250
+ const seriesPrivate = series;
251
+ // Emit series header once per series
252
+ if (!serializedSeriesHeaders.has(seriesId)) {
253
+ lines.push(JSON.stringify({
254
+ type: 'series',
255
+ seriesId: seriesId,
256
+ labelSet: seriesPrivate.labelSet,
257
+ metadata: seriesPrivate.metadata
258
+ }));
259
+ serializedSeriesHeaders.add(seriesId);
260
+ }
261
+ (seriesPrivate.chunks || []).forEach((chunk, chunkIndex) => {
262
+ const slicedStartTimes = chunk.startTimes.slice(0, chunk.cursor);
263
+ const slicedEndTimes = chunk.endTimes.slice(0, chunk.cursor);
264
+ const slicedValues = chunk.values.slice(0, chunk.cursor);
265
+ const slicedHistograms = chunk.histograms.slice(0, chunk.cursor);
266
+ lines.push(JSON.stringify({
267
+ type: 'chunk',
268
+ seriesId: seriesId,
269
+ chunkIndex: chunkIndex,
270
+ startTimes: Array.from(slicedStartTimes),
271
+ endTimes: Array.from(slicedEndTimes),
272
+ values: Array.from(slicedValues),
273
+ histograms: Array.from(slicedHistograms),
274
+ cursor: chunk.cursor,
275
+ minEndTime: chunk.minEndTime,
276
+ maxEndTime: chunk.maxEndTime,
277
+ isHistogram: chunk.isHistogram
278
+ }));
279
+ });
280
+ }
281
+ // Metric ID index for fast lookup
282
+ for (const [metricId, seriesIdSet] of this.metricIdIndex.entries()) {
283
+ lines.push(JSON.stringify({
284
+ type: 'index',
285
+ id: metricId,
286
+ seriesIds: Array.from(seriesIdSet)
287
+ }));
288
+ }
289
+ return lines.join('\n');
290
+ }
291
+ /**
292
+ * Deserialize from line-delimited JSON format - restore from chunk lines
293
+ */
294
+ deserializeFromLineDelimitedJson(lineDelimitedJsonData) {
295
+ try {
296
+ const lines = lineDelimitedJsonData.trim().split('\n');
297
+ for (const line of lines) {
298
+ if (!line.trim())
299
+ continue;
300
+ const record = JSON.parse(line);
301
+ switch (record.type) {
302
+ case 'header':
303
+ // Just metadata
304
+ break;
305
+ case 'scope':
306
+ this.scopes.set(record.id, record.data);
307
+ break;
308
+ case 'metric':
309
+ this.metricMetadataMap.set(record.id, record.data);
310
+ break;
311
+ case 'index':
312
+ this.metricIdIndex.set(record.id, new Set(record.seriesIds));
313
+ break;
314
+ case 'series': {
315
+ // Create series stub - will be populated by subsequent chunk lines
316
+ const metadata = this.metricMetadataMap.get(record.seriesId.substring(0, record.seriesId.lastIndexOf('$'))) || record.metadata;
317
+ if (metadata) {
318
+ const series = new Series_js_1.Series(record.labelSet, metadata, this.chunkSize, this.maxChunks);
319
+ this.series.set(record.seriesId, series);
320
+ }
321
+ break;
322
+ }
323
+ case 'chunk': {
324
+ // Restore chunk to series
325
+ const series = this.series.get(record.seriesId);
326
+ if (series) {
327
+ const chunk = new Chunk_js_1.Chunk(record.cursor || record.startTimes.length, record.isHistogram);
328
+ const chunkPrivate = chunk;
329
+ chunkPrivate.startTimes = new Float64Array(record.startTimes);
330
+ chunkPrivate.endTimes = new Float64Array(record.endTimes);
331
+ chunkPrivate.values = new Float64Array(record.values);
332
+ chunkPrivate.histograms = record.histograms;
333
+ chunkPrivate.cursor = record.cursor;
334
+ chunkPrivate.minEndTime = record.minEndTime;
335
+ chunkPrivate.maxEndTime = record.maxEndTime;
336
+ chunkPrivate.isHistogram = record.isHistogram;
337
+ const seriesPrivate = series;
338
+ seriesPrivate.chunks.push(chunk);
339
+ }
340
+ break;
341
+ }
342
+ }
343
+ }
344
+ }
345
+ catch {
346
+ logger_js_1.default.error(`Failed to deserialize metrics from line-delimited JSON`);
347
+ }
348
+ }
349
+ /**
350
+ * Save registry to disk as line-delimited JSON (one chunk per line)
351
+ */
352
+ saveToDisk(filePath) {
353
+ try {
354
+ const lineDelimitedJsonData = this.serializeToLineDelimitedJson();
355
+ fs_1.default.writeFileSync(filePath, lineDelimitedJsonData);
356
+ }
357
+ catch {
358
+ logger_js_1.default.error(`Failed to save metrics to disk at ${filePath}`);
359
+ }
360
+ }
361
+ /**
362
+ * Load registry from disk (line-delimited JSON format)
363
+ */
364
+ loadFromDisk(filePath) {
365
+ try {
366
+ if (!fs_1.default.existsSync(filePath)) {
367
+ return;
368
+ }
369
+ const lineDelimitedJsonData = fs_1.default.readFileSync(filePath, 'utf-8');
370
+ this.deserializeFromLineDelimitedJson(lineDelimitedJsonData);
371
+ }
372
+ catch {
373
+ logger_js_1.default.error(`Failed to load metrics from disk at ${filePath}`);
374
+ // Fallback to memory.
375
+ this.reset();
376
+ }
377
+ }
378
+ }
379
+ exports.SeriesRegistry = SeriesRegistry;
380
+ // Utility: Create deterministic ID from attributes
381
+ function attributesToId(attributes) {
382
+ return Object.keys(attributes).sort().map(key => `${key}=${attributes[key]}`).join(',') || 'no_attrs';
383
+ }
384
+ function scopeToId(scope) {
385
+ return `${scope.name}@${scope.version ?? 'no_version'}`;
386
+ }
387
+ function makeMetricId(scopeId, metricName) {
388
+ return `${scopeId}:${metricName}`;
389
+ }
390
+ function makeSeriesId(metricId, attributesId) {
391
+ return `${metricId}$${attributesId}`;
392
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,77 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.rawToOtel = rawToOtel;
4
+ /**
5
+ * Convert raw format to OpenTelemetry format
6
+ *
7
+ * Raw format structure (same for GET response and INSERT input):
8
+ * {
9
+ * scope: { name, version },
10
+ * descriptor: { unit, description, ... },
11
+ * series: [{ attributes, startTimes[], endTimes[], values[] }]
12
+ * }
13
+ *
14
+ * OTEL format structure:
15
+ * {
16
+ * scope: { name, version },
17
+ * metrics: [{ descriptor, dataPoints: [{ attributes, startTime, endTime, value }] }]
18
+ * }
19
+ *
20
+ * Groups dataPoints by endTime to reconstruct original exports
21
+ */
22
+ function rawToOtel(rawScopeMetrics) {
23
+ const groupedByScope = new Map();
24
+ for (const result of rawScopeMetrics) {
25
+ // Validate that result matches expected raw format
26
+ if (!result.scope) {
27
+ throw new Error(`Invalid metric format: Missing 'scope' property. ` +
28
+ `Expected format: {scope: {name, version}, descriptor: {name, ...}, series: [{attributes, startTimes, endTimes, values}]}. ` +
29
+ `Got: ${JSON.stringify(result).substring(0, 200)}`);
30
+ }
31
+ if (!result.descriptor) {
32
+ throw new Error(`Invalid metric format: Missing 'descriptor' property in metric. ` +
33
+ `Expected format: {scope: {name, version}, descriptor: {name, ...}, series: [{attributes, startTimes, endTimes, values}]}`);
34
+ }
35
+ if (!Array.isArray(result.series)) {
36
+ throw new Error(`Invalid metric format: 'series' must be an array. ` +
37
+ `Expected format: {scope: {name, version}, descriptor: {name, ...}, series: [{attributes, startTimes, endTimes, values}]}`);
38
+ }
39
+ const scopeId = result.scope.version
40
+ ? `${result.scope.name}@${result.scope.version}`
41
+ : `${result.scope.name}@none`;
42
+ if (!groupedByScope.has(scopeId)) {
43
+ groupedByScope.set(scopeId, {
44
+ scope: {
45
+ name: result.scope.name,
46
+ version: result.scope.version || ''
47
+ },
48
+ metrics: []
49
+ });
50
+ }
51
+ const scopeData = groupedByScope.get(scopeId);
52
+ const dataPoints = [];
53
+ for (const series of result.series) {
54
+ for (let i = 0; i < series.endTimes.length; i++) {
55
+ const startTimeNs = series.startTimes?.[i];
56
+ const endTimeNs = series.endTimes[i];
57
+ if (!startTimeNs)
58
+ continue;
59
+ const startSec = Math.floor(startTimeNs / 1_000_000_000);
60
+ const startNano = startTimeNs % 1_000_000_000;
61
+ const endSec = Math.floor(endTimeNs / 1_000_000_000);
62
+ const endNano = endTimeNs % 1_000_000_000;
63
+ dataPoints.push({
64
+ attributes: series.attributes,
65
+ startTime: [startSec, startNano],
66
+ endTime: [endSec, endNano],
67
+ value: series.values[i]
68
+ });
69
+ }
70
+ }
71
+ scopeData.metrics.push({
72
+ descriptor: result.descriptor,
73
+ dataPoints
74
+ });
75
+ }
76
+ return Array.from(groupedByScope.values());
77
+ }