@devmoods/observability 0.2.0 → 0.4.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.
@@ -1,14 +1,28 @@
1
1
  <?xml version="1.0" encoding="UTF-8"?>
2
- <coverage generated="1776097472327" clover="3.2.0">
3
- <project timestamp="1776097472327" name="All files">
4
- <metrics statements="5" coveredstatements="5" conditionals="0" coveredconditionals="0" methods="3" coveredmethods="3" elements="8" coveredelements="8" complexity="0" loc="5" ncloc="5" packages="1" files="1" classes="1"/>
2
+ <coverage generated="1777360795740" clover="3.2.0">
3
+ <project timestamp="1777360795740" name="All files">
4
+ <metrics statements="19" coveredstatements="19" conditionals="1" coveredconditionals="1" methods="10" coveredmethods="10" elements="30" coveredelements="30" complexity="0" loc="19" ncloc="19" packages="1" files="1" classes="1"/>
5
5
  <file name="index.ts" path="/Users/hanse/dev/github.com/devmoods/express-extras/packages/@devmoods/observability/src/index.ts">
6
- <metrics statements="5" coveredstatements="5" conditionals="0" coveredconditionals="0" methods="3" coveredmethods="3"/>
6
+ <metrics statements="19" coveredstatements="19" conditionals="1" coveredconditionals="1" methods="10" coveredmethods="10"/>
7
7
  <line num="5" count="1" type="stmt"/>
8
8
  <line num="6" count="1" type="stmt"/>
9
9
  <line num="11" count="1" type="stmt"/>
10
10
  <line num="17" count="3" type="stmt"/>
11
11
  <line num="18" count="3" type="stmt"/>
12
+ <line num="56" count="8" type="stmt"/>
13
+ <line num="59" count="7" type="stmt"/>
14
+ <line num="60" count="7" type="stmt"/>
15
+ <line num="61" count="2" type="stmt"/>
16
+ <line num="66" count="3" type="stmt"/>
17
+ <line num="70" count="3" type="stmt"/>
18
+ <line num="71" count="4" type="stmt"/>
19
+ <line num="76" count="1" type="stmt"/>
20
+ <line num="77" count="1" type="stmt"/>
21
+ <line num="86" count="1" type="stmt"/>
22
+ <line num="87" count="1" type="stmt"/>
23
+ <line num="92" count="1" type="stmt"/>
24
+ <line num="93" count="1" type="stmt"/>
25
+ <line num="98" count="1" type="stmt"/>
12
26
  </file>
13
27
  </project>
14
28
  </coverage>
