feat: blog!
All checks were successful
Publish and deploy / publish (push) Successful in 39s
Publish and deploy / deploy (push) Successful in 8s

This commit is contained in:
alina 🌸 2025-01-26 19:00:11 +03:00
parent 434a78eb90
commit e8d29d32da
Signed by: teidesu
SSH key fingerprint: SHA256:uNeCpw6aTSU4aIObXLvHfLkDa82HWH9EiOj9AXOIRpI
16 changed files with 1005 additions and 84 deletions

View file

@ -1,8 +1,39 @@
import node from '@astrojs/node' import node from '@astrojs/node'
import solid from '@astrojs/solid-js' import solid from '@astrojs/solid-js'
import { Graphviz } from '@hpcc-js/wasm-graphviz'
import { defineConfig } from 'astro/config' import { defineConfig } from 'astro/config'
import { toString } from 'mdast-util-to-string'
import getReadingTime from 'reading-time'
import { visit } from 'unist-util-visit'
import UnoCSS from 'unocss/astro' import UnoCSS from 'unocss/astro'
function remarkReadingTime() {
return function (tree, { data }) {
const textOnPage = toString(tree)
const readingTime = getReadingTime(textOnPage)
data.astro.frontmatter.minutesRead = readingTime.text
}
}
function remarkGraphvizSvg() {
return async function (tree) {
const graphviz = await Graphviz.load()
const instances = []
visit(tree, { type: 'code', lang: 'dot' }, (node, index, parent) => {
instances.push([node.value, index, parent])
})
for (const [dot, index, parent] of instances) {
const svg = graphviz.dot(dot, 'svg')
parent.children.splice(index, 1, {
type: 'html',
value: `<div class="graphviz-svg">${svg}</div>`,
})
}
}
}
// https://astro.build/config // https://astro.build/config
export default defineConfig({ export default defineConfig({
output: 'server', output: 'server',
@ -12,6 +43,19 @@ export default defineConfig({
injectReset: true, injectReset: true,
}), }),
], ],
markdown: {
remarkPlugins: [
remarkReadingTime,
remarkGraphvizSvg,
],
smartypants: false,
shikiConfig: {
themes: {
dark: 'catppuccin-mocha',
light: 'catppuccin-latte',
},
},
},
vite: { vite: {
esbuild: { jsx: 'automatic' }, esbuild: { jsx: 'automatic' },
define: { define: {

View file

@ -17,6 +17,7 @@
"@astrojs/solid-js": "^5.0.4", "@astrojs/solid-js": "^5.0.4",
"@fuman/fetch": "0.0.10", "@fuman/fetch": "0.0.10",
"@fuman/utils": "0.0.10", "@fuman/utils": "0.0.10",
"@hpcc-js/wasm-graphviz": "^1.7.0",
"@iconify-json/gravity-ui": "^1.2.4", "@iconify-json/gravity-ui": "^1.2.4",
"@mtcute/dispatcher": "^0.17.0", "@mtcute/dispatcher": "^0.17.0",
"@mtcute/node": "^0.17.0", "@mtcute/node": "^0.17.0",
@ -31,11 +32,15 @@
"dotenv": "^16.4.5", "dotenv": "^16.4.5",
"drizzle-kit": "^0.23.1", "drizzle-kit": "^0.23.1",
"drizzle-orm": "^0.32.1", "drizzle-orm": "^0.32.1",
"mdast-util-to-string": "^4.0.0",
"parse-duration": "^1.1.0", "parse-duration": "^1.1.0",
"rate-limiter-flexible": "^5.0.3", "rate-limiter-flexible": "^5.0.3",
"reading-time": "^1.5.0",
"remark-graphviz-svg": "^0.2.0",
"solid-js": "^1.8.19", "solid-js": "^1.8.19",
"tailwind-merge": "^2.6.0", "tailwind-merge": "^2.6.0",
"typescript": "^5.7.3", "typescript": "^5.7.3",
"unist-util-visit": "^5.0.0",
"unocss": "^65.4.3", "unocss": "^65.4.3",
"zod": "^3.23.8", "zod": "^3.23.8",
"zod-validation-error": "^3.3.1" "zod-validation-error": "^3.3.1"
@ -47,6 +52,7 @@
"@unocss/eslint-plugin": "^65.4.3", "@unocss/eslint-plugin": "^65.4.3",
"eslint": "9.19.0", "eslint": "9.19.0",
"eslint-plugin-astro": "^1.2.3", "eslint-plugin-astro": "^1.2.3",
"eslint-plugin-solid": "0.14.5" "eslint-plugin-solid": "0.14.5",
"postcss-nesting": "13.0.1"
} }
} }

View file

