miolo 3.0.0-beta.21 → 3.0.0-beta.210

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 (246) hide show
  1. package/bin/build/build.mjs +53 -0
  2. package/bin/build/build_bin.mjs +17 -0
  3. package/bin/build/cli/client.mjs +52 -0
  4. package/bin/build/cli/css.mjs +22 -0
  5. package/bin/build/cli/index.mjs +40 -0
  6. package/bin/build/cli/ssr.mjs +21 -0
  7. package/bin/build/server/aliases.mjs +112 -0
  8. package/bin/build/server/babel.config.js +24 -0
  9. package/bin/build/server/banner.mjs +20 -0
  10. package/bin/build/server/bundle.mjs +111 -0
  11. package/bin/build/server/fix.mjs +15 -0
  12. package/bin/build/server/index.mjs +69 -0
  13. package/bin/build/server/options.mjs +83 -0
  14. package/bin/create/auth.mjs +23 -0
  15. package/bin/create/copy.mjs +175 -0
  16. package/bin/create/docker.mjs +25 -0
  17. package/bin/create/index.mjs +137 -0
  18. package/bin/create/pkgjson.mjs +72 -0
  19. package/bin/create/prepare-template.mjs +158 -0
  20. package/bin/create/validation.mjs +27 -0
  21. package/bin/dev/dev.mjs +32 -23
  22. package/bin/dev/dev_start.mjs +6 -5
  23. package/bin/index.mjs +94 -52
  24. package/bin/prod-bin/create-bin.mjs +42 -27
  25. package/bin/prod-bin/run.mjs +13 -9
  26. package/bin/{prod-run → run}/pid.mjs +4 -4
  27. package/bin/run/restart.mjs +13 -0
  28. package/bin/run/start.mjs +18 -0
  29. package/bin/run/stop.mjs +20 -0
  30. package/bin/util.mjs +35 -11
  31. package/package.json +59 -39
  32. package/src/config/.env +34 -12
  33. package/src/config/defaults.mjs +253 -185
  34. package/src/config/env.mjs +40 -22
  35. package/src/config/index.mjs +19 -24
  36. package/src/config/util.mjs +25 -10
  37. package/src/db-conn.mjs +34 -0
  38. package/src/engines/cron/emails.mjs +10 -5
  39. package/src/engines/cron/index.mjs +45 -51
  40. package/src/engines/cron/init.mjs +16 -17
  41. package/src/engines/cron/ipsum.mjs +65 -60
  42. package/src/engines/cron/syscheck.mjs +30 -30
  43. package/src/engines/emailer/index.mjs +1 -2
  44. package/src/engines/emailer/queue.mjs +14 -20
  45. package/src/engines/emailer/transporter.mjs +86 -74
  46. package/src/engines/geoip/index.mjs +23 -28
  47. package/src/engines/http/index.mjs +26 -15
  48. package/src/engines/logger/buildErrorEmailBody.mjs +72 -0
  49. package/src/engines/logger/index.mjs +114 -122
  50. package/src/engines/logger/injectStackTrace.mjs +59 -0
  51. package/src/engines/logger/logger_mail.mjs +47 -61
  52. package/src/engines/logger/reopenTransportOnHupSignal.mjs +12 -13
  53. package/src/engines/parser/Parser.mjs +77 -60
  54. package/src/engines/parser/index.mjs +1 -1
  55. package/src/engines/schema/diffObjs.mjs +41 -0
  56. package/src/engines/schema/index.mjs +4 -0
  57. package/src/engines/schema/input.mjs +54 -0
  58. package/src/engines/schema/output.mjs +66 -0
  59. package/src/engines/socket/index.mjs +44 -46
  60. package/src/index.mjs +15 -10
  61. package/src/middleware/auth/basic.mjs +41 -40
  62. package/src/middleware/auth/custom.mjs +10 -13
  63. package/src/middleware/auth/guest.mjs +27 -27
  64. package/src/middleware/auth/passport/index.mjs +374 -0
  65. package/src/middleware/auth/passport/session/index.mjs +43 -0
  66. package/src/middleware/auth/{credentials → passport}/session/store.mjs +35 -15
  67. package/src/middleware/auth/passport/session/store_koa_redis.mjs +3 -0
  68. package/src/middleware/context/cache/index.mjs +78 -33
  69. package/src/middleware/context/cache/options.mjs +19 -21
  70. package/src/middleware/context/db.mjs +45 -20
  71. package/src/middleware/context/index.mjs +12 -12
  72. package/src/middleware/extra.mjs +4 -5
  73. package/src/middleware/http/body.mjs +25 -25
  74. package/src/middleware/http/catcher.mjs +81 -8
  75. package/src/middleware/http/custom_blacklist.mjs +19 -16
  76. package/src/middleware/http/headers.mjs +37 -34
  77. package/src/middleware/http/ratelimit.mjs +16 -23
  78. package/src/middleware/http/request.mjs +60 -65
  79. package/src/middleware/routes/catch_js_error.mjs +30 -23
  80. package/src/middleware/routes/robots.mjs +4 -7
  81. package/src/middleware/routes/router/crud/attachCrudRoutes.mjs +108 -90
  82. package/src/middleware/routes/router/crud/getCrudConfig.mjs +31 -55
  83. package/src/middleware/routes/router/defaults.mjs +6 -19
  84. package/src/middleware/routes/router/index.mjs +17 -21
  85. package/src/middleware/routes/router/queries/attachQueriesRoutes.mjs +227 -50
  86. package/src/middleware/routes/router/queries/getQueriesConfig.mjs +45 -55
  87. package/src/middleware/routes/router/utils.mjs +41 -26
  88. package/src/middleware/ssr/context.mjs +5 -7
  89. package/src/middleware/ssr/html.mjs +66 -43
  90. package/src/middleware/ssr/loader.mjs +11 -14
  91. package/src/middleware/ssr/ssr_render.mjs +39 -22
  92. package/src/middleware/static/index.mjs +33 -14
  93. package/src/middleware/vite/devserver.mjs +38 -22
  94. package/src/middleware/vite/watcher.mjs +12 -14
  95. package/src/server-cron.mjs +13 -8
  96. package/src/server-dev.mjs +13 -16
  97. package/src/server.mjs +49 -51
  98. package/template/.agent/skills/miolo-app-arch/SKILL.md +218 -0
  99. package/template/.agent/skills/miolo-auth/SKILL.md +450 -0
  100. package/template/.agent/skills/miolo-cli-router/SKILL.md +394 -0
  101. package/template/.agent/skills/miolo-database/SKILL.md +358 -0
  102. package/template/.agent/skills/miolo-react-patterns/SKILL.md +426 -0
  103. package/template/.agent/skills/miolo-routing/SKILL.md +326 -0
  104. package/template/.agent/skills/miolo-schemas/SKILL.md +329 -0
  105. package/template/.agent/skills/miolo-session-context/SKILL.md +397 -0
  106. package/template/.agent/skills/miolo-ssr/SKILL.md +433 -0
  107. package/template/.editorconfig +18 -0
  108. package/template/.env +120 -0
  109. package/template/biome.json +63 -0
  110. package/template/components.json +21 -0
  111. package/template/db/init.sh +89 -0
  112. package/template/db/sql/00_drop.sql +2 -0
  113. package/template/db/sql/01_users.sql +31 -0
  114. package/template/db/sql/02_todos.sql +20 -0
  115. package/template/docker/Dockerfile +13 -0
  116. package/template/docker/docker-compose.yaml +79 -0
  117. package/template/gitignore +42 -0
  118. package/template/jsconfig.json +18 -0
  119. package/template/package.json +88 -0
  120. package/template/postcss.config.js +9 -0
  121. package/template/src/cli/App.jsx +25 -0
  122. package/template/src/cli/components/JsonTreeViewer.jsx +128 -0
  123. package/template/src/cli/components/shadcn-io/spinner/index.jsx +232 -0
  124. package/template/src/cli/components/stepper.jsx +408 -0
  125. package/template/src/cli/components/ui/avatar.jsx +36 -0
  126. package/template/src/cli/components/ui/badge.jsx +31 -0
  127. package/template/src/cli/components/ui/breadcrumb.jsx +97 -0
  128. package/template/src/cli/components/ui/card.jsx +73 -0
  129. package/template/src/cli/components/ui/collapsible.jsx +16 -0
  130. package/template/src/cli/components/ui/dropdown-menu.jsx +179 -0
  131. package/template/src/cli/components/ui/field.jsx +217 -0
  132. package/template/src/cli/components/ui/input.jsx +19 -0
  133. package/template/src/cli/components/ui/label.jsx +17 -0
  134. package/template/src/cli/components/ui/pagination.jsx +99 -0
  135. package/template/src/cli/components/ui/patched/alert.jsx +56 -0
  136. package/template/src/cli/components/ui/patched/button.jsx +45 -0
  137. package/template/src/cli/components/ui/patched/dialog.jsx +114 -0
  138. package/template/src/cli/components/ui/patched/sidebar.jsx +660 -0
  139. package/template/src/cli/components/ui/select.jsx +141 -0
  140. package/template/src/cli/components/ui/separator.jsx +21 -0
  141. package/template/src/cli/components/ui/sheet.jsx +115 -0
  142. package/template/src/cli/components/ui/skeleton.jsx +13 -0
  143. package/template/src/cli/components/ui/sonner.jsx +22 -0
  144. package/template/src/cli/components/ui/switch.jsx +25 -0
  145. package/template/src/cli/components/ui/table.jsx +88 -0
  146. package/template/src/cli/components/ui/textarea.jsx +16 -0
  147. package/template/src/cli/components/ui/tooltip.jsx +45 -0
  148. package/template/src/cli/config/store_keys.mjs +2 -0
  149. package/template/src/cli/context/data/DataContext.jsx +5 -0
  150. package/template/src/cli/context/data/DataProvider.jsx +44 -0
  151. package/template/src/cli/context/data/useBreads.mjs +15 -0
  152. package/template/src/cli/context/data/useDataContext.mjs +4 -0
  153. package/template/src/cli/context/session/SessionContext.mjs +4 -0
  154. package/template/src/cli/context/session/SessionProvider.jsx +31 -0
  155. package/template/src/cli/context/session/makePermissioner.mjs +34 -0
  156. package/template/src/cli/context/session/useSessionContext.mjs +6 -0
  157. package/template/src/cli/context/theme/ThemeContext.mjs +4 -0
  158. package/template/src/cli/context/theme/ThemeProvider.jsx +49 -0
  159. package/template/src/cli/context/theme/useThemeContext.mjs +6 -0
  160. package/template/src/cli/context/ui/UIContext.jsx +5 -0
  161. package/template/src/cli/context/ui/UIProvider.jsx +16 -0
  162. package/template/src/cli/context/ui/useUIContext.mjs +4 -0
  163. package/template/src/cli/context/util.mjs +17 -0
  164. package/template/src/cli/entry-cli.jsx +33 -0
  165. package/template/src/cli/hooks/useIsMobile.mjs +19 -0
  166. package/template/src/cli/hooks/useStoragedState.mjs +63 -0
  167. package/template/src/cli/index.html +29 -0
  168. package/template/src/cli/layout/app-sidebar.jsx +25 -0
  169. package/template/src/cli/layout/main-layout.jsx +63 -0
  170. package/template/src/cli/layout/nav-last-todos.jsx +72 -0
  171. package/template/src/cli/layout/nav-main.jsx +39 -0
  172. package/template/src/cli/layout/nav-user.jsx +105 -0
  173. package/template/src/cli/layout/prop-switcher.jsx +93 -0
  174. package/template/src/cli/lib/utils.mjs +10 -0
  175. package/template/src/cli/pages/Index.jsx +13 -0
  176. package/template/src/cli/pages/IndexOffline.jsx +13 -0
  177. package/template/src/cli/pages/IndexOnline.jsx +18 -0
  178. package/template/src/cli/pages/dash/Dashboard.jsx +29 -0
  179. package/template/src/cli/pages/offline/Login.jsx +43 -0
  180. package/template/src/cli/pages/offline/LoginForm.jsx +115 -0
  181. package/template/src/cli/pages/security/Security.jsx +39 -0
  182. package/template/src/cli/pages/security/SecurityForm.jsx +106 -0
  183. package/template/src/cli/pages/todos/TodoActions.jsx +99 -0
  184. package/template/src/cli/pages/todos/TodoAdd.jsx +43 -0
  185. package/template/src/cli/pages/todos/TodoList.jsx +60 -0
  186. package/template/src/cli/pages/todos/Todos.jsx +23 -0
  187. package/template/src/cli/pages/todos/context/TodosContext.jsx +5 -0
  188. package/template/src/cli/pages/todos/context/TodosProvider.jsx +191 -0
  189. package/template/src/cli/pages/todos/context/useTodosContext.mjs +4 -0
  190. package/template/src/ns/models/Todo.mjs +29 -0
  191. package/template/src/ns/models/TodoList.mjs +8 -0
  192. package/template/src/ns/models/User.mjs +40 -0
  193. package/template/src/server/bot/check_today.mjs +10 -0
  194. package/template/src/server/io/cache/base.mjs +21 -0
  195. package/template/src/server/io/db/filter.mjs +92 -0
  196. package/template/src/server/io/db/todos/delete.mjs +29 -0
  197. package/template/src/server/io/db/todos/find.mjs +13 -0
  198. package/template/src/server/io/db/todos/read.mjs +83 -0
  199. package/template/src/server/io/db/todos/toggle.mjs +37 -0
  200. package/template/src/server/io/db/todos/upsave.mjs +32 -0
  201. package/template/src/server/io/db/triggers/user.mjs +13 -0
  202. package/template/src/server/io/db/users/auth.mjs +132 -0
  203. package/template/src/server/io/db/users/pwd.mjs +38 -0
  204. package/template/src/server/io/db/users/save.mjs +17 -0
  205. package/template/src/server/miolo/auth/basic.mjs +15 -0
  206. package/template/src/server/miolo/auth/guest.mjs +3 -0
  207. package/template/src/server/miolo/auth/passport.mjs +73 -0
  208. package/template/src/server/miolo/cache.mjs +11 -0
  209. package/template/src/server/miolo/cron/foo.mjs +7 -0
  210. package/template/src/server/miolo/cron/index.mjs +28 -0
  211. package/template/src/server/miolo/cron/invalidate.mjs +21 -0
  212. package/template/src/server/miolo/db.mjs +36 -0
  213. package/template/src/server/miolo/http.mjs +14 -0
  214. package/template/src/server/miolo/index.mjs +43 -0
  215. package/template/src/server/miolo/routes/crud.mjs +16 -0
  216. package/template/src/server/miolo/routes/index.mjs +8 -0
  217. package/template/src/server/miolo/ssr/entry-server.jsx +13 -0
  218. package/template/src/server/miolo/ssr/loader.mjs +18 -0
  219. package/template/src/server/routes/index.mjs +66 -0
  220. package/template/src/server/routes/todos/mod.mjs +52 -0
  221. package/template/src/server/routes/todos/read.mjs +45 -0
  222. package/template/src/server/routes/todos/special.mjs +47 -0
  223. package/template/src/server/routes/users/user.mjs +54 -0
  224. package/template/src/server/server.mjs +10 -0
  225. package/template/src/server/utils/crypt.mjs +38 -0
  226. package/template/src/server/utils/io.mjs +15 -0
  227. package/template/src/server/utils/pwdfor.mjs +25 -0
  228. package/template/src/server/utils/schema.mjs +22 -0
  229. package/template/src/static/img/default/profile.png +0 -0
  230. package/template/src/static/img/favicon.ico +0 -0
  231. package/template/src/static/img/miolo_logo.png +0 -0
  232. package/template/src/static/img/miolo_name.png +0 -0
  233. package/template/src/static/public/manifest.json +21 -0
  234. package/template/src/static/public/sw.js +79 -0
  235. package/template/src/static/style/globals.css +156 -0
  236. package/template/src/static/style/json-tree.css +54 -0
  237. package/template/src/static/style/skeleton.css +49 -0
  238. package/bin/prod-build/build-client.mjs +0 -67
  239. package/bin/prod-build/build-server.mjs +0 -58
  240. package/bin/prod-run/restart.mjs +0 -9
  241. package/bin/prod-run/start.mjs +0 -15
  242. package/bin/prod-run/stop.mjs +0 -20
  243. package/src/engines/logger/verify.mjs +0 -22
  244. package/src/middleware/auth/credentials/index.mjs +0 -151
  245. package/src/middleware/auth/credentials/session/index.mjs +0 -24
  246. package/src/middleware/auth/credentials/session/store_koa_redis.mjs +0 -3
