miolo 3.0.0-beta.13 → 3.0.0-beta.131

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 (52) hide show
  1. package/bin/{dev.mjs → dev/dev.mjs} +19 -5
  2. package/bin/{dev_start.mjs → dev/dev_start.mjs} +3 -2
  3. package/bin/index.mjs +47 -33
  4. package/bin/prod-bin/create-bin.mjs +33 -0
  5. package/bin/prod-bin/run.mjs +35 -0
  6. package/bin/{build-client.mjs → prod-build/build-client.mjs} +17 -5
  7. package/bin/{build-server.mjs → prod-build/build-server.mjs} +12 -10
  8. package/bin/prod-run/pid.mjs +13 -0
  9. package/bin/{restart.mjs → prod-run/restart.mjs} +3 -4
  10. package/bin/prod-run/start.mjs +14 -0
  11. package/bin/{stop.mjs → prod-run/stop.mjs} +4 -3
  12. package/bin/util.mjs +2 -29
  13. package/package.json +25 -26
  14. package/{bin → src/config}/.env +17 -6
  15. package/src/config/defaults.mjs +443 -422
  16. package/src/config/env.mjs +52 -0
  17. package/src/config/index.mjs +17 -10
  18. package/src/config/util.mjs +41 -0
  19. package/src/engines/emailer/queue.mjs +3 -2
  20. package/src/engines/emailer/transporter.mjs +10 -9
  21. package/src/engines/http/index.mjs +1 -1
  22. package/src/engines/parser/Parser.mjs +25 -7
  23. package/src/engines/schema/index.mjs +40 -0
  24. package/src/index.mjs +3 -1
  25. package/src/middleware/auth/basic.mjs +5 -2
  26. package/src/middleware/auth/credentials/index.mjs +75 -43
  27. package/src/middleware/auth/credentials/session/index.mjs +7 -0
  28. package/src/middleware/auth/credentials/session/store.mjs +9 -0
  29. package/src/middleware/context/index.mjs +1 -1
  30. package/src/middleware/http/catcher.mjs +78 -2
  31. package/src/middleware/http/catcher.old.mjs +82 -0
  32. package/src/middleware/http/custom_blacklist.mjs +3 -1
  33. package/src/middleware/routes/catch_js_error.mjs +14 -2
  34. package/src/middleware/routes/router/crud/attachCrudRoutes.mjs +24 -12
  35. package/src/middleware/routes/router/crud/getCrudConfig.mjs +4 -6
  36. package/src/middleware/routes/router/defaults.mjs +1 -11
  37. package/src/middleware/routes/router/index.mjs +0 -1
  38. package/src/middleware/routes/router/queries/attachQueriesRoutes.mjs +70 -13
  39. package/src/middleware/routes/router/queries/getQueriesConfig.mjs +21 -21
  40. package/src/middleware/routes/router/utils.mjs +43 -25
  41. package/src/middleware/ssr/context.mjs +1 -1
  42. package/src/middleware/ssr/fallbackIndex.mjs +2 -8
  43. package/src/middleware/ssr/html.mjs +39 -31
  44. package/src/middleware/vite/devserver.mjs +22 -8
  45. package/src/server-cron.mjs +3 -4
  46. package/src/server-dev.mjs +3 -3
  47. package/src/server.mjs +3 -5
  48. package/bin/create-bin.mjs +0 -38
  49. package/bin/env.mjs +0 -39
  50. package/bin/prod_start.mjs +0 -9
  51. package/bin/start.mjs +0 -17
  52. package/src/engines/logger/verify.mjs +0 -22
@@ -3,36 +3,54 @@
3
3
  */
4
4
  import qs from 'qs'
5
5
 