@ -23,6 +23,9 @@ importers:
'@fuman/utils': '@fuman/utils':
specifier: 0.0.10 specifier: 0.0.10
version: 0.0.10 version: 0.0.10
'@hpcc-js/wasm-graphviz':
specifier: ^1.7.0
version: 1.7.0
'@iconify-json/gravity-ui': '@iconify-json/gravity-ui':
specifier: ^1.2.4 specifier: ^1.2.4
version: 1.2.4 version: 1.2.4
@ -65,12 +68,21 @@ importers:
drizzle-orm: drizzle-orm:
specifier: ^0.32.1 specifier: ^0.32.1
version: 0.32.1(@types/better-sqlite3@7.6.11)(better-sqlite3@11.1.2) version: 0.32.1(@types/better-sqlite3@7.6.11)(better-sqlite3@11.1.2)
mdast-util-to-string:
specifier: ^4.0.0
version: 4.0.0
parse-duration: parse-duration:
specifier: ^1.1.0 specifier: ^1.1.0
version: 1.1.0 version: 1.1.0
rate-limiter-flexible: rate-limiter-flexible:
specifier: ^5.0.3 specifier: ^5.0.3
version: 5.0.3 version: 5.0.3
reading-time:
specifier: ^1.5.0
version: 1.5.0
remark-graphviz-svg:
specifier: ^0.2.0
version: 0.2.0
solid-js: solid-js:
specifier: ^1.8.19 specifier: ^1.8.19
version: 1.8.19 version: 1.8.19
@ -80,6 +92,9 @@ importers:
typescript: typescript:
specifier: ^5.7.3 specifier: ^5.7.3
version: 5.7.3 version: 5.7.3
unist-util-visit:
specifier: ^5.0.0
version: 5.0.0
unocss: unocss:
specifier: ^65.4.3 specifier: ^65.4.3
version: 65.4.3(postcss@8.5.1)(rollup@4.32.0)(vite@6.0.11(@types/node@22.0.2)(jiti@2.4.2)(sugarss@4.0.1(postcss@8.5.1))(tsx@4.19.2)(yaml@2.5.0))(vue@3.5.13(typescript@5.7.3)) version: 65.4.3(postcss@8.5.1)(rollup@4.32.0)(vite@6.0.11(@types/node@22.0.2)(jiti@2.4.2)(sugarss@4.0.1(postcss@8.5.1))(tsx@4.19.2)(yaml@2.5.0))(vue@3.5.13(typescript@5.7.3))
@ -111,6 +126,9 @@ importers:
eslint-plugin-solid: eslint-plugin-solid:
specifier: 0.14.5 specifier: 0.14.5
version: 0.14.5(eslint@9.19.0(jiti@2.4.2))(typescript@5.7.3) version: 0.14.5(eslint@9.19.0(jiti@2.4.2))(typescript@5.7.3)
postcss-nesting:
specifier: 13.0.1
version: 13.0.1(postcss@8.5.1)
packages: packages:
@ -340,6 +358,18 @@ packages:
'@clack/prompts@0.9.1': '@clack/prompts@0.9.1':
resolution: {integrity: sha512-JIpyaboYZeWYlyP0H+OoPPxd6nqueG/CmN6ixBiNFsIDHREevjIf0n0Ohh5gr5C8pEDknzgvz+pIJ8dMhzWIeg==} resolution: {integrity: sha512-JIpyaboYZeWYlyP0H+OoPPxd6nqueG/CmN6ixBiNFsIDHREevjIf0n0Ohh5gr5C8pEDknzgvz+pIJ8dMhzWIeg==}
'@csstools/selector-resolve-nested@3.0.0':
resolution: {integrity: sha512-ZoK24Yku6VJU1gS79a5PFmC8yn3wIapiKmPgun0hZgEI5AOqgH2kiPRsPz1qkGv4HL+wuDLH83yQyk6inMYrJQ==}
engines: {node: '>=18'}
peerDependencies:
postcss-selector-parser: ^7.0.0
'@csstools/selector-specificity@5.0.0':
resolution: {integrity: sha512-PCqQV3c4CoVm3kdPhyeZ07VmBRdH2EpMFA/pd9OASpOEC3aXNGoqPDAZ80D0cLpMBxnmk0+yNhGsEx31hq7Gtw==}
engines: {node: '>=18'}
peerDependencies:
postcss-selector-parser: ^7.0.0
'@drizzle-team/brocli@0.8.2': '@drizzle-team/brocli@0.8.2':
resolution: {integrity: sha512-zTrFENsqGvOkBOuHDC1pXCkDXNd2UhP4lI3gYGhQ1R1SPeAAfqzPsV1dcpMy4uNU6kB5VpU5NGhvwxVNETR02A==} resolution: {integrity: sha512-zTrFENsqGvOkBOuHDC1pXCkDXNd2UhP4lI3gYGhQ1R1SPeAAfqzPsV1dcpMy4uNU6kB5VpU5NGhvwxVNETR02A==}
@ -963,10 +993,6 @@ packages:
peerDependencies: peerDependencies:
eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 eslint: ^6.0.0 || ^7.0.0 || >=8.0.0
'@eslint-community/regexpp@4.11.0':
resolution: {integrity: sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A==}
engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0}
'@eslint-community/regexpp@4.12.1': '@eslint-community/regexpp@4.12.1':
resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==} resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==}
engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0}
@ -1031,6 +1057,13 @@ packages:
'@fuman/utils@0.0.10': '@fuman/utils@0.0.10':
resolution: {integrity: sha512-KVlDx0S1Og7IWcPi93f1T45WPfCSUV6/A4dQb36zZRtb8KECl1BK2u9WkNVI+sjrjKCb3xijjY5gq4lS3PqH5g==} resolution: {integrity: sha512-KVlDx0S1Og7IWcPi93f1T45WPfCSUV6/A4dQb36zZRtb8KECl1BK2u9WkNVI+sjrjKCb3xijjY5gq4lS3PqH5g==}
'@hpcc-js/wasm-graphviz@1.7.0':
resolution: {integrity: sha512-/1XEmubfDz1UolKiVh6H4BrGX0DZwR39Y2h76LdqR17FZ3gCJoUs4jV7kUywedXerbwx1szhU3dBsNcvkEmjiQ==}
'@hpcc-js/wasm@1.20.1':
resolution: {integrity: sha512-M1Blw6CdDkiurEmUTDFf7DDOVXOVblSKHBcc/+pUiBj2lIXgxkVOZhIm54zNspQdpurv7KKJ7VbylYMQRm4IyQ==}
hasBin: true
'@humanfs/core@0.19.1': '@humanfs/core@0.19.1':
resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==}
engines: {node: '>=18.18.0'} engines: {node: '>=18.18.0'}
@ -1379,6 +1412,10 @@ packages:
peerDependencies: peerDependencies:
solid-js: ^1.6.0 solid-js: ^1.6.0
'@trysound/sax@0.2.0':
resolution: {integrity: sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==}
engines: {node: '>=10.13.0'}
'@types/babel__core@7.20.5': '@types/babel__core@7.20.5':
resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==}
@ -1415,6 +1452,9 @@ packages:
'@types/events@3.0.0': '@types/events@3.0.0':
resolution: {integrity: sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g==} resolution: {integrity: sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g==}
'@types/hast@2.3.10':
resolution: {integrity: sha512-McWspRw8xx8J9HurkVBfYj0xKoE25tOFlHGdx4MJ5xORQrMGZNqJhVQWaIbm6Oyla5kYOXtDiopzKRJzEOkwJw==}
'@types/hast@3.0.4': '@types/hast@3.0.4':
resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==} resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==}
@ -1436,6 +1476,12 @@ packages:
'@types/normalize-package-data@2.4.4': '@types/normalize-package-data@2.4.4':
resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==} resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==}
'@types/svgo@2.6.4':
resolution: {integrity: sha512-l4cmyPEckf8moNYHdJ+4wkHvFxjyW6ulm9l4YGaOxeyBWPhBOT0gvni1InpFPdzx1dKf/2s62qGITwxNWnPQng==}
'@types/unist@2.0.11':
resolution: {integrity: sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==}
'@types/unist@3.0.2': '@types/unist@3.0.2':
resolution: {integrity: sha512-dqId9J8K/vGi5Zr7oo212BGii5m3q5Hxlkwy3WpYuKPklmBEvsbMYYyLxAQpSffdLl/gdW0XUpKWFvYmyoWCoQ==} resolution: {integrity: sha512-dqId9J8K/vGi5Zr7oo212BGii5m3q5Hxlkwy3WpYuKPklmBEvsbMYYyLxAQpSffdLl/gdW0XUpKWFvYmyoWCoQ==}
@ -1935,6 +1981,10 @@ packages:
comma-separated-tokens@2.0.3: comma-separated-tokens@2.0.3:
resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==}
commander@7.2.0:
resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==}
engines: {node: '>= 10'}
comment-parser@1.4.1: comment-parser@1.4.1:
resolution: {integrity: sha512-buhp5kePrmda3vhc5B9t7pUQXAb2Tnd0qgpkIhPhkHXxJpiPJ11H0ZEU0oBpJ2QztSbzG/ZxMj/CHsYJqRHmyg==} resolution: {integrity: sha512-buhp5kePrmda3vhc5B9t7pUQXAb2Tnd0qgpkIhPhkHXxJpiPJ11H0ZEU0oBpJ2QztSbzG/ZxMj/CHsYJqRHmyg==}
engines: {node: '>= 12.0.0'} engines: {node: '>= 12.0.0'}
@ -1972,15 +2022,30 @@ packages:
crossws@0.3.3: crossws@0.3.3:
resolution: {integrity: sha512-/71DJT3xJlqSnBr83uGJesmVHSzZEvgxHt/fIKxBAAngqMHmnBWQNxCphVxxJ2XL3xleu5+hJD6IQ3TglBedcw==} resolution: {integrity: sha512-/71DJT3xJlqSnBr83uGJesmVHSzZEvgxHt/fIKxBAAngqMHmnBWQNxCphVxxJ2XL3xleu5+hJD6IQ3TglBedcw==}
css-select@4.3.0:
resolution: {integrity: sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==}
css-tree@1.1.3:
resolution: {integrity: sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==}
engines: {node: '>=8.0.0'}
css-tree@3.1.0: css-tree@3.1.0:
resolution: {integrity: sha512-0eW44TGN5SQXU1mWSkKwFstI/22X2bG1nYzZTYMAWjylYURhse752YgbE4Cx46AC+bAvI+/dYTPRk1LqSUnu6w==} resolution: {integrity: sha512-0eW44TGN5SQXU1mWSkKwFstI/22X2bG1nYzZTYMAWjylYURhse752YgbE4Cx46AC+bAvI+/dYTPRk1LqSUnu6w==}
engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0} engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0}
css-what@6.1.0:
resolution: {integrity: sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==}
engines: {node: '>= 6'}
cssesc@3.0.0: cssesc@3.0.0:
resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==}
engines: {node: '>=4'} engines: {node: '>=4'}
hasBin: true hasBin: true
csso@4.2.0:
resolution: {integrity: sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==}
engines: {node: '>=8.0.0'}
csstype@3.1.3: csstype@3.1.3:
resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
@ -2692,12 +2757,18 @@ packages:
hast-util-from-html@2.0.3: hast-util-from-html@2.0.3:
resolution: {integrity: sha512-CUSRHXyKjzHov8yKsQjGOElXy/3EKpyX56ELnkHH34vDVw1N1XSQ1ZcAvTyAPtGqLTuKP/uxM+aLkSPqF/EtMw==} resolution: {integrity: sha512-CUSRHXyKjzHov8yKsQjGOElXy/3EKpyX56ELnkHH34vDVw1N1XSQ1ZcAvTyAPtGqLTuKP/uxM+aLkSPqF/EtMw==}
hast-util-from-parse5@7.1.2:
resolution: {integrity: sha512-Nz7FfPBuljzsN3tCQ4kCBKqdNhQE2l0Tn+X1ubgKBPRoiDIu1mL08Cfw4k7q71+Duyaw7DXDN+VTAp4Vh3oCOw==}
hast-util-from-parse5@8.0.1: hast-util-from-parse5@8.0.1:
resolution: {integrity: sha512-Er/Iixbc7IEa7r/XLtuG52zoqn/b3Xng/w6aZQ0xGVxzhw5xUFxcRqdPzP6yFi/4HBYRaifaI5fQ1RH8n0ZeOQ==} resolution: {integrity: sha512-Er/Iixbc7IEa7r/XLtuG52zoqn/b3Xng/w6aZQ0xGVxzhw5xUFxcRqdPzP6yFi/4HBYRaifaI5fQ1RH8n0ZeOQ==}
hast-util-is-element@3.0.0: hast-util-is-element@3.0.0:
resolution: {integrity: sha512-Val9mnv2IWpLbNPqc/pUem+a7Ipj2aHacCwgNfTiK0vJKl0LF+4Ba4+v1oPHFpf3bLYmreq0/l3Gud9S5OH42g==} resolution: {integrity: sha512-Val9mnv2IWpLbNPqc/pUem+a7Ipj2aHacCwgNfTiK0vJKl0LF+4Ba4+v1oPHFpf3bLYmreq0/l3Gud9S5OH42g==}
hast-util-parse-selector@3.1.1:
resolution: {integrity: sha512-jdlwBjEexy1oGz0aJ2f4GKMaVKkA9jwjr4MjAAI22E5fM/TXVZHuS5OpONtdeIkRKqAaryQ2E9xNQxijoThSZA==}
hast-util-parse-selector@4.0.0: hast-util-parse-selector@4.0.0:
resolution: {integrity: sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==} resolution: {integrity: sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==}
@ -2719,6 +2790,9 @@ packages:
hast-util-whitespace@3.0.0: hast-util-whitespace@3.0.0:
resolution: {integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==} resolution: {integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==}
hastscript@7.2.0:
resolution: {integrity: sha512-TtYPq24IldU8iKoJQqvZOuhi5CyCQRAbvDOX0x1eW6rsHSxa/1i2CCiptNTotGHJ3VoHRGmqiv6/D3q113ikkw==}
hastscript@8.0.0: hastscript@8.0.0:
resolution: {integrity: sha512-dMOtzCEd3ABUeSIISmrETiKuyydk1w0pa+gE/uormcTpSYuaNJPbX1NU3JLyscSLjwAQM8bWMhhIlnCqnRvDTw==} resolution: {integrity: sha512-dMOtzCEd3ABUeSIISmrETiKuyydk1w0pa+gE/uormcTpSYuaNJPbX1NU3JLyscSLjwAQM8bWMhhIlnCqnRvDTw==}
@ -2799,6 +2873,10 @@ packages:
resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==}
engines: {node: '>=8'} engines: {node: '>=8'}
is-buffer@2.0.5:
resolution: {integrity: sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==}
engines: {node: '>=4'}
is-builtin-module@3.2.1: is-builtin-module@3.2.1:
resolution: {integrity: sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==} resolution: {integrity: sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==}
engines: {node: '>=6'} engines: {node: '>=6'}
@ -2867,10 +2945,6 @@ packages:
resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==}
hasBin: true hasBin: true
jsdoc-type-pratt-parser@4.0.0:
resolution: {integrity: sha512-YtOli5Cmzy3q4dP26GraSOeAhqecewG04hoO8DY56CH4KJ9Fvv5qKWUCCo3HZob7esJQHCv6/+bnTy72xZZaVQ==}
engines: {node: '>=12.0.0'}
jsdoc-type-pratt-parser@4.1.0: jsdoc-type-pratt-parser@4.1.0:
resolution: {integrity: sha512-Hicd6JK5Njt2QB6XYFS7ok9e37O8AYk3jTcppG4YVQnYjOemymvTcmc7OWsmq/Qqj5TdRFO5/x/tIPmBeRtGHg==} resolution: {integrity: sha512-Hicd6JK5Njt2QB6XYFS7ok9e37O8AYk3jTcppG4YVQnYjOemymvTcmc7OWsmq/Qqj5TdRFO5/x/tIPmBeRtGHg==}
engines: {node: '>=12.0.0'} engines: {node: '>=12.0.0'}
@ -3039,6 +3113,9 @@ packages:
mdast-util-to-string@4.0.0: mdast-util-to-string@4.0.0:
resolution: {integrity: sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==} resolution: {integrity: sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==}
mdn-data@2.0.14:
resolution: {integrity: sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==}
mdn-data@2.12.2: mdn-data@2.12.2:
resolution: {integrity: sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA==} resolution: {integrity: sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA==}
@ -3319,6 +3396,9 @@ packages:
parse-latin@7.0.0: parse-latin@7.0.0:
resolution: {integrity: sha512-mhHgobPPua5kZ98EF4HWiH167JWBfl4pvAIXXdbaVohtK7a6YBOy56kvhCqduqyo/f3yrHFWmqmiMg/BkBkYYQ==} resolution: {integrity: sha512-mhHgobPPua5kZ98EF4HWiH167JWBfl4pvAIXXdbaVohtK7a6YBOy56kvhCqduqyo/f3yrHFWmqmiMg/BkBkYYQ==}
parse5@6.0.1:
resolution: {integrity: sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==}
parse5@7.1.2: parse5@7.1.2:
resolution: {integrity: sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==} resolution: {integrity: sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==}
@ -3378,10 +3458,20 @@ packages:
resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==} resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==}
engines: {node: '>=4'} engines: {node: '>=4'}
postcss-nesting@13.0.1:
resolution: {integrity: sha512-VbqqHkOBOt4Uu3G8Dm8n6lU5+9cJFxiuty9+4rcoyRPO9zZS1JIs6td49VIoix3qYqELHlJIn46Oih9SAKo+yQ==}
engines: {node: '>=18'}
peerDependencies:
postcss: ^8.4
postcss-selector-parser@6.1.1: postcss-selector-parser@6.1.1:
resolution: {integrity: sha512-b4dlw/9V8A71rLIDsSwVmak9z2DuBUB7CA1/wSdelNEzqsjoSPeADTWNO09lpH49Diy3/JIZ2bSPB1dI3LJCHg==} resolution: {integrity: sha512-b4dlw/9V8A71rLIDsSwVmak9z2DuBUB7CA1/wSdelNEzqsjoSPeADTWNO09lpH49Diy3/JIZ2bSPB1dI3LJCHg==}
engines: {node: '>=4'} engines: {node: '>=4'}
postcss-selector-parser@7.0.0:
resolution: {integrity: sha512-9RbEr1Y7FFfptd/1eEdntyjMwLeghW1bHX9GWjXo19vx4ytPQhANltvVxDggzJl7mnWM+dX28kb6cyS/4iQjlQ==}
engines: {node: '>=4'}
postcss@8.4.40: postcss@8.4.40:
resolution: {integrity: sha512-YF2kKIUzAofPMpfH6hOi2cGnv/HrUlfucspc7pDyvv7kGdqXrfj8SCl/t8owkEgKEuu8ZcRjSOxFxVLqwChZ2Q==} resolution: {integrity: sha512-YF2kKIUzAofPMpfH6hOi2cGnv/HrUlfucspc7pDyvv7kGdqXrfj8SCl/t8owkEgKEuu8ZcRjSOxFxVLqwChZ2Q==}
engines: {node: ^10 || ^12 || >=14} engines: {node: ^10 || ^12 || >=14}
@ -3463,6 +3553,9 @@ packages:
resolution: {integrity: sha512-h80JrZu/MHUZCyHu5ciuoI0+WxsCxzxJTILn6Fs8rxSnFPh+UVHYfeIxK1nVGugMqkfC4vJcBOYbkfkwYK0+gw==} resolution: {integrity: sha512-h80JrZu/MHUZCyHu5ciuoI0+WxsCxzxJTILn6Fs8rxSnFPh+UVHYfeIxK1nVGugMqkfC4vJcBOYbkfkwYK0+gw==}
engines: {node: '>= 14.18.0'} engines: {node: '>= 14.18.0'}
reading-time@1.5.0:
resolution: {integrity: sha512-onYyVhBNr4CmAxFsKS7bz+uTLRakypIe4R+5A824vBSkQy/hB3fZepoVEf8OVAxzLvK+H/jm9TzpI3ETSm64Kg==}
refa@0.12.1: refa@0.12.1:
resolution: {integrity: sha512-J8rn6v4DBb2nnFqkqwy6/NnTYMcgLA+sLr0iIO41qpv0n+ngb7ksag2tMRl0inb1bbO/esUwzW1vbJi7K0sI0g==} resolution: {integrity: sha512-J8rn6v4DBb2nnFqkqwy6/NnTYMcgLA+sLr0iIO41qpv0n+ngb7ksag2tMRl0inb1bbO/esUwzW1vbJi7K0sI0g==}
engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0}
@ -3488,6 +3581,9 @@ packages:
resolution: {integrity: sha512-qx+xQGZVsy55CH0a1hiVwHmqjLryfh7wQyF5HO07XJ9f7dQMY/gPQHhlyDkIzJKC+x2fUCpCcUODUUUFrm7SHA==} resolution: {integrity: sha512-qx+xQGZVsy55CH0a1hiVwHmqjLryfh7wQyF5HO07XJ9f7dQMY/gPQHhlyDkIzJKC+x2fUCpCcUODUUUFrm7SHA==}
hasBin: true hasBin: true
rehype-parse@8.0.5:
resolution: {integrity: sha512-Ds3RglaY/+clEX2U2mHflt7NlMA72KspZ0JLUJgBBLpRddBcEw3H8uYZQliQriku22NZpYMfjDdSgHcjxue24A==}
rehype-parse@9.0.0: rehype-parse@9.0.0:
resolution: {integrity: sha512-WG7nfvmWWkCR++KEkZevZb/uw41E8TsH4DsY9UxsTbIXCVGbAs4S+r8FrQ+OtH5EEQAs+5UxKC42VinkmpA1Yw==} resolution: {integrity: sha512-WG7nfvmWWkCR++KEkZevZb/uw41E8TsH4DsY9UxsTbIXCVGbAs4S+r8FrQ+OtH5EEQAs+5UxKC42VinkmpA1Yw==}
@ -3506,6 +3602,9 @@ packages:
remark-gfm@4.0.0: remark-gfm@4.0.0:
resolution: {integrity: sha512-U92vJgBPkbw4Zfu/IiW2oTZLSL3Zpv+uI7My2eq8JxKgqraFdU8YUGicEJCEgSbeaG+QDFqIcwwfMTOEelPxuA==} resolution: {integrity: sha512-U92vJgBPkbw4Zfu/IiW2oTZLSL3Zpv+uI7My2eq8JxKgqraFdU8YUGicEJCEgSbeaG+QDFqIcwwfMTOEelPxuA==}
remark-graphviz-svg@0.2.0:
resolution: {integrity: sha512-vG270z+b8jqS3yh1cXkFlZQdPRy2WYCquS82Hw2TDfoxqJYTS2P4I7/17IjDtRgrtdznELp0D5QSXqMD35BifA==}
remark-parse@11.0.0: remark-parse@11.0.0:
resolution: {integrity: sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==} resolution: {integrity: sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==}
@ -3693,6 +3792,10 @@ packages:
stable-hash@0.0.4: stable-hash@0.0.4:
resolution: {integrity: sha512-LjdcbuBeLcdETCrPn9i8AYAZ1eCtu4ECAWtP7UleOiZ9LzVxRzzUZEoZ8zB24nhkQnDWyET0I+3sWokSDS3E7g==} resolution: {integrity: sha512-LjdcbuBeLcdETCrPn9i8AYAZ1eCtu4ECAWtP7UleOiZ9LzVxRzzUZEoZ8zB24nhkQnDWyET0I+3sWokSDS3E7g==}
stable@0.1.8:
resolution: {integrity: sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==}
deprecated: 'Modern JS already guarantees Array#sort() is a stable sort, so this library is deprecated. See the compatibility table on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#browser_compatibility'
statuses@2.0.1: statuses@2.0.1:
resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==}
engines: {node: '>= 0.8'} engines: {node: '>= 0.8'}
@ -3756,6 +3859,11 @@ packages:
resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
svgo@2.8.0:
resolution: {integrity: sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg==}
engines: {node: '>=10.13.0'}
hasBin: true
synckit@0.6.2: synckit@0.6.2:
resolution: {integrity: sha512-Vhf+bUa//YSTYKseDiiEuQmhGCoIF3CVBhunm3r/DQnYiGT4JssmnKQc44BIyOZRK2pKjXXAgbhfmbeoC9CJpA==} resolution: {integrity: sha512-Vhf+bUa//YSTYKseDiiEuQmhGCoIF3CVBhunm3r/DQnYiGT4JssmnKQc44BIyOZRK2pKjXXAgbhfmbeoC9CJpA==}
engines: {node: '>=12.20'} engines: {node: '>=12.20'}
@ -3897,12 +4005,18 @@ packages:
unenv@1.10.0: unenv@1.10.0:
resolution: {integrity: sha512-wY5bskBQFL9n3Eca5XnhH6KbUo/tfvkwm9OpcdCvLaeA7piBNbavbOKJySEwQ1V0RH6HvNlSAFRTpvTqgKRQXQ==} resolution: {integrity: sha512-wY5bskBQFL9n3Eca5XnhH6KbUo/tfvkwm9OpcdCvLaeA7piBNbavbOKJySEwQ1V0RH6HvNlSAFRTpvTqgKRQXQ==}
unified@10.1.2:
resolution: {integrity: sha512-pUSWAi/RAnVy1Pif2kAoeWNBa3JVrx0MId2LASj8G+7AiHWoKZNTomq6LG326T68U7/e263X6fTdcXIy7XnF7Q==}
unified@11.0.5: unified@11.0.5:
resolution: {integrity: sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==} resolution: {integrity: sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==}
unist-util-find-after@5.0.0: unist-util-find-after@5.0.0:
resolution: {integrity: sha512-amQa0Ep2m6hE2g72AugUItjbuM8X8cGQnFoHk0pGfrFeT9GZhzN5SW8nRsiGKK7Aif4CrACPENkA6P/Lw6fHGQ==} resolution: {integrity: sha512-amQa0Ep2m6hE2g72AugUItjbuM8X8cGQnFoHk0pGfrFeT9GZhzN5SW8nRsiGKK7Aif4CrACPENkA6P/Lw6fHGQ==}
unist-util-is@5.2.1:
resolution: {integrity: sha512-u9njyyfEh43npf1M+yGKDGVPbY/JWEemg5nH05ncKPfi+kBbKBJoTdsogMu33uhytuLlv9y0O7GH7fEdwLdLQw==}
unist-util-is@6.0.0: unist-util-is@6.0.0:
resolution: {integrity: sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==} resolution: {integrity: sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==}
@ -3915,15 +4029,24 @@ packages:
unist-util-remove-position@5.0.0: unist-util-remove-position@5.0.0:
resolution: {integrity: sha512-Hp5Kh3wLxv0PHj9m2yZhhLt58KzPtEYKQQ4yxfYFEO7EvHwzyDYnduhHnY1mDxoqr7VUwVuHXk9RXKIiYS1N8Q==} resolution: {integrity: sha512-Hp5Kh3wLxv0PHj9m2yZhhLt58KzPtEYKQQ4yxfYFEO7EvHwzyDYnduhHnY1mDxoqr7VUwVuHXk9RXKIiYS1N8Q==}
unist-util-stringify-position@3.0.3:
resolution: {integrity: sha512-k5GzIBZ/QatR8N5X2y+drfpWG8IDBzdnVj6OInRNWm1oXrzydiaAT2OQiA8DPRRZyAKb9b6I2a6PxYklZD0gKg==}
unist-util-stringify-position@4.0.0: unist-util-stringify-position@4.0.0:
resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==} resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==}
unist-util-visit-children@3.0.0: unist-util-visit-children@3.0.0:
resolution: {integrity: sha512-RgmdTfSBOg04sdPcpTSD1jzoNBjt9a80/ZCzp5cI9n1qPzLZWF9YdvWGN2zmTumP1HWhXKdUWexjy/Wy/lJ7tA==} resolution: {integrity: sha512-RgmdTfSBOg04sdPcpTSD1jzoNBjt9a80/ZCzp5cI9n1qPzLZWF9YdvWGN2zmTumP1HWhXKdUWexjy/Wy/lJ7tA==}
unist-util-visit-parents@5.1.3:
resolution: {integrity: sha512-x6+y8g7wWMyQhL1iZfhIPhDAs7Xwbn9nRosDXl7qoPTSCy0yNxnKc+hWokFifWQIDGi154rdUqKvbCa4+1kLhg==}
unist-util-visit-parents@6.0.1: unist-util-visit-parents@6.0.1:
resolution: {integrity: sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==} resolution: {integrity: sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==}
unist-util-visit@4.1.2:
resolution: {integrity: sha512-MSd8OUGISqHdVvfY9TPhyK2VdUrPgxkUtWSuMHF6XAAFuL4LokseigBnZtPnJMu+FbynTkFNnFlyjxpVKujMRg==}
unist-util-visit@5.0.0: unist-util-visit@5.0.0:
resolution: {integrity: sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==} resolution: {integrity: sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==}
@ -4022,12 +4145,21 @@ packages:
validate-npm-package-license@3.0.4: validate-npm-package-license@3.0.4:
resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==}
vfile-location@4.1.0:
resolution: {integrity: sha512-YF23YMyASIIJXpktBa4vIGLJ5Gs88UB/XePgqPmTa7cDA+JeO3yclbpheQYCHjVHBn/yePzrXuygIL+xbvRYHw==}
vfile-location@5.0.3: vfile-location@5.0.3:
resolution: {integrity: sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==} resolution: {integrity: sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==}
vfile-message@3.1.4:
resolution: {integrity: sha512-fa0Z6P8HUrQN4BZaX05SIVXic+7kE3b05PWAtPuYP9QLHsLKYR7/AlLW3NtOrpXRLeawpDLMsVkmk5DG0NXgWw==}
vfile-message@4.0.2: vfile-message@4.0.2:
resolution: {integrity: sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==} resolution: {integrity: sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==}
vfile@5.3.7:
resolution: {integrity: sha512-r7qlzkgErKjobAmyNIkkSpizsFPYiUPuJb5pNW1RB4JcYVZhs4lIbVqk8XPk033CV/1z8ss5pkax8SuhGpcG8g==}
vfile@6.0.3: vfile@6.0.3:
resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==} resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==}
@ -4287,6 +4419,10 @@ packages:
resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==}
engines: {node: '>=12'} engines: {node: '>=12'}
yargs@17.6.0:
resolution: {integrity: sha512-8H/wTDqlSwoSnScvV2N/JHfLWOKuh5MVla9hqLjK3nsfyy6Y4kDSYSvkU5YCUEPOSnRXfIyx3Sq+B/IWudTo4g==}
engines: {node: '>=12'}
yargs@17.7.2: yargs@17.7.2:
resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==}
engines: {node: '>=12'} engines: {node: '>=12'}
@ -4664,6 +4800,14 @@ snapshots:
picocolors: 1.1.1 picocolors: 1.1.1
sisteransi: 1.0.5 sisteransi: 1.0.5
'@csstools/selector-resolve-nested@3.0.0(postcss-selector-parser@7.0.0)':
dependencies:
postcss-selector-parser: 7.0.0
'@csstools/selector-specificity@5.0.0(postcss-selector-parser@7.0.0)':
dependencies:
postcss-selector-parser: 7.0.0
'@drizzle-team/brocli@0.8.2': {} '@drizzle-team/brocli@0.8.2': {}
'@emmetio/abbreviation@2.3.3': '@emmetio/abbreviation@2.3.3':
@ -5005,7 +5149,7 @@ snapshots:
dependencies: dependencies:
escape-string-regexp: 4.0.0 escape-string-regexp: 4.0.0
eslint: 9.19.0(jiti@2.4.2) eslint: 9.19.0(jiti@2.4.2)
ignore: 5.3.1 ignore: 5.3.2
'@eslint-community/eslint-utils@4.4.0(eslint@9.19.0(jiti@2.4.2))': '@eslint-community/eslint-utils@4.4.0(eslint@9.19.0(jiti@2.4.2))':
dependencies: dependencies:
@ -5017,8 +5161,6 @@ snapshots:
eslint: 9.19.0(jiti@2.4.2) eslint: 9.19.0(jiti@2.4.2)
eslint-visitor-keys: 3.4.3 eslint-visitor-keys: 3.4.3
'@eslint-community/regexpp@4.11.0': {}
'@eslint-community/regexpp@4.12.1': {} '@eslint-community/regexpp@4.12.1': {}
'@eslint/compat@1.2.5(eslint@9.19.0(jiti@2.4.2))': '@eslint/compat@1.2.5(eslint@9.19.0(jiti@2.4.2))':
@ -5078,6 +5220,12 @@ snapshots:
'@fuman/utils@0.0.10': {} '@fuman/utils@0.0.10': {}
'@hpcc-js/wasm-graphviz@1.7.0': {}
'@hpcc-js/wasm@1.20.1':
dependencies:
yargs: 17.6.0
'@humanfs/core@0.19.1': {} '@humanfs/core@0.19.1': {}
'@humanfs/node@0.16.6': '@humanfs/node@0.16.6':
@ -5387,6 +5535,8 @@ snapshots:
'@tanstack/query-core': 5.51.21 '@tanstack/query-core': 5.51.21
solid-js: 1.8.19 solid-js: 1.8.19
'@trysound/sax@0.2.0': {}
'@types/babel__core@7.20.5': '@types/babel__core@7.20.5':
dependencies: dependencies:
'@babel/parser': 7.25.3 '@babel/parser': 7.25.3
@ -5431,6 +5581,10 @@ snapshots:
'@types/events@3.0.0': {} '@types/events@3.0.0': {}
'@types/hast@2.3.10':
dependencies:
'@types/unist': 2.0.11
'@types/hast@3.0.4': '@types/hast@3.0.4':
dependencies: dependencies:
'@types/unist': 3.0.2 '@types/unist': 3.0.2
@ -5453,11 +5607,17 @@ snapshots:
'@types/normalize-package-data@2.4.4': {} '@types/normalize-package-data@2.4.4': {}
'@types/svgo@2.6.4':
dependencies:
'@types/node': 22.0.2
'@types/unist@2.0.11': {}
'@types/unist@3.0.2': {} '@types/unist@3.0.2': {}
'@typescript-eslint/eslint-plugin@8.21.0(@typescript-eslint/parser@8.21.0(eslint@9.19.0(jiti@2.4.2))(typescript@5.7.3))(eslint@9.19.0(jiti@2.4.2))(typescript@5.7.3)': '@typescript-eslint/eslint-plugin@8.21.0(@typescript-eslint/parser@8.21.0(eslint@9.19.0(jiti@2.4.2))(typescript@5.7.3))(eslint@9.19.0(jiti@2.4.2))(typescript@5.7.3)':
dependencies: dependencies:
'@eslint-community/regexpp': 4.11.0 '@eslint-community/regexpp': 4.12.1
'@typescript-eslint/parser': 8.21.0(eslint@9.19.0(jiti@2.4.2))(typescript@5.7.3) '@typescript-eslint/parser': 8.21.0(eslint@9.19.0(jiti@2.4.2))(typescript@5.7.3)
'@typescript-eslint/scope-manager': 8.21.0 '@typescript-eslint/scope-manager': 8.21.0
'@typescript-eslint/type-utils': 8.21.0(eslint@9.19.0(jiti@2.4.2))(typescript@5.7.3) '@typescript-eslint/type-utils': 8.21.0(eslint@9.19.0(jiti@2.4.2))(typescript@5.7.3)
@ -5465,7 +5625,7 @@ snapshots:
'@typescript-eslint/visitor-keys': 8.21.0 '@typescript-eslint/visitor-keys': 8.21.0
eslint: 9.19.0(jiti@2.4.2) eslint: 9.19.0(jiti@2.4.2)
graphemer: 1.4.0 graphemer: 1.4.0
ignore: 5.3.1 ignore: 5.3.2
natural-compare: 1.4.0 natural-compare: 1.4.0
ts-api-utils: 2.0.0(typescript@5.7.3) ts-api-utils: 2.0.0(typescript@5.7.3)
typescript: 5.7.3 typescript: 5.7.3
@ -6218,6 +6378,8 @@ snapshots:
comma-separated-tokens@2.0.3: {} comma-separated-tokens@2.0.3: {}
commander@7.2.0: {}
comment-parser@1.4.1: {} comment-parser@1.4.1: {}
common-ancestor-path@1.0.1: {} common-ancestor-path@1.0.1: {}
@ -6248,13 +6410,32 @@ snapshots:
dependencies: dependencies:
uncrypto: 0.1.3 uncrypto: 0.1.3
css-select@4.3.0:
dependencies:
boolbase: 1.0.0
css-what: 6.1.0
domhandler: 4.3.1
domutils: 2.8.0
nth-check: 2.1.1
css-tree@1.1.3:
dependencies:
mdn-data: 2.0.14
source-map: 0.6.1
css-tree@3.1.0: css-tree@3.1.0:
dependencies: dependencies:
mdn-data: 2.12.2 mdn-data: 2.12.2
source-map-js: 1.2.1 source-map-js: 1.2.1
css-what@6.1.0: {}
cssesc@3.0.0: {} cssesc@3.0.0: {}
csso@4.2.0:
dependencies:
css-tree: 1.1.3
csstype@3.1.3: {} csstype@3.1.3: {}
date-fns@3.6.0: {} date-fns@3.6.0: {}
@ -6584,7 +6765,7 @@ snapshots:
eslint-plugin-es-x@7.8.0(eslint@9.19.0(jiti@2.4.2)): eslint-plugin-es-x@7.8.0(eslint@9.19.0(jiti@2.4.2)):
dependencies: dependencies:
'@eslint-community/eslint-utils': 4.4.1(eslint@9.19.0(jiti@2.4.2)) '@eslint-community/eslint-utils': 4.4.1(eslint@9.19.0(jiti@2.4.2))
'@eslint-community/regexpp': 4.11.0 '@eslint-community/regexpp': 4.12.1
eslint: 9.19.0(jiti@2.4.2) eslint: 9.19.0(jiti@2.4.2)
eslint-compat-utils: 0.5.1(eslint@9.19.0(jiti@2.4.2)) eslint-compat-utils: 0.5.1(eslint@9.19.0(jiti@2.4.2))
@ -6598,7 +6779,7 @@ snapshots:
enhanced-resolve: 5.17.1 enhanced-resolve: 5.17.1
eslint: 9.19.0(jiti@2.4.2) eslint: 9.19.0(jiti@2.4.2)
eslint-import-resolver-node: 0.3.9 eslint-import-resolver-node: 0.3.9
get-tsconfig: 4.7.6 get-tsconfig: 4.10.0
is-glob: 4.0.3 is-glob: 4.0.3
minimatch: 9.0.5 minimatch: 9.0.5
semver: 7.6.3 semver: 7.6.3
@ -6616,7 +6797,7 @@ snapshots:
debug: 4.4.0 debug: 4.4.0
escape-string-regexp: 4.0.0 escape-string-regexp: 4.0.0
eslint: 9.19.0(jiti@2.4.2) eslint: 9.19.0(jiti@2.4.2)
espree: 10.1.0 espree: 10.3.0
esquery: 1.6.0 esquery: 1.6.0
parse-imports: 2.1.1 parse-imports: 2.1.1
semver: 7.6.3 semver: 7.6.3
@ -6627,7 +6808,7 @@ snapshots:
eslint-plugin-jsonc@2.19.1(eslint@9.19.0(jiti@2.4.2)): eslint-plugin-jsonc@2.19.1(eslint@9.19.0(jiti@2.4.2)):
dependencies: dependencies:
'@eslint-community/eslint-utils': 4.4.0(eslint@9.19.0(jiti@2.4.2)) '@eslint-community/eslint-utils': 4.4.1(eslint@9.19.0(jiti@2.4.2))
eslint: 9.19.0(jiti@2.4.2) eslint: 9.19.0(jiti@2.4.2)
eslint-compat-utils: 0.6.4(eslint@9.19.0(jiti@2.4.2)) eslint-compat-utils: 0.6.4(eslint@9.19.0(jiti@2.4.2))
eslint-json-compat-utils: 0.2.1(eslint@9.19.0(jiti@2.4.2))(jsonc-eslint-parser@2.4.0) eslint-json-compat-utils: 0.2.1(eslint@9.19.0(jiti@2.4.2))(jsonc-eslint-parser@2.4.0)
@ -6665,11 +6846,11 @@ snapshots:
eslint-plugin-regexp@2.7.0(eslint@9.19.0(jiti@2.4.2)): eslint-plugin-regexp@2.7.0(eslint@9.19.0(jiti@2.4.2)):
dependencies: dependencies:
'@eslint-community/eslint-utils': 4.4.0(eslint@9.19.0(jiti@2.4.2)) '@eslint-community/eslint-utils': 4.4.1(eslint@9.19.0(jiti@2.4.2))
'@eslint-community/regexpp': 4.11.0 '@eslint-community/regexpp': 4.12.1
comment-parser: 1.4.1 comment-parser: 1.4.1
eslint: 9.19.0(jiti@2.4.2) eslint: 9.19.0(jiti@2.4.2)
jsdoc-type-pratt-parser: 4.0.0 jsdoc-type-pratt-parser: 4.1.0
refa: 0.12.1 refa: 0.12.1
regexp-ast-analysis: 0.7.1 regexp-ast-analysis: 0.7.1
scslre: 0.3.0 scslre: 0.3.0
@ -6700,7 +6881,7 @@ snapshots:
eslint-plugin-unicorn@56.0.1(eslint@9.19.0(jiti@2.4.2)): eslint-plugin-unicorn@56.0.1(eslint@9.19.0(jiti@2.4.2)):
dependencies: dependencies:
'@babel/helper-validator-identifier': 7.25.9 '@babel/helper-validator-identifier': 7.25.9
'@eslint-community/eslint-utils': 4.4.0(eslint@9.19.0(jiti@2.4.2)) '@eslint-community/eslint-utils': 4.4.1(eslint@9.19.0(jiti@2.4.2))
ci-info: 4.1.0 ci-info: 4.1.0
clean-regexp: 1.0.0 clean-regexp: 1.0.0
core-js-compat: 3.40.0 core-js-compat: 3.40.0
@ -6725,7 +6906,7 @@ snapshots:
eslint-plugin-vue@9.32.0(eslint@9.19.0(jiti@2.4.2)): eslint-plugin-vue@9.32.0(eslint@9.19.0(jiti@2.4.2)):
dependencies: dependencies:
'@eslint-community/eslint-utils': 4.4.0(eslint@9.19.0(jiti@2.4.2)) '@eslint-community/eslint-utils': 4.4.1(eslint@9.19.0(jiti@2.4.2))
eslint: 9.19.0(jiti@2.4.2) eslint: 9.19.0(jiti@2.4.2)
globals: 13.24.0 globals: 13.24.0
natural-compare: 1.4.0 natural-compare: 1.4.0
@ -7034,6 +7215,16 @@ snapshots:
vfile: 6.0.3 vfile: 6.0.3
vfile-message: 4.0.2 vfile-message: 4.0.2
hast-util-from-parse5@7.1.2:
dependencies:
'@types/hast': 2.3.10
'@types/unist': 2.0.11
hastscript: 7.2.0
property-information: 6.5.0
vfile: 5.3.7
vfile-location: 4.1.0
web-namespaces: 2.0.1
hast-util-from-parse5@8.0.1: hast-util-from-parse5@8.0.1:
dependencies: dependencies:
'@types/hast': 3.0.4 '@types/hast': 3.0.4
@ -7049,6 +7240,10 @@ snapshots:
dependencies: dependencies:
'@types/hast': 3.0.4 '@types/hast': 3.0.4
hast-util-parse-selector@3.1.1:
dependencies:
'@types/hast': 2.3.10
hast-util-parse-selector@4.0.0: hast-util-parse-selector@4.0.0:
dependencies: dependencies:
'@types/hast': 3.0.4 '@types/hast': 3.0.4
@ -7119,6 +7314,14 @@ snapshots:
dependencies: dependencies:
'@types/hast': 3.0.4 '@types/hast': 3.0.4
hastscript@7.2.0:
dependencies:
'@types/hast': 2.3.10
comma-separated-tokens: 2.0.3
hast-util-parse-selector: 3.1.1
property-information: 6.5.0
space-separated-tokens: 2.0.2
hastscript@8.0.0: hastscript@8.0.0:
dependencies: dependencies:
'@types/hast': 3.0.4 '@types/hast': 3.0.4
@ -7199,6 +7402,8 @@ snapshots:
dependencies: dependencies:
binary-extensions: 2.3.0 binary-extensions: 2.3.0
is-buffer@2.0.5: {}
is-builtin-module@3.2.1: is-builtin-module@3.2.1:
dependencies: dependencies:
builtin-modules: 3.3.0 builtin-modules: 3.3.0
@ -7250,8 +7455,6 @@ snapshots:
dependencies: dependencies:
argparse: 2.0.1 argparse: 2.0.1
jsdoc-type-pratt-parser@4.0.0: {}
jsdoc-type-pratt-parser@4.1.0: {} jsdoc-type-pratt-parser@4.1.0: {}
jsesc@0.5.0: {} jsesc@0.5.0: {}
@ -7493,6 +7696,8 @@ snapshots:
dependencies: dependencies:
'@types/mdast': 4.0.4 '@types/mdast': 4.0.4
mdn-data@2.0.14: {}
mdn-data@2.12.2: {} mdn-data@2.12.2: {}
merge-anything@5.1.7: merge-anything@5.1.7:
@ -7871,6 +8076,8 @@ snapshots:
unist-util-visit-children: 3.0.0 unist-util-visit-children: 3.0.0
vfile: 6.0.3 vfile: 6.0.3
parse5@6.0.1: {}
parse5@7.1.2: parse5@7.1.2:
dependencies: dependencies:
entities: 4.5.0 entities: 4.5.0
@ -7913,11 +8120,23 @@ snapshots:
pluralize@8.0.0: {} pluralize@8.0.0: {}
postcss-nesting@13.0.1(postcss@8.5.1):
dependencies:
'@csstools/selector-resolve-nested': 3.0.0(postcss-selector-parser@7.0.0)
'@csstools/selector-specificity': 5.0.0(postcss-selector-parser@7.0.0)
postcss: 8.5.1
postcss-selector-parser: 7.0.0
postcss-selector-parser@6.1.1: postcss-selector-parser@6.1.1:
dependencies: dependencies:
cssesc: 3.0.0 cssesc: 3.0.0
util-deprecate: 1.0.2 util-deprecate: 1.0.2
postcss-selector-parser@7.0.0:
dependencies:
cssesc: 3.0.0
util-deprecate: 1.0.2
postcss@8.4.40: postcss@8.4.40:
dependencies: dependencies:
nanoid: 3.3.7 nanoid: 3.3.7
@ -8012,9 +8231,11 @@ snapshots:
readdirp@4.1.1: {} readdirp@4.1.1: {}
reading-time@1.5.0: {}
refa@0.12.1: refa@0.12.1:
dependencies: dependencies:
'@eslint-community/regexpp': 4.11.0 '@eslint-community/regexpp': 4.12.1
regex-recursion@5.1.1: regex-recursion@5.1.1:
dependencies: dependencies:
@ -8029,7 +8250,7 @@ snapshots:
regexp-ast-analysis@0.7.1: regexp-ast-analysis@0.7.1:
dependencies: dependencies:
'@eslint-community/regexpp': 4.11.0 '@eslint-community/regexpp': 4.12.1
refa: 0.12.1 refa: 0.12.1
regexp-tree@0.1.27: {} regexp-tree@0.1.27: {}
@ -8038,6 +8259,13 @@ snapshots:
dependencies: dependencies:
jsesc: 0.5.0 jsesc: 0.5.0
rehype-parse@8.0.5:
dependencies:
'@types/hast': 2.3.10
hast-util-from-parse5: 7.1.2
parse5: 6.0.1
unified: 10.1.2
rehype-parse@9.0.0: rehype-parse@9.0.0:
dependencies: dependencies:
'@types/hast': 3.0.4 '@types/hast': 3.0.4
@ -8080,6 +8308,15 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
remark-graphviz-svg@0.2.0:
dependencies:
'@hpcc-js/wasm': 1.20.1
'@types/svgo': 2.6.4
rehype-parse: 8.0.5
svgo: 2.8.0
unified: 10.1.2
unist-util-visit: 4.1.2
remark-parse@11.0.0: remark-parse@11.0.0:
dependencies: dependencies:
'@types/mdast': 4.0.4 '@types/mdast': 4.0.4
@ -8188,7 +8425,7 @@ snapshots:
scslre@0.3.0: scslre@0.3.0:
dependencies: dependencies:
'@eslint-community/regexpp': 4.11.0 '@eslint-community/regexpp': 4.12.1
refa: 0.12.1 refa: 0.12.1
regexp-ast-analysis: 0.7.1 regexp-ast-analysis: 0.7.1
@ -8345,6 +8582,8 @@ snapshots:
stable-hash@0.0.4: {} stable-hash@0.0.4: {}
stable@0.1.8: {}
statuses@2.0.1: {} statuses@2.0.1: {}
string-width@4.2.3: string-width@4.2.3:
@ -8405,6 +8644,16 @@ snapshots:
supports-preserve-symlinks-flag@1.0.0: {} supports-preserve-symlinks-flag@1.0.0: {}
svgo@2.8.0:
dependencies:
'@trysound/sax': 0.2.0
commander: 7.2.0
css-select: 4.3.0
css-tree: 1.1.3
csso: 4.2.0
picocolors: 1.1.1
stable: 0.1.8
synckit@0.6.2: synckit@0.6.2:
dependencies: dependencies:
tslib: 2.6.3 tslib: 2.6.3
@ -8480,7 +8729,7 @@ snapshots:
tsx@4.19.2: tsx@4.19.2:
dependencies: dependencies:
esbuild: 0.23.1 esbuild: 0.23.1
get-tsconfig: 4.7.6 get-tsconfig: 4.10.0
optionalDependencies: optionalDependencies:
fsevents: 2.3.3 fsevents: 2.3.3
@ -8532,6 +8781,16 @@ snapshots:
node-fetch-native: 1.6.6 node-fetch-native: 1.6.6
pathe: 1.1.2 pathe: 1.1.2
unified@10.1.2:
dependencies:
'@types/unist': 2.0.11
bail: 2.0.2
extend: 3.0.2
is-buffer: 2.0.5
is-plain-obj: 4.1.0
trough: 2.2.0
vfile: 5.3.7
unified@11.0.5: unified@11.0.5:
dependencies: dependencies:
'@types/unist': 3.0.2 '@types/unist': 3.0.2
@ -8547,6 +8806,10 @@ snapshots:
'@types/unist': 3.0.2 '@types/unist': 3.0.2
unist-util-is: 6.0.0 unist-util-is: 6.0.0
unist-util-is@5.2.1:
dependencies:
'@types/unist': 2.0.11
unist-util-is@6.0.0: unist-util-is@6.0.0:
dependencies: dependencies:
'@types/unist': 3.0.2 '@types/unist': 3.0.2
@ -8565,6 +8828,10 @@ snapshots:
'@types/unist': 3.0.2 '@types/unist': 3.0.2
unist-util-visit: 5.0.0 unist-util-visit: 5.0.0
unist-util-stringify-position@3.0.3:
dependencies:
'@types/unist': 2.0.11
unist-util-stringify-position@4.0.0: unist-util-stringify-position@4.0.0:
dependencies: dependencies:
'@types/unist': 3.0.2 '@types/unist': 3.0.2
@ -8573,11 +8840,22 @@ snapshots:
dependencies: dependencies:
'@types/unist': 3.0.2 '@types/unist': 3.0.2
unist-util-visit-parents@5.1.3:
dependencies:
'@types/unist': 2.0.11
unist-util-is: 5.2.1
unist-util-visit-parents@6.0.1: unist-util-visit-parents@6.0.1:
dependencies: dependencies:
'@types/unist': 3.0.2 '@types/unist': 3.0.2
unist-util-is: 6.0.0 unist-util-is: 6.0.0
unist-util-visit@4.1.2:
dependencies:
'@types/unist': 2.0.11
unist-util-is: 5.2.1
unist-util-visit-parents: 5.1.3
unist-util-visit@5.0.0: unist-util-visit@5.0.0:
dependencies: dependencies:
'@types/unist': 3.0.2 '@types/unist': 3.0.2
@ -8647,16 +8925,33 @@ snapshots:
spdx-correct: 3.2.0 spdx-correct: 3.2.0
spdx-expression-parse: 3.0.1 spdx-expression-parse: 3.0.1
vfile-location@4.1.0:
dependencies:
'@types/unist': 2.0.11
vfile: 5.3.7
vfile-location@5.0.3: vfile-location@5.0.3:
dependencies: dependencies:
'@types/unist': 3.0.2 '@types/unist': 3.0.2
vfile: 6.0.3 vfile: 6.0.3
vfile-message@3.1.4:
dependencies:
'@types/unist': 2.0.11
unist-util-stringify-position: 3.0.3
vfile-message@4.0.2: vfile-message@4.0.2:
dependencies: dependencies:
'@types/unist': 3.0.2 '@types/unist': 3.0.2
unist-util-stringify-position: 4.0.0 unist-util-stringify-position: 4.0.0
vfile@5.3.7:
dependencies:
'@types/unist': 2.0.11
is-buffer: 2.0.5
unist-util-stringify-position: 3.0.3
vfile-message: 3.1.4
vfile@6.0.3: vfile@6.0.3:
dependencies: dependencies:
'@types/unist': 3.0.2 '@types/unist': 3.0.2
@ -8897,6 +9192,16 @@ snapshots:
yargs-parser@21.1.1: {} yargs-parser@21.1.1: {}
yargs@17.6.0:
dependencies:
cliui: 8.0.1
escalade: 3.2.0
get-caller-file: 2.0.5
require-directory: 2.1.1
string-width: 4.2.3
y18n: 5.0.8
yargs-parser: 21.1.1
yargs@17.7.2: yargs@17.7.2:
dependencies: dependencies:
cliui: 8.0.1 cliui: 8.0.1