@@ -1,2 +1,2 @@
1
- {"/Users/hanse/dev/github.com/devmoods/express-extras/packages/@devmoods/observability/src/index.ts": {"path":"/Users/hanse/dev/github.com/devmoods/express-extras/packages/@devmoods/observability/src/index.ts","statementMap":{"0":{"start":{"line":5,"column":14},"end":{"line":5,"column":30}},"1":{"start":{"line":6,"column":2},"end":{"line":20,"column":null}},"2":{"start":{"line":11,"column":6},"end":{"line":11,"column":null}},"3":{"start":{"line":17,"column":22},"end":{"line":17,"column":43}},"4":{"start":{"line":18,"column":6},"end":{"line":18,"column":null}}},"fnMap":{"0":{"name":"startTimer","decl":{"start":{"line":4,"column":16},"end":{"line":4,"column":29}},"loc":{"start":{"line":4,"column":29},"end":{"line":20,"column":null}},"line":4},"1":{"name":"(anonymous_1)","decl":{"start":{"line":10,"column":4},"end":{"line":10,"column":12}},"loc":{"start":{"line":10,"column":12},"end":{"line":11,"column":null}},"line":10},"2":{"name":"(anonymous_2)","decl":{"start":{"line":16,"column":4},"end":{"line":16,"column":18}},"loc":{"start":{"line":16,"column":18},"end":{"line":18,"column":null}},"line":16}},"branchMap":{},"s":{"0":1,"1":1,"2":1,"3":3,"4":3},"f":{"0":1,"1":1,"2":3},"b":{},"meta":{"lastBranch":0,"lastFunction":3,"lastStatement":5,"seen":{"f:4:16:4:29":0,"s:5:14:5:30":0,"s:6:2:20:Infinity":1,"f:10:4:10:12":1,"s:11:6:11:Infinity":2,"f:16:4:16:18":2,"s:17:22:17:43":3,"s:18:6:18:Infinity":4}}}
1
+ {"/Users/hanse/dev/github.com/devmoods/express-extras/packages/@devmoods/observability/src/index.ts": {"path":"/Users/hanse/dev/github.com/devmoods/express-extras/packages/@devmoods/observability/src/index.ts","statementMap":{"0":{"start":{"line":5,"column":14},"end":{"line":5,"column":30}},"1":{"start":{"line":6,"column":2},"end":{"line":20,"column":null}},"2":{"start":{"line":11,"column":6},"end":{"line":11,"column":null}},"3":{"start":{"line":17,"column":22},"end":{"line":17,"column":43}},"4":{"start":{"line":18,"column":6},"end":{"line":18,"column":null}},"5":{"start":{"line":56,"column":23},"end":{"line":56,"column":48}},"6":{"start":{"line":59,"column":4},"end":{"line":59,"column":null}},"7":{"start":{"line":60,"column":4},"end":{"line":61,"column":null}},"8":{"start":{"line":61,"column":6},"end":{"line":61,"column":null}},"9":{"start":{"line":66,"column":4},"end":{"line":66,"column":null}},"10":{"start":{"line":70,"column":4},"end":{"line":71,"column":null}},"11":{"start":{"line":71,"column":6},"end":{"line":71,"column":null}},"12":{"start":{"line":76,"column":4},"end":{"line":77,"column":null}},"13":{"start":{"line":77,"column":6},"end":{"line":77,"column":null}},"14":{"start":{"line":86,"column":4},"end":{"line":87,"column":null}},"15":{"start":{"line":87,"column":6},"end":{"line":87,"column":null}},"16":{"start":{"line":92,"column":4},"end":{"line":93,"column":null}},"17":{"start":{"line":93,"column":6},"end":{"line":93,"column":null}},"18":{"start":{"line":98,"column":23},"end":{"line":98,"column":36}}},"fnMap":{"0":{"name":"startTimer","decl":{"start":{"line":4,"column":16},"end":{"line":4,"column":29}},"loc":{"start":{"line":4,"column":29},"end":{"line":20,"column":null}},"line":4},"1":{"name":"(anonymous_1)","decl":{"start":{"line":10,"column":4},"end":{"line":10,"column":12}},"loc":{"start":{"line":10,"column":12},"end":{"line":11,"column":null}},"line":10},"2":{"name":"(anonymous_2)","decl":{"start":{"line":16,"column":4},"end":{"line":16,"column":18}},"loc":{"start":{"line":16,"column":18},"end":{"line":18,"column":null}},"line":16},"3":{"name":"(anonymous_3)","decl":{"start":{"line":58,"column":2},"end":{"line":58,"column":12}},"loc":{"start":{"line":58,"column":49},"end":{"line":61,"column":null}},"line":58},"4":{"name":"(anonymous_4)","decl":{"start":{"line":60,"column":4},"end":{"line":60,"column":17}},"loc":{"start":{"line":60,"column":17},"end":{"line":61,"column":null}},"line":60},"5":{"name":"(anonymous_5)","decl":{"start":{"line":65,"column":2},"end":{"line":65,"column":28}},"loc":{"start":{"line":65,"column":28},"end":{"line":66,"column":null}},"line":65},"6":{"name":"(anonymous_6)","decl":{"start":{"line":69,"column":2},"end":{"line":69,"column":12}},"loc":{"start":{"line":69,"column":74},"end":{"line":71,"column":null}},"line":69},"7":{"name":"(anonymous_7)","decl":{"start":{"line":75,"column":2},"end":{"line":75,"column":8}},"loc":{"start":{"line":75,"column":74},"end":{"line":77,"column":null}},"line":75},"8":{"name":"(anonymous_8)","decl":{"start":{"line":81,"column":2},"end":{"line":81,"column":null}},"loc":{"start":{"line":85,"column":10},"end":{"line":87,"column":null}},"line":85},"9":{"name":"(anonymous_9)","decl":{"start":{"line":91,"column":2},"end":{"line":91,"column":9}},"loc":{"start":{"line":91,"column":75},"end":{"line":93,"column":null}},"line":91}},"branchMap":{"0":{"loc":{"start":{"line":69,"column":26},"end":{"line":69,"column":37}},"type":"default-arg","locations":[{"start":{"line":69,"column":34},"end":{"line":69,"column":37}}],"line":69}},"s":{"0":1,"1":1,"2":1,"3":3,"4":3,"5":8,"6":7,"7":7,"8":2,"9":3,"10":3,"11":4,"12":1,"13":1,"14":1,"15":1,"16":1,"17":1,"18":1},"f":{"0":1,"1":1,"2":3,"3":7,"4":2,"5":3,"6":3,"7":1,"8":1,"9":1},"b":{"0":[3]},"meta":{"lastBranch":1,"lastFunction":10,"lastStatement":19,"seen":{"f:4:16:4:29":0,"s:5:14:5:30":0,"s:6:2:20:Infinity":1,"f:10:4:10:12":1,"s:11:6:11:Infinity":2,"f:16:4:16:18":2,"s:17:22:17:43":3,"s:18:6:18:Infinity":4,"s:56:23:56:48":5,"f:58:2:58:12":3,"s:59:4:59:Infinity":6,"s:60:4:61:Infinity":7,"f:60:4:60:17":4,"s:61:6:61:Infinity":8,"f:65:2:65:28":5,"s:66:4:66:Infinity":9,"f:69:2:69:12":6,"b:69:34:69:37":0,"s:70:4:71:Infinity":10,"s:71:6:71:Infinity":11,"f:75:2:75:8":7,"s:76:4:77:Infinity":12,"s:77:6:77:Infinity":13,"f:81:2:81:Infinity":8,"s:86:4:87:Infinity":14,"s:87:6:87:Infinity":15,"f:91:2:91:9":9,"s:92:4:93:Infinity":16,"s:93:6:93:Infinity":17,"s:98:23:98:36":18}}}
2
2
  }
@@ -25,28 +25,28 @@
25
25
  <div class='fl pad1y space-right2'>
26
26
  <span class="strong">100% </span>
27
27
  <span class="quiet">Statements</span>
28
- <span class='fraction'>5/5</span>
28
+ <span class='fraction'>19/19</span>
29
29
  </div>
30
30
 
31
31
 
32
32
  <div class='fl pad1y space-right2'>
33
33
  <span class="strong">100% </span>
34
34
  <span class="quiet">Branches</span>
35
- <span class='fraction'>0/0</span>
35
+ <span class='fraction'>1/1</span>
36
36
  </div>
37
37
 
38
38
 
39
39
  <div class='fl pad1y space-right2'>
40
40
  <span class="strong">100% </span>
41
41
  <span class="quiet">Functions</span>
42
- <span class='fraction'>3/3</span>
42
+ <span class='fraction'>10/10</span>
43
43
  </div>
44
44
 
45
45
 
46
46
  <div class='fl pad1y space-right2'>
47
47
  <span class="strong">100% </span>
48
48
  <span class="quiet">Lines</span>
49
- <span class='fraction'>5/5</span>
49
+ <span class='fraction'>19/19</span>
50
50
  </div>
51
51
 
52
52
 
@@ -84,13 +84,13 @@
84
84
  <div class="chart"><div class="cover-fill cover-full" style="width: 100%"></div><div class="cover-empty" style="width: 0%"></div></div>
85
85
  </td>
86
86
  <td data-value="100" class="pct high">100%</td>
87
- <td data-value="5" class="abs high">5/5</td>
87
+ <td data-value="19" class="abs high">19/19</td>
88
88
  <td data-value="100" class="pct high">100%</td>
89
- <td data-value="0" class="abs high">0/0</td>
89
+ <td data-value="1" class="abs high">1/1</td>
90
90
  <td data-value="100" class="pct high">100%</td>
91
- <td data-value="3" class="abs high">3/3</td>
91
+ <td data-value="10" class="abs high">10/10</td>
92
92
  <td data-value="100" class="pct high">100%</td>
93
- <td data-value="5" class="abs high">5/5</td>
93
+ <td data-value="19" class="abs high">19/19</td>
94
94
  </tr>
95
95
 
96
96
  </tbody>
@@ -101,7 +101,7 @@
101
101
  <div class='footer quiet pad2 space-top1 center small'>
102
102
  Code coverage generated by
103
103
  <a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
104
- at 2026-04-13T16:24:32.321Z
104
+ at 2026-04-28T07:19:55.736Z
105
105
  </div>
106
106
  <script src="prettify.js"></script>
107
107
  <script>
@@ -25,28 +25,28 @@
25
25
  <div class='fl pad1y space-right2'>
26
26
  <span class="strong">100% </span>
27
27
  <span class="quiet">Statements</span>
28
- <span class='fraction'>5/5</span>
28
+ <span class='fraction'>19/19</span>
29
29
  </div>
30
30
 
31
31
 
32
32
  <div class='fl pad1y space-right2'>
33
33
  <span class="strong">100% </span>
34
34
  <span class="quiet">Branches</span>
35
- <span class='fraction'>0/0</span>
35
+ <span class='fraction'>1/1</span>
36
36
  </div>
37
37
 
38
38
 
39
39
  <div class='fl pad1y space-right2'>
40
40
  <span class="strong">100% </span>
41
41
  <span class="quiet">Functions</span>
42
- <span class='fraction'>3/3</span>
42
+ <span class='fraction'>10/10</span>
43
43
  </div>
44
44
 
45
45
 
46
46
  <div class='fl pad1y space-right2'>
47
47
  <span class="strong">100% </span>
48
48
  <span class="quiet">Lines</span>
49
- <span class='fraction'>5/5</span>
49
+ <span class='fraction'>19/19</span>
50
50
  </div>
51
51
 
52
52
 
@@ -89,7 +89,79 @@
89
89
  <a name='L24'></a><a href='#L24'>24</a>
90
90
  <a name='L25'></a><a href='#L25'>25</a>
91
91
  <a name='L26'></a><a href='#L26'>26</a>
92
- <a name='L27'></a><a href='#L27'>27</a></td><td class="line-coverage quiet"><span class="cline-any cline-neutral">&nbsp;</span>
92
+ <a name='L27'></a><a href='#L27'>27</a>
93
+ <a name='L28'></a><a href='#L28'>28</a>
94
+ <a name='L29'></a><a href='#L29'>29</a>
95
+ <a name='L30'></a><a href='#L30'>30</a>
96
+ <a name='L31'></a><a href='#L31'>31</a>
97
+ <a name='L32'></a><a href='#L32'>32</a>
98
+ <a name='L33'></a><a href='#L33'>33</a>
99
+ <a name='L34'></a><a href='#L34'>34</a>
100
+ <a name='L35'></a><a href='#L35'>35</a>
101
+ <a name='L36'></a><a href='#L36'>36</a>
102
+ <a name='L37'></a><a href='#L37'>37</a>
103
+ <a name='L38'></a><a href='#L38'>38</a>
104
+ <a name='L39'></a><a href='#L39'>39</a>
105
+ <a name='L40'></a><a href='#L40'>40</a>
106
+ <a name='L41'></a><a href='#L41'>41</a>
107
+ <a name='L42'></a><a href='#L42'>42</a>
108
+ <a name='L43'></a><a href='#L43'>43</a>
109
+ <a name='L44'></a><a href='#L44'>44</a>
110
+ <a name='L45'></a><a href='#L45'>45</a>
111
+ <a name='L46'></a><a href='#L46'>46</a>
112
+ <a name='L47'></a><a href='#L47'>47</a>
113
+ <a name='L48'></a><a href='#L48'>48</a>
114
+ <a name='L49'></a><a href='#L49'>49</a>
115
+ <a name='L50'></a><a href='#L50'>50</a>
116
+ <a name='L51'></a><a href='#L51'>51</a>
117
+ <a name='L52'></a><a href='#L52'>52</a>
118
+ <a name='L53'></a><a href='#L53'>53</a>
119
+ <a name='L54'></a><a href='#L54'>54</a>
120
+ <a name='L55'></a><a href='#L55'>55</a>
121
+ <a name='L56'></a><a href='#L56'>56</a>
122
+ <a name='L57'></a><a href='#L57'>57</a>
123
+ <a name='L58'></a><a href='#L58'>58</a>
124
+ <a name='L59'></a><a href='#L59'>59</a>
125
+ <a name='L60'></a><a href='#L60'>60</a>
126
+ <a name='L61'></a><a href='#L61'>61</a>
127
+ <a name='L62'></a><a href='#L62'>62</a>
128
+ <a name='L63'></a><a href='#L63'>63</a>
129
+ <a name='L64'></a><a href='#L64'>64</a>
130
+ <a name='L65'></a><a href='#L65'>65</a>
131
+ <a name='L66'></a><a href='#L66'>66</a>
132
+ <a name='L67'></a><a href='#L67'>67</a>
133
+ <a name='L68'></a><a href='#L68'>68</a>
134
+ <a name='L69'></a><a href='#L69'>69</a>
135
+ <a name='L70'></a><a href='#L70'>70</a>
136
+ <a name='L71'></a><a href='#L71'>71</a>
137
+ <a name='L72'></a><a href='#L72'>72</a>
138
+ <a name='L73'></a><a href='#L73'>73</a>
139
+ <a name='L74'></a><a href='#L74'>74</a>
140
+ <a name='L75'></a><a href='#L75'>75</a>
141
+ <a name='L76'></a><a href='#L76'>76</a>
142
+ <a name='L77'></a><a href='#L77'>77</a>
143
+ <a name='L78'></a><a href='#L78'>78</a>
144
+ <a name='L79'></a><a href='#L79'>79</a>
145
+ <a name='L80'></a><a href='#L80'>80</a>
146
+ <a name='L81'></a><a href='#L81'>81</a>
147
+ <a name='L82'></a><a href='#L82'>82</a>
148
+ <a name='L83'></a><a href='#L83'>83</a>
149
+ <a name='L84'></a><a href='#L84'>84</a>
150
+ <a name='L85'></a><a href='#L85'>85</a>
151
+ <a name='L86'></a><a href='#L86'>86</a>
152
+ <a name='L87'></a><a href='#L87'>87</a>
153
+ <a name='L88'></a><a href='#L88'>88</a>
154
+ <a name='L89'></a><a href='#L89'>89</a>
155
+ <a name='L90'></a><a href='#L90'>90</a>
156
+ <a name='L91'></a><a href='#L91'>91</a>
157
+ <a name='L92'></a><a href='#L92'>92</a>
158
+ <a name='L93'></a><a href='#L93'>93</a>
159
+ <a name='L94'></a><a href='#L94'>94</a>
160
+ <a name='L95'></a><a href='#L95'>95</a>
161
+ <a name='L96'></a><a href='#L96'>96</a>
162
+ <a name='L97'></a><a href='#L97'>97</a>
163
+ <a name='L98'></a><a href='#L98'>98</a>
164
+ <a name='L99'></a><a href='#L99'>99</a></td><td class="line-coverage quiet"><span class="cline-any cline-neutral">&nbsp;</span>
93
165
  <span class="cline-any cline-neutral">&nbsp;</span>
94
166
  <span class="cline-any cline-neutral">&nbsp;</span>
95
167
  <span class="cline-any cline-neutral">&nbsp;</span>
@@ -115,6 +187,78 @@
115
187
  <span class="cline-any cline-neutral">&nbsp;</span>
116
188
  <span class="cline-any cline-neutral">&nbsp;</span>
117
189
  <span class="cline-any cline-neutral">&nbsp;</span>
190
+ <span class="cline-any cline-neutral">&nbsp;</span>
191
+ <span class="cline-any cline-neutral">&nbsp;</span>
192
+ <span class="cline-any cline-neutral">&nbsp;</span>
193
+ <span class="cline-any cline-neutral">&nbsp;</span>
194
+ <span class="cline-any cline-neutral">&nbsp;</span>
195
+ <span class="cline-any cline-neutral">&nbsp;</span>
196
+ <span class="cline-any cline-neutral">&nbsp;</span>
197
+ <span class="cline-any cline-neutral">&nbsp;</span>
198
+ <span class="cline-any cline-neutral">&nbsp;</span>
199
+ <span class="cline-any cline-neutral">&nbsp;</span>
200
+ <span class="cline-any cline-neutral">&nbsp;</span>
201
+ <span class="cline-any cline-neutral">&nbsp;</span>
202
+ <span class="cline-any cline-neutral">&nbsp;</span>
203
+ <span class="cline-any cline-neutral">&nbsp;</span>
204
+ <span class="cline-any cline-neutral">&nbsp;</span>
205
+ <span class="cline-any cline-neutral">&nbsp;</span>
206
+ <span class="cline-any cline-neutral">&nbsp;</span>
207
+ <span class="cline-any cline-neutral">&nbsp;</span>
208
+ <span class="cline-any cline-neutral">&nbsp;</span>
209
+ <span class="cline-any cline-neutral">&nbsp;</span>
210
+ <span class="cline-any cline-neutral">&nbsp;</span>
211
+ <span class="cline-any cline-neutral">&nbsp;</span>
212
+ <span class="cline-any cline-neutral">&nbsp;</span>
213
+ <span class="cline-any cline-neutral">&nbsp;</span>
214
+ <span class="cline-any cline-neutral">&nbsp;</span>
215
+ <span class="cline-any cline-neutral">&nbsp;</span>
216
+ <span class="cline-any cline-neutral">&nbsp;</span>
217
+ <span class="cline-any cline-neutral">&nbsp;</span>
218
+ <span class="cline-any cline-neutral">&nbsp;</span>
219
+ <span class="cline-any cline-yes">8x</span>
220
+ <span class="cline-any cline-neutral">&nbsp;</span>
221
+ <span class="cline-any cline-neutral">&nbsp;</span>
222
+ <span class="cline-any cline-yes">7x</span>
223
+ <span class="cline-any cline-yes">7x</span>
224
+ <span class="cline-any cline-yes">2x</span>
225
+ <span class="cline-any cline-neutral">&nbsp;</span>
226
+ <span class="cline-any cline-neutral">&nbsp;</span>
227
+ <span class="cline-any cline-neutral">&nbsp;</span>
228
+ <span class="cline-any cline-neutral">&nbsp;</span>
229
+ <span class="cline-any cline-yes">3x</span>
230
+ <span class="cline-any cline-neutral">&nbsp;</span>
231
+ <span class="cline-any cline-neutral">&nbsp;</span>
232
+ <span class="cline-any cline-neutral">&nbsp;</span>
233
+ <span class="cline-any cline-yes">3x</span>
234
+ <span class="cline-any cline-yes">4x</span>
235
+ <span class="cline-any cline-neutral">&nbsp;</span>
236
+ <span class="cline-any cline-neutral">&nbsp;</span>
237
+ <span class="cline-any cline-neutral">&nbsp;</span>
238
+ <span class="cline-any cline-neutral">&nbsp;</span>
239
+ <span class="cline-any cline-yes">1x</span>
240
+ <span class="cline-any cline-yes">1x</span>
241
+ <span class="cline-any cline-neutral">&nbsp;</span>
242
+ <span class="cline-any cline-neutral">&nbsp;</span>
243
+ <span class="cline-any cline-neutral">&nbsp;</span>
244
+ <span class="cline-any cline-neutral">&nbsp;</span>
245
+ <span class="cline-any cline-neutral">&nbsp;</span>
246
+ <span class="cline-any cline-neutral">&nbsp;</span>
247
+ <span class="cline-any cline-neutral">&nbsp;</span>
248
+ <span class="cline-any cline-neutral">&nbsp;</span>
249
+ <span class="cline-any cline-yes">1x</span>
250
+ <span class="cline-any cline-yes">1x</span>
251
+ <span class="cline-any cline-neutral">&nbsp;</span>
252
+ <span class="cline-any cline-neutral">&nbsp;</span>
253
+ <span class="cline-any cline-neutral">&nbsp;</span>
254
+ <span class="cline-any cline-neutral">&nbsp;</span>
255
+ <span class="cline-any cline-yes">1x</span>
256
+ <span class="cline-any cline-yes">1x</span>
257
+ <span class="cline-any cline-neutral">&nbsp;</span>
258
+ <span class="cline-any cline-neutral">&nbsp;</span>
259
+ <span class="cline-any cline-neutral">&nbsp;</span>
260
+ <span class="cline-any cline-neutral">&nbsp;</span>
261
+ <span class="cline-any cline-yes">1x</span>
118
262
  <span class="cline-any cline-neutral">&nbsp;</span></td><td class="text"><pre class="prettyprint lang-js">/**
119
263
  * Start a high resolution timer
120
264
  */
@@ -141,6 +285,78 @@ export interface LoggerAdapter {
141
285
  captureError(error: Error | null, attributes?: Record&lt;string, any&gt;): void;
142
286
  write(chunk: Record&lt;string, any&gt;): boolean;
143
287
  }
288
+ &nbsp;
289
+ export type MetricAttributes = Record&lt;string, string | number | boolean&gt;;
290
+ &nbsp;
291
+ /**
292
+ * Common interface for metric tracking backends (Prometheus, Sentry, etc.).
293
+ * Inspired by Node.js diagnostics channels — adapters subscribe to a shared
294
+ * metrics channel and receive all published measurements.
295
+ */
296
+ export interface MetricsAdapter {
297
+ /** Increment a monotonic counter */
298
+ increment(name: string, value?: number, attributes?: MetricAttributes): void;
299
+ /** Record the current value of a gauge */
300
+ gauge(name: string, value: number, attributes?: MetricAttributes): void;
301
+ /** Record a sample in a distribution/histogram */
302
+ distribution(
303
+ name: string,
304
+ value: number,
305
+ attributes?: MetricAttributes,
306
+ ): void;
307
+ /** Record a timing in milliseconds */
308
+ timing(name: string, value: number, attributes?: MetricAttributes): void;
309
+ }
310
+ &nbsp;
311
+ /**
312
+ * A publish/subscribe metrics channel. Adapters register via `subscribe` and
313
+ * receive all subsequent measurements. Unsubscribe by calling the returned
314
+ * dispose function — the same pattern as `diagnostics_channel.subscribe`.
315
+ */
316
+ export class Metrics implements MetricsAdapter {
317
+ readonly #adapters = new Set&lt;MetricsAdapter&gt;();
318
+ &nbsp;
319
+ subscribe(adapter: MetricsAdapter): () =&gt; void {
320
+ this.#adapters.add(adapter);
321
+ return () =&gt; {
322
+ this.#adapters.delete(adapter);
323
+ };
324
+ }
325
+ &nbsp;
326
+ hasSubscribers(): boolean {
327
+ return this.#adapters.size &gt; 0;
328
+ }
329
+ &nbsp;
330
+ increment(name: string, value = 1, attributes?: MetricAttributes): void {
331
+ for (const adapter of this.#adapters) {
332
+ adapter.increment(name, value, attributes);
333
+ }
334
+ }
335
+ &nbsp;
336
+ gauge(name: string, value: number, attributes?: MetricAttributes): void {
337
+ for (const adapter of this.#adapters) {
338
+ adapter.gauge(name, value, attributes);
339
+ }
340
+ }
341
+ &nbsp;
342
+ distribution(
343
+ name: string,
344
+ value: number,
345
+ attributes?: MetricAttributes,
346
+ ): void {
347
+ for (const adapter of this.#adapters) {
348
+ adapter.distribution(name, value, attributes);
349
+ }
350
+ }
351
+ &nbsp;
352
+ timing(name: string, value: number, attributes?: MetricAttributes): void {
353
+ for (const adapter of this.#adapters) {
354
+ adapter.timing(name, value, attributes);
355
+ }
356
+ }
357
+ }
358
+ &nbsp;
359
+ export const metrics = new Metrics();
144
360
  &nbsp;</pre></td></tr></table></pre>
145
361
 
146
362
  <div class='push'></div><!-- for sticky footer -->
@@ -148,7 +364,7 @@ export interface LoggerAdapter {
148
364
  <div class='footer quiet pad2 space-top1 center small'>
149
365
  Code coverage generated by
150
366
  <a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
151
- at 2026-04-13T16:24:32.321Z
367
+ at 2026-04-28T07:19:55.736Z
152
368
  </div>
153
369
  <script src="prettify.js"></script>
154
370
  <script>
package/dist/index.d.ts CHANGED
@@ -15,3 +15,34 @@ export interface LoggerAdapter {
15
15
  captureError(error: Error | null, attributes?: Record<string, any>): void;
16
16
  write(chunk: Record<string, any>): boolean;
17
17
  }
18
+ export type MetricAttributes = Record<string, string | number | boolean>;
19
+ /**
20
+ * Common interface for metric tracking backends (Prometheus, Sentry, etc.).
21
+ * Inspired by Node.js diagnostics channels — adapters subscribe to a shared
22
+ * metrics channel and receive all published measurements.
23
+ */
24
+ export interface MetricsAdapter {
25
+ /** Increment a monotonic counter */
26
+ increment(name: string, value?: number, attributes?: MetricAttributes): void;
27
+ /** Record the current value of a gauge */
28
+ gauge(name: string, value: number, attributes?: MetricAttributes): void;
29
+ /** Record a sample in a distribution/histogram */
30
+ distribution(name: string, value: number, attributes?: MetricAttributes): void;
31
+ /** Record a timing in milliseconds */
32
+ timing(name: string, value: number, attributes?: MetricAttributes): void;
33
+ }
34
+ /**
35
+ * A publish/subscribe metrics channel. Adapters register via `subscribe` and
36
+ * receive all subsequent measurements. Unsubscribe by calling the returned
37
+ * dispose function — the same pattern as `diagnostics_channel.subscribe`.
38
+ */
39
+ export declare class Metrics implements MetricsAdapter {
40
+ #private;
41
+ subscribe(adapter: MetricsAdapter): () => void;
42
+ hasSubscribers(): boolean;
43
+ increment(name: string, value?: number, attributes?: MetricAttributes): void;
44
+ gauge(name: string, value: number, attributes?: MetricAttributes): void;
45
+ distribution(name: string, value: number, attributes?: MetricAttributes): void;
46
+ timing(name: string, value: number, attributes?: MetricAttributes): void;
47
+ }
48
+ export declare const metrics: Metrics;
package/dist/index.js CHANGED
@@ -19,3 +19,41 @@ export function startTimer() {
19
19
  },
20
20
  };
21
21
  }
