@sera4/essentia 1.1.64 → 1.1.66-rc.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 (245) hide show
  1. package/coverage/base.css +224 -0
  2. package/coverage/block-navigation.js +87 -0
  3. package/coverage/configurations/index.html +116 -0
  4. package/coverage/configurations/server-config.ts.html +94 -0
  5. package/coverage/dns/DnsCache.ts.html +598 -0
  6. package/coverage/dns/index.html +131 -0
  7. package/coverage/dns/index.ts.html +94 -0
  8. package/coverage/favicon.png +0 -0
  9. package/coverage/health/index.html +116 -0
  10. package/coverage/health/index.ts.html +778 -0
  11. package/coverage/index.html +161 -0
  12. package/coverage/lcov-report/base.css +224 -0
  13. package/coverage/lcov-report/block-navigation.js +87 -0
  14. package/coverage/lcov-report/configurations/index.html +116 -0
  15. package/coverage/lcov-report/configurations/server-config.ts.html +94 -0
  16. package/coverage/lcov-report/dns/DnsCache.ts.html +598 -0
  17. package/coverage/lcov-report/dns/index.html +131 -0
  18. package/coverage/lcov-report/dns/index.ts.html +94 -0
  19. package/coverage/lcov-report/favicon.png +0 -0
  20. package/coverage/lcov-report/health/index.html +116 -0
  21. package/coverage/lcov-report/health/index.ts.html +778 -0
  22. package/coverage/lcov-report/index.html +161 -0
  23. package/coverage/lcov-report/logger/index.html +131 -0
  24. package/coverage/lcov-report/logger/index.ts.html +94 -0
  25. package/coverage/lcov-report/logger/s4-logger.ts.html +514 -0
  26. package/coverage/lcov-report/prettify.css +1 -0
  27. package/coverage/lcov-report/prettify.js +2 -0
  28. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  29. package/coverage/lcov-report/sorter.js +196 -0
  30. package/coverage/lcov.info +747 -0
  31. package/coverage/logger/index.html +131 -0
  32. package/coverage/logger/index.ts.html +94 -0
  33. package/coverage/logger/s4-logger.ts.html +514 -0
  34. package/coverage/prettify.css +1 -0
  35. package/coverage/prettify.js +2 -0
  36. package/coverage/sort-arrow-sprite.png +0 -0
  37. package/coverage/sorter.js +196 -0
  38. package/coverage/tmp/coverage-24302-1746472691313-0.json +1 -0
  39. package/coverage/tmp/coverage-24302-1746472691366-1.json +1 -0
  40. package/dist/cache/index.d.ts +22 -0
  41. package/dist/cache/index.js +166 -0
  42. package/dist/cache/index.js.map +1 -0
  43. package/dist/constants/index.d.ts +6 -0
  44. package/dist/constants/index.js +9 -0
  45. package/dist/constants/index.js.map +1 -0
  46. package/dist/coverage/block-navigation.d.ts +1 -0
  47. package/dist/coverage/block-navigation.js +70 -0
  48. package/dist/coverage/block-navigation.js.map +1 -0
  49. package/dist/coverage/lcov-report/block-navigation.d.ts +1 -0
  50. package/dist/coverage/lcov-report/block-navigation.js +70 -0
  51. package/dist/coverage/lcov-report/block-navigation.js.map +1 -0
  52. package/dist/coverage/lcov-report/prettify.d.ts +0 -0
  53. package/dist/coverage/lcov-report/prettify.js +477 -0
  54. package/dist/coverage/lcov-report/prettify.js.map +1 -0
  55. package/dist/coverage/lcov-report/sorter.d.ts +1 -0
  56. package/dist/coverage/lcov-report/sorter.js +163 -0
  57. package/dist/coverage/lcov-report/sorter.js.map +1 -0
  58. package/dist/coverage/prettify.d.ts +0 -0
  59. package/dist/coverage/prettify.js +477 -0
  60. package/dist/coverage/prettify.js.map +1 -0
  61. package/dist/coverage/sorter.d.ts +1 -0
  62. package/dist/coverage/sorter.js +163 -0
  63. package/dist/coverage/sorter.js.map +1 -0
  64. package/dist/formatter/index.d.ts +37 -0
  65. package/dist/formatter/index.js +75 -0
  66. package/dist/formatter/index.js.map +1 -0
  67. package/dist/hal/index.d.ts +5 -0
  68. package/dist/hal/index.js +69 -0
  69. package/dist/hal/index.js.map +1 -0
  70. package/dist/health/health-logger.d.ts +9 -0
  71. package/dist/health/health-logger.js +34 -0
  72. package/dist/health/health-logger.js.map +1 -0
  73. package/dist/health/index.d.ts +19 -0
  74. package/dist/health/index.js +175 -0
  75. package/dist/health/index.js.map +1 -0
  76. package/dist/helpers/index.d.ts +1 -0
  77. package/dist/helpers/index.js +2 -0
  78. package/dist/helpers/index.js.map +1 -0
  79. package/dist/helpers/test-server-wrapper.d.ts +120 -0
  80. package/dist/helpers/test-server-wrapper.js +165 -0
  81. package/dist/helpers/test-server-wrapper.js.map +1 -0
  82. package/dist/index.d.ts +1 -0
  83. package/dist/index.js +17 -0
  84. package/dist/index.js.map +1 -0
  85. package/dist/last-commit.d.ts +1 -0
  86. package/dist/last-commit.js +12 -0
  87. package/dist/last-commit.js.map +1 -0
  88. package/dist/last_commit/index.d.ts +7 -0
  89. package/dist/last_commit/index.js +83 -0
  90. package/dist/last_commit/index.js.map +1 -0
  91. package/dist/paginator/s4-pagination.d.ts +9 -0
  92. package/dist/paginator/s4-pagination.js +49 -0
  93. package/dist/paginator/s4-pagination.js.map +1 -0
  94. package/dist/paginator/sql-pagination.d.ts +27 -0
  95. package/dist/paginator/sql-pagination.js +166 -0
  96. package/dist/paginator/sql-pagination.js.map +1 -0
  97. package/dist/paper-trail/helpers.d.ts +11 -0
  98. package/dist/paper-trail/helpers.js +74 -0
  99. package/dist/paper-trail/helpers.js.map +1 -0
  100. package/dist/paper-trail/index.d.ts +13 -0
  101. package/dist/paper-trail/index.js +662 -0
  102. package/dist/paper-trail/index.js.map +1 -0
  103. package/dist/prompts/index.d.ts +14 -0
  104. package/dist/prompts/index.js +63 -0
  105. package/dist/prompts/index.js.map +1 -0
  106. package/dist/queue/index.d.ts +166 -0
  107. package/dist/queue/index.js +371 -0
  108. package/dist/queue/index.js.map +1 -0
  109. package/dist/queue/publisher.d.ts +31 -0
  110. package/dist/queue/publisher.js +84 -0
  111. package/dist/queue/publisher.js.map +1 -0
  112. package/dist/queue/queue-logger.d.ts +9 -0
  113. package/dist/queue/queue-logger.js +35 -0
  114. package/dist/queue/queue-logger.js.map +1 -0
  115. package/dist/queue/subscriber.d.ts +39 -0
  116. package/dist/queue/subscriber.js +97 -0
  117. package/dist/queue/subscriber.js.map +1 -0
  118. package/dist/safe_proxy/index.d.ts +13 -0
  119. package/dist/safe_proxy/index.js +54 -0
  120. package/dist/safe_proxy/index.js.map +1 -0
  121. package/dist/serializer/index.d.ts +5 -0
  122. package/dist/serializer/index.js +83 -0
  123. package/dist/serializer/index.js.map +1 -0
  124. package/dist/test/debug-array.d.ts +0 -0
  125. package/dist/test/debug-array.js +1 -0
  126. package/dist/test/debug-array.js.map +1 -0
  127. package/dist/test/debug-setJSONB.d.ts +0 -0
  128. package/dist/test/debug-setJSONB.js +1 -0
  129. package/dist/test/debug-setJSONB.js.map +1 -0
  130. package/dist/test/prompt.test.d.ts +1 -0
  131. package/dist/test/prompt.test.js +74 -0
  132. package/dist/test/prompt.test.js.map +1 -0
  133. package/dist/test/s4-formatter.spec.d.ts +1 -0
  134. package/dist/test/s4-formatter.spec.js +67 -0
  135. package/dist/test/s4-formatter.spec.js.map +1 -0
  136. package/dist/test/s4-pagination.spec.d.ts +1 -0
  137. package/dist/test/s4-pagination.spec.js +170 -0
  138. package/dist/test/s4-pagination.spec.js.map +1 -0
  139. package/dist/test/safe-proxy.spec.d.ts +1 -0
  140. package/dist/test/safe-proxy.spec.js +252 -0
  141. package/dist/test/safe-proxy.spec.js.map +1 -0
  142. package/dist/test/serializer.spec.d.ts +1 -0
  143. package/dist/test/serializer.spec.js +27 -0
  144. package/dist/test/serializer.spec.js.map +1 -0
  145. package/dist/test/utils.spec.d.ts +1 -0
  146. package/dist/test/utils.spec.js +446 -0
  147. package/dist/test/utils.spec.js.map +1 -0
  148. package/dist/ts/cache/index.d.ts +33 -0
  149. package/dist/ts/cache/index.js +171 -0
  150. package/dist/ts/cache/index.js.map +1 -0
  151. package/dist/ts/configurations/server-config.d.ts +4 -0
  152. package/dist/ts/configurations/server-config.js +2 -0
  153. package/dist/ts/configurations/server-config.js.map +1 -0
  154. package/dist/ts/constants/index.d.ts +7 -0
  155. package/dist/ts/constants/index.js +8 -0
  156. package/dist/ts/constants/index.js.map +1 -0
  157. package/dist/ts/cycle/index.d.ts +53 -0
  158. package/dist/ts/cycle/index.js +169 -0
  159. package/dist/ts/cycle/index.js.map +1 -0
  160. package/dist/ts/dns/DnsCache.d.ts +50 -0
  161. package/dist/ts/dns/DnsCache.js +137 -0
  162. package/dist/ts/dns/DnsCache.js.map +1 -0
  163. package/dist/ts/dns/index.d.ts +4 -0
  164. package/dist/ts/dns/index.js +4 -0
  165. package/dist/ts/dns/index.js.map +1 -0
  166. package/dist/ts/formatter/index.d.ts +44 -0
  167. package/dist/ts/formatter/index.js +76 -0
  168. package/dist/ts/formatter/index.js.map +1 -0
  169. package/dist/ts/hal/index.d.ts +26 -0
  170. package/dist/ts/hal/index.js +70 -0
  171. package/dist/ts/hal/index.js.map +1 -0
  172. package/dist/ts/health/index.d.ts +53 -0
  173. package/dist/ts/health/index.js +165 -0
  174. package/dist/ts/health/index.js.map +1 -0
  175. package/dist/ts/helpers/index.d.ts +1 -0
  176. package/dist/ts/helpers/index.js +2 -0
  177. package/dist/ts/helpers/index.js.map +1 -0
  178. package/dist/ts/helpers/test-server-wrapper.d.ts +56 -0
  179. package/dist/ts/helpers/test-server-wrapper.js +144 -0
  180. package/dist/ts/helpers/test-server-wrapper.js.map +1 -0
  181. package/dist/ts/index.d.ts +22 -0
  182. package/dist/ts/index.js +27 -0
  183. package/dist/ts/index.js.map +1 -0
  184. package/dist/ts/last-commit/index.d.ts +12 -0
  185. package/dist/ts/last-commit/index.js +82 -0
  186. package/dist/ts/last-commit/index.js.map +1 -0
  187. package/dist/ts/logger/index.d.ts +3 -0
  188. package/dist/ts/logger/index.js +4 -0
  189. package/dist/ts/logger/index.js.map +1 -0
  190. package/dist/ts/logger/s4-logger.d.ts +23 -0
  191. package/dist/ts/logger/s4-logger.js +105 -0
  192. package/dist/ts/logger/s4-logger.js.map +1 -0
  193. package/dist/ts/paginator/index.d.ts +2 -0
  194. package/dist/ts/paginator/index.js +3 -0
  195. package/dist/ts/paginator/index.js.map +1 -0
  196. package/dist/ts/paginator/s4-pagination.d.ts +26 -0
  197. package/dist/ts/paginator/s4-pagination.js +49 -0
  198. package/dist/ts/paginator/s4-pagination.js.map +1 -0
  199. package/dist/ts/paginator/sql-pagination.d.ts +52 -0
  200. package/dist/ts/paginator/sql-pagination.js +157 -0
  201. package/dist/ts/paginator/sql-pagination.js.map +1 -0
  202. package/dist/ts/paper-trail/helpers.d.ts +14 -0
  203. package/dist/ts/paper-trail/helpers.js +74 -0
  204. package/dist/ts/paper-trail/helpers.js.map +1 -0
  205. package/dist/ts/paper-trail/index.d.ts +54 -0
  206. package/dist/ts/paper-trail/index.js +236 -0
  207. package/dist/ts/paper-trail/index.js.map +1 -0
  208. package/dist/ts/prompts/index.d.ts +21 -0
  209. package/dist/ts/prompts/index.js +68 -0
  210. package/dist/ts/prompts/index.js.map +1 -0
  211. package/dist/ts/queue/index.d.ts +232 -0
  212. package/dist/ts/queue/index.js +440 -0
  213. package/dist/ts/queue/index.js.map +1 -0
  214. package/dist/ts/queue/publisher.d.ts +41 -0
  215. package/dist/ts/queue/publisher.js +84 -0
  216. package/dist/ts/queue/publisher.js.map +1 -0
  217. package/dist/ts/queue/queue-logger.d.ts +10 -0
  218. package/dist/ts/queue/queue-logger.js +35 -0
  219. package/dist/ts/queue/queue-logger.js.map +1 -0
  220. package/dist/ts/queue/subscriber.d.ts +55 -0
  221. package/dist/ts/queue/subscriber.js +98 -0
  222. package/dist/ts/queue/subscriber.js.map +1 -0
  223. package/dist/ts/safe-proxy/index.d.ts +35 -0
  224. package/dist/ts/safe-proxy/index.js +54 -0
  225. package/dist/ts/safe-proxy/index.js.map +1 -0
  226. package/dist/ts/serializer/index.d.ts +52 -0
  227. package/dist/ts/serializer/index.js +83 -0
  228. package/dist/ts/serializer/index.js.map +1 -0
  229. package/dist/ts/task-scheduler/index.d.ts +130 -0
  230. package/dist/ts/task-scheduler/index.js +259 -0
  231. package/dist/ts/task-scheduler/index.js.map +1 -0
  232. package/dist/ts/utils/index.d.ts +49 -0
  233. package/dist/ts/utils/index.js +157 -0
  234. package/dist/ts/utils/index.js.map +1 -0
  235. package/package.json +10 -8
  236. package/paper-trail/index.js +36 -28
  237. package/queue/index.js +57 -69
  238. package/scripts/publish.sh +46 -0
  239. package/test/health.spec.js +213 -0
  240. package/test/prompt.test.js +91 -0
  241. package/test/s4-formatter.spec.js +80 -0
  242. package/test/s4-pagination.spec.js +196 -0
  243. package/test/serializer.spec.js +34 -0
  244. package/test/utils.spec.js +145 -0
  245. package/package.tar.gz +0 -0