@@ -0,0 +1,59 @@
1
+ import { format } from "winston"
2
+
3
+ const injectStackTrace = format((info) => {
4
+ // Solo nos interesa inyectar stack si es nivel error y NO tiene stack
5
+ if (info.level === "error" && !info.stack) {
6
+ const syntheticError = new Error(info.message)
7
+ const stackLines = syntheticError.stack.split("\n")
8
+
9
+ // 1. Definimos nuestro "Ancla".
10
+ // Es el archivo que actúa de frontera entre tu framework y el código de negocio.
11
+ // Usamos una cadena parcial única para identificarlo.
12
+ // DEV: 'src/engines/logger/index.mjs'
13
+ // PROD: ''
14
+
15
+ // 2. Buscamos en qué línea del stack aparece tu wrapper
16
+ let wrapperIndex = stackLines.findIndex((line) => line.includes("src/engines/logger/index.mjs"))
17
+ if (wrapperIndex === -1) {
18
+ for (const [lidx, l] of stackLines.entries()) {
19
+ if (
20
+ l.includes(
21
+ `${process.env.MIOLO_NAME || "miolo"}.${process.env.MIOLO_BUILD_SERVER_EXT || "node.bundle.mjs"}`
22
+ )
23
+ ) {
24
+ const segs = ["Format.transform", "DerivedLogger"]
25
+ segs.forEach((seg) => {
26
+ if (l.includes(seg)) {
27
+ wrapperIndex = lidx
28
+ }
29
+ })
30
+ }
31
+ }
32
+ }
33
+
34
+ let cleanStackLines = stackLines
35
+
36
+ if (wrapperIndex !== -1) {
37
+ // CASO ÉXITO: Encontramos tu wrapper.
38
+ // El stack útil empieza justo en la línea SIGUIENTE (+1).
39
+ // Mantenemos la cabecera (línea 0) y pegamos del wrapper hacia abajo.
40
+ cleanStackLines = [stackLines[0], ...stackLines.slice(wrapperIndex + 1)]
41
+ }
42
+ // FALLBACK: Si por alguna razón extraña no pasa por tu wrapper,
43
+ // hacemos una limpieza conservadora básica (winston y node internals).
44
+ cleanStackLines = cleanStackLines.filter(
45
+ (line) =>
46
+ !line.includes("node_modules/winston") &&
47
+ !line.includes("node_modules/logform") &&
48
+ !line.includes("node_modules/koa-compose") &&
49
+ !line.includes("node:internal") &&
50
+ !line.includes("node_modules/readable-stream") &&
51
+ !line.includes("injectStackTrace.mjs")
52
+ )
53
+
54
+ info.stack = cleanStackLines.join("\n")
55
+ }
56
+ return info
57
+ })
58
+
59
+ export default injectStackTrace
@@ -1,89 +1,75 @@
1
1
  // "use strict";