22
+ /**
23
+ * A publish/subscribe metrics channel. Adapters register via `subscribe` and
24
+ * receive all subsequent measurements. Unsubscribe by calling the returned
25
+ * dispose function — the same pattern as `diagnostics_channel.subscribe`.
26
+ */
27
+ export class Metrics {
28
+ #adapters = new Set();
29
+ subscribe(adapter) {
30
+ this.#adapters.add(adapter);
31
+ return () => {
32
+ this.#adapters.delete(adapter);
33
+ };
34
+ }
35
+ hasSubscribers() {
36
+ return this.#adapters.size > 0;
37
+ }
38
+ increment(name, value = 1, attributes) {
39
+ for (const adapter of this.#adapters) {
40
+ adapter.increment(name, value, attributes);
41
+ }
42
+ }
43
+ gauge(name, value, attributes) {
44
+ for (const adapter of this.#adapters) {
45
+ adapter.gauge(name, value, attributes);
46
+ }
47
+ }
48
+ distribution(name, value, attributes) {
49
+ for (const adapter of this.#adapters) {
50
+ adapter.distribution(name, value, attributes);
51
+ }
52
+ }
53
+ timing(name, value, attributes) {
54
+ for (const adapter of this.#adapters) {
55
+ adapter.timing(name, value, attributes);
56
+ }
57
+ }
58
+ }
59
+ export const metrics = new Metrics();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@devmoods/observability",
3
- "version": "0.2.0",
3
+ "version": "0.4.0",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -14,11 +14,12 @@
14
14
  },