View file

@ -1,7 +1,9 @@
import UnoCSS from '@unocss/postcss' import UnoCSS from '@unocss/postcss'
import nesting from 'postcss-nesting'
export default { export default {
plugins: [ plugins: [
UnoCSS(), UnoCSS(),
nesting(),
], ],
} }

View file

@ -36,8 +36,8 @@ function LastSeenItem(props: { first?: boolean, item: TLastSeenItem }) {
props.first && 'pos-relative list-none cursor-pointer rounded-md hover:bg-control-bg-hover active:select-none [&::-webkit-details-marker]:hidden', props.first && 'pos-relative list-none cursor-pointer rounded-md hover:bg-control-bg-hover active:select-none [&::-webkit-details-marker]:hidden',
)} )}
> >
<div class="max-w-full flex items-center overflow-hidden"> <div class="max-w-full flex flex-col overflow-hidden sm:flex-row sm:items-center">
<div class="width-min max-w-full flex items-center overflow-hidden"> <div class="max-w-full w-min flex flex-row items-center overflow-hidden">
<Link <Link
class="max-w-200px overflow-hidden text-ellipsis whitespace-nowrap lg:max-w-300px" class="max-w-200px overflow-hidden text-ellipsis whitespace-nowrap lg:max-w-300px"
href={props.item.link} href={props.item.link}
@ -52,7 +52,7 @@ function LastSeenItem(props: { first?: boolean, item: TLastSeenItem }) {
</span> </span>
)} )}
</div> </div>
<i class="ml-2 whitespace-nowrap text-xs text-text-secondary lg:ml-0"> <i class="ml-2 whitespace-nowrap text-xs text-text-secondary">
{'@ '} {'@ '}
<Link href={props.item.sourceLink} target="_blank"> <Link href={props.item.sourceLink} target="_blank">
{props.item.source} {props.item.source}
@ -62,7 +62,7 @@ function LastSeenItem(props: { first?: boolean, item: TLastSeenItem }) {
</i> </i>
</div> </div>
<Show when={props.first}> <Show when={props.first}>
<div class="before:ml-1em before:whitespace-nowrap before:text-xs before:text-text-secondary before:content-['<'] lg:before:content-['(click_to_expand)'] md:before:content-['(expand)']" /> <div data-expand-label class="before:(ml-1em whitespace-nowrap text-xs text-text-secondary content-['<'] md:content-['(click_to_expand)'] sm:content-['(expand)'])" />
</Show> </Show>
</Dynamic> </Dynamic>
) )
@ -173,7 +173,7 @@ export function PageMain(props: {
if (!props.data.lastSeen?.length) return if (!props.data.lastSeen?.length) return
return ( return (
<details class="open:mb-1"> <details class="open:mb-1 [&[open]_[data-expand-label]]:before:(content-['v'] md:content-['(click_to_collapse)'] sm:content-['(collapse)'])">
<LastSeenItem first item={props.data.lastSeen[0]} /> <LastSeenItem first item={props.data.lastSeen[0]} />
<For each={props.data.lastSeen.slice(1)}> <For each={props.data.lastSeen.slice(1)}>
{it => <LastSeenItem item={it} />} {it => <LastSeenItem item={it} />}

View file

@ -61,7 +61,7 @@ export function TextArea(props: TextAreaProps) {
<textarea <textarea
{...rest} {...rest}
class={cn( class={cn(
'border border-control-outline bg-control-bg rounded-md text-text-primary min-h-4em resize-none p-2 transition-all text-sm placeholder-text-secondary cursor-pointer hover:bg-control-bg-hover-alt disabled:cursor-not-allowed disabled:bg-control-bg-disabled disabled:border-text-disabled disabled:placeholder-text-disabled focus:border-text-primary focus:bg-control-bg-active focus:outline-1 focus:outline-text-primary focus:hover:cursor-text', 'outline-none border border-control-outline bg-control-bg rounded-md text-text-primary min-h-4em resize-none p-2 transition-all text-sm placeholder-text-secondary cursor-pointer hover:bg-control-bg-hover-alt disabled:cursor-not-allowed disabled:bg-control-bg-disabled disabled:border-text-disabled disabled:placeholder-text-disabled focus:(border-text-primary bg-control-bg-active outline outline-text-primary hover:cursor-text outline-offset-0)',
my.class, my.class,
)} )}
onInput={onInput} onInput={onInput}

View file

@ -32,7 +32,7 @@ export function TextTable(props: TextTableProps) {
return ( return (
<div <div
class={cn( class={cn(
'grid grid-cols-[1fr_4fr] md:grid-cols-[1fr_2fr] overflow-hidden border-spacing-0 line-height-18px', 'grid grid-cols-[1fr_2fr] sm:grid-cols-[1fr_4fr] overflow-hidden border-spacing-0 line-height-18px',
props.wrap ? 'whitespace-pre-wrap' : 'text-ellipsis whitespace-pre', props.wrap ? 'whitespace-pre-wrap' : 'text-ellipsis whitespace-pre',
props.fill && 'w-full', props.fill && 'w-full',
)} )}

27
src/content.config.ts Normal file
View file

@ -0,0 +1,27 @@
import { glob } from 'astro/loaders'
import { type CollectionEntry, defineCollection, z } from 'astro:content'
const blog = defineCollection({
loader: glob({
base: './src/content/posts',
pattern: [
'*.md',
...(import.meta.env.PROD ? ['!sample.md'] : []),
],
}),
schema: () => z.object({
title: z.string(),
description: z.string(),
date: z.coerce.date(),
}),
})
export const collections = { blog }
export function sortPostsByDate(a: CollectionEntry<'blog'>, b: CollectionEntry<'blog'>) {
return a.data.date.valueOf() - b.data.date.valueOf()
}
export function sortPostsByDateReverse(a: CollectionEntry<'blog'>, b: CollectionEntry<'blog'>) {
return b.data.date.valueOf() - a.data.date.valueOf()
}

View file

@ -0,0 +1,317 @@
---
date: '2025-01-25'
title: 'how i built mtcute repl'
description: 'a tale about browser inconsistencies, threading in javascript and insane workarounds'
---
hey so uhh quick intro
for the past like two years i've been working on [mtcute](https://github.com/mtcute/mtcute), an mtproto client library in typescript. in case you didn't know, mtproto **FUCKING SUCKS**, but that's for another post.
and i've always wanted to build an online in-browser interactive tool that would allow me to poke around with telegram api, with minimum friction and maximum convenience. there are quite a few use-cases for this, and im not really going to go into detail here, but trust me, it's a pretty cool idea.
and so after a few weeks of work, i made a thing: [play.mtcute.dev](https://play.mtcute.dev)!<br/>
and while building it, i encountered *quite a few* issues that i want to share
## mtcute is a library
and as such, we need to somehow make it available to the user's code.
something like [`https://esm.sh`](https://esm.sh) would probably work, but i *really* don't trust such services. and hosting something like this myself would defeat the entire point of it being fully in-browser.
so i decided to go with a different approach.
instead of using a cdn, i download the library directly from `registry.npmjs.org` (along with all its dependencies), untar and save them to indexeddb. and then... well, we need to somehow run it.
> one might ask how is npm better than esm.sh in terms of security, and to that i have no definite answer,
> but like, if npm starts serving malicious code half the internet will be screwed anyway
initially i wanted to simply go with something like `esbuild-wasm` with user's code as an entrypoint, bundle everything into a single file and then just run that file as a web worker.
except esbuild-wasm weighs about **11mb** 🥴
not even considering the bundling performance overhead, that's a lot of wasted bandwidth. surely there's a better way?
### import maps
a good friend of mine (s/o [@kamillaova](https://github.com/kamillaova)) reminded me about import maps.
in case you haven't heard of them, it's a [recent addition](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script/type/importmap) to the web platform,
allowing you to map esm imports to *somewhere*.
a simple `esm.sh` sourcemap for mtcute would look something like this:
```html
<script type="importmap">
{
"imports": {
"@mtcute/web": "https://esm.sh/@mtcute/web",
}
}
</script>
```
the issues were immediately obvious, however:
1. the library must be available by url
2. dynamic import maps are [not invented yet](https://github.com/WICG/import-maps/issues/92),
meaning we can't add an import map at runtime
3. import maps are [not supported by web workers](https://github.com/WICG/import-maps/issues/2)
but still, import maps would allow us to avoid bundling *at all*!
and the issues above *could probably be worked around*.<br/>
so i still decided to give it a try.
### importing by url
to make the library available by url without any external backend, we can just serve it from a service worker.
service workers allow intercepting all requests on our origin, and they also have full access
to indexeddb (where we store the downloaded code).<br/>
so serving the library from a service worker is as simple as:
```js
globalThis.addEventListener('fetch', (event) => {
if (event.request.url.startsWith('/sw/runtime/')) {
event.respondWith(serveFromIdb(event.request.url))
}
})
```
and then we can just use that url in the import map:
```html
<script type="importmap">
{
"imports": {
"@mtcute/web": "/sw/runtime/@mtcute/web/index.js",
...
}
}
</script>
```
> note: since mtcute and all its deps use npm-s `package.json`, when generating the import map we
> need to keep in mind the respective `exports` fields (and also `module/main/browser` god damnit 🤮)
### dynamic import maps
the remaining issues are very much linked together we need to provide import maps at runtime.
since we can't use a web worker, let's use an iframe instead!
and since we can't add import maps at runtime, we can just generate the entire html page on the fly:
```js
const html = `
<html>
<script type="importmap">${generateImportMap()}</script>
...some more html idk...
</html>
`
const url = URL.createObjectURL(new Blob([html], { type: 'text/html' }))
const iframe = document.createElement('iframe')
iframe.src = url
document.body.appendChild(iframe)
```
except... **[BAM](https://crbug.com/880768)!**
due to this chrome bug, our generated iframe won't be able to load the libraries
from our service worker, because it's not considered the *same origin*
no biggie, let's just serve the html directly from our service worker:
```js
globalThis.addEventListener('fetch', (event) => {
if (event.request.url === '/sw/runtime/_iframe.html') {
event.respondWith(generateIframeHtml())
}
})
```
and at that point, everything *seemed* to work??
## threading in browsers
as you probably know, javascript at its core is single-threaded. this is useful in most cases, however can be quite annoying in some others. especially for this particular one a repl.
in case a user ends up writing a computation-heavy task (or just accidentally do a `while (true) {}`),
the entire page will **freeze and potentially crash**. and it is something i specifically don't want in our case.
an obvious solution just run the user-provided code in a web worker..?
except we can't (see above).
<p class="thought">
but wait. we are already running the user's code in an iframe! <br/>
isn't it already a separate thread?
</p>
<span class="shout">NO</span><br/>
i used to think that it would run in a separate thread too, honestly.
but when i actually tried to run a `while (true) {}` **in an iframe**, it froze the entire page, not just the iframe.
the same issue can be seen in many in-browser playgrounds out there, like [solid.js one](https://playground.solidjs.com/),
[vue.js one](https://play.vuejs.org), and probably more...
### but why?
> this section is very much a *probably*, i didn't do a lot of research on this, but this sounds reasonable enough
afaiu, the fact that it's run on the same thread is primarily due to the `contentWindow` api.
see, when the iframe is same-origin, we can actually access its DOM from the parent window:
```js
const iframe = document.querySelector('iframe')
iframe.contentWindow.document.body.innerHTML = 'meow'
```
and because of that, browsers have to share the same javascript runtime thread between the iframe and the parent window.
for cross-origin iframes, however, the only way of communication is via `postMessage` (similar to web workers!),
and as such the browser can create a separate thread for it.
### what even is a cross-origin iframe?
i found [this post](https://webperf.tips/tip/iframe-multi-process/) that explains the issue in detail,
as well as providing *some pointers* on how to work around it.
tldr this is very much implementation-specific. but usually, the iframe is considered a *cross-origin*
if the `etld+1` of the parent and the child are different.
> etld is the [effective top-level domain](https://developer.mozilla.org/en-US/docs/Glossary/eTLD).<br/>
> (dont worry, i haven't heard this term before either)
>
> example.com and very.example.com have the same etld+1 (`example.com`), but example.com and example.co.uk don't.
one way we can force the browser to isolate the iframe is using the `sandbox` attribute:
```html
<iframe sandbox="allow-scripts"></iframe>
```
but... well, it's no longer cross-origin :D
and that's an issue!<br />
because we can no longer access our service worker from the iframe and load the libraries from it.
## what do we do?
so basically to make things work the way i intended, i would need some kind of `sandbox` attribute that would
isolate the javascript runtime thread (and disable the `contentWindow` api that i dont even use anyway),
while the frame is still considered same-origin.
and browsers don't have anything like that!! :<
at this point i basically had two options:
- give up on trying to separate the worker into a separate thread
- make an actual **cross-origin** iframe where most of the work would happen,
and our "main" window would just be a frontend talking to it via `postMessage`
### the great separation
i went with the latter because i really wanted to make my repl resilient to broken user code.
> <span class="big">important!</span>
>
> having two origins might be a security concern! what if a bad actor embeds my "worker" iframe in their website
> and steals everything?
>
> i had to be extra careful to verify the origin of every embedded message, to make sure it's our "frontend" talking
> to our "worker" iframe, and not some malicious website.
at this point i already had like 90% of the project finished, so refactoring everything to two-origin
architecture took some effort, but it was definitely worth it.
i had to move the following to the "worker" iframe:
- authorization and session management
- library downloading
- the service worker, along with iframe html generation
and the overall architecture ended up looking something like this:
<!-- i hate graphviz -->
```dot
digraph G {
subgraph cluster_worker {
label = "worker origin";
labeljust = "r";
sw[label="service worker",shape=rect,style=dashed]
worker[label = "worker iframe",shape=rect]
runner[label = "runner iframe",shape=cds]
idb[label = "indexeddb",shape=cylinder]
spacer[label = "",shape=rect,style=invis,fixedsize=true,width=2]
worker -> sw [label=" talks"]
sw -> runner [label=" serves"]
sw -> idb [label="stores libs",constraint=false]
runner -> idb [label="stores sessions",constraint=false]
}
frontend
frontend -> worker [dir="both",label="embeds + talks",constraint=false]
frontend -> runner [dir="both",label=" embeds + talks ",constraint=false]
}
```
- **frontend** is the actual app the user interacts with. just a normal frontend app, but instead of some rest api, it uses `postMessage` to talk to...
- **worker iframe** which implements most of the business logic, as well as manages the service worker and storage
- **service worker** is used to serve the library code, as well as the...
- **runner iframe** which is the "sandbox" in which the user's code is actually run
phew. that's some enterprise-grade backend architecture, right in your browser!<br/>
i really hope that one day browsers will make this kind of stuff easier to implement 🙏
### cross-origin is not the silver bullet, actually
at this point everything was working fine... in chrome :D
as soon as i opened the page in firefox, i was greeted with an incredibly helpful "The operation is insecure"
after some digging, it turned out that firefox has something called [state partitioning](https://developer.mozilla.org/en-US/docs/Web/Privacy/State_Partitioning)
tldr: normally browsers always keyed websites' data by their origin. but trackers can (and do! ~~*cant have shit in this economy*~~) abuse this to track users across websites.
a simple example of that would be:
```js
// https://tracking.tei.su/get-user-id.html
globalThis.addEventListener('message', (event) => {
if (!localStorage.userId) localStorage.userId = crypto.randomUUID()
event.source.postMessage(localStorage.userId)
})
// which can then be used to track users across websites by simply doing:
const iframe = document.createElement('iframe')
iframe.src = 'https://tracking.tei.su/get-user-id.html'
document.body.appendChild(iframe)
iframe.addEventListener('message', (event) => {
console.log('you are %s!', event.data)
})
```
to avoid this, firefox keys the data by a combination of the iframe's origin and the top window's origin.
this way, the above code would return different results for different websites.
<p class="thought">
this... doesn't really sound like an issue for our case though?
</p>
ikr?? we barely store anything outside of the worker's origin, and only access our worker from a single origin.
but for **WHATEVER REASON** (likely due to some bug in firefox) our runner iframe *seemed* to have a separate service worker from the worker iframe. and a separate indexeddb. and only in some cases. 🥴
some stuff did seemingly get fixed by simply updating firefox to the latest version, and some other stuff i had to refactor from the worker iframe to the runner iframe. ugh, so annoying.
...
i have no idea how people even write outros so uhh<br/>
thanks for reading this rambling of a post i guess?
ok bye

View file

@ -0,0 +1,38 @@
---
date: '1970-01-01'
title: 'test post'
description: 'sample post to test markdown rendering'
---
## sub title
### sub sub title
#### sub sub sub title
##### sub sub sub sub title
###### sub sub sub sub sub title
> test quote
some text **bold** and _italic_ and `code` and also ~~strikethrough~~ as well as <u>underline</u>
- list item
- list item
- list item
- sub list item
- sub list item
1. list item
2. list item
3. list item
1. sub list item
2. sub list item
| table header | table header |
| ------------ | ------------ |
| table cell | table cell |
| table cell | table cell |
```js
const a = 1
```
a [link](https://github.com/teidesu/tei.su)

View file

@ -86,6 +86,6 @@ const finalOg = { ...defaultOgTags, ...og }
} }
body { body {
@apply text-sm m-0 p-0 overflow-x-hidden overflow-y-auto bg-bg font-mono text-text-primary; @apply text-sm m-0 p-0 overflow-x-hidden overflow-y-auto bg-bg font-mono text-text-primary min-h-screen;
} }
</style> </style>

View file

@ -7,11 +7,12 @@ import Header from './Header.astro'
<BaseLayout {...Astro.props}> <BaseLayout {...Astro.props}>
<slot name="head" slot="head" /> <slot name="head" slot="head" />
<div class="app"> <div class="min-h-screen flex flex-col items-center">
<div class="content"> <div class="min-h-screen w-full flex flex-col gap-6 p-6 md:w-720px">
<Header /> <Header />
<slot /> <slot />
<footer class="footer"> <div class="flex-1" />
<footer class="mx-4 border-t border-text-secondary pt-2 text-center text-2xs text-text-secondary">
<div> <div>
&lt;3 teidesu &lt;3 teidesu
{' / '} {' / '}
@ -32,37 +33,3 @@ import Header from './Header.astro'
</div> </div>
</div> </div>
</BaseLayout> </BaseLayout>
<style>
@import '../../components/shared.css';
.app {
display: flex;
justify-content: center;
}
.content {
display: flex;
flex-direction: column;
gap: 1.5em;
overflow: hidden;
width: 900px;
padding: 24px;
@media (max-width: 900px) {
width: 720px;
}
@media (--tablet) {
width: 100%;
}
}
.footer {
border-top: 1px solid var(--text-secondary);
padding-top: 8px;
margin-inline: 16;
text-align: center;
color: var(--text-secondary);
@mixin font-2xs;
}
</style>

View file

@ -3,8 +3,9 @@ import karin from '~/assets/karin.gif'
import { Link } from '../../components/ui/Link.tsx' import { Link } from '../../components/ui/Link.tsx'
const PAGES = [ const PAGES = [
{ name: 'hewwo', path: '/' }, { name: 'hewwo', path: '/', match: /^\/$/ },
{ name: 'donate', path: ['/donate', '/$'] }, { name: 'blog', path: '/blog', match: /^\/blog(\/|$)/ },
{ name: 'donate', path: '/donate', match: /^\/(donate|\$)\/?$/ },
] ]
--- ---
<header class="pos-relative flex items-center justify-center gap-2"> <header class="pos-relative flex items-center justify-center gap-2">
@ -24,16 +25,11 @@ const PAGES = [
) )
} }
let isActive const isActive = page.match.test(Astro.url.pathname)
if (Array.isArray(page.path)) {
isActive = page.path.includes(Astro.url.pathname)
} else {
isActive = Astro.url.pathname === page.path
}
if (isActive) { if (isActive) {
elements.push( elements.push(
<span class="font-bold">{page.name}</span>, <a href={page.path} class="font-bold">{page.name}</a>,
) )
} else { } else {
const href = Array.isArray(page.path) ? page.path[0] : page.path const href = Array.isArray(page.path) ? page.path[0] : page.path

176
src/pages/blog/[slug].astro Normal file
View file

@ -0,0 +1,176 @@
---
import { getCollection, render } from 'astro:content'
import DefaultLayout from '../../layouts/DefaultLayout/DefaultLayout.astro'
export const prerender = true
export async function getStaticPaths() {
const posts = await getCollection('blog')
return posts.map(post => ({
params: { slug: post.id },
props: { post },
}))
}
const { post } = Astro.props
const { Content, remarkPluginFrontmatter } = await render(post);
---
<DefaultLayout
title={post.data.title}
og={{
title: post.data.title,
description: post.data.description,
date: post.data.date.toISOString(),
}}
>
<div class="flex flex-col gap-4">
<div class="flex flex-col gap-1 border-b border-text-secondary pb-2">
<div class="text-2xl font-bold">{post.data.title}</div>
<p class="text-xs text-text-secondary">
{post.data.date.toLocaleString('en-US', {
month: 'long',
day: 'numeric',
year: 'numeric',
}).toLowerCase()}
//
{remarkPluginFrontmatter.minutesRead}
</p>
</div>
<div data-md-content>
<Content />
</div>
<script>
document.querySelectorAll('h1, h2, h3, h4, h5, h6').forEach((el) => {
const anchor = document.createElement('a')
anchor.setAttribute('data-anchor-link', '')
anchor.setAttribute('href', `#${el.id}`)
anchor.textContent = '#'
el.appendChild(anchor)
})
</script>
</div>
</DefaultLayout>
<style is:global>
[data-md-content] {
--text-content: var(--text-primary);
@media (prefers-color-scheme: dark) {
/* our default primary text color is not suitable for large bodies of text */
--text-content: #bea7b0;
.astro-code,
.astro-code span {
color: var(--shiki-dark) !important;
background-color: var(--control-bg) !important;
/* Optional, if you also want font styles */
font-style: var(--shiki-dark-font-style) !important;
font-weight: var(--shiki-dark-font-weight) !important;
text-decoration: var(--shiki-dark-text-decoration) !important;
}
}
color: var(--text-content);
h1 { @apply text-4xl font-bold mb-4; }
h2 { @apply text-3xl font-bold mb-3 mt-3; }
h3 { @apply text-2xl font-bold mb-2 mt-2; }
h4 { @apply text-xl font-bold mb-2; }
h5 { @apply text-lg font-bold mb-2; }
h6 { @apply text-md font-bold mb-2; }
.big { @apply text-2xl font-bold; }
.shout { @apply text-4xl font-bold; }
.thought {
&::before {
content: '💭';
@apply text-4xl font-bold;
}
@apply p-4 mb-4 bg-control-bg-hover rounded-lg flex flex-row gap-4 items-center;
}
h1, h2, h3, h4, h5, h6 {
@apply relative text-text-primary cursor-pointer -ml-1em pl-1em;
[data-anchor-link] {
@apply hidden absolute left-0 text-text-secondary no-underline hover:underline;
}
&:hover [data-anchor-link] {
@apply inline;
}
}
p {
@apply text-sm mb-5;
line-height: 1.5;
}
ul { @apply list-disc list-outside mb-4 ml-4; }
ol { @apply mb-4 list-decimal list-outside ml-2em; }
li {
@apply text-sm mt-1;
line-height: 1.5;
}
code {
@apply text-sm bg-control-bg-hover px-1 rounded-md;
color: var(--text-content);
}
blockquote {
@apply border-l-4 border-text-secondary pl-4 mb-4 py-2;
p { @apply text-sm mb-0; }
p + p { @apply mt-4; }
}
pre {
@apply bg-control-bg-hover text-text-secondary p-2 rounded-md mb-4;
code { @apply bg-transparent; }
}
table {
@apply border-collapse border-solid border-text-secondary border-spacing-0 mb-2;
th, td {
@apply border border-text-secondary p-1;
}
th {
@apply font-bold text-left;
}
td {
@apply text-right;
}
}
a {
@apply text-text-accent underline hover:no-underline;
}
.graphviz-svg {
@apply w-full flex justify-center mb-4;
svg {
@apply border border-text-secondary rounded-md
}
.graph {
text {
@apply font-mono;
fill: var(--text-content);
font-size: 10px;
line-height: 1;
}
path, ellipse, polygon { @apply stroke-text-secondary; }
> polygon {
@apply fill-transparent stroke-none;
}
}
}
}
</style>

View file

@ -0,0 +1,40 @@
---
import { parallelMap } from '@fuman/utils'
import { getCollection, render } from 'astro:content'
import { Link } from '../../components/ui/Link.tsx'
import { sortPostsByDateReverse } from '../../content.config.ts'
import DefaultLayout from '../../layouts/DefaultLayout/DefaultLayout.astro'
export const prerender = true
const posts = (await getCollection('blog')).sort(sortPostsByDateReverse)
const postsRendered = await parallelMap(posts, async post => ({
post,
rendered: await render(post),
}))
---
<DefaultLayout title="alina's silly little blog">
{postsRendered.map(({ post, rendered }) => (
<article class="flex flex-col">
<Link href={`/blog/${post.id}`} class="mb-1 w-max text-lg font-bold">
{post.data.title}
</Link>
<p class="mb-2 text-xs text-text-secondary">
<time datetime={post.data.date.toISOString()}>
{post.data.date.toLocaleString('en-US', {
month: 'long',
day: 'numeric',
year: 'numeric',
}).toLowerCase()}
</time>
//
{rendered.remarkPluginFrontmatter.minutesRead}
</p>
<p class="text-sm text-text-primary">
{post.data.description}
</p>
</article>
))}
</DefaultLayout>

View file

@ -1,10 +1,13 @@
import { defineConfig, presetIcons, presetUno } from 'unocss' import { defineConfig, presetIcons, presetUno, transformerVariantGroup } from 'unocss'
export default defineConfig({ export default defineConfig({
presets: [ presets: [
presetUno(), presetUno(),
presetIcons(), presetIcons(),
], ],
transformers: [
transformerVariantGroup(),
],
shortcuts: { shortcuts: {
'content-dblslash': [ 'content-dblslash': [
{ content: '"//"' }, { content: '"//"' },