fhirsmith 0.3.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.
Files changed (277) hide show
  1. package/CHANGELOG.md +42 -0
  2. package/FHIRsmith.png +0 -0
  3. package/README.md +277 -0
  4. package/config-template.json +144 -0
  5. package/library/folder-setup.js +58 -0
  6. package/library/html-server.js +166 -0
  7. package/library/html.js +835 -0
  8. package/library/i18nsupport.js +259 -0
  9. package/library/languages.js +779 -0
  10. package/library/logger-telnet.js +205 -0
  11. package/library/logger.js +279 -0
  12. package/library/package-manager.js +876 -0
  13. package/library/utilities.js +196 -0
  14. package/library/version-utilities.js +1056 -0
  15. package/npmprojector/config-example.json +13 -0
  16. package/npmprojector/indexer.js +394 -0
  17. package/npmprojector/npmprojector.js +395 -0
  18. package/npmprojector/readme.md +174 -0
  19. package/npmprojector/watcher.js +335 -0
  20. package/package.json +119 -0
  21. package/packages/package-crawler.js +846 -0
  22. package/packages/packages-template.html +126 -0
  23. package/packages/packages.js +2838 -0
  24. package/passwords.ini +2 -0
  25. package/publisher/publisher-template.html +208 -0
  26. package/publisher/publisher.js +2167 -0
  27. package/publisher/task-draft.js +458 -0
  28. package/registry/api.js +735 -0
  29. package/registry/crawler.js +637 -0
  30. package/registry/model.js +513 -0
  31. package/registry/readme.md +243 -0
  32. package/registry/registry-data.json +121015 -0
  33. package/registry/registry-template.html +126 -0
  34. package/registry/registry.js +1395 -0
  35. package/registry/test-runner.js +237 -0
  36. package/root-template.html +124 -0
  37. package/server.js +524 -0
  38. package/shl/private-key.pem +5 -0
  39. package/shl/public-key.pem +18 -0
  40. package/shl/shl.js +1125 -0
  41. package/shl/vhl.js +69 -0
  42. package/static/FHIRsmith128.png +0 -0
  43. package/static/FHIRsmith16.png +0 -0
  44. package/static/FHIRsmith32.png +0 -0
  45. package/static/FHIRsmith64.png +0 -0
  46. package/static/assets/css/bootstrap-fhir.css +5302 -0
  47. package/static/assets/css/bootstrap-glyphicons.css +2 -0
  48. package/static/assets/css/bootstrap.css +4097 -0
  49. package/static/assets/css/jquery-ui.css +523 -0
  50. package/static/assets/css/jquery-ui.structure.css +863 -0
  51. package/static/assets/css/jquery-ui.structure.min.css +5 -0
  52. package/static/assets/css/jquery-ui.theme.css +439 -0
  53. package/static/assets/css/jquery-ui.theme.min.css +5 -0
  54. package/static/assets/css/jquery.ui.all.css +7 -0
  55. package/static/assets/css/modules.css +18 -0
  56. package/static/assets/css/project.css +367 -0
  57. package/static/assets/css/pygments-manni.css +66 -0
  58. package/static/assets/css/tags.css +74 -0
  59. package/static/assets/css/xml.css +2 -0
  60. package/static/assets/fonts/glyphiconshalflings-regular.eot +0 -0
  61. package/static/assets/fonts/glyphiconshalflings-regular.otf +0 -0
  62. package/static/assets/fonts/glyphiconshalflings-regular.svg +175 -0
  63. package/static/assets/fonts/glyphiconshalflings-regular.ttf +0 -0
  64. package/static/assets/fonts/glyphiconshalflings-regular.woff +0 -0
  65. package/static/assets/ico/apple-touch-icon-114-precomposed.png +0 -0
  66. package/static/assets/ico/apple-touch-icon-144-precomposed.png +0 -0
  67. package/static/assets/ico/apple-touch-icon-57-precomposed.png +0 -0
  68. package/static/assets/ico/apple-touch-icon-72-precomposed.png +0 -0
  69. package/static/assets/ico/favicon.ico +0 -0
  70. package/static/assets/ico/favicon.png +0 -0
  71. package/static/assets/images/fhir-logo-www.png +0 -0
  72. package/static/assets/images/fhir-logo.png +0 -0
  73. package/static/assets/images/hl7-logo.png +0 -0
  74. package/static/assets/images/logo_ansinew.jpg +0 -0
  75. package/static/assets/images/search.png +0 -0
  76. package/static/assets/images/stripe.png +0 -0
  77. package/static/assets/images/target.png +0 -0
  78. package/static/assets/images/tx-registry-root.gif +0 -0
  79. package/static/assets/images/tx-registry.png +0 -0
  80. package/static/assets/images/tx-server.png +0 -0
  81. package/static/assets/images/tx-version.png +0 -0
  82. package/static/assets/js/bootstrap.min.js +6 -0
  83. package/static/assets/js/fhir-gw.js +259 -0
  84. package/static/assets/js/fhir.js +2 -0
  85. package/static/assets/js/html5shiv.js +8 -0
  86. package/static/assets/js/jcookie.js +96 -0
  87. package/static/assets/js/jquery-ui.min.js +6 -0
  88. package/static/assets/js/jquery.js +10716 -0
  89. package/static/assets/js/jquery.min.js +2 -0
  90. package/static/assets/js/jquery.ui.core.js +314 -0
  91. package/static/assets/js/jquery.ui.draggable.js +825 -0
  92. package/static/assets/js/jquery.ui.mouse.js +162 -0
  93. package/static/assets/js/jquery.ui.resizable.js +842 -0
  94. package/static/assets/js/jquery.ui.widget.js +268 -0
  95. package/static/assets/js/json2.js +487 -0
  96. package/static/assets/js/jtip.js +97 -0
  97. package/static/assets/js/respond.min.js +6 -0
  98. package/static/assets/js/statuspage.js +70 -0
  99. package/static/assets/js/xml.js +2 -0
  100. package/static/dist/js/bootstrap.js +1964 -0
  101. package/static/favicon.png +0 -0
  102. package/static/fhir.css +626 -0
  103. package/static/icon-fhir-16.png +0 -0
  104. package/static/images/ui-bg_diagonals-thick_18_b81900_40x40.png +0 -0
  105. package/static/images/ui-bg_diagonals-thick_20_666666_40x40.png +0 -0
  106. package/static/images/ui-bg_flat_10_000000_40x100.png +0 -0
  107. package/static/images/ui-bg_glass_100_f6f6f6_1x400.png +0 -0
  108. package/static/images/ui-bg_glass_100_fdf5ce_1x400.png +0 -0
  109. package/static/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
  110. package/static/images/ui-bg_gloss-wave_35_f6a828_500x100.png +0 -0
  111. package/static/images/ui-bg_highlight-soft_100_eeeeee_1x100.png +0 -0
  112. package/static/images/ui-bg_highlight-soft_75_ffe45c_1x100.png +0 -0
  113. package/static/images/ui-icons_222222_256x240.png +0 -0
  114. package/static/images/ui-icons_228ef1_256x240.png +0 -0
  115. package/static/images/ui-icons_ef8c08_256x240.png +0 -0
  116. package/static/images/ui-icons_ffd27a_256x240.png +0 -0
  117. package/static/images/ui-icons_ffffff_256x240.png +0 -0
  118. package/static/js/jquery.effects.blind.js +49 -0
  119. package/static/js/jquery.effects.bounce.js +78 -0
  120. package/static/js/jquery.effects.clip.js +54 -0
  121. package/static/js/jquery.effects.core.js +763 -0
  122. package/static/js/jquery.effects.drop.js +50 -0
  123. package/static/js/jquery.effects.explode.js +79 -0
  124. package/static/js/jquery.effects.fade.js +32 -0
  125. package/static/js/jquery.effects.fold.js +56 -0
  126. package/static/js/jquery.effects.highlight.js +50 -0
  127. package/static/js/jquery.effects.pulsate.js +51 -0
  128. package/static/js/jquery.effects.scale.js +178 -0
  129. package/static/js/jquery.effects.shake.js +57 -0
  130. package/static/js/jquery.effects.slide.js +50 -0
  131. package/static/js/jquery.effects.transfer.js +45 -0
  132. package/static/js/jquery.ui.accordion.js +611 -0
  133. package/static/js/jquery.ui.autocomplete.js +612 -0
  134. package/static/js/jquery.ui.button.js +416 -0
  135. package/static/js/jquery.ui.datepicker.js +1823 -0
  136. package/static/js/jquery.ui.dialog.js +878 -0
  137. package/static/js/jquery.ui.droppable.js +296 -0
  138. package/static/js/jquery.ui.position.js +252 -0
  139. package/static/js/jquery.ui.progressbar.js +109 -0
  140. package/static/js/jquery.ui.selectable.js +266 -0
  141. package/static/js/jquery.ui.slider.js +666 -0
  142. package/static/js/jquery.ui.sortable.js +1077 -0
  143. package/static/js/jquery.ui.tabs.js +758 -0
  144. package/stats.js +80 -0
  145. package/test-cache/vsac/vsac-valuesets.db +0 -0
  146. package/token/nginx_passport_setup.md +383 -0
  147. package/token/security_guide.md +294 -0
  148. package/token/token-template.html +330 -0
  149. package/token/token.js +1300 -0
  150. package/translations/Messages.properties +1510 -0
  151. package/translations/Messages_ar.properties +1399 -0
  152. package/translations/Messages_de.properties +836 -0
  153. package/translations/Messages_es.properties +737 -0
  154. package/translations/Messages_fr.properties +1 -0
  155. package/translations/Messages_ja.properties +893 -0
  156. package/translations/Messages_nl.properties +1357 -0
  157. package/translations/Messages_pt.properties +1302 -0
  158. package/translations/Messages_ru.properties +1 -0
  159. package/translations/Messages_uz.properties +1 -0
  160. package/translations/Messages_zh.properties +1 -0
  161. package/translations/rendering-phrases.properties +1128 -0
  162. package/translations/rendering-phrases_ar.properties +1091 -0
  163. package/translations/rendering-phrases_de.properties +6 -0
  164. package/translations/rendering-phrases_es.properties +6 -0
  165. package/translations/rendering-phrases_fr.properties +624 -0
  166. package/translations/rendering-phrases_ja.properties +21 -0
  167. package/translations/rendering-phrases_nl.properties +970 -0
  168. package/translations/rendering-phrases_pt.properties +1020 -0
  169. package/translations/rendering-phrases_ru.properties +1094 -0
  170. package/translations/rendering-phrases_uz.properties +1 -0
  171. package/translations/rendering-phrases_zh.properties +1 -0
  172. package/tx/README.md +418 -0
  173. package/tx/cm/cm-api.js +110 -0
  174. package/tx/cm/cm-database.js +735 -0
  175. package/tx/cm/cm-package.js +325 -0
  176. package/tx/cs/cs-api.js +789 -0
  177. package/tx/cs/cs-areacode.js +615 -0
  178. package/tx/cs/cs-country.js +1110 -0
  179. package/tx/cs/cs-cpt.js +785 -0
  180. package/tx/cs/cs-cs.js +1579 -0
  181. package/tx/cs/cs-currency.js +539 -0
  182. package/tx/cs/cs-db.js +1321 -0
  183. package/tx/cs/cs-hgvs.js +329 -0
  184. package/tx/cs/cs-lang.js +465 -0
  185. package/tx/cs/cs-loinc.js +1485 -0
  186. package/tx/cs/cs-mimetypes.js +238 -0
  187. package/tx/cs/cs-ndc.js +704 -0
  188. package/tx/cs/cs-omop.js +1025 -0
  189. package/tx/cs/cs-provider-api.js +43 -0
  190. package/tx/cs/cs-provider-list.js +37 -0
  191. package/tx/cs/cs-rxnorm.js +808 -0
  192. package/tx/cs/cs-snomed.js +1102 -0
  193. package/tx/cs/cs-ucum.js +514 -0
  194. package/tx/cs/cs-unii.js +271 -0
  195. package/tx/cs/cs-uri.js +218 -0
  196. package/tx/cs/cs-usstates.js +305 -0
  197. package/tx/dev.fhir.org.yml +14 -0
  198. package/tx/fixtures/test-cases-setup.json +18 -0
  199. package/tx/fixtures/test-cases.yml +16 -0
  200. package/tx/html/codesystem-operations.liquid +25 -0
  201. package/tx/html/home-metrics.liquid +247 -0
  202. package/tx/html/operations-form.liquid +148 -0
  203. package/tx/html/search-form.liquid +62 -0
  204. package/tx/html/tx-template.html +133 -0
  205. package/tx/html/valueset-operations.liquid +54 -0
  206. package/tx/importers/atc-to-fhir.js +316 -0
  207. package/tx/importers/import-loinc.module.js +1536 -0
  208. package/tx/importers/import-ndc.module.js +1088 -0
  209. package/tx/importers/import-rxnorm.module.js +898 -0
  210. package/tx/importers/import-sct.module.js +2457 -0
  211. package/tx/importers/import-unii.module.js +601 -0
  212. package/tx/importers/readme.md +453 -0
  213. package/tx/importers/subset-loinc.module.js +1081 -0
  214. package/tx/importers/subset-rxnorm.module.js +938 -0
  215. package/tx/importers/tx-import-base.js +351 -0
  216. package/tx/importers/tx-import-settings.js +310 -0
  217. package/tx/importers/tx-import.js +357 -0
  218. package/tx/library/canonical-resource.js +88 -0
  219. package/tx/library/capabilitystatement.js +292 -0
  220. package/tx/library/codesystem.js +774 -0
  221. package/tx/library/conceptmap.js +568 -0
  222. package/tx/library/designations.js +932 -0
  223. package/tx/library/errors.js +77 -0
  224. package/tx/library/extensions.js +117 -0
  225. package/tx/library/namingsystem.js +322 -0
  226. package/tx/library/operation-outcome.js +127 -0
  227. package/tx/library/parameters.js +105 -0
  228. package/tx/library/renderer.js +1559 -0
  229. package/tx/library/terminologycapabilities.js +418 -0
  230. package/tx/library/ucum-parsers.js +1029 -0
  231. package/tx/library/ucum-service.js +370 -0
  232. package/tx/library/ucum-types.js +1099 -0
  233. package/tx/library/valueset.js +543 -0
  234. package/tx/library.js +676 -0
  235. package/tx/ocl/cm-ocl.js +106 -0
  236. package/tx/ocl/cs-ocl.js +39 -0
  237. package/tx/ocl/vs-ocl.js +105 -0
  238. package/tx/operation-context.js +568 -0
  239. package/tx/params.js +613 -0
  240. package/tx/provider.js +403 -0
  241. package/tx/sct/ecl.js +1560 -0
  242. package/tx/sct/expressions.js +2077 -0
  243. package/tx/sct/structures.js +1396 -0
  244. package/tx/tx-html.js +1063 -0
  245. package/tx/tx.fhir.org.yml +39 -0
  246. package/tx/tx.js +927 -0
  247. package/tx/vs/vs-api.js +112 -0
  248. package/tx/vs/vs-database.js +786 -0
  249. package/tx/vs/vs-package.js +358 -0
  250. package/tx/vs/vs-vsac.js +366 -0
  251. package/tx/workers/batch-validate.js +129 -0
  252. package/tx/workers/batch.js +361 -0
  253. package/tx/workers/closure.js +32 -0
  254. package/tx/workers/expand.js +1845 -0
  255. package/tx/workers/lookup.js +407 -0
  256. package/tx/workers/metadata.js +467 -0
  257. package/tx/workers/operations.js +34 -0
  258. package/tx/workers/read.js +164 -0
  259. package/tx/workers/search.js +384 -0
  260. package/tx/workers/subsumes.js +334 -0
  261. package/tx/workers/translate.js +492 -0
  262. package/tx/workers/validate.js +2504 -0
  263. package/tx/workers/worker.js +904 -0
  264. package/tx/xml/capabilitystatement-xml.js +63 -0
  265. package/tx/xml/codesystem-xml.js +62 -0
  266. package/tx/xml/conceptmap-xml.js +65 -0
  267. package/tx/xml/namingsystem-xml.js +65 -0
  268. package/tx/xml/operationoutcome-xml.js +127 -0
  269. package/tx/xml/parameters-xml.js +312 -0
  270. package/tx/xml/terminologycapabilities-xml.js +64 -0
  271. package/tx/xml/valueset-xml.js +64 -0
  272. package/tx/xml/xml-base.js +603 -0
  273. package/vcl/vcl-parser.js +1098 -0
  274. package/vcl/vcl.js +253 -0
  275. package/windows-install.js +19 -0
  276. package/xig/xig-template.html +124 -0
  277. package/xig/xig.js +3049 -0