15
15
  "scripts": {
16
16
  "clean": "rm -rf dist",
17
- "lint": "eslint",
17
+ "lint": "oxlint .",
18
+ "format": "oxfmt .",
18
19
  "test": "vitest",
19
20
  "test:coverage": "pnpm run test --coverage",
20
- "build": "pnpm run clean && tsgo",
21
+ "build": "pnpm run clean && tsc",
21
22
  "prepublishOnly": "pnpm run clean && pnpm run build"
22
23
  },
23
- "gitHead": "ea4f51756aca98bd050cf98128bf4d4b3da1bf38"
24
+ "gitHead": "97c5d5f84397815a726bf44ae3f401f24c852fc6"
24
25
  }
@@ -1,4 +1,4 @@
1
- import { startTimer } from '../index.js';
1
+ import { Metrics, metrics, startTimer } from '../index.js';
2
2
 
3
3
  test('startTimer', async () => {
4
4
  const timer = startTimer();
@@ -15,3 +15,118 @@ test('startTimer', async () => {
15
15
  duration = timer.getDuration();
16
16
  expect(duration).toBeLessThan(0.01);
17
17
  });
18
+
19
+ test('Metrics: hasSubscribers returns false with no adapters', () => {
20
+ const m = new Metrics();
21
+ expect(m.hasSubscribers()).toBe(false);
22
+ });
23
+
24
+ test('Metrics: subscribe adds adapter and returns unsubscribe function', () => {
25
+ const m = new Metrics();
26
+ const adapter = {
27
+ increment: vi.fn(),
28
+ gauge: vi.fn(),
29
+ distribution: vi.fn(),
30
+ timing: vi.fn(),
31
+ };
32
+
33
+ const unsubscribe = m.subscribe(adapter);
34
+ expect(m.hasSubscribers()).toBe(true);
35
+
36
+ unsubscribe();
37
+ expect(m.hasSubscribers()).toBe(false);
38
+ });
39
+
40
+ test('Metrics: routes increment to all adapters with default value', () => {
41
+ const m = new Metrics();
42
+ const a = {
43
+ increment: vi.fn(),
44
+ gauge: vi.fn(),
45
+ distribution: vi.fn(),
46
+ timing: vi.fn(),
47
+ };
48
+ const b = {
49
+ increment: vi.fn(),
50
+ gauge: vi.fn(),
51
+ distribution: vi.fn(),
52
+ timing: vi.fn(),
53
+ };
54
+
55
+ m.subscribe(a);
56
+ m.subscribe(b);
57
+
58
+ m.increment('req');
59
+ expect(a.increment).toHaveBeenCalledWith('req', 1, undefined);
60
+ expect(b.increment).toHaveBeenCalledWith('req', 1, undefined);
61
+
62
+ m.increment('req', 5, { status: '200' });
63
+ expect(a.increment).toHaveBeenCalledWith('req', 5, { status: '200' });
64
+ });
65
+
66
+ test('Metrics: routes gauge to all adapters', () => {
67
+ const m = new Metrics();
68
+ const adapter = {
69
+ increment: vi.fn(),
70
+ gauge: vi.fn(),
71
+ distribution: vi.fn(),
72
+ timing: vi.fn(),
73
+ };
74
+ m.subscribe(adapter);
75
+
76
+ m.gauge('cpu', 0.75, { host: 'web-1' });
77
+ expect(adapter.gauge).toHaveBeenCalledWith('cpu', 0.75, { host: 'web-1' });
78
+ });
79
+
80
+ test('Metrics: routes distribution to all adapters', () => {
81
+ const m = new Metrics();
82
+ const adapter = {
83
+ increment: vi.fn(),
84
+ gauge: vi.fn(),
85
+ distribution: vi.fn(),
86
+ timing: vi.fn(),
87
+ };
88
+ m.subscribe(adapter);
89
+
90
+ m.distribution('payload.size', 1024);
91
+ expect(adapter.distribution).toHaveBeenCalledWith(
92
+ 'payload.size',
93
+ 1024,
94
+ undefined,
95
+ );
96
+ });
97
+
98
+ test('Metrics: routes timing to all adapters', () => {
99
+ const m = new Metrics();
100
+ const adapter = {
101
+ increment: vi.fn(),
102
+ gauge: vi.fn(),
103
+ distribution: vi.fn(),
104
+ timing: vi.fn(),
105
+ };
106
+ m.subscribe(adapter);
107
+
108
+ m.timing('db.query', 42, { table: 'users' });
109
+ expect(adapter.timing).toHaveBeenCalledWith('db.query', 42, {
110
+ table: 'users',
111
+ });
112
+ });
113
+
114
+ test('Metrics: does not route to unsubscribed adapters', () => {
115
+ const m = new Metrics();
116
+ const adapter = {
117
+ increment: vi.fn(),
118
+ gauge: vi.fn(),
119
+ distribution: vi.fn(),
120
+ timing: vi.fn(),
121
+ };
122
+
123
+ const unsubscribe = m.subscribe(adapter);
124
+ unsubscribe();
125
+
126
+ m.increment('req');
127
+ expect(adapter.increment).not.toHaveBeenCalled();
128
+ });
129
+
130
+ test('metrics is a shared Metrics instance', () => {
131
+ expect(metrics).toBeInstanceOf(Metrics);
132
+ });
package/src/index.ts CHANGED
@@ -24,3 +24,75 @@ export interface LoggerAdapter {
24
24
  captureError(error: Error | null, attributes?: Record<string, any>): void;
25
25
  write(chunk: Record<string, any>): boolean;
26
26
  }
27
+
28
+ export type MetricAttributes = Record<string, string | number | boolean>;
29
+
30
+ /**
31
+ * Common interface for metric tracking backends (Prometheus, Sentry, etc.).
32
+ * Inspired by Node.js diagnostics channels — adapters subscribe to a shared
33
+ * metrics channel and receive all published measurements.
34
+ */
35
+ export interface MetricsAdapter {
36
+ /** Increment a monotonic counter */
37
+ increment(name: string, value?: number, attributes?: MetricAttributes): void;
38
+ /** Record the current value of a gauge */
39
+ gauge(name: string, value: number, attributes?: MetricAttributes): void;
40
+ /** Record a sample in a distribution/histogram */
41
+ distribution(
42
+ name: string,
43
+ value: number,
44
+ attributes?: MetricAttributes,
45
+ ): void;
46
+ /** Record a timing in milliseconds */
47
+ timing(name: string, value: number, attributes?: MetricAttributes): void;
48
+ }
49
+
50
+ /**
51
+ * A publish/subscribe metrics channel. Adapters register via `subscribe` and
52
+ * receive all subsequent measurements. Unsubscribe by calling the returned
53
+ * dispose function — the same pattern as `diagnostics_channel.subscribe`.
54
+ */
55
+ export class Metrics implements MetricsAdapter {
56
+ readonly #adapters = new Set<MetricsAdapter>();
57
+
58
+ subscribe(adapter: MetricsAdapter): () => void {
59
+ this.#adapters.add(adapter);
60
+ return () => {
61
+ this.#adapters.delete(adapter);
62
+ };
63
+ }
64
+
65
+ hasSubscribers(): boolean {
66
+ return this.#adapters.size > 0;
67
+ }
68
+
69
+ increment(name: string, value = 1, attributes?: MetricAttributes): void {
70
+ for (const adapter of this.#adapters) {
71
+ adapter.increment(name, value, attributes);
72
+ }
73
+ }
74
+
75
+ gauge(name: string, value: number, attributes?: MetricAttributes): void {
76
+ for (const adapter of this.#adapters) {
77
+ adapter.gauge(name, value, attributes);
78
+ }
79
+ }
80
+
81
+ distribution(
82
+ name: string,
83
+ value: number,
84
+ attributes?: MetricAttributes,
85
+ ): void {
86
+ for (const adapter of this.#adapters) {
87
+ adapter.distribution(name, value, attributes);
88
+ }
89
+ }
90
+
91
+ timing(name: string, value: number, attributes?: MetricAttributes): void {
92
+ for (const adapter of this.#adapters) {
93
+ adapter.timing(name, value, attributes);
94
+ }
95
+ }
96
+ }
97
+
98
+ export const metrics = new Metrics();