@@ -1,4 +1,5 @@
1
- import * as jsdiff from 'diff';
1
+ import { createRequire } from 'module';
2
+ const jsdiff = createRequire(import.meta.url)('diff');
2
3
  import _ from 'lodash';
3
4
  import helpers from './helpers.js';
4
5
  import paginator from "../paginator/sql-pagination.js";
@@ -297,35 +298,42 @@ paperTrail.init = (sequelize, sequelizePackage, optionsArg) => {
297
298
  );
298
299
  }
299
300
 
300
- let objectsChanged = new Set();
301
- // if the previous version or new version contain objects; push them onto this list
302
- _.forEach(previousVersion, (value, key) => {
303
- if (typeof(value) === 'object' ) {
304
- objectsChanged.add(key);
305
- // blank it so we can set it equal to ONLY the values changed in the next segment
306
- previousVersion[key] = {};
307
- }
308
- });
309
- _.forEach(currentVersion, (value, key) => {
310
- if (typeof(value) === 'object' ) {
311
- objectsChanged.add(key);
312
- currentVersion[key] = {};
313
- }
314
- });
301
+ // For create/restore, keep full currentVersion (including JSONB/plain objects) so
302
+ // the revision document has complete newValues. For update, blank object values
303
+ // and repopulate only from delta so we store minimal nested changes.
304
+ const blankObjectsAndApplyDelta = !createOperation && !restoreOperation;
305
+
306
+ if (blankObjectsAndApplyDelta) {
307
+ let objectsChanged = new Set();
308
+ // if the previous version or new version contain objects; push them onto this list
309
+ _.forEach(previousVersion, (value, key) => {
310
+ if (typeof(value) === 'object' ) {
311
+ objectsChanged.add(key);
312
+ // blank it so we can set it equal to ONLY the values changed in the next segment
313
+ previousVersion[key] = {};
314
+ }
315
+ });
316
+ _.forEach(currentVersion, (value, key) => {
317
+ if (typeof(value) === 'object' ) {
318
+ objectsChanged.add(key);
319
+ currentVersion[key] = {};
320
+ }
321
+ });
315
322
 
316
323
 
317
- // we don't want to see a full object; we want to see only
318
- // x.y.z -> value
319
- if (delta && objectsChanged.size > 0 ) {
320
- // the format of these are A,E,D,N (added, edited, deleted, new) for the kind
321
- // the array is either the key or key + subkeys ['options','subkey','subkey2'] etc.
322
- // {kind: 'E', path: Array(2), lhs: false, rhs: true}
323
- for (const difference of delta) {
324
- // blank the currentVersion[difference.path[0]]
325
- // and previousVersion[difference.path[0]]
326
- _.set(currentVersion, difference.path, difference.rhs);
327
- _.set(previousVersion, difference.path, difference.lhs);
328
- };
324
+ // we don't want to see a full object; we want to see only
325
+ // x.y.z -> value
326
+ if (delta && objectsChanged.size > 0 ) {
327
+ // the format of these are A,E,D,N (added, edited, deleted, new) for the kind
328
+ // the array is either the key or key + subkeys ['options','subkey','subkey2'] etc.
329
+ // {kind: 'E', path: Array(2), lhs: false, rhs: true}
330
+ for (const difference of delta) {
331
+ // blank the currentVersion[difference.path[0]]
332
+ // and previousVersion[difference.path[0]]
333
+ _.set(currentVersion, difference.path, difference.rhs);
334
+ _.set(previousVersion, difference.path, difference.lhs);
335
+ };
336
+ }
329
337
  }
330
338
 
331
339
  let document;
package/queue/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  "use strict";
2
- import amqp from "amqplib/callback_api.js";
2
+ import amqp from "amqplib";
3
3
  import logger from "./queue-logger.js";
4
4
  import urlParse from "url-parse";
5
5
  import { Publisher } from "./publisher.js";
@@ -11,6 +11,7 @@ import { Subscriber } from "./subscriber.js";
11
11
  class S4Queue {
12
12
  constructor() {
13
13
  this.connection = null;
14
+ this.connectionTestChannel = null;
14
15
  this.retry = 10;
15
16
  this.pubs = {};
16
17
  this.subs = {};
@@ -30,15 +31,20 @@ class S4Queue {
30
31
  async testConnection() {
31
32
  if (!this.isConnected()) {
32
33
  this.connectionTestChannel = null;
33
- throw("queue service not connected")
34
+ throw new Error("queue service not connected");
34
35
  }
35
36
 
36
- this.connectionTestChannel = this.connectionTestChannel || await this.connection.createChannel();
37
37
  if (!this.connectionTestChannel) {
38
- throw("queue service failed to obtain a channel")
38
+ const ch = await this.connection.createChannel();
39
+ if (!ch) {
40
+ throw new Error("queue service failed to obtain a channel");
41
+ }
42
+ ch.on("error", () => { this.connectionTestChannel = null; });
43
+ ch.on("close", () => { this.connectionTestChannel = null; });
44
+ this.connectionTestChannel = ch;
39
45
  }
40
46
 
41
- await this.connectionTestChannel.assertExchange("--health-check--", "topic", {durable: false});
47
+ await this.connectionTestChannel.assertExchange("--health-check--", "topic", { durable: false });
42
48
  }
43
49
 
44
50
  /**
@@ -56,60 +62,51 @@ class S4Queue {
56
62
  const retry = parseInt(options.retryInterval);
57
63
  this.retry = retry || 10;
58
64
 
59
- logger.debug("Attempting connection...");
65
+ // We may run into issues with SNI TLS if
66
+ // we host with 3rd parties using our own certs.
67
+ // To avoid any issues, let's parse the url
68
+ // and provide the server name in the socket options
69
+ const urlParts = urlParse(options.connectionUrl);
70
+ const socketOptions = { servername: urlParts.hostname };
60
71
 
61
- // The docs say there is amqp.connect returning
62
- // a promise, but this version doesn't seem to like
63
- // when a callback isn't provided, therefore run
64
- // in a new Promise
65
- return new Promise((resolve, reject) => {
66
- // We may run into issues with SNI TLS if
67
- // we host with 3rd parties using our own certs.
68
- // To avoid any issues, let's parse the url
69
- // and provide the server name in the socket options
70
- const urlParts = urlParse(options.connectionUrl);
71
- const socketOptions = {servername: urlParts.hostname};
72
+ return new Promise((resolve) => {
73
+ const attempt = async () => {
74
+ logger.debug("Attempting connection...");
75
+ try {
76
+ const conn = await amqp.connect(options.connectionUrl, socketOptions);
72
77
 
73
- amqp.connect(options.connectionUrl, socketOptions, (err, conn) => {
78
+ this.connection = conn;
79
+ this.updatePubsAndSubsConnectionRef(conn);
74
80
 
75
- if (err) {
76
- logger.error("Error while connecting", err)
81
+ this.connection.on("error", (err) => {
82
+ logger.error("connection error", err);
83
+ // Do not attempt to reconnect as the
84
+ // 'close' event will also be emitted
85
+ // if this error closes the connection
86
+ });
87
+
88
+ this.connection.on("close", (err) => {
89
+ logger.error("connection closed");
90
+ this.connection = null;
91
+ this.connectionTestChannel = null;
92
+ if (err) {
93
+ logger.error("connection closed due to error:", err);
94
+ logger.error("Reconnecting in", this.retry, "seconds");
95
+ setTimeout(attempt, this.retry * 1000);
96
+ } else {
97
+ logger.debug("Will not re-attempt");
98
+ }
99
+ });
100
+
101
+ logger.debug("Connected");
102
+ resolve();
103
+ } catch (err) {
104
+ logger.error("Error while connecting", err);
77
105
  logger.error("Attempting again in", this.retry, "seconds");
78
- return setTimeout(() => this.openConnection(options), this.retry * 1000);
106
+ setTimeout(attempt, this.retry * 1000);
79
107
  }
80
-
81
- this.connection = conn;
82
- if (!this.connection) {
83
- return reject(new Error("Failed to obtain connection"));
84
- } else {
85
- this.updatePubsAndSubsConnectionRef(conn);
86
- }
87
-
88
- this.connection.on("error", (err) => {
89
- logger.error("connection error", err);
90
- // Do not attempt to reconnect as the
91
- // 'close' event will also be emitted
92
- // if this error closes the connection
93
- });
94
-
95
- this.connection.on("close", (err) => {
96
- logger.error("connection closed");
97
- this.connection = null;
98
- if (err) {
99
- logger.error("connection closed due to error:", err);
100
- logger.error("Reconnecting in", this.retry, "seconds");
101
- setTimeout(() => this.openConnection(options), this.retry * 1000);
102
- } else {
103
- // If there was no error, it is us who closed
104
- // this connection, thus a reconnection will not
105
- // be attempted.
106
- logger.debug("Will not re-attempt");
107
- }
108
- });
109
-
110
- logger.debug("Connected");
111
- resolve();
112
- });
108
+ };
109
+ attempt();
113
110
  });
114
111
  }
115
112
 
@@ -119,21 +116,12 @@ class S4Queue {
119
116
  * and subscribers
120
117
  */
121
118
  async closeConnection() {
122
- return new Promise((resolve, reject) => {
123
- if (!this.connection) {
124
- return resolve();
125
- }
126
- this.connection.close((err) => {
127
- if (err) {
128
- reject(err);
129
- } else {
130
- this.subs = {};
131
- this.pubs = {};
132
- this.connection = null;
133
- resolve();
134
- }
135
- });
136
- });
119
+ if (!this.connection) return;
120
+ await this.connection.close();
121
+ this.subs = {};
122
+ this.pubs = {};
123
+ this.connectionTestChannel = null;
124
+ this.connection = null;
137
125
  }
138
126
 
139
127
  /**
@@ -0,0 +1,46 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ ROOT="$(cd "$(dirname "$0")/.." && pwd)"
5
+ cd "$ROOT"
6
+
7
+ # ── Version resolution ────────────────────────────────────────────────────────
8
+ VERSION="${BUILD_VERSION:-}"
9
+
10
+ if [ -z "$VERSION" ]; then
11
+ VERSION=$(node -p "require('./package.json').version")
12
+ fi
13
+
14
+ if [ -z "$VERSION" ] || [ "$VERSION" = "0.0.0" ]; then
15
+ echo "error: no version specified — set BUILD_VERSION or package.json version" >&2
16
+ exit 1
17
+ fi
18
+
19
+ PACKAGE=$(node -p "require('./package.json').name")
20
+ echo "publishing $PACKAGE@$VERSION"
21
+
22
+ # ── Stamp version, restore on exit ───────────────────────────────────────────
23
+ ORIGINAL_VERSION=$(node -p "require('./package.json').version")
24
+ restore_version() {
25
+ npm pkg set version="$ORIGINAL_VERSION" --silent
26
+ }
27
+ trap restore_version EXIT
28
+
29
+ npm pkg set version="$VERSION" --silent
30
+
31
+ # ── Build ─────────────────────────────────────────────────────────────────────
32
+ echo "building..."
33
+ npm run build
34
+
35
+ # ── Dist-tag: prerelease versions get 'next', stable versions get 'latest' ───
36
+ if [[ "$VERSION" =~ -[a-zA-Z] ]]; then
37
+ DIST_TAG="next"
38
+ else
39
+ DIST_TAG="latest"
40
+ fi
41
+
42
+ # ── Publish ───────────────────────────────────────────────────────────────────
43
+ echo "publishing with tag: $DIST_TAG"
44
+ npm publish --access public --tag "$DIST_TAG"
45
+
46
+ echo "done: $PACKAGE@$VERSION"
@@ -0,0 +1,213 @@
1
+ import { expect, should as chaiShould } from "chai";
2
+ import sinon from "sinon";
3
+ import express from "express";
4
+ import { healthCheck as hc } from "../index.js";
5
+
6
+ const should = chaiShould();
7
+
8
+ const mockResponse = () => {
9
+ const res = {};
10
+ res.status = sinon.stub().returns(res);
11
+ res.setHeader = sinon.stub();
12
+ res.json = sinon.stub().returns(res);
13
+ res.send = sinon.spy();
14
+ return res;
15
+ };
16
+
17
+ /* algorithm for shuffling an array */
18
+ const shuffle = (a) => {
19
+ for (let i = a.length - 1; i > 0; i--) {
20
+ const j = Math.floor(Math.random() * (i + 1));
21
+ [a[i], a[j]] = [a[j], a[i]];
22
+ }
23
+ return a;
24
+ }
25
+
26
+ describe("HealthCheck Testing", () => {
27
+ let healthCheck = new hc();
28
+
29
+ describe("Health Check Sanity", () => {
30
+ it ("Should have NO predefined health checks", () => {
31
+ return expect(healthCheck.healthChecks).to.be.an('array').that.is.empty;
32
+ });
33
+
34
+ it ("Should have have a sane default path route", () => {
35
+ return expect(healthCheck.defaultPath()).to.match(/health_check/);
36
+ });
37
+
38
+ it ("Should have have sane response defaults", () => {
39
+ expect(healthCheck.defaultResponse).to.have.property("status", 200);
40
+ expect(healthCheck.defaultResponse).to.have.property("errors").to.be.an('object').to.be.empty;
41
+ })
42
+ });
43
+
44
+ describe("Health Check Router", () => {
45
+ it ("Should throw without a proper router object", () => {
46
+ expect(healthCheck.routes.bind(healthCheck)).to.throw('missing_express_router');
47
+ })
48
+
49
+ it ("Should return a proper router object with an improved route", () => {
50
+ let router = express.Router();
51
+ let improved_router = healthCheck.routes(router);
52
+ expect(improved_router).to.equal(router);
53
+ })
54
+ });
55
+
56
+ describe("Check Health Checks", () => {
57
+ let req, res;
58
+
59
+ beforeEach("Setup hc object", () => {
60
+ res = mockResponse();
61
+ req = { baseUrl: "server.example.com/api/health_check.json" };
62
+ healthCheck = new hc();
63
+ })
64
+
65
+ it ("Returns ok with no additional health check routes defined", (done) => {
66
+ req = { baseUrl: "server.example.com/api/health_check.json" };
67
+
68
+ const expectedResult = { check: 1, healthy: "true", message: "success" }
69
+
70
+ healthCheck.checkHealth(req, res)
71
+ .then((r) => {
72
+ sinon.assert.alwaysCalledWith(res.status, 200);
73
+ sinon.assert.calledOnce(res.status);
74
+ sinon.assert.alwaysCalledWith(res.json, expectedResult)
75
+ done()
76
+ });
77
+ })
78
+
79
+ it("Returns success with a simple successful (v1) health check defined", (done) => {
80
+ const sampleCheck = async () => {
81
+ return { name: "db", "result": "success", critical: false };
82
+ }
83
+
84
+ healthCheck.addCheck(sampleCheck);
85
+ expect(healthCheck.healthChecks).to.be.an('array').with.lengthOf(1);
86
+
87
+ const expectedResult = { check: 1, healthy: "true", message: "success" }
88
+ healthCheck.checkHealth(req, res)
89
+ .then((r) => {
90
+ sinon.assert.alwaysCalledWith(res.status, 200);
91
+ sinon.assert.alwaysCalledWith(res.json, expectedResult);
92
+ done()
93
+ });
94
+ })
95
+
96
+ it("Returns fail with a failed health check", (done) => {
97
+ const sampleCheck = async () => {
98
+ return { name: "db", "result": "fail" };
99
+ }
100
+
101
+ healthCheck.addCheck(sampleCheck);
102
+ expect(healthCheck.healthChecks).to.be.an('array').with.lengthOf(1);
103
+
104
+ const expectedResult = { check: 1, healthy: "false", message: "fail" }
105
+ healthCheck.checkHealth(req, res)
106
+ .then(e => { done() })
107
+ .catch(err => {
108
+ sinon.assert.alwaysCalledWith(res.status, 500);
109
+ sinon.assert.alwaysCalledWith(res.json, expectedResult);
110
+ done()
111
+ })
112
+ })
113
+
114
+ it("Returns unhealthy with an OK and failed health check", (done) => {
115
+ const sampleFail = async () => {
116
+ return { name: "db", "result": "fail" };
117
+ }
118
+
119
+ const sampleSuccess= async () => {
120
+ return { name: "redis", "result": "success" };
121
+ }
122
+
123
+ shuffle([sampleFail, sampleSuccess]).forEach(el => {
124
+ healthCheck.addCheck(el);
125
+ })
126
+
127
+ expect(healthCheck.healthChecks).to.be.an('array').with.lengthOf(2);
128
+
129
+ healthCheck.checkHealth(req, res)
130
+ .then(e => { done() })
131
+ .catch(err => {
132
+ sinon.assert.alwaysCalledWith(res.status, 500);
133
+ sinon.assert.alwaysCalledWith(res.json, 'fail');
134
+ done()
135
+ })
136
+ })
137
+
138
+ it("Returns healthy/200 when non-critical element fails (v2)", async () => {
139
+ const sampleCheck= async () => { throw new Error({status: 500}) }
140
+
141
+ healthCheck.setupCheck({ name: "sample", critical: false, healthFunction: sampleCheck });
142
+ expect(healthCheck.healthChecks).to.be.an('array').with.lengthOf(1);
143
+
144
+ await healthCheck.checkHealth(req, res)
145
+
146
+ sinon.assert.alwaysCalledWith(res.status, 200);
147
+
148
+ const expectedResult = {
149
+ check: 1,
150
+ healthy: "true",
151
+ message: "fail",
152
+ services: [{ critical: false, name: "sample", result: "fail", status: undefined }]
153
+ }
154
+ sinon.assert.alwaysCalledWith(res.json, expectedResult);
155
+ })
156
+
157
+ it("Returns unhealthy/500 when a critical component returns failure (v2)", async () => {
158
+ const sampleCheck= async () => { throw new Error({status: 500}) }
159
+
160
+ healthCheck.setupCheck({ name: "sample", critical: true, healthFunction: sampleCheck });
161
+ expect(healthCheck.healthChecks).to.be.an('array').with.lengthOf(1);
162
+
163
+ await healthCheck.checkHealth(req, res)
164
+
165
+ const expectedResult = {
166
+ check: 1, healthy: false,
167
+ message: "fail",
168
+ services: [{ critical: true, name: "sample", result: "fail", status: undefined }]
169
+ }
170
+ sinon.assert.alwaysCalledWith(res.status, 500);
171
+ sinon.assert.alwaysCalledWith(res.json, expectedResult);
172
+ })
173
+
174
+ it("Returns unhealthy/500 when a critical component returns failure (v2)", async () => {
175
+ const sampleCheck= async () => { throw new Error({status: 500}) }
176
+ const sampleCheck2= async () => { return true }
177
+
178
+ healthCheck.setupCheck({ name: "sample", critical: true, healthFunction: sampleCheck });
179
+ healthCheck.setupCheck({ name: "sample2", critical: true, healthFunction: sampleCheck2 });
180
+ expect(healthCheck.healthChecks).to.be.an('array').with.lengthOf(2);
181
+
182
+ await healthCheck.checkHealth(req, res)
183
+
184
+ const expectedResult = {
185
+ check: 1, healthy: false,
186
+ message: "fail",
187
+ services: [
188
+ { critical: true, name: "sample", result: "fail", status: undefined },
189
+ { critical: true, name: "sample2", result: "success" }]
190
+ }
191
+ sinon.assert.alwaysCalledWith(res.status, 500);
192
+ sinon.assert.alwaysCalledWith(res.json, expectedResult);
193
+ })
194
+
195
+ it("Returns a different ID each time it runscheck", (done) => {
196
+ let req2, res2;
197
+ res2 = mockResponse();
198
+
199
+ req = { baseUrl: "server.example.com/api/health_check.json" };
200
+ req2 = req
201
+
202
+ healthCheck.checkHealth(req, res)
203
+ .then(() => {
204
+ expect(healthCheck.lastCallId).to.equal(1)
205
+ healthCheck.checkHealth(req2, res2)
206
+ .then(() => {
207
+ expect(healthCheck.lastCallId).to.equal(2)
208
+ done()
209
+ })
210
+ })
211
+ })
212
+ })
213
+ });
@@ -0,0 +1,91 @@
1
+ import { expect } from "chai";
2
+ import sinon from "sinon";
3
+ import prompt from "prompt";
4
+ import { ArgumentParser } from 'argparse';
5
+ import logger from "../logger/s4-logger.js";
6
+
7
+ import { setupCLIParser, cliSleep, promptCLIAsync, commonCLIInit, waitCLIPrompt, continueCLIPrompt } from "../prompts/index.js";
8
+
9
+ describe("Prompt Functions", () => {
10
+ describe("Setup Parser", () => {
11
+ let loggerStub;
12
+
13
+ beforeEach(() => {
14
+ loggerStub = sinon.stub(logger, "info");
15
+ });
16
+
17
+ afterEach(() => {
18
+ loggerStub.restore();
19
+ });
20
+
21
+ it("should create an ArgumentParser with the given description", () => {
22
+ const description = "Test Description";
23
+ const parser = setupCLIParser(description);
24
+
25
+ expect(parser).to.be.instanceOf(ArgumentParser);
26
+ expect(loggerStub.calledWith(description)).to.be.true;
27
+ });
28
+ });
29
+
30
+ describe("cliSleep", () => {
31
+ it("should return a promise that resolves after the specified time", async () => {
32
+ const clock = sinon.useFakeTimers();
33
+ const ms = 1000;
34
+
35
+ const promise = cliSleep(ms);
36
+ clock.tick(ms);
37
+
38
+ await promise;
39
+ clock.restore();
40
+ });
41
+ });
42
+
43
+ describe("Prompt Async", () => {
44
+ let promptStub;
45
+
46
+ beforeEach(() => {
47
+ promptStub = sinon.stub(prompt, "get");
48
+ });
49
+
50
+ afterEach(() => {
51
+ promptStub.restore();
52
+ });
53
+
54
+ it("should resolve with response when prompt succeeds", async () => {
55
+ const options = { name: "test" };
56
+ const mockResponse = { name: "response" };
57
+
58
+ promptStub.yields(null, mockResponse);
59
+
60
+ const result = await promptCLIAsync(options);
61
+ expect(result).to.equal(mockResponse);
62
+ expect(promptStub.calledWith(options)).to.be.true;
63
+ });
64
+
65
+ it("should reject with error when prompt fails", async () => {
66
+ const options = { name: "test" };
67
+ const mockError = new Error("Prompt error");
68
+
69
+ promptStub.yields(mockError, null);
70
+
71
+ try {
72
+ await promptCLIAsync(options);
73
+ expect.fail("Should have thrown an error");
74
+ } catch (error) {
75
+ expect(error).to.equal(mockError);
76
+ }
77
+ });
78
+ });
79
+
80
+ describe("Prompt Objects", () => {
81
+ it("should define waitPrompt with correct structure", () => {
82
+ expect(waitCLIPrompt).to.have.property("Push <enter> when done");
83
+ expect(waitCLIPrompt["Push <enter> when done"]).to.have.property("type", "boolean");
84
+ });
85
+
86
+ it("should define continuePrompt with correct structure", () => {
87
+ expect(continueCLIPrompt).to.have.property("Push <enter> to continue");
88
+ expect(continueCLIPrompt["Push <enter> to continue"]).to.have.property("type", "boolean");
89
+ });
90
+ });
91
+ });
@@ -0,0 +1,80 @@
1
+ import should from "should";
2
+ import { expect as expect } from "chai";
3
+
4
+ import S4Formatter from "../formatter/index.js";
5
+
6
+ describe("Formatter", function () {
7
+ let response;
8
+
9
+ describe("Sending legacy format []", function () {
10
+ before("Setup", function () {
11
+ const error = { status: 200, error: ["error1"] }
12
+ response = S4Formatter.formatError(error);
13
+ })
14
+
15
+ describe("Default values", function () {
16
+ it("should have a generic error", function () {
17
+ response.should.have.property("errors");
18
+ });
19
+
20
+ it("should have error code set correctly", function () {
21
+ response.should.have.property("status", 200);
22
+ });
23
+ });
24
+ });
25
+
26
+ describe("Support null error payloads", function () {
27
+ before("Setup", function () {
28
+ response = S4Formatter.formatError(null);
29
+ })
30
+
31
+ describe("Default values", function () {
32
+ it("Should have a default error level and blank errors", function () {
33
+ expect(response).to.have.property("status",422);
34
+ expect(response).to.have.property("errors").to.be.empty;
35
+ });
36
+
37
+ });
38
+ });
39
+
40
+ describe("Should not fold in funky keys/values", function () {
41
+ before("Setup", function () {
42
+ response = S4Formatter.formatError({randomKey: 123});
43
+ })
44
+
45
+ describe("Default values", function () {
46
+ it("Should not funky key name folded in", function () {
47
+ response.should.not.have.property("randomKey", {});
48
+ });
49
+ });
50
+ });
51
+
52
+ describe("Support validator formats", function () {
53
+ before("Setup", function () {
54
+ let error = {
55
+ "status": 422,
56
+ "errors": {
57
+ "password": {
58
+ "location": "body",
59
+ "param": "password",
60
+ "value": "badPassw0rd",
61
+ "msg": "weak"
62
+ }
63
+ }
64
+ }
65
+
66
+ response = S4Formatter.formatError(error);
67
+ })
68
+
69
+ describe("Default values", function () {
70
+ it("Should have a default error level and blank errors", function () {
71
+ response.should.have.property("status", 422);
72
+ expect(response).to.be.an("object");
73
+ expect(response).to.have.property('errors');
74
+ expect(Object.keys(response['errors'])).to.have.lengthOf('1')
75
+ response.should.have.property("errors");
76
+ });
77
+
78
+ });
79
+ });
80
+ })