2
- import util from 'util'
3
- import winston from 'winston'
4
- import { uncolor } from 'tinguir'
5
-
6
- function init_logger_to_mail(config, emailer) {
7
-
8
-
9
- let MailerLogger = function (options) {
10
- winston.Transport.call(this, options);
11
- options = options || {};
12
-
13
- this.level = config.level || 'info'
14
- this.ename = config?.name || emailer.defaults.name
15
- this.to = config.to || emailer.defaults.to
16
- this.from = config.from || emailer.defaults.from
17
- this.humanReadableUnhandledException = options.humanReadableUnhandledException || true;
18
- this.handleExceptions = options.handleExceptions || true;
19
- this.json = options.json || false;
20
- this.colorize = options.colorize || false;
21
2
 
3
+ import util from "node:util"
4
+ import { uncolor } from "tinguir"
5
+ import winston from "winston"
6
+ import buildErrorEmailBody from "./buildErrorEmailBody.mjs"
22
7
 
23
-
24
-
25
- };
8
+ function init_logger_to_mail(config, emailer) {
9
+ const MailerLogger = function (options) {
10
+ winston.Transport.call(this, options)
11
+ options = options || {}
12
+
13
+ this.level = config.level || "info"
14
+ this.ename = config?.name || emailer.defaults.name
15
+ this.to = config.to || emailer.defaults.to
16
+ this.from = config.from || emailer.defaults.from
17
+ this.humanReadableUnhandledException = options.humanReadableUnhandledException || true
18
+ this.handleExceptions = options.handleExceptions || true
19
+ this.json = options.json || false
20
+ this.colorize = options.colorize || false
21
+ }
26
22
 
27
23
  /** @extends winston.Transport */
28
- util.inherits(MailerLogger, winston.Transport);
24
+ util.inherits(MailerLogger, winston.Transport)
29
25
 
30
26
  //
31
27
  // Expose the name of this Transport on the prototype
32
28
  //
33
- MailerLogger.prototype.name = 'MailerLogger';
34
-
29
+ MailerLogger.prototype.name = "MailerLogger"
35
30
 
36
31
  MailerLogger.prototype.log = function (info, callback) {
37
- let self = this;
38
-
39
- let title = ''
40
- let body = ''
41
-
32
+ let title = ""
42
33
  try {
43
34
  try {
44
- title= info.message.split("\n")[0]
45
- } catch(e) {
46
- title= info.message.toString();
35
+ title = info.message.split("\n")[0]
36
+ } catch (_) {
37
+ title = info.message.toString()
47
38
  }
48
-
49
- title= uncolor(title)
50
-
51
- } catch(err) {
39
+ title = uncolor(title)
40
+ } catch (err) {
52
41
  title = `Could not create a title for the error (${err.toString()})`
53
42
  }
54
43
 
44
+ let body = info?.message || ""
55
45
  try {
56
- try {
57
- body = uncolor(info.message)
58
- } catch(err) {
59
- body = info.message.toString()
60
- }
61
- } catch(err) {
62
- body = `Could not create a body for the error (${err.toString()})`
63
- }
64
-
65
- try {
66
- let subject = `${config?.name} [${info.level.toUpperCase()}] ${title}`
46
+ body = uncolor(body)
47
+ } catch (_) {}
48
+
49
+ const html = buildErrorEmailBody(info)
67
50
 
68
- let mail= {
69
- from : this.from,
70
- to : this.to,
71
- subject : subject,
72
- text : body
51
+ try {
52
+ const subject = `${config?.name} [${info.level.toUpperCase()}] ${title}`
53
+
54
+ const mail = {
55
+ from: this.from,
56
+ to: this.to,
57
+ subject: subject,
58
+ text: body,
59
+ html: html
73
60
  }
74
61
 
75
62
  emailer.queue_email(mail)
76
- self.emit("logged")
63
+ this.emit("logged")
77
64
  callback(null, true)
78
-
79
- } catch(error) {
65
+ } catch (_) {
80
66
  // TODO - How to exxpose info from here
81
- self.emit("logged");
82
- callback(null, true);
67
+ this.emit("logged")
68
+ callback(null, true)
83
69
  }
84
- };
85
-
70
+ }
71
+
86
72
  return MailerLogger
87
73
  }
88
74
 
89
- export {init_logger_to_mail}
75
+ export { init_logger_to_mail }
@@ -1,18 +1,18 @@
1
1
  /**
2
2
  * https://gist.github.com/suprememoocow/5133080
3
- *
3
+ *
4
4
  * https://github.com/winstonjs/winston/issues/943
5
- *
5
+ *
6
6
  * A function for reopening a winston File logging transport post logrotation on a HUP signal.
7
- * To send a HUP to your node service, use the postrotate configuration option from logrotate.
7
+ * To send a HUP to your node service, use the postrotate configuration option from logrotate.
8
8
  * `postrotate kill -HUP ‘cat /var/run/mynodeservice.pid‘`
9
9
  */
10
- import path from 'path'
11
- import fs from 'fs'
12
10
 
13
- export function reopenTransportOnHupSignal(fileTransport) {
14
- process.on('SIGHUP', function() {
11
+ import fs from "node:fs"
12
+ import path from "node:path"
15
13
 
14
+ export function reopenTransportOnHupSignal(fileTransport) {
15
+ process.on("SIGHUP", () => {
16
16
  const fullname = path.join(fileTransport.dirname, fileTransport._getFile(false))
17
17
 
18
18
  //console.log(`[miolo][file-logger] SIGHUP received. Check if we need to re-open log file ${fullname}...`)
@@ -25,21 +25,21 @@ export function reopenTransportOnHupSignal(fileTransport) {
25
25
  fileTransport._stream.destroy() // Soon()
26
26
  }
27
27
 
28
- let stream = fs.createWriteStream(fullname, fileTransport.options)
28
+ const stream = fs.createWriteStream(fullname, fileTransport.options)
29
29
  stream.setMaxListeners(Infinity)
30
30
 
31
31
  fileTransport._size = 0
32
32
  fileTransport._stream = stream
33
33
 
34
34
  //fileTransport.once('flush', function () {
35
- fileTransport.opening = false
36
- fileTransport.emit('open', fullname)
35
+ fileTransport.opening = false
36
+ fileTransport.emit("open", fullname)
37
37
  //})
38
38
 
39
39
  //fileTransport.flush()
40
40
 
41
41
  console.log(`[miolo][file-logger] Reopened ${fullname} successfully`)
42
- } catch(error) {
42
+ } catch (error) {
43
43
  console.error(`[miolo][file-logger] Error reopening ${fullname}: ${error.toString()}`)
44
44
  }
45
45
  }
@@ -52,6 +52,5 @@ export function reopenTransportOnHupSignal(fileTransport) {
52
52
  })
53
53
  */
54
54
  return reopen()
55
-
56
55
  })
57
- }
56
+ }
@@ -1,126 +1,143 @@
1
1
  export default class Parser {
2
-
3
- parse_value_str(v, required= false, def= undefined) {
4
- if ((v==null) || (v==undefined)) {
2
+ parse_value_str(v, required = false, def = undefined) {
3
+ if (v === null || v === undefined) {
5
4
  if (required) {
6
- throw `Wrong str value passed: ${v}`
5
+ throw `Wrong str value passed: ${v}`
7
6
  }
8
7
  return def
9
8
  }
10
-
9
+
11
10
  return v.toString()
12
11
  }
13
-
14
- parse_field_str(fields, name, required= false, def= undefined) {
15
- if ( ! (name in fields)) {
12
+
13
+ parse_field_str(fields, name, required = false, def = undefined) {
14
+ if (!(name in fields)) {
16
15
  if (required) {
17
- throw `Expected str value not passed for ${name}`
16
+ throw `Expected str value not passed for ${name}`
18
17
  }
19
18
  return def
20
19
  }
21
- return this.parse_value_str(fields[name], required, def)
20
+ try {
21
+ return this.parse_value_str(fields[name], required, def)
22
+ } catch (e) {
23
+ throw `parse_field_str() Error for ${name}: ${e}`
24
+ }
22
25
  }
23
-
24
- parse_value_int(v, required= false, def= undefined) {
25
- let vi= parseInt(v)
26
- if ((vi==null) || (isNaN(vi))) {
26
+
27
+ parse_value_int(v, required = false, def = undefined) {
28
+ const vi = parseInt(v, 10)
29
+ if (vi === null || Number.isNaN(Number(vi))) {
27
30
  if (required) {
28
- throw `Wrong int value passed: ${v}`
31
+ throw `Wrong int value passed: ${v}`
29
32
  }
30
33
  return def
31
34
  }
32
-
35
+
33
36
  return vi
34
37
  }
35
-
36
- parse_field_int(fields, name, required= false, def= undefined) {
37
- if ( ! (name in fields)) {
38
+
39
+ parse_field_int(fields, name, required = false, def = undefined) {
40
+ if (!(name in fields)) {
38
41
  if (required) {
39
- throw `Expected int value not passed for ${name}`
42
+ throw `Expected int value not passed for ${name}`
40
43
  }
41
44
  return def
42
45
  }
43
- return this.parse_value_int(fields[name], required, def)
46
+ try {
47
+ return this.parse_value_int(fields[name], required, def)
48
+ } catch (e) {
49
+ throw `parse_field_int() Error for ${name}: ${e}`
50
+ }
44
51
  }
45
-
46
- parse_value_float(v, required= false, def= undefined) {
47
- const vf= parseFloat(v)
48
- if ((vf==null) || (isNaN(vf))) {
52
+
53
+ parse_value_float(v, required = false, def = undefined) {
54
+ const vf = parseFloat(v)
55
+ if (vf === null || Number.isNaN(Number(vf))) {
49
56
  if (required) {
50
- throw `Wrong float value passed: ${v}`
57
+ throw `Wrong float value passed: ${v}`
51
58
  }
52
59
  return def
53
60
  }
54
-
61
+
55
62
  return v
56
63
  }
57
-
58
- parse_field_float(fields, name, required= false, def= undefined) {
59
- if ( ! (name in fields)) {
64
+
65
+ parse_field_float(fields, name, required = false, def = undefined) {
66
+ if (!(name in fields)) {
60
67
  if (required) {
61
- throw `Expected float value not passed for ${name}`
68
+ throw `Expected float value not passed for ${name}`
62
69
  }
63
70
  return def
64
71
  }
65
-
66
- return this.parse_value_float(fields[name], required, def)
72
+ try {
73
+ return this.parse_value_float(fields[name], required, def)
74
+ } catch (e) {
75
+ throw `parse_field_float() Error for ${name}: ${e}`
76
+ }
67
77
  }
68
-
69
- parse_value_bool(v, required= false, def= undefined) {
70
- if ((v==null) || (v==undefined)) {
78
+
79
+ parse_value_bool(v, required = false, def = undefined) {
80
+ if (v === null || v === undefined) {
71
81
  if (required) {
72
- throw `Wrong bool value passed for ${name}`
82
+ throw `Wrong bool value passed for ${name}`
73
83
  }
74
84
  return undefined
75
85
  }
76
- if ( (v===true) || (v==='true') ||(v==='True') ||(v===1) ||(v==='1')) {
86
+ if (v === true || v === "true" || v === "True" || v === 1 || v === "1") {
77
87
  return true
78
88
  }
79
- if ( (v===false) || (v==='false') ||(v==='False') ||(v===0) ||(v==='0')) {
89
+ if (v === false || v === "false" || v === "False" || v === 0 || v === "0") {
80
90
  return false
81
91
  }
82
-
92
+
83
93
  return def
84
94
  }
85
-
86
- parse_field_bool(fields, name, required= false, def= undefined) {
87
- if ( ! (name in fields)) {
95
+
96
+ parse_field_bool(fields, name, required = false, def = undefined) {
97
+ if (!(name in fields)) {
88
98
  if (required) {
89
- throw `Expected bool value not passed for ${name}`
99
+ throw `Expected bool value not passed for ${name}`
90
100
  }
91
101
  return def
92
102
  }
93
-
94
- return this.parse_value_bool(fields[name], required, def)
103
+ try {
104
+ return this.parse_value_bool(fields[name], required, def)
105
+ } catch (e) {
106
+ throw `parse_field_bool() Error for ${name}: ${e}`
107
+ }
95
108
  }
96
-
97
- parse_value_obj(v, required= false, def= undefined) {
98
- if ((v==null) || (v==undefined)) {
109
+
110
+ parse_value_obj(v, required = false, def = undefined) {
111
+ if (v === null || v === undefined) {
99
112
  if (required) {
100
- throw `Wrong obj value passed: ${v}`
113
+ throw `Wrong obj value passed: ${v}`
101
114
  }
102
115
  return def
103
116
  }
104
- if (Object.keys(v).length==0) {
117
+ if (Object.keys(v).length === 0) {
105
118
  if (required) {
106
119
  throw `Empty obj value passed: ${v}`
107
120
  }
108
121
  return def
109
- }
110
- if (typeof v != 'object') {
122
+ }
123
+ if (typeof v !== "object") {
111
124
  throw `Wrong obj value passed: ${v}`
112
- }
113
-
125
+ }
126
+
114
127
  return v
115
128
  }
116
-
117
- parse_field_obj(fields, name, required= false, def= undefined) {
118
- if ( ! (name in fields)) {
129
+
130
+ parse_field_obj(fields, name, required = false, def = undefined) {
131
+ if (!(name in fields)) {
119
132
  if (required) {
120
- throw `Expected obj value not passed for ${name}`
133
+ throw `Expected obj value not passed for ${name}`
121
134
  }
122
135
  return def
123
136
  }
124
- return this.parse_value_obj(fields[name], required, def)
137
+ try {
138
+ return this.parse_value_obj(fields[name], required, def)
139
+ } catch (e) {
140
+ throw `parse_field_obj() Error for ${name}: ${e}`
141
+ }
125
142
  }
126
143
  }
@@ -1,6 +1,6 @@
1
1
  import Parser from "./Parser.mjs"
2
2
 
3
3
  export const init_parser = () => {
4
- const p = new Parser()
4
+ const p = new Parser()
5
5
  return p
6
6
  }
@@ -0,0 +1,41 @@
1
+ export function diffObjs(o1, o2) {
2
+ const missingKeys = []
3
+
4
+ function findMissing(obj1, obj2, prefix = "") {
5
+ for (const key in obj1) {
6
+ if (Object.hasOwn(obj1, key)) {
7
+ const fullKey = prefix ? `${prefix}.${key}` : key
8
+
9
+ if (!obj2 || !(key in obj2)) {
10
+ missingKeys.push(`'${fullKey}'`)
11
+ } else if (
12
+ typeof obj1[key] === "object" &&
13
+ obj1[key] !== null &&
14
+ typeof obj2[key] === "object" &&
15
+ obj2[key] !== null &&
16
+ !Array.isArray(obj1[key]) &&
17
+ !Array.isArray(obj2[key])
18
+ ) {
19
+ findMissing(obj1[key], obj2[key], fullKey)
20
+ }
21
+ }
22
+ }
23
+ }
24
+
25
+ findMissing(o1, o2)
26
+
27
+ if (missingKeys.length === 0) {
28
+ return ""
29
+ }
30
+
31
+ const isPlural = missingKeys.length > 1
32
+ let keysStr = ""
33
+ if (!isPlural) {
34
+ keysStr = missingKeys[0]
35
+ } else {
36
+ const lastKey = missingKeys.pop()
37
+ keysStr = `${missingKeys.join(", ")} and ${lastKey}`
38
+ }
39
+
40
+ return keysStr
41
+ }
@@ -0,0 +1,4 @@
1
+ import { with_miolo_input_schema } from "./input.mjs"
2
+ import { with_miolo_output_schema } from "./output.mjs"
3
+
4
+ export { with_miolo_input_schema, with_miolo_output_schema }
@@ -0,0 +1,54 @@
1
+ import Joi from "joi"
2
+
3
+ export function with_miolo_input_schema(fn, schema, options) {
4
+ return async (ctx, params) => {
5
+ let error
6
+
7
+ // Check schema is actually a schema
8
+ if (!schema || !Joi.isSchema(schema)) {
9
+ error = `Expecting input schema for ${fn.name} but something else was found (${typeof schema})`
10
+ ctx.miolo.logger.silly(`[validation][${fn.name}] ${error}`)
11
+ throw new Error(error)
12
+ }
13
+
14
+ // perform validation
15
+ let v
16
+ try {
17
+ v = schema.validate(params, {
18
+ ...(options || {})
19
+ })
20
+ } catch (uerror) {
21
+ error = `Unexpected error validating input data for ${fn.name}: ${uerror?.message || uerror}`
22
+ ctx.miolo.logger.silly(`[validation][${fn.name}] ${error}`)
23
+ throw new Error(error)
24
+ }
25
+
26
+ // raise validation errors
27
+ if (v?.error) {
28
+ error = `Input schema invalidated data for ${fn.name}: ${v.error}\n${v.error.annotate(true)}`
29
+ ctx.miolo.logger.silly(`[validation][${fn.name}] ${error}`)
30
+ throw new Error(error)
31
+ }
32
+
33
+ // check parsed value is ok
34
+ if (!v?.value) {
35
+ const description = schema.describe()
36
+
37
+ // Check if schema was deliberately set to allow only null
38
+ // schema = Joi.any().allow(null)
39
+ const isOnlyNull =
40
+ description.type === "any" &&
41
+ description.allow &&
42
+ description.allow.length === 1 &&
43
+ description.allow[0] === null
44
+
45
+ if (!isOnlyNull) {
46
+ error = `Input schema returned unknown result for ${fn.name}: ${JSON.stringify(v)}`
47
+ ctx.miolo.logger.silly(`[validation][${fn.name}] ${error}`)
48
+ throw new Error(error)
49
+ }
50
+ }
51
+
52
+ return await fn(ctx, v.value)
53
+ }
54
+ }
@@ -0,0 +1,66 @@
1
+ import Joi from "joi"
2
+ import { diffObjs } from "./diffObjs.mjs"
3
+
4
+ export function with_miolo_output_schema(fn, schema, options) {
5
+ const fnName = fn?.name ? `[${fn.name}]` : ""
6
+
7
+ return async (ctx, params) => {
8
+ let error
9
+
10
+ // Check schema is actually a schema
11
+ if (!schema || !Joi.isSchema(schema)) {
12
+ error = `Expecting output schema for ${fn.name} but something else was found (${typeof schema})`
13
+ ctx.miolo.logger.silly(`[validation]${fnName} ${error}`)
14
+ throw new Error(error)
15
+ }
16
+
17
+ // Call the function first
18
+ const result = await fn(ctx, params)
19
+
20
+ // perform validation over the result
21
+ let v
22
+ try {
23
+ v = schema.validate(result, {
24
+ stripUnknown: true,
25
+ ...(options || {})
26
+ })
27
+ } catch (uerror) {
28
+ error = `Unexpected error validating output data for ${fn.name}: ${uerror?.message || uerror}`
29
+ ctx.miolo.logger.silly(`[validation]${fnName} ${error}`)
30
+ throw new Error(error)
31
+ }
32
+
33
+ // raise validation errors
34
+ if (v?.error) {
35
+ error = `Output schema invalidated data for ${fn.name}: ${v.error}\n${v.error.annotate(true)}`
36
+ ctx.miolo.logger.silly(`[validation]${fnName} ${error}`)
37
+ throw new Error(error)
38
+ }
39
+
40
+ // check parsed value is ok
41
+ if (!v?.value) {
42
+ const description = schema.describe()
43
+
44
+ // Check if schema was deliberately set to allow only null
45
+ // schema = Joi.any().allow(null)
46
+ const isOnlyNull =
47
+ description.type === "any" &&
48
+ description.allow &&
49
+ description.allow.length === 1 &&
50
+ description.allow[0] === null
51
+
52
+ if (!isOnlyNull) {
53
+ error = `Output schema returned unknown result for ${fn.name}: ${JSON.stringify(v)}`
54
+ ctx.miolo.logger.silly(`[validation]${fnName} ${error}`)
55
+ throw new Error(error)
56
+ }
57
+ }
58
+
59
+ const diff = diffObjs(result, v.value)
60
+ if (diff) {
61
+ ctx.miolo.logger.debug(`[validation]${fnName} Output schema has removed ${diff}`)
62
+ }
63
+
64
+ return v.value
65
+ }
66
+ }