6
- export function query_string_to_json(url) {
7
- let search= url.indexOf('?')>=0 ? url.substr(url.indexOf('?')+1) : '';
8
- if (search) {
9
- return qs.parse(search)
6
+ export function query_string_to_json(url) {
7
+ const search = url.includes('?') ? url.substring(url.indexOf('?') + 1) : ''
8
+ if (!search) return {}
9
+
10
+ const parsed = qs.parse(search)
11
+ //
12
+ function reviveTypes(value) {
13
+ if (Array.isArray(value)) return value.map(reviveTypes)
14
+ if (value && typeof value === 'object') {
15
+ const result = {}
16
+ for (const key in value) {
17
+ if (Object.hasOwn(value, key)) result[key] = reviveTypes(value[key])
18
+ }
19
+ return result
20
+ }
21
+ // Try to convert to boolean, number o null
22
+ if (value === 'true') return true
23
+ if (value === 'false') return false
24
+ if (value === 'null') return null
25
+ if (!isNaN(value) && value !== '') return Number(value)
26
+ return value
10
27
  }
11
- return {}
28
+
29
+ return reviveTypes(parsed)
12
30
  }
13
31
 
14
- export function make_endpoint_from_fn(fn, field) {
15
32
 
16
- async function endpoint_from_fn(ctx) {
17
- const params = ctx.request.body
18
- try {
19
- ctx.miolo.logger.debug(`[router] ${fn.name}() Calling with params ${JSON.stringify(params)}`)
20
- } catch (_) {
21
- ctx.miolo.logger.debug(`[router] ${fn.name}()Calling with params (?)`)
33
+ export function ensure_response_is_ok_data(ctx, response) {
34
+ if ((response?.ok === undefined) && (response?.data === undefined) && (response?.error === undefined)) {
35
+ ctx.miolo.logger.debug(`[router] Response without ok/[data/error] fields. It is: ${JSON.stringify(response)}. Let's wrap it on an ok response`)
36
+ return {
37
+ ok: true,
38
+ data: response || {}
22
39
  }
23
-
24
- const result = await fn(ctx.miolo, params)
25
-
26
- try {
27
- ctx.miolo.logger.debug(`[router] ${fn.name}() Called with result ${JSON.stringify(result)}`)
28
- } catch (_) {
29
- ctx.miolo.logger.debug(`[router] ${fn.name}() Called with result (?)`)
40
+ }
41
+ if (response?.error !== undefined) {
42
+ return {
43
+ ok: false,
44
+ error: response.error,
45
+ data: response?.data
30
46
  }
31
-
32
- ctx.body = result
33
47
  }
34
48
 
35
- Object.defineProperty(endpoint_from_fn, 'name', {value: fn.name, writable: false})
36
-
37
- return endpoint_from_fn
38
- }
49
+ const ok = response?.ok !== false
50
+ const data = (response?.data!==undefined)
51
+ ? response.data
52
+ : (response!==undefined)
53
+ ? response
54
+ : {}
55
+ return {ok, data}
56
+ }
@@ -13,7 +13,7 @@ export const ssr_context_builder_make = (app, ssrConfig) => {
13
13
  ssr_data: ssr_data,
14
14
  extra: ctx?.extra
15
15
  }
16
-
16
+
17
17
  return context
18
18
  }
19
19
 
@@ -13,16 +13,10 @@ export const fallbackIndexHTML = `
13
13
  <!-- Touch Icons - iOS and Android 2.1+ 180x180 pixels in size. -->
14
14
  <!--<link rel="apple-touch-icon-precomposed" href="/favicon.ico"/>-->
15
15
  <!-- Firefox, Chrome, Safari, IE 11+ and Opera. 196x196 pixels in size. -->
16
- <link rel="icon" href="/favicon.ico"/>
17
-
18
-
19
- <script>
20
- window.__CONTEXT = {context}
21
- </script>
16
+ <link rel="icon" href="/favicon.ico"/>
22
17
  </head>
23
-
24
18
  <body>
25
- <div id="root">{children}</div>
19
+
26
20
  </body>
27
21
  </html>
28
22
  `
@@ -1,8 +1,21 @@
1
1
  import path from 'path'
2
2
  import { readFileSync } from 'fs'
3
3
  import { fallbackIndexHTML } from './fallbackIndex.mjs'
4
+ import { nanoid } from 'nanoid'
5
+
6
+ let _script_version = undefined
7
+ const get_script_version = () => {
8
+ if (!_script_version) {
9
+ _script_version = nanoid()
10
+ }
11
+ return _script_version
12
+ }
4
13
 
5
14
  function _html_read(htmlFile) {
15
+
16
+ if (!htmlFile) {
17
+ return fallbackIndexHTML
18
+ }
6
19
  try {
7
20
  const isProduction = process.env.NODE_ENV === 'production'
8
21
  const proot = (p) => path.join(process.cwd(), p)
@@ -18,28 +31,30 @@ function _html_read(htmlFile) {
18
31
  }
19
32
  }
20
33
 
34
+ function _feed_html(htmlString, client, context, ssr_html) {
35
+ const contextScript = `<script> window.__CONTEXT = ${JSON.stringify(context, null, 2)}</script>`
36
+ const rootDiv = `<div id="root">${ssr_html}</div>`
37
+
38
+ const webClient = client.startsWith('./')
39
+ ? client.replace('./', '/')
40
+ : client.startsWith('/')
41
+ ? client
42
+ : `/${client}`
21
43
 
22
- function _add_client_script_to_body(htmlString, client) {
23
44
  const linkTag =
24
45
  process.env.NODE_ENV === 'production'
25
- ? ` <script src="${client}" async></script>`
26
- : ` <script type="module" src="${client}"></script>`
46
+ ? ` <script src="${webClient}?v=${get_script_version()}" async></script>`
47
+ : ` <script type="module" src="${webClient}"></script>`
27
48
 
28
- // Expresión regular para encontrar la etiqueta </body> de cierre.
29
- // Usamos un grupo de captura para mantener el contenido antes de </body>.
30
- const bodyCloseTagRegex = /(<\/body>)/i;
31
-
32
- // Busca la etiqueta </body> en el HTML.
33
- const match = htmlString.match(bodyCloseTagRegex);
34
-
35
- if (match) {
36
- // Si se encuentra </body>, inserta la etiqueta <link> justo antes de ella.
37
- return htmlString.replace(bodyCloseTagRegex, `${linkTag}\n$&`);
38
- } else {
39
- // Si no se encuentra </body>, devuelve el HTML original sin modificar.
40
- console.warn(`[miolo] No se encontró la etiqueta <body> en el HTML proporcionado.`);
41
- return htmlString;
42
- }
49
+ // head
50
+ const headCloseTagRegex = /(<\/head>)/i
51
+ htmlString= htmlString.replace(headCloseTagRegex, `${contextScript}\n$&`)
52
+
53
+ // body
54
+ const bodyCloseTagRegex = /(<\/body>)/i
55
+ htmlString= htmlString.replace(bodyCloseTagRegex, `${rootDiv}\n${linkTag}\n$&`)
56
+
57
+ return htmlString
43
58
  }
44
59
 
45
60
 
@@ -48,12 +63,7 @@ export const ssr_html_renderer_make = async (app, ssrConfig, htmlFile, client, d
48
63
  const isProduction = process.env.NODE_ENV === 'production'
49
64
 
50
65
  // check HTML
51
- let tmplHtml = _html_read(htmlFile)
52
- for (const vrb of ['{context}', '{children}']) {
53
- if (tmplHtml.indexOf(vrb) < 0) {
54
- app.context.miolo.logger.error(`[ssr] Provided HTML for rendering has no ${vrb} template variable`)
55
- }
56
- }
66
+ const tmplHtml = _html_read(htmlFile)
57
67
 
58
68
  const ssr_html_renderer = async (ctx, context) => {
59
69
  let base_html= tmplHtml
@@ -74,9 +84,11 @@ export const ssr_html_renderer_make = async (app, ssrConfig, htmlFile, client, d
74
84
  // if non-vite or prod
75
85
  } else {
76
86
  try {
77
- render = (await import(ssrConfig.server)).render
87
+ if (ssrConfig?.server !== 'false') {
88
+ render = (await import(ssrConfig.server)).render
89
+ }
78
90
  } catch(error) {
79
- ctx.miolo.logger.error(`SSR Error:\n${error.toString()}\n${error.stack}`)
91
+ ctx.miolo.logger.error(`SSR Error for ${ssrConfig.server}:\n${error.toString()}\n${error.stack}`)
80
92
  }
81
93
  }
82
94
 
@@ -94,11 +106,7 @@ export const ssr_html_renderer_make = async (app, ssrConfig, htmlFile, client, d
94
106
  `
95
107
  }
96
108
 
97
- let parsed_html = base_html
98
- .replace('{context}', JSON.stringify(context, null, 2))
99
- .replace('{children}', ssr_html)
100
-
101
- parsed_html = _add_client_script_to_body(parsed_html, client)
109
+ const parsed_html = _feed_html(base_html, client, context, ssr_html)
102
110
 
103
111
  return parsed_html
104
112
  }
@@ -1,31 +1,45 @@
1
1
 
2
2
  import koaConnect from 'koa-connect'
3
+ // import { resolve } from 'path'
4
+ import {createServer} from 'vite'
5
+ import react from '@vitejs/plugin-react'
6
+ import tailwindVite from '@tailwindcss/vite'
3
7
 
4
8
  // Vite Dev Server
5
9
  export async function init_vite_dev_server_middleware(app, viteConfig) {
6
10
  const isProduction = process.env.NODE_ENV === 'production'
7
11
  let vite
8
-
12
+
9
13
  if (!isProduction) {
10
- const { createServer } = await import('vite')
11
- const react = await import('@vitejs/plugin-react')
14
+ // const tailwindConfigPath = resolve(process.cwd(), 'tailwind.config.js')
15
+ // const tailwindConfig = await import(tailwindConfigPath)
12
16
 
13
17
  vite = await createServer({
14
- server: { middlewareMode: true },
18
+ server: {
19
+ middlewareMode: true ,
20
+ port: process.env?.MIOLO_PORT || 8001,
21
+ hmr: {
22
+ port: process.env?.MIOLO_DEV_PORT || ((process.env?.MIOLO_PORT || 8001) - 1000)
23
+ }
24
+ },
15
25
  appType: 'custom',
16
- plugins: [react.default(
26
+ plugins: [react(
17
27
  {
18
28
  babel: {
19
29
  plugins: [
20
30
  ["@babel/plugin-proposal-decorators", { "legacy": true }]
21
31
  ]
22
32
  }
23
- }
24
- )],
33
+ }),
34
+ tailwindVite(
35
+ // @tailwindcss/vite does not accept params
36
+ //{config: tailwindConfig}
37
+ )
38
+ ],
25
39
  ...viteConfig || {},
26
40
  //
27
41
  base: viteConfig?.base || '/',
28
- root: viteConfig?.root || '',
42
+ root: viteConfig?.root || process.cwd(),
29
43
  watch: {
30
44
  // During tests we edit the files too fast and sometimes chokidar
31
45
  // misses change events, so enforce polling for consistency
@@ -1,16 +1,15 @@
1
- import { init_config } from './config/index.mjs'
1
+ import { init_config } from './config/index.mjs'
2
2
  import { init_context_middleware } from './middleware/context/index.mjs'
3
3
  import { init_cron } from './engines/cron/index.mjs'
4
4
 
5
- export async function miolo_cron(sconfig) {
6
-
5
+ export async function miolo_cron(makeConfig) {
7
6
  const app = {
8
7
  use: () => {},
9
8
  context: {}
10
9
  }
11
10
 
12
11
  // Init some pieces
13
- const config = init_config(sconfig)
12
+ const config = init_config(makeConfig)
14
13
 
15
14
  // attach to app some custom miolo methods
16
15
  init_context_middleware(app, config)
@@ -14,8 +14,8 @@ import { init_watcher_dev_server_middleware } from './middleware/vite/watcher.mj
14
14
  * miolo_dev() will be available thourgh a different
15
15
  * package import (miolo/server-dev)
16
16
  */
17
- export async function miolo_dev(sconfig) {
18
-
17
+ export async function miolo_dev(makeConfig) {
18
+
19
19
  // Vite DEV server init
20
20
  const devInit= async (app, config) => {
21
21
  await init_vite_dev_server_middleware(app, config.build.vite)
@@ -37,7 +37,7 @@ export async function miolo_dev(sconfig) {
37
37
  }
38
38
  }
39
39
 
40
- return await miolo(sconfig, devInit, devRender)
40
+ return await miolo(makeConfig, devInit, devRender)
41
41
  }
42
42
 
43
43
 
package/src/server.mjs CHANGED
@@ -26,13 +26,12 @@ import { init_ssr_render_middleware } from './middleware/ssr/ssr_render.mjs
26
26
  import { init_cron } from './engines/cron/index.mjs'
27
27
  import { init_http_server } from './engines/http/index.mjs'
28
28
 
29
- async function miolo(sconfig, devInit= undefined, devRender= undefined) {
30
-
29
+ async function miolo(makeConfig, devInit= undefined, devRender= undefined) {
31
30
  const app = new Koa()
32
31
 
33
32
  // Init some pieces
34
- const config = init_config(sconfig)
35
-
33
+ const config = init_config(makeConfig)
34
+
36
35
  // attach to app some custom miolo methods
37
36
  init_context_middleware(app, config)
38
37
 
@@ -42,7 +41,6 @@ async function miolo(sconfig, devInit= undefined, devRender= undefined) {
42
41
  // await init_vite_dev_server_middleware(app, config.build.vite)
43
42
  }
44
43
 
45
-
46
44
  // CORS and other headers
47
45
  init_headers_middleware(app, config.http)
48
46
 
@@ -1,38 +0,0 @@
1
- import {readFileSync, writeFileSync} from 'node:fs'
2
- import path from 'path'
3
- import { fileURLToPath } from 'url'
4
-
5
- const __my_filename = fileURLToPath(import.meta.url)
6
- const __my_dirname = path.dirname(__my_filename)
7
-
8
-
9
- export default async function(appName, dest, serverName) {
10
- console.log(`[${appName}][prod][create-bin] Creating bin files...`)
11
-
12
- const readSource = (f) => readFileSync(path.resolve(__my_dirname, f), {encoding:'utf8'})
13
- const writeDest = (f, content) => writeFileSync(path.join(process.cwd(), dest, f), content, {encoding:'utf8',flag:'w'})
14
-
15
- const utilContent = readSource('./util.mjs')
16
- writeDest('util.mjs', utilContent)
17
-
18
- let startContent = readSource('./start.mjs')
19
- startContent = startContent.replace('export default async function', 'async function start')
20
- startContent = startContent.replace('(appName, dest, serverName)', `(appName= '${appName}', dest= '${dest}', serverName= '${serverName}')`)
21
- startContent+= '\n'
22
- startContent+= 'start()'
23
- writeDest('start.mjs', startContent)
24
-
25
- let stopContent = readSource('./stop.mjs')
26
- stopContent = stopContent.replace('export default async function', 'async function stop')
27
- stopContent = stopContent.replace('(appName)', `(appName= '${appName}')`)
28
- stopContent+= '\n'
29
- stopContent+= 'stop()'
30
- writeDest('stop.mjs', stopContent)
31
-
32
- let restartContent = readSource('./restart.mjs')
33
- restartContent = restartContent.replace('export default async function', 'async function restart')
34
- restartContent = restartContent.replace('(appName, dest, serverName)', `(appName= '${appName}', dest= '${dest}', serverName= '${serverName}')`)
35
- restartContent+= '\n'
36
- restartContent+= 'restart()'
37
- writeDest('restart.mjs', restartContent)
38
- }
package/bin/env.mjs DELETED
@@ -1,39 +0,0 @@
1
- import { config } from '@dotenvx/dotenvx'
2
- import path from 'path'
3
- import { fileURLToPath } from 'url'
4
- import fs from 'fs'
5
-
6
- function _find_closest_package_json(dir) {
7
- if (fs.existsSync(path.join(dir, 'package.json'))) {
8
- return dir
9
- }
10
- const parentDir = path.dirname(dir)
11
- if (parentDir === dir) {
12
- return null
13
- }
14
- return _find_closest_package_json(parentDir)
15
- }
16
-
17
-
18
- function _get_miolo_config_path() {
19
- const __filename = fileURLToPath(import.meta.url)
20
- const __dirname = path.dirname(__filename)
21
-
22
- return path.join(__dirname, '.env')
23
- }
24
-
25
-
26
- export function init_env_config() {
27
-
28
- const debug = process.env.DOTENVX_DEBUG === 'true'
29
-
30
- // miolo defaults
31
- const libEnvPath = _get_miolo_config_path()
32
- config({ path: libEnvPath, debug })
33
-
34
- // proyect config
35
- const proyEnvPath = _find_closest_package_json(process.cwd())
36
- if (proyEnvPath) {
37
- config({ path: path.join(proyEnvPath, '.env'), override: true, debug })
38
- }
39
- }
@@ -1,9 +0,0 @@
1
- import path from 'node:path'
2
- import {miolo} from '../src/server.mjs'
3
-
4
- export default async function _miolo_prod_start_server() {
5
- const config = await import(path.join(process.cwd(), process.env.MIOLO_CONFIG))
6
- const app = await miolo(config.default)
7
- await app.start()
8
- return app
9
- }
package/bin/start.mjs DELETED
@@ -1,17 +0,0 @@
1
- import path from 'node:path'
2
- import { pidFileCreate } from "./util.mjs"
3
-
4
- export default async function(appName, dest) {
5
- console.log(`[${appName}][prod][start] Starting server...`)
6
-
7
- const serverExt = 'node.bundle.mjs'
8
- const destFile = path.join(process.cwd(), `${dest}/${appName}.${serverExt}`)
9
-
10
- const srv_module = await import(destFile)
11
- const server = srv_module.default
12
-
13
- const pid = pidFileCreate(appName)
14
- console.log(`[${appName}][prod][start] Starting server. PID is ${pid}...`)
15
-
16
- await server()
17
- }
@@ -1,22 +0,0 @@
1
- import {make_logger} from './index.mjs'
2
-
3
-
4
- function verify_logger(config) {
5
- const logger= make_logger(config)
6
- try {
7
- console.info('[miolo][Verify][LOGGER] Verifying...')
8
-
9
- logger.error('Error message')
10
- logger.warn('Warn message')
11
- logger.info('Info message')
12
- logger.verbose('Verbose message')
13
- logger.debug('Debug message')
14
- logger.silly('Silly message')
15
-
16
- } catch(e) {
17
- console.error('[miolo][Verify][LOGGER] ERROR: ')
18
- console.error(e)
19
- }
20
- }
21
-
22
- export {verify_logger}