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.
- package/bin/build/build.mjs +53 -0
- package/bin/build/build_bin.mjs +17 -0
- package/bin/build/cli/client.mjs +52 -0
- package/bin/build/cli/css.mjs +22 -0
- package/bin/build/cli/index.mjs +40 -0
- package/bin/build/cli/ssr.mjs +21 -0
- package/bin/build/server/aliases.mjs +112 -0
- package/bin/build/server/babel.config.js +24 -0
- package/bin/build/server/banner.mjs +20 -0
- package/bin/build/server/bundle.mjs +111 -0
- package/bin/build/server/fix.mjs +15 -0
- package/bin/build/server/index.mjs +69 -0
- package/bin/build/server/options.mjs +83 -0
- package/bin/create/auth.mjs +23 -0
- package/bin/create/copy.mjs +175 -0
- package/bin/create/docker.mjs +25 -0
- package/bin/create/index.mjs +137 -0
- package/bin/create/pkgjson.mjs +72 -0
- package/bin/create/prepare-template.mjs +158 -0
- package/bin/create/validation.mjs +27 -0
- package/bin/dev/dev.mjs +32 -23
- package/bin/dev/dev_start.mjs +6 -5
- package/bin/index.mjs +94 -52
- package/bin/prod-bin/create-bin.mjs +42 -27
- package/bin/prod-bin/run.mjs +13 -9
- package/bin/{prod-run → run}/pid.mjs +4 -4
- package/bin/run/restart.mjs +13 -0
- package/bin/run/start.mjs +18 -0
- package/bin/run/stop.mjs +20 -0
- package/bin/util.mjs +35 -11
- package/package.json +59 -39
- package/src/config/.env +34 -12
- package/src/config/defaults.mjs +253 -185
- package/src/config/env.mjs +40 -22
- package/src/config/index.mjs +19 -24
- package/src/config/util.mjs +25 -10
- package/src/db-conn.mjs +34 -0
- package/src/engines/cron/emails.mjs +10 -5
- package/src/engines/cron/index.mjs +45 -51
- package/src/engines/cron/init.mjs +16 -17
- package/src/engines/cron/ipsum.mjs +65 -60
- package/src/engines/cron/syscheck.mjs +30 -30
- package/src/engines/emailer/index.mjs +1 -2
- package/src/engines/emailer/queue.mjs +14 -20
- package/src/engines/emailer/transporter.mjs +86 -74
- package/src/engines/geoip/index.mjs +23 -28
- package/src/engines/http/index.mjs +26 -15
- package/src/engines/logger/buildErrorEmailBody.mjs +72 -0
- package/src/engines/logger/index.mjs +114 -122
- package/src/engines/logger/injectStackTrace.mjs +59 -0
- package/src/engines/logger/logger_mail.mjs +47 -61
- package/src/engines/logger/reopenTransportOnHupSignal.mjs +12 -13
- package/src/engines/parser/Parser.mjs +77 -60
- package/src/engines/parser/index.mjs +1 -1
- package/src/engines/schema/diffObjs.mjs +41 -0
- package/src/engines/schema/index.mjs +4 -0
- package/src/engines/schema/input.mjs +54 -0
- package/src/engines/schema/output.mjs +66 -0
- package/src/engines/socket/index.mjs +44 -46
- package/src/index.mjs +15 -10
- package/src/middleware/auth/basic.mjs +41 -40
- package/src/middleware/auth/custom.mjs +10 -13
- package/src/middleware/auth/guest.mjs +27 -27
- package/src/middleware/auth/passport/index.mjs +374 -0
- package/src/middleware/auth/passport/session/index.mjs +43 -0
- package/src/middleware/auth/{credentials → passport}/session/store.mjs +35 -15
- package/src/middleware/auth/passport/session/store_koa_redis.mjs +3 -0
- package/src/middleware/context/cache/index.mjs +78 -33
- package/src/middleware/context/cache/options.mjs +19 -21
- package/src/middleware/context/db.mjs +45 -20
- package/src/middleware/context/index.mjs +12 -12
- package/src/middleware/extra.mjs +4 -5
- package/src/middleware/http/body.mjs +25 -25
- package/src/middleware/http/catcher.mjs +81 -8
- package/src/middleware/http/custom_blacklist.mjs +19 -16
- package/src/middleware/http/headers.mjs +37 -34
- package/src/middleware/http/ratelimit.mjs +16 -23
- package/src/middleware/http/request.mjs +60 -65
- package/src/middleware/routes/catch_js_error.mjs +30 -23
- package/src/middleware/routes/robots.mjs +4 -7
- package/src/middleware/routes/router/crud/attachCrudRoutes.mjs +108 -90
- package/src/middleware/routes/router/crud/getCrudConfig.mjs +31 -55
- package/src/middleware/routes/router/defaults.mjs +6 -19
- package/src/middleware/routes/router/index.mjs +17 -21
- package/src/middleware/routes/router/queries/attachQueriesRoutes.mjs +227 -50
- package/src/middleware/routes/router/queries/getQueriesConfig.mjs +45 -55
- package/src/middleware/routes/router/utils.mjs +41 -26
- package/src/middleware/ssr/context.mjs +5 -7
- package/src/middleware/ssr/html.mjs +66 -43
- package/src/middleware/ssr/loader.mjs +11 -14
- package/src/middleware/ssr/ssr_render.mjs +39 -22
- package/src/middleware/static/index.mjs +33 -14
- package/src/middleware/vite/devserver.mjs +38 -22
- package/src/middleware/vite/watcher.mjs +12 -14
- package/src/server-cron.mjs +13 -8
- package/src/server-dev.mjs +13 -16
- package/src/server.mjs +49 -51
- package/template/.agent/skills/miolo-app-arch/SKILL.md +218 -0
- package/template/.agent/skills/miolo-auth/SKILL.md +450 -0
- package/template/.agent/skills/miolo-cli-router/SKILL.md +394 -0
- package/template/.agent/skills/miolo-database/SKILL.md +358 -0
- package/template/.agent/skills/miolo-react-patterns/SKILL.md +426 -0
- package/template/.agent/skills/miolo-routing/SKILL.md +326 -0
- package/template/.agent/skills/miolo-schemas/SKILL.md +329 -0
- package/template/.agent/skills/miolo-session-context/SKILL.md +397 -0
- package/template/.agent/skills/miolo-ssr/SKILL.md +433 -0
- package/template/.editorconfig +18 -0
- package/template/.env +120 -0
- package/template/biome.json +63 -0
- package/template/components.json +21 -0
- package/template/db/init.sh +89 -0
- package/template/db/sql/00_drop.sql +2 -0
- package/template/db/sql/01_users.sql +31 -0
- package/template/db/sql/02_todos.sql +20 -0
- package/template/docker/Dockerfile +13 -0
- package/template/docker/docker-compose.yaml +79 -0
- package/template/gitignore +42 -0
- package/template/jsconfig.json +18 -0
- package/template/package.json +88 -0
- package/template/postcss.config.js +9 -0
- package/template/src/cli/App.jsx +25 -0
- package/template/src/cli/components/JsonTreeViewer.jsx +128 -0
- package/template/src/cli/components/shadcn-io/spinner/index.jsx +232 -0
- package/template/src/cli/components/stepper.jsx +408 -0
- package/template/src/cli/components/ui/avatar.jsx +36 -0
- package/template/src/cli/components/ui/badge.jsx +31 -0
- package/template/src/cli/components/ui/breadcrumb.jsx +97 -0
- package/template/src/cli/components/ui/card.jsx +73 -0
- package/template/src/cli/components/ui/collapsible.jsx +16 -0
- package/template/src/cli/components/ui/dropdown-menu.jsx +179 -0
- package/template/src/cli/components/ui/field.jsx +217 -0
- package/template/src/cli/components/ui/input.jsx +19 -0
- package/template/src/cli/components/ui/label.jsx +17 -0
- package/template/src/cli/components/ui/pagination.jsx +99 -0
- package/template/src/cli/components/ui/patched/alert.jsx +56 -0
- package/template/src/cli/components/ui/patched/button.jsx +45 -0
- package/template/src/cli/components/ui/patched/dialog.jsx +114 -0
- package/template/src/cli/components/ui/patched/sidebar.jsx +660 -0
- package/template/src/cli/components/ui/select.jsx +141 -0
- package/template/src/cli/components/ui/separator.jsx +21 -0
- package/template/src/cli/components/ui/sheet.jsx +115 -0
- package/template/src/cli/components/ui/skeleton.jsx +13 -0
- package/template/src/cli/components/ui/sonner.jsx +22 -0
- package/template/src/cli/components/ui/switch.jsx +25 -0
- package/template/src/cli/components/ui/table.jsx +88 -0
- package/template/src/cli/components/ui/textarea.jsx +16 -0
- package/template/src/cli/components/ui/tooltip.jsx +45 -0
- package/template/src/cli/config/store_keys.mjs +2 -0
- package/template/src/cli/context/data/DataContext.jsx +5 -0
- package/template/src/cli/context/data/DataProvider.jsx +44 -0
- package/template/src/cli/context/data/useBreads.mjs +15 -0
- package/template/src/cli/context/data/useDataContext.mjs +4 -0
- package/template/src/cli/context/session/SessionContext.mjs +4 -0
- package/template/src/cli/context/session/SessionProvider.jsx +31 -0
- package/template/src/cli/context/session/makePermissioner.mjs +34 -0
- package/template/src/cli/context/session/useSessionContext.mjs +6 -0
- package/template/src/cli/context/theme/ThemeContext.mjs +4 -0
- package/template/src/cli/context/theme/ThemeProvider.jsx +49 -0
- package/template/src/cli/context/theme/useThemeContext.mjs +6 -0
- package/template/src/cli/context/ui/UIContext.jsx +5 -0
- package/template/src/cli/context/ui/UIProvider.jsx +16 -0
- package/template/src/cli/context/ui/useUIContext.mjs +4 -0
- package/template/src/cli/context/util.mjs +17 -0
- package/template/src/cli/entry-cli.jsx +33 -0
- package/template/src/cli/hooks/useIsMobile.mjs +19 -0
- package/template/src/cli/hooks/useStoragedState.mjs +63 -0
- package/template/src/cli/index.html +29 -0
- package/template/src/cli/layout/app-sidebar.jsx +25 -0
- package/template/src/cli/layout/main-layout.jsx +63 -0
- package/template/src/cli/layout/nav-last-todos.jsx +72 -0
- package/template/src/cli/layout/nav-main.jsx +39 -0
- package/template/src/cli/layout/nav-user.jsx +105 -0
- package/template/src/cli/layout/prop-switcher.jsx +93 -0
- package/template/src/cli/lib/utils.mjs +10 -0
- package/template/src/cli/pages/Index.jsx +13 -0
- package/template/src/cli/pages/IndexOffline.jsx +13 -0
- package/template/src/cli/pages/IndexOnline.jsx +18 -0
- package/template/src/cli/pages/dash/Dashboard.jsx +29 -0
- package/template/src/cli/pages/offline/Login.jsx +43 -0
- package/template/src/cli/pages/offline/LoginForm.jsx +115 -0
- package/template/src/cli/pages/security/Security.jsx +39 -0
- package/template/src/cli/pages/security/SecurityForm.jsx +106 -0
- package/template/src/cli/pages/todos/TodoActions.jsx +99 -0
- package/template/src/cli/pages/todos/TodoAdd.jsx +43 -0
- package/template/src/cli/pages/todos/TodoList.jsx +60 -0
- package/template/src/cli/pages/todos/Todos.jsx +23 -0
- package/template/src/cli/pages/todos/context/TodosContext.jsx +5 -0
- package/template/src/cli/pages/todos/context/TodosProvider.jsx +191 -0
- package/template/src/cli/pages/todos/context/useTodosContext.mjs +4 -0
- package/template/src/ns/models/Todo.mjs +29 -0
- package/template/src/ns/models/TodoList.mjs +8 -0
- package/template/src/ns/models/User.mjs +40 -0
- package/template/src/server/bot/check_today.mjs +10 -0
- package/template/src/server/io/cache/base.mjs +21 -0
- package/template/src/server/io/db/filter.mjs +92 -0
- package/template/src/server/io/db/todos/delete.mjs +29 -0
- package/template/src/server/io/db/todos/find.mjs +13 -0
- package/template/src/server/io/db/todos/read.mjs +83 -0
- package/template/src/server/io/db/todos/toggle.mjs +37 -0
- package/template/src/server/io/db/todos/upsave.mjs +32 -0
- package/template/src/server/io/db/triggers/user.mjs +13 -0
- package/template/src/server/io/db/users/auth.mjs +132 -0
- package/template/src/server/io/db/users/pwd.mjs +38 -0
- package/template/src/server/io/db/users/save.mjs +17 -0
- package/template/src/server/miolo/auth/basic.mjs +15 -0
- package/template/src/server/miolo/auth/guest.mjs +3 -0
- package/template/src/server/miolo/auth/passport.mjs +73 -0
- package/template/src/server/miolo/cache.mjs +11 -0
- package/template/src/server/miolo/cron/foo.mjs +7 -0
- package/template/src/server/miolo/cron/index.mjs +28 -0
- package/template/src/server/miolo/cron/invalidate.mjs +21 -0
- package/template/src/server/miolo/db.mjs +36 -0
- package/template/src/server/miolo/http.mjs +14 -0
- package/template/src/server/miolo/index.mjs +43 -0
- package/template/src/server/miolo/routes/crud.mjs +16 -0
- package/template/src/server/miolo/routes/index.mjs +8 -0
- package/template/src/server/miolo/ssr/entry-server.jsx +13 -0
- package/template/src/server/miolo/ssr/loader.mjs +18 -0
- package/template/src/server/routes/index.mjs +66 -0
- package/template/src/server/routes/todos/mod.mjs +52 -0
- package/template/src/server/routes/todos/read.mjs +45 -0
- package/template/src/server/routes/todos/special.mjs +47 -0
- package/template/src/server/routes/users/user.mjs +54 -0
- package/template/src/server/server.mjs +10 -0
- package/template/src/server/utils/crypt.mjs +38 -0
- package/template/src/server/utils/io.mjs +15 -0
- package/template/src/server/utils/pwdfor.mjs +25 -0
- package/template/src/server/utils/schema.mjs +22 -0
- package/template/src/static/img/default/profile.png +0 -0
- package/template/src/static/img/favicon.ico +0 -0
- package/template/src/static/img/miolo_logo.png +0 -0
- package/template/src/static/img/miolo_name.png +0 -0
- package/template/src/static/public/manifest.json +21 -0
- package/template/src/static/public/sw.js +79 -0
- package/template/src/static/style/globals.css +156 -0
- package/template/src/static/style/json-tree.css +54 -0
- package/template/src/static/style/skeleton.css +49 -0
- package/bin/prod-build/build-client.mjs +0 -67
- package/bin/prod-build/build-server.mjs +0 -58
- package/bin/prod-run/restart.mjs +0 -9
- package/bin/prod-run/start.mjs +0 -15
- package/bin/prod-run/stop.mjs +0 -20
- package/src/engines/logger/verify.mjs +0 -22
- package/src/middleware/auth/credentials/index.mjs +0 -151
- package/src/middleware/auth/credentials/session/index.mjs +0 -24
- package/src/middleware/auth/credentials/session/store_koa_redis.mjs +0 -3
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { intre_now } from "intre"
|
|
2
|
+
|
|
3
|
+
export async function r_todo_count_last_hours(ctx, params) {
|
|
4
|
+
ctx.miolo.logger.verbose(
|
|
5
|
+
`[r_todo_count_last_hours] Counting last ${params.hours} hours todos... `
|
|
6
|
+
)
|
|
7
|
+
|
|
8
|
+
const conn = await ctx.miolo.db.get_connection()
|
|
9
|
+
// TODO : handle transactions
|
|
10
|
+
const options = { transaction: undefined }
|
|
11
|
+
|
|
12
|
+
const one_hour_ago = intre_now() - 60 * 60 * parseInt(params.hours, 10)
|
|
13
|
+
|
|
14
|
+
const query = `
|
|
15
|
+
SELECT COUNT(1) as cnt
|
|
16
|
+
FROM todo
|
|
17
|
+
WHERE created_at >= $1`
|
|
18
|
+
|
|
19
|
+
const data = await conn.select(query, [one_hour_ago], options)
|
|
20
|
+
const res = data[0].cnt
|
|
21
|
+
|
|
22
|
+
ctx.miolo.logger.verbose(
|
|
23
|
+
`[r_todo_count_last_hours] Counted last ${params.hours} hours todos: ${res} `
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
return { ok: true, data: { count: res } }
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export async function r_todo_insert_fake(ctx, params) {
|
|
30
|
+
ctx.miolo.logger.verbose(`[r_todo_insert_fake] Inserting fake todo... `)
|
|
31
|
+
|
|
32
|
+
const conn = await ctx.miolo.db.get_connection()
|
|
33
|
+
// TODO : handle transactions
|
|
34
|
+
const options = { transaction: undefined }
|
|
35
|
+
|
|
36
|
+
const Todos = await conn.get_model("todo")
|
|
37
|
+
|
|
38
|
+
const d = {
|
|
39
|
+
description: "Fake todo",
|
|
40
|
+
done: params?.done === true
|
|
41
|
+
}
|
|
42
|
+
const tid = await Todos.insert(d, options)
|
|
43
|
+
|
|
44
|
+
ctx.miolo.logger.verbose(`[r_todo_insert_fake] Inserted fake todo with id ${tid}`)
|
|
45
|
+
|
|
46
|
+
return { ok: true, data: { id: tid } }
|
|
47
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { db_password_change } from "#server/io/db/users/pwd.mjs"
|
|
2
|
+
import { db_user_save } from "#server/io/db/users/save.mjs"
|
|
3
|
+
import { generateRandomPassword } from "#server/utils/crypt.mjs"
|
|
4
|
+
|
|
5
|
+
export async function r_forgot(ctx, params) {
|
|
6
|
+
const logger = ctx.miolo.logger
|
|
7
|
+
|
|
8
|
+
const { email } = params
|
|
9
|
+
|
|
10
|
+
const new_pwd = generateRandomPassword()
|
|
11
|
+
const msg = await db_password_change(ctx.miolo, email, new_pwd)
|
|
12
|
+
|
|
13
|
+
if (msg === undefined) {
|
|
14
|
+
const emailer = ctx.miolo.emailer
|
|
15
|
+
|
|
16
|
+
const mail = {
|
|
17
|
+
to: email,
|
|
18
|
+
subject: "miolo-sample: Nueva contraseña",
|
|
19
|
+
text: `Tu nueva contraseña es: ${new_pwd}
|
|
20
|
+
Por favor, inicia sesión con ella y, desde "Mi Cuenta",
|
|
21
|
+
cámbiala por una que vayas a recordar mejor.`
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const info = await emailer.send(mail)
|
|
25
|
+
if (info?.ok) {
|
|
26
|
+
logger.info(`Reseted password for ${email}`)
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return {
|
|
31
|
+
success: msg === undefined,
|
|
32
|
+
info: msg
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export async function r_change_password(ctx, params) {
|
|
37
|
+
const logger = ctx.miolo.logger
|
|
38
|
+
const { username, passwords } = params
|
|
39
|
+
|
|
40
|
+
const { ok, msg } = await db_password_change(ctx.miolo, username, passwords)
|
|
41
|
+
|
|
42
|
+
logger.info(`Changed password for ${username}`)
|
|
43
|
+
|
|
44
|
+
if (!ok) {
|
|
45
|
+
return { ok: false, error: msg }
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return { ok: true, data: { msg } }
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export async function r_user_save(ctx, params) {
|
|
52
|
+
const res = await db_user_save(ctx.miolo, params)
|
|
53
|
+
return res
|
|
54
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { miolo } from "miolo"
|
|
2
|
+
import makeConfig from "#server/miolo/index.mjs"
|
|
3
|
+
|
|
4
|
+
export default async function miolo_sample_server() {
|
|
5
|
+
console.log("[miolo-sample] Initing server...")
|
|
6
|
+
|
|
7
|
+
const server = await miolo(makeConfig)
|
|
8
|
+
const app = await server.start()
|
|
9
|
+
return app
|
|
10
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import crypto from "node:crypto"
|
|
2
|
+
|
|
3
|
+
const PASSWORD_LENGTH = 18
|
|
4
|
+
const LOWERCASE_ALPHABET = "abcdefghijklmnopqrstuvwxyz" // 26 chars
|
|
5
|
+
const UPPERCASE_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" // 26 chars
|
|
6
|
+
const NUMBERS = "0123456789" // 10 chars
|
|
7
|
+
const SYMBOLS = ",./<>?;'\":[]\\|}{=-_+`~!@#$%^&*()" // 32 chars
|
|
8
|
+
const ALPHANUMERIC_CHARS = LOWERCASE_ALPHABET + UPPERCASE_ALPHABET + NUMBERS // 62 chars
|
|
9
|
+
const ALL_CHARS = ALPHANUMERIC_CHARS + SYMBOLS // 94 chars
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Hash the password
|
|
13
|
+
* @private
|
|
14
|
+
* @param str {string}
|
|
15
|
+
* @param options {object}
|
|
16
|
+
* @returns {string}
|
|
17
|
+
*/
|
|
18
|
+
function sha512(str, salt) {
|
|
19
|
+
return crypto.createHmac("sha512", salt).update(str).digest("hex")
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function _generateRandomPassword(length, alphabet) {
|
|
23
|
+
const rb = crypto.randomBytes(length)
|
|
24
|
+
let rp = ""
|
|
25
|
+
|
|
26
|
+
for (let i = 0; i < length; i++) {
|
|
27
|
+
rb[i] = rb[i] % alphabet.length
|
|
28
|
+
rp += alphabet[rb[i]]
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return rp
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function generateRandomPassword() {
|
|
35
|
+
return _generateRandomPassword(PASSWORD_LENGTH, ALL_CHARS)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export { generateRandomPassword, sha512 }
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { readdir } from "node:fs"
|
|
2
|
+
|
|
3
|
+
function readDirAsync(path) {
|
|
4
|
+
return new Promise((resolve, reject) => {
|
|
5
|
+
readdir(path, (error, result) => {
|
|
6
|
+
if (error) {
|
|
7
|
+
reject(error)
|
|
8
|
+
} else {
|
|
9
|
+
resolve(result)
|
|
10
|
+
}
|
|
11
|
+
})
|
|
12
|
+
})
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export { readDirAsync }
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import fs from "node:fs"
|
|
2
|
+
import path from "node:path"
|
|
3
|
+
import { fileURLToPath } from "node:url"
|
|
4
|
+
import { sha512 } from "./crypt.mjs"
|
|
5
|
+
|
|
6
|
+
function _loadSalt() {
|
|
7
|
+
let dir = path.dirname(fileURLToPath(import.meta.url))
|
|
8
|
+
while (dir !== path.parse(dir).root && !fs.existsSync(path.join(dir, "package.json"))) {
|
|
9
|
+
dir = path.dirname(dir)
|
|
10
|
+
}
|
|
11
|
+
const env = fs.readFileSync(path.join(dir, ".env"), "utf8")
|
|
12
|
+
return env.match(/^MIOLO_SESSION_SALT=(.*)$/m)?.[1]?.trim()
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
async function _pwd_for() {
|
|
16
|
+
const args = process.argv.slice(2)
|
|
17
|
+
const pwd = args[0]
|
|
18
|
+
|
|
19
|
+
const salt = _loadSalt()
|
|
20
|
+
|
|
21
|
+
const cpwd = sha512(pwd, salt)
|
|
22
|
+
console.log([salt, cpwd])
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
_pwd_for()
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import Joi from "joi"
|
|
2
|
+
|
|
3
|
+
export const req_int = Joi.number().integer().required()
|
|
4
|
+
|
|
5
|
+
export const opt_float = Joi.number().optional().empty(null)
|
|
6
|
+
export const opt_float_null = Joi.number().optional().allow(null)
|
|
7
|
+
|
|
8
|
+
export const opt_int = Joi.number().integer().optional().empty(null)
|
|
9
|
+
export const opt_int_null = Joi.number().integer().optional().allow(null)
|
|
10
|
+
|
|
11
|
+
export const opt_int_or_arr = Joi.alternatives()
|
|
12
|
+
.try(Joi.number().integer(), Joi.array().items(Joi.number().integer()))
|
|
13
|
+
.optional()
|
|
14
|
+
.empty(null)
|
|
15
|
+
|
|
16
|
+
export const req_str = Joi.string().required()
|
|
17
|
+
export const opt_str = Joi.string().optional().empty(null)
|
|
18
|
+
export const opt_str_null = Joi.string().optional().allow(null)
|
|
19
|
+
|
|
20
|
+
export const bool_false = Joi.boolean().optional().default(false)
|
|
21
|
+
export const bool_true = Joi.boolean().optional().default(true)
|
|
22
|
+
export const bool_null = Joi.boolean().optional().allow(null)
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "miolo-sample",
|
|
3
|
+
"short_name": "miolo-sample",
|
|
4
|
+
"start_url": "/",
|
|
5
|
+
"display": "standalone",
|
|
6
|
+
"background_color": "#ffffff",
|
|
7
|
+
"theme_color": "#fb64b6",
|
|
8
|
+
"description": "A simple PWA for miolo-sample",
|
|
9
|
+
"icons": [
|
|
10
|
+
{
|
|
11
|
+
"src": "/favicon.ico",
|
|
12
|
+
"sizes": "30x30",
|
|
13
|
+
"type": "image/x-icon"
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"src": "/static/img/miolo_logo.png",
|
|
17
|
+
"sizes": "600x600",
|
|
18
|
+
"type": "image/png"
|
|
19
|
+
}
|
|
20
|
+
]
|
|
21
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
const CACHE_NAME = "miolo-sample-cache-v1"
|
|
2
|
+
|
|
3
|
+
// 1. EVENTO INSTALL: Ocurre la primera vez que el usuario entra.
|
|
4
|
+
// Aquí cacheamos el "App Shell" (lo básico para que la app abra sin red).
|
|
5
|
+
self.addEventListener("install", (_event) => {
|
|
6
|
+
/*console.log(`[miolo][sw] installing`)
|
|
7
|
+
event.waitUntil(
|
|
8
|
+
caches.open(CACHE_NAME).then((cache) => {
|
|
9
|
+
console.log(`[miolo][sw] installing - caching main`)
|
|
10
|
+
// Rutas estáticas clave (ajusta según cómo sirva Miolo tus archivos)
|
|
11
|
+
return cache.addAll([
|
|
12
|
+
"/"
|
|
13
|
+
// "/index.html",
|
|
14
|
+
// "/icon-192x192.png"
|
|
15
|
+
// Si sabes las URLs de tu JS/CSS, añádelas aquí.
|
|
16
|
+
])
|
|
17
|
+
})
|
|
18
|
+
)
|
|
19
|
+
self.skipWaiting()*/
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
// 2. EVENTO ACTIVATE: Limpia cachés antiguas si cambias el CACHE_NAME
|
|
23
|
+
self.addEventListener("activate", (event) => {
|
|
24
|
+
console.log(`[miolo][sw] activating`)
|
|
25
|
+
event.waitUntil(
|
|
26
|
+
caches.keys().then((cacheNames) => {
|
|
27
|
+
const oldCaches = cacheNames.filter((name) => name !== CACHE_NAME)
|
|
28
|
+
console.log(`[miolo][sw] activating - cleaning old caches ${oldCaches}`)
|
|
29
|
+
return Promise.all(oldCaches.map((name) => caches.delete(name)))
|
|
30
|
+
})
|
|
31
|
+
)
|
|
32
|
+
self.clients.claim()
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
// 3. EVENTO FETCH: El núcleo de tu estrategia
|
|
36
|
+
self.addEventListener("fetch", (_event) => {
|
|
37
|
+
/*console.log(`[miolo][sw] fetching`)
|
|
38
|
+
// ESTRATEGIA PARA LLAMADAS POST
|
|
39
|
+
if (event.request.method === "POST" || event.request.method === "PUT") {
|
|
40
|
+
event.respondWith(
|
|
41
|
+
fetch(event.request).catch(() => {
|
|
42
|
+
console.log(`[miolo][sw] fetching - fetch failed, returning 503 error`)
|
|
43
|
+
// Si el fetch falla (porque no hay red), devolvemos un error controlado 503
|
|
44
|
+
return new Response(
|
|
45
|
+
JSON.stringify({ error: "Estás sin conexión. No se pueden guardar los cambios." }),
|
|
46
|
+
{
|
|
47
|
+
headers: { "Content-Type": "application/json" },
|
|
48
|
+
status: 503,
|
|
49
|
+
statusText: "Service Unavailable"
|
|
50
|
+
}
|
|
51
|
+
)
|
|
52
|
+
})
|
|
53
|
+
)
|
|
54
|
+
return // Cortamos aquí para los POST
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// ESTRATEGIA PARA LLAMADAS GET (Network First, fallback to Cache)
|
|
58
|
+
// Intenta ir a internet siempre para tener los resultados frescos.
|
|
59
|
+
// Si falla, tira de lo que tenga guardado en caché.
|
|
60
|
+
event.respondWith(
|
|
61
|
+
fetch(event.request)
|
|
62
|
+
.then((networkResponse) => {
|
|
63
|
+
console.log(`[miolo][sw] fetching - fetch ok, caching it`)
|
|
64
|
+
// Guardamos una copia en la caché para la próxima vez que se quede offline
|
|
65
|
+
const responseClone = networkResponse.clone()
|
|
66
|
+
caches.open(CACHE_NAME).then((cache) => {
|
|
67
|
+
// No cacheamos extensiones raras del navegador
|
|
68
|
+
if (event.request.url.startsWith("http")) {
|
|
69
|
+
cache.put(event.request, responseClone)
|
|
70
|
+
}
|
|
71
|
+
})
|
|
72
|
+
return networkResponse
|
|
73
|
+
})
|
|
74
|
+
.catch(() => {
|
|
75
|
+
// Si no hay internet, devuelve la versión guardada en caché
|
|
76
|
+
return caches.match(event.request)
|
|
77
|
+
})
|
|
78
|
+
)*/
|
|
79
|
+
})
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
@import "tailwindcss";
|
|
2
|
+
@import "tw-animate-css";
|
|
3
|
+
|
|
4
|
+
@import "./json-tree.css";
|
|
5
|
+
@import "./skeleton.css";
|
|
6
|
+
|
|
7
|
+
/* biome-ignore lint/suspicious/noUnknownAtRules: biome-ignore */
|
|
8
|
+
@tailwind base;
|
|
9
|
+
/* biome-ignore lint/suspicious/noUnknownAtRules: biome-ignore */
|
|
10
|
+
@tailwind components;
|
|
11
|
+
/* biome-ignore lint/suspicious/noUnknownAtRules: biome-ignore */
|
|
12
|
+
@tailwind utilities;
|
|
13
|
+
|
|
14
|
+
@custom-variant dark (&:is(.dark *));
|
|
15
|
+
|
|
16
|
+
:root {
|
|
17
|
+
--background: oklch(1 0 0);
|
|
18
|
+
--foreground: oklch(0.145 0 0);
|
|
19
|
+
--card: oklch(1 0 0);
|
|
20
|
+
--card-foreground: oklch(0.145 0 0);
|
|
21
|
+
--popover: oklch(1 0 0);
|
|
22
|
+
--popover-foreground: oklch(0.145 0 0);
|
|
23
|
+
--primary: oklch(0.205 0 0);
|
|
24
|
+
--primary-foreground: oklch(0.985 0 0);
|
|
25
|
+
--secondary: oklch(0.97 0 0);
|
|
26
|
+
--secondary-foreground: oklch(0.205 0 0);
|
|
27
|
+
--muted: oklch(0.97 0 0);
|
|
28
|
+
--muted-foreground: oklch(0.556 0 0);
|
|
29
|
+
--accent: oklch(0.97 0 0);
|
|
30
|
+
--accent-foreground: oklch(0.205 0 0);
|
|
31
|
+
--destructive: oklch(0.577 0.245 27.325);
|
|
32
|
+
--destructive-foreground: oklch(0.577 0.245 27.325);
|
|
33
|
+
--border: oklch(0.922 0 0);
|
|
34
|
+
--input: oklch(0.922 0 0);
|
|
35
|
+
--ring: oklch(0.708 0 0);
|
|
36
|
+
--chart-1: oklch(0.646 0.222 41.116);
|
|
37
|
+
--chart-2: oklch(0.6 0.118 184.704);
|
|
38
|
+
--chart-3: oklch(0.398 0.07 227.392);
|
|
39
|
+
--chart-4: oklch(0.828 0.189 84.429);
|
|
40
|
+
--chart-5: oklch(0.769 0.188 70.08);
|
|
41
|
+
--radius: 0.625rem;
|
|
42
|
+
--sidebar: oklch(0.985 0 0);
|
|
43
|
+
--sidebar-foreground: oklch(0.145 0 0);
|
|
44
|
+
--sidebar-primary: oklch(0.205 0 0);
|
|
45
|
+
--sidebar-primary-foreground: oklch(0.985 0 0);
|
|
46
|
+
--sidebar-accent: oklch(0.97 0 0);
|
|
47
|
+
--sidebar-accent-foreground: oklch(0.205 0 0);
|
|
48
|
+
--sidebar-border: oklch(0.922 0 0);
|
|
49
|
+
--sidebar-ring: oklch(0.708 0 0);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.dark {
|
|
53
|
+
--background: oklch(0.145 0 0);
|
|
54
|
+
--foreground: oklch(0.985 0 0);
|
|
55
|
+
--card: oklch(0.145 0 0);
|
|
56
|
+
--card-foreground: oklch(0.985 0 0);
|
|
57
|
+
--popover: oklch(0.145 0 0);
|
|
58
|
+
--popover-foreground: oklch(0.985 0 0);
|
|
59
|
+
--primary: oklch(0.985 0 0);
|
|
60
|
+
--primary-foreground: oklch(0.205 0 0);
|
|
61
|
+
--secondary: oklch(0.269 0 0);
|
|
62
|
+
--secondary-foreground: oklch(0.985 0 0);
|
|
63
|
+
--muted: oklch(0.269 0 0);
|
|
64
|
+
--muted-foreground: oklch(0.708 0 0);
|
|
65
|
+
--accent: oklch(0.269 0 0);
|
|
66
|
+
--accent-foreground: oklch(0.985 0 0);
|
|
67
|
+
--destructive: oklch(0.396 0.141 25.723);
|
|
68
|
+
--destructive-foreground: oklch(0.637 0.237 25.331);
|
|
69
|
+
--border: oklch(0.269 0 0);
|
|
70
|
+
--input: oklch(0.269 0 0);
|
|
71
|
+
--ring: oklch(0.439 0 0);
|
|
72
|
+
--chart-1: oklch(0.488 0.243 264.376);
|
|
73
|
+
--chart-2: oklch(0.696 0.17 162.48);
|
|
74
|
+
--chart-3: oklch(0.769 0.188 70.08);
|
|
75
|
+
--chart-4: oklch(0.627 0.265 303.9);
|
|
76
|
+
--chart-5: oklch(0.645 0.246 16.439);
|
|
77
|
+
--sidebar: oklch(0.205 0 0);
|
|
78
|
+
--sidebar-foreground: oklch(0.985 0 0);
|
|
79
|
+
--sidebar-primary: oklch(0.488 0.243 264.376);
|
|
80
|
+
--sidebar-primary-foreground: oklch(0.985 0 0);
|
|
81
|
+
--sidebar-accent: oklch(0.269 0 0);
|
|
82
|
+
--sidebar-accent-foreground: oklch(0.985 0 0);
|
|
83
|
+
--sidebar-border: oklch(0.269 0 0);
|
|
84
|
+
--sidebar-ring: oklch(0.439 0 0);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
@theme inline {
|
|
88
|
+
--color-background: var(--background);
|
|
89
|
+
--color-foreground: var(--foreground);
|
|
90
|
+
--color-card: var(--card);
|
|
91
|
+
--color-card-foreground: var(--card-foreground);
|
|
92
|
+
--color-popover: var(--popover);
|
|
93
|
+
--color-popover-foreground: var(--popover-foreground);
|
|
94
|
+
--color-primary: var(--primary);
|
|
95
|
+
--color-primary-foreground: var(--primary-foreground);
|
|
96
|
+
--color-secondary: var(--secondary);
|
|
97
|
+
--color-secondary-foreground: var(--secondary-foreground);
|
|
98
|
+
--color-muted: var(--muted);
|
|
99
|
+
--color-muted-foreground: var(--muted-foreground);
|
|
100
|
+
--color-accent: var(--accent);
|
|
101
|
+
--color-accent-foreground: var(--accent-foreground);
|
|
102
|
+
--color-destructive: var(--destructive);
|
|
103
|
+
--color-destructive-foreground: var(--destructive-foreground);
|
|
104
|
+
--color-border: var(--border);
|
|
105
|
+
--color-input: var(--input);
|
|
106
|
+
--color-ring: var(--ring);
|
|
107
|
+
--color-chart-1: var(--chart-1);
|
|
108
|
+
--color-chart-2: var(--chart-2);
|
|
109
|
+
--color-chart-3: var(--chart-3);
|
|
110
|
+
--color-chart-4: var(--chart-4);
|
|
111
|
+
--color-chart-5: var(--chart-5);
|
|
112
|
+
--radius-sm: calc(var(--radius) - 4px);
|
|
113
|
+
--radius-md: calc(var(--radius) - 2px);
|
|
114
|
+
--radius-lg: var(--radius);
|
|
115
|
+
--radius-xl: calc(var(--radius) + 4px);
|
|
116
|
+
--color-sidebar: var(--sidebar);
|
|
117
|
+
--color-sidebar-foreground: var(--sidebar-foreground);
|
|
118
|
+
--color-sidebar-primary: var(--sidebar-primary);
|
|
119
|
+
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
|
|
120
|
+
--color-sidebar-accent: var(--sidebar-accent);
|
|
121
|
+
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
|
122
|
+
--color-sidebar-border: var(--sidebar-border);
|
|
123
|
+
--color-sidebar-ring: var(--sidebar-ring);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
@layer base {
|
|
127
|
+
* {
|
|
128
|
+
@apply border-border outline-ring/50;
|
|
129
|
+
}
|
|
130
|
+
body {
|
|
131
|
+
@apply bg-background text-foreground;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/* Sidebar */
|
|
135
|
+
:root {
|
|
136
|
+
--sidebar: oklch(0.985 0 0);
|
|
137
|
+
--sidebar-foreground: oklch(0.145 0 0);
|
|
138
|
+
--sidebar-primary: oklch(0.205 0 0);
|
|
139
|
+
--sidebar-primary-foreground: oklch(0.985 0 0);
|
|
140
|
+
--sidebar-accent: oklch(0.97 0 0);
|
|
141
|
+
--sidebar-accent-foreground: oklch(0.205 0 0);
|
|
142
|
+
--sidebar-border: oklch(0.922 0 0);
|
|
143
|
+
--sidebar-ring: oklch(0.708 0 0);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
.dark {
|
|
147
|
+
--sidebar: oklch(0.205 0 0);
|
|
148
|
+
--sidebar-foreground: oklch(0.985 0 0);
|
|
149
|
+
--sidebar-primary: oklch(0.488 0.243 264.376);
|
|
150
|
+
--sidebar-primary-foreground: oklch(0.985 0 0);
|
|
151
|
+
--sidebar-accent: oklch(0.269 0 0);
|
|
152
|
+
--sidebar-accent-foreground: oklch(0.985 0 0);
|
|
153
|
+
--sidebar-border: oklch(1 0 0 / 10%);
|
|
154
|
+
--sidebar-ring: oklch(0.439 0 0);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
.json-tree-viewer {
|
|
2
|
+
font-family: monospace;
|
|
3
|
+
font-size: 14px;
|
|
4
|
+
line-height: 1.5;
|
|
5
|
+
background-color: #272822; /* Fondo oscuro */
|
|
6
|
+
color: #f8f8f2; /* Color de texto claro */
|
|
7
|
+
padding: 10px;
|
|
8
|
+
border-radius: 4px;
|
|
9
|
+
overflow: auto;
|
|
10
|
+
max-height: 500px;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
.json-node {
|
|
14
|
+
margin-left: 20px; /* Indentación para nodos anidados */
|
|
15
|
+
white-space: nowrap; /* Evita que los valores se rompan en múltiples líneas */
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
.json-key {
|
|
19
|
+
color: #a6e22e; /* Color para las claves (verde) */
|
|
20
|
+
font-weight: bold;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
.json-value-string {
|
|
24
|
+
color: #e6db74; /* Color para strings (amarillo) */
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.json-value-number {
|
|
28
|
+
color: #ae81ff; /* Color para números (morado) */
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.json-value-boolean {
|
|
32
|
+
color: #fd971f; /* Color para booleanos (naranja) */
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.json-value-null {
|
|
36
|
+
color: #66d9ef; /* Color para null (azul claro) */
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.json-object,
|
|
40
|
+
.json-array {
|
|
41
|
+
display: inline-flex; /* Permite que el icono y el contenido estén en la misma línea */
|
|
42
|
+
align-items: center;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.toggle-icon {
|
|
46
|
+
cursor: pointer;
|
|
47
|
+
margin-right: 5px;
|
|
48
|
+
color: #66d9ef; /* Color para los iconos de expandir/contraer */
|
|
49
|
+
font-size: 0.8em; /* Un poco más pequeño */
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.indent {
|
|
53
|
+
margin-left: 20px; /* Indentación para el contenido expandido */
|
|
54
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
@keyframes pulse {
|
|
2
|
+
50% {
|
|
3
|
+
opacity: 0.5;
|
|
4
|
+
}
|
|
5
|
+
}
|
|
6
|
+
/*
|
|
7
|
+
.skeleton-loading h1,
|
|
8
|
+
.skeleton-loading h2,
|
|
9
|
+
.skeleton-loading h3,
|
|
10
|
+
.skeleton-loading p,
|
|
11
|
+
.skeleton-loading span,
|
|
12
|
+
.skeleton-loading div:not(:empty) {
|
|
13
|
+
@apply
|
|
14
|
+
bg-gray-200
|
|
15
|
+
dark:bg-gray-700
|
|
16
|
+
rounded-md
|
|
17
|
+
text-transparent
|
|
18
|
+
relative
|
|
19
|
+
overflow-hidden;
|
|
20
|
+
animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
|
|
21
|
+
|
|
22
|
+
/ * Esto asegura que los elementos vacíos tengan un tamaño * /
|
|
23
|
+
min-height: 1rem;
|
|
24
|
+
}
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
.skeleton-loading input,
|
|
28
|
+
.skeleton-loading button,
|
|
29
|
+
.skeleton-loading a,
|
|
30
|
+
.skeleton-loading .skeleton-apply {
|
|
31
|
+
@apply bg-gray-200 dark:bg-gray-700 rounded-md text-transparent relative overflow-hidden;
|
|
32
|
+
animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
|
|
33
|
+
pointer-events: none; /* Para deshabilitar la interacción */
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/* Manejo de imágenes y elementos con aspecto */
|
|
37
|
+
.skeleton-loading img,
|
|
38
|
+
.skeleton-loading [data-aspect] {
|
|
39
|
+
@apply bg-gray-200 dark:bg-gray-700;
|
|
40
|
+
animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/*
|
|
44
|
+
.skeleton-loading h1,
|
|
45
|
+
.skeleton-loading h2,
|
|
46
|
+
.skeleton-loading h3 {
|
|
47
|
+
height: 1.5em; / * Define una altura para los títulos * /
|
|
48
|
+
}
|
|
49
|
+
*/
|
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
import path from 'node:path'
|
|
3
|
-
import {xeiraBundle} from 'xeira'
|
|
4
|
-
import { cleanFolder, copyFileSync, isFileExistingSync } from '../util.mjs'
|
|
5
|
-
|
|
6
|
-
function _addCssLinkToHead(appName, htmlString, dest) {
|
|
7
|
-
|
|
8
|
-
const webDest = dest.startsWith('./')
|
|
9
|
-
? dest.replace('./', '/')
|
|
10
|
-
: dest.startsWith('/')
|
|
11
|
-
? dest
|
|
12
|
-
: `/${dest}`
|
|
13
|
-
|
|
14
|
-
const linkTag = ` <link href="${webDest}/${appName}.${process.env.MIOLO_BUILD_CLIENT_SUFFIX}.css" rel="stylesheet" media="all">`
|
|
15
|
-
|
|
16
|
-
// Expresión regular para encontrar la etiqueta </head> de cierre.
|
|
17
|
-
// Usamos un grupo de captura para mantener el contenido antes de </head>.
|
|
18
|
-
const headCloseTagRegex = /(<\/head>)/i;
|
|
19
|
-
|
|
20
|
-
// Busca la etiqueta </head> en el HTML.
|
|
21
|
-
const match = htmlString.match(headCloseTagRegex);
|
|
22
|
-
|
|
23
|
-
if (match) {
|
|
24
|
-
console.log(`[${appName}][prod] adding css link ${linkTag} to <head>`)
|
|
25
|
-
|
|
26
|
-
// Si se encuentra </head>, inserta la etiqueta <link> justo antes de ella.
|
|
27
|
-
return htmlString.replace(headCloseTagRegex, `${linkTag}\n$&`);
|
|
28
|
-
} else {
|
|
29
|
-
// Si no se encuentra </head>, devuelve el HTML original sin modificar.
|
|
30
|
-
console.warn(`[${appName}][prod] No se encontró la etiqueta <head> en el HTML proporcionado.`);
|
|
31
|
-
return htmlString;
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
export default async function(appName, entry, htmlFile, dest) {
|
|
37
|
-
console.log(`[${appName}][prod] Building client from entry ${entry}`)
|
|
38
|
-
|
|
39
|
-
// Clean dest folder
|
|
40
|
-
cleanFolder(dest)
|
|
41
|
-
|
|
42
|
-
// Build client
|
|
43
|
-
await xeiraBundle({
|
|
44
|
-
source_index: entry,
|
|
45
|
-
target: 'browser',
|
|
46
|
-
bundle_folder: dest,
|
|
47
|
-
bundle_name: appName,
|
|
48
|
-
bundle_extension: process.env.MIOLO_BUILD_CLIENT_SUFFIX,
|
|
49
|
-
bundler: 'rollup',
|
|
50
|
-
bundle_node_polyfill: true
|
|
51
|
-
})
|
|
52
|
-
|
|
53
|
-
// Fix css file
|
|
54
|
-
const destCssFile = `${dest}/${appName}.${process.env.MIOLO_BUILD_CLIENT_SUFFIX}.css`
|
|
55
|
-
const destCssFileExists = isFileExistingSync(destCssFile)
|
|
56
|
-
|
|
57
|
-
console.log(`[${appName}][prod] destCssFile ${destCssFile} exists?: ${destCssFileExists}`)
|
|
58
|
-
|
|
59
|
-
console.log(`[${appName}][prod] Copying HTML file ${htmlFile} to ${dest}/index.html`)
|
|
60
|
-
copyFileSync(path.join(process.cwd(), htmlFile), path.join(process.cwd(), `${dest}/index.html`), (content) => {
|
|
61
|
-
if (destCssFileExists) {
|
|
62
|
-
content = _addCssLinkToHead(appName, content, dest)
|
|
63
|
-
}
|
|
64
|
-
return content
|
|
65
|
-
})
|
|
66
|
-
|
|
67
|
-
}
|