@@ -0,0 +1,205 @@
1
+ /**
2
+ * Custom Winston transport for streaming logs over a TCP socket
3
+ * This allows viewing logs remotely via telnet or a custom client
4
+ */
5
+ const winston = require('winston');
6
+ const net = require('net');
7
+ const Transport = winston.Transport;
8
+
9
+ /**
10
+ * Winston transport that streams logs to connected TCP clients
11
+ * @extends {winston.Transport}
12
+ */
13
+ class SocketTransport extends Transport {
14
+ /**
15
+ * Create a new socket transport
16
+ * @param {Object} options - Transport options
17
+ * @param {string} [options.host='127.0.0.1'] - Host to bind to
18
+ * @param {number} [options.port=9300] - Port to listen on
19
+ * @param {string} [options.level='info'] - Minimum log level to stream
20
+ * @param {Function} [options.format] - Custom formatter function (msg, level, meta) => string
21
+ */
22
+ constructor(options = {}) {
23
+ super(options);
24
+ this.name = 'socket';
25
+ this.level = options.level || 'info';
26
+ this.clients = new Set();
27
+ this.format = options.format || this._defaultFormat;
28
+
29
+ // Create TCP server
30
+ this.server = net.createServer((socket) => {
31
+ console.log(`Client connected to log stream from ${socket.remoteAddress}`);
32
+
33
+ // Send welcome message
34
+ socket.write(`=== Connected to log stream (${new Date().toISOString()}) ===\n`);
35
+ socket.write(`=== Log level: ${this.level} ===\n\n`);
36
+
37
+ // Add to clients set
38
+ this.clients.add(socket);
39
+
40
+ socket.on('close', () => {
41
+ console.log(`Client disconnected from log stream: ${socket.remoteAddress}`);
42
+ this.clients.delete(socket);
43
+ });
44
+
45
+ socket.on('error', (err) => {
46
+ console.error(`Socket error: ${err.message}`);
47
+ this.clients.delete(socket);
48
+ });
49
+
50
+ // Support for simple commands
51
+ socket.on('data', (data) => {
52
+ const command = data.toString().trim().toLowerCase();
53
+
54
+ if (command === 'help') {
55
+ socket.write(this._getHelpText());
56
+ } else if (command === 'stats') {
57
+ socket.write(this._getStatsText());
58
+ } else if (command === 'quit' || command === 'exit') {
59
+ socket.end('=== Disconnected ===\n');
60
+ } else if (command.startsWith('level ')) {
61
+ const newLevel = command.split(' ')[1];
62
+ if (['error', 'warn', 'info', 'debug', 'verbose', 'silly'].includes(newLevel)) {
63
+ this.level = newLevel;
64
+ socket.write(`=== Log level changed to ${newLevel} ===\n`);
65
+ } else {
66
+ socket.write(`=== Invalid log level: ${newLevel} ===\n`);
67
+ }
68
+ }
69
+ });
70
+ });
71
+
72
+ // Start listening
73
+ const port = options.port || 9300;
74
+ const host = options.host || '127.0.0.1';
75
+
76
+ this.server.listen(port, host, () => {
77
+ console.log(`Log socket server running on ${host}:${port}`);
78
+ });
79
+
80
+ this.server.on('error', (err) => {
81
+ console.error(`Socket transport error: ${err.message}`);
82
+ });
83
+ }
84
+
85
+ /**
86
+ * Winston transport method called for each log
87
+ * @param {Object} info - Log information
88
+ * @param {Function} callback - Callback function
89
+ */
90
+ log(info, callback) {
91
+ setImmediate(() => {
92
+ this.emit('logged', info);
93
+ });
94
+
95
+ // Skip if no clients connected
96
+ if (this.clients.size === 0) {
97
+ callback();
98
+ return;
99
+ }
100
+
101
+ // Format the log entry
102
+ const logEntry = this.format(info);
103
+
104
+ // Send to all connected clients
105
+ const deadClients = [];
106
+ for (const client of this.clients) {
107
+ try {
108
+ client.write(logEntry);
109
+ } catch (err) {
110
+ deadClients.push(client);
111
+ }
112
+ }
113
+
114
+ // Remove dead connections
115
+ deadClients.forEach(client => this.clients.delete(client));
116
+
117
+ callback();
118
+ }
119
+
120
+ /**
121
+ * Default log formatter
122
+ * @param {Object} info - Log information
123
+ * @returns {string} Formatted log entry
124
+ * @private
125
+ */
126
+ _defaultFormat(info) {
127
+ const timestamp = info.timestamp || new Date().toISOString();
128
+ const level = info.level.toUpperCase().padEnd(7);
129
+ const message = info.message || '';
130
+
131
+ // Extract metadata excluding standard fields
132
+ const meta = { ...info };
133
+ delete meta.timestamp;
134
+ delete meta.level;
135
+ delete meta.message;
136
+
137
+ const metaStr = Object.keys(meta).length > 0
138
+ ? ` ${JSON.stringify(meta)}`
139
+ : '';
140
+
141
+ return `${timestamp} [${level}] ${message}${metaStr}\n`;
142
+ }
143
+
144
+ /**
145
+ * Get help text for connected clients
146
+ * @returns {string} Help text
147
+ * @private
148
+ */
149
+ _getHelpText() {
150
+ return `
151
+ === Log Viewer Commands ===
152
+ help - Show this help
153
+ stats - Show connection statistics
154
+ level <level> - Change log level (error, warn, info, debug, verbose, silly)
155
+ quit/exit - Disconnect
156
+
157
+ === Log Format ===
158
+ TIMESTAMP [LEVEL] MESSAGE {metadata}
159
+
160
+ === Examples ===
161
+ level debug - Show debug and higher priority logs
162
+ level error - Show only error logs
163
+
164
+ `.trim() + '\n\n';
165
+ }
166
+
167
+ /**
168
+ * Get statistics text for connected clients
169
+ * @returns {string} Statistics text
170
+ * @private
171
+ */
172
+ _getStatsText() {
173
+ return `
174
+ === Log Statistics ===
175
+ Connected clients: ${this.clients.size}
176
+ Current log level: ${this.level}
177
+ Server started: ${this.server.startTime || 'unknown'}
178
+ Current time: ${new Date().toISOString()}
179
+
180
+ `.trim() + '\n\n';
181
+ }
182
+
183
+ /**
184
+ * Close the socket server
185
+ * @param {Function} [callback] - Callback function
186
+ */
187
+ close(callback) {
188
+ // Notify all clients
189
+ for (const client of this.clients) {
190
+ try {
191
+ client.end('=== Log server shutting down ===\n');
192
+ } catch (err) {
193
+ // Ignore errors during shutdown
194
+ }
195
+ }
196
+
197
+ // Close server
198
+ this.server.close(callback);
199
+ }
200
+ }
201
+
202
+ // Register the transport with Winston
203
+ winston.transports.Socket = SocketTransport;
204
+
205
+ module.exports = SocketTransport;
@@ -0,0 +1,279 @@
1
+ const winston = require('winston');
2
+ require('winston-daily-rotate-file');
3
+ const fs = require('fs');
4
+ const folders = require('./folder-setup');
5
+
6
+ class Logger {
7
+ static _instance = null;
8
+
9
+ static getInstance(options = {}) {
10
+ if (!Logger._instance) {
11
+ Logger._instance = new Logger(options);
12
+ }
13
+ return Logger._instance;
14
+ }
15
+
16
+ constructor(options = {}) {
17
+ this.options = {
18
+ level: options.level || 'info',
19
+ logDir: options.logDir || folders.logsDir(),
20
+ console: options.console !== undefined ? options.console : true,
21
+ consoleErrors: options.consoleErrors !== undefined ? options.consoleErrors : false,
22
+ telnetErrors: options.telnetErrors !== undefined ? options.telnetErrors : false,
23
+ file: {
24
+ filename: options.file?.filename || 'server-%DATE%.log',
25
+ datePattern: options.file?.datePattern || 'YYYY-MM-DD',
26
+ maxSize: options.file?.maxSize || '20m',
27
+ maxFiles: options.file?.maxFiles || 14
28
+ }
29
+ };
30
+
31
+ // Ensure log directory exists
32
+ if (!fs.existsSync(this.options.logDir)) {
33
+ fs.mkdirSync(this.options.logDir, { recursive: true });
34
+ }
35
+
36
+ // Telnet clients storage
37
+ this.telnetClients = new Set();
38
+
39
+ this._createLogger();
40
+
41
+ // Log logger initialization
42
+ this.info('Logger initialized @ ' + this.options.logDir, {
43
+ level: this.options.level,
44
+ logDir: this.options.logDir
45
+ });
46
+ }
47
+
48
+ _createLogger() {
49
+ // Define formats for file output (with full metadata including stack traces)
50
+ const fileFormats = [
51
+ winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss.SSS' }),
52
+ winston.format.errors({ stack: true }),
53
+ winston.format.splat(),
54
+ winston.format.json()
55
+ ];
56
+
57
+ // Create transports
58
+ const transports = [];
59
+
60
+ // Add file transport with rotation (includes ALL levels with full metadata)
61
+ const fileTransport = new winston.transports.DailyRotateFile({
62
+ dirname: this.options.logDir,
63
+ filename: this.options.file.filename,
64
+ datePattern: this.options.file.datePattern,
65
+ maxSize: this.options.file.maxSize,
66
+ maxFiles: this.options.file.maxFiles,
67
+ level: this.options.level,
68
+ format: winston.format.combine(...fileFormats)
69
+ });
70
+ transports.push(fileTransport);
71
+
72
+ // Add console transport if enabled
73
+ if (this.options.console) {
74
+ const consoleFormat = winston.format.combine(
75
+ winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss.SSS' }),
76
+ winston.format.errors({ stack: true }),
77
+ winston.format.colorize({ all: true }),
78
+ winston.format.printf(info => {
79
+ const stack = info.stack ? `\n${info.stack}` : '';
80
+ return `${info.timestamp} ${info.level.padEnd(7)} ${info.message}${stack}`;
81
+ })
82
+ );
83
+
84
+ const consoleTransport = new winston.transports.Console({
85
+ level: this.options.level,
86
+ format: consoleFormat
87
+ });
88
+
89
+ transports.push(consoleTransport);
90
+ }
91
+
92
+ // Create the winston logger
93
+ this.logger = winston.createLogger({
94
+ level: this.options.level,
95
+ transports,
96
+ exitOnError: false
97
+ });
98
+ }
99
+
100
+ // Telnet client management
101
+ addTelnetClient(socket) {
102
+ this.telnetClients.add(socket);
103
+ }
104
+
105
+ removeTelnetClient(socket) {
106
+ this.telnetClients.delete(socket);
107
+ }
108
+
109
+ _sendToTelnet(level, message, stack, options) {
110
+ // Check if we should send errors/warnings to telnet
111
+ if (!options.telnetErrors && (level === 'error' || level === 'warn')) {
112
+ return;
113
+ }
114
+
115
+ const timestamp = new Date().toISOString().replace('T', ' ').substring(0, 23);
116
+ let line = `${timestamp} ${level.padEnd(7)} ${message}\n`;
117
+ if (stack) {
118
+ line += stack + '\n';
119
+ }
120
+
121
+ for (const client of this.telnetClients) {
122
+ try {
123
+ client.write(line);
124
+ } catch (e) {
125
+ // Client disconnected, remove it
126
+ this.telnetClients.delete(client);
127
+ }
128
+ }
129
+ }
130
+
131
+ _shouldLogToConsole(level, options) {
132
+ if (level === 'error' || level === 'warn') {
133
+ return options.consoleErrors;
134
+ }
135
+ return true;
136
+ }
137
+
138
+ _log(level, messageOrError, meta, options) {
139
+ let message;
140
+ let stack;
141
+
142
+ // Check if we should skip console for errors/warnings
143
+ const skipConsole = !this._shouldLogToConsole(level, options);
144
+
145
+ // Handle Error objects
146
+ if (messageOrError instanceof Error) {
147
+ message = messageOrError.message;
148
+ stack = messageOrError.stack;
149
+ if (skipConsole) {
150
+ // Log only to file transport
151
+ this.logger.transports
152
+ .filter(t => !(t instanceof winston.transports.Console))
153
+ .forEach(t => t.log({ level, message, stack, ...meta }));
154
+ } else {
155
+ this.logger[level](message, {stack, ...meta});
156
+ }
157
+ } else {
158
+ message = String(messageOrError);
159
+ stack = meta?.stack;
160
+ if (skipConsole) {
161
+ this.logger.transports
162
+ .filter(t => !(t instanceof winston.transports.Console))
163
+ .forEach(t => t.log({ level, message, ...meta }));
164
+ } else {
165
+ this.logger[level](message, meta);
166
+ }
167
+ }
168
+
169
+ this._sendToTelnet(level, message, stack, options);
170
+ }
171
+
172
+ error(message, meta = {}) {
173
+ this._log('error', message, meta, this.options);
174
+ }
175
+
176
+ warn(message, meta = {}) {
177
+ this._log('warn', message, meta, this.options);
178
+ }
179
+
180
+ info(message, meta = {}) {
181
+ this._log('info', message, meta, this.options);
182
+ }
183
+
184
+ debug(message, meta = {}) {
185
+ this._log('debug', message, meta, this.options);
186
+ }
187
+
188
+ verbose(message, meta = {}) {
189
+ this._log('verbose', message, meta, this.options);
190
+ }
191
+
192
+ log(level, message, meta = {}) {
193
+ this._log(level, message, meta, this.options);
194
+ }
195
+
196
+ child(defaultMeta = {}) {
197
+ const self = this;
198
+
199
+ // Build module-specific options
200
+ const childOptions = {
201
+ consoleErrors: defaultMeta.consoleErrors ?? self.options.consoleErrors,
202
+ telnetErrors: defaultMeta.telnetErrors ?? self.options.telnetErrors
203
+ };
204
+
205
+ // Remove our custom options from defaultMeta so they don't pollute log output
206
+ const cleanMeta = { ...defaultMeta };
207
+ delete cleanMeta.consoleErrors;
208
+ delete cleanMeta.telnetErrors;
209
+
210
+ if (cleanMeta.module) {
211
+ const modulePrefix = `{${cleanMeta.module}}`;
212
+
213
+ return {
214
+ error: (messageOrError, meta = {}) => {
215
+ if (messageOrError instanceof Error) {
216
+ const prefixedError = new Error(`${modulePrefix}: ${messageOrError.message}`);
217
+ prefixedError.stack = messageOrError.stack;
218
+ self._log('error', prefixedError, meta, childOptions);
219
+ } else {
220
+ self._log('error', `${modulePrefix}: ${messageOrError}`, meta, childOptions);
221
+ }
222
+ },
223
+ warn: (messageOrError, meta = {}) => {
224
+ if (messageOrError instanceof Error) {
225
+ const prefixedError = new Error(`${modulePrefix}: ${messageOrError.message}`);
226
+ prefixedError.stack = messageOrError.stack;
227
+ self._log('warn', prefixedError, meta, childOptions);
228
+ } else {
229
+ self._log('warn', `${modulePrefix}: ${messageOrError}`, meta, childOptions);
230
+ }
231
+ },
232
+ info: (message, meta = {}) => self._log('info', `${modulePrefix}: ${message}`, meta, childOptions),
233
+ debug: (message, meta = {}) => self._log('debug', `${modulePrefix}: ${message}`, meta, childOptions),
234
+ verbose: (message, meta = {}) => self._log('verbose', `${modulePrefix}: ${message}`, meta, childOptions),
235
+ log: (level, message, meta = {}) => self._log(level, `${modulePrefix}: ${message}`, meta, childOptions)
236
+ };
237
+ }
238
+
239
+ // For other metadata without module prefix
240
+ const childLogger = {
241
+ error: (messageOrError, meta = {}) => self._log('error', messageOrError, { ...cleanMeta, ...meta }, childOptions),
242
+ warn: (messageOrError, meta = {}) => self._log('warn', messageOrError, { ...cleanMeta, ...meta }, childOptions),
243
+ info: (message, meta = {}) => self._log('info', message, { ...cleanMeta, ...meta }, childOptions),
244
+ debug: (message, meta = {}) => self._log('debug', message, { ...cleanMeta, ...meta }, childOptions),
245
+ verbose: (message, meta = {}) => self._log('verbose', message, { ...cleanMeta, ...meta }, childOptions),
246
+ log: (level, message, meta = {}) => self._log(level, message, { ...cleanMeta, ...meta }, childOptions)
247
+ };
248
+
249
+ return childLogger;
250
+ }
251
+
252
+ setLevel(level) {
253
+ this.options.level = level;
254
+ this.logger.transports.forEach(transport => {
255
+ transport.level = level;
256
+ });
257
+ this.info(`Log level changed to ${level}`);
258
+ }
259
+
260
+ setConsoleErrors(enabled) {
261
+ this.options.consoleErrors = enabled;
262
+ this.info(`Console errors ${enabled ? 'enabled' : 'disabled'}`);
263
+ }
264
+
265
+ setTelnetErrors(enabled) {
266
+ this.options.telnetErrors = enabled;
267
+ this.info(`Telnet errors ${enabled ? 'enabled' : 'disabled'}`);
268
+ }
269
+
270
+ stream() {
271
+ return {
272
+ write: (message) => {
273
+ this.info(message.trim());
274
+ }
275
+ };
276
+ }
277
+ }
278
+
279
+ module.exports = Logger;