From f89d32d6cec37d11f84074b912c2ebfae1837466 Mon Sep 17 00:00:00 2001 From: Atridad Lahiji Date: Thu, 26 Jun 2025 16:06:15 -0600 Subject: [PATCH] Overhauled resume system --- package.json | 8 +- pnpm-lock.yaml | 595 +++++++++++++++++++++++++++ public/files/resume.json | 483 ---------------------- public/files/resume.toml | 174 ++++++++ src/components/PdfDownloadButton.tsx | 77 ++++ src/config/data.ts | 2 +- src/pages/api/resume/pdf.ts | 321 +++++++++++++++ src/pages/resume.astro | 419 +++++++++---------- 8 files changed, 1374 insertions(+), 705 deletions(-) delete mode 100644 public/files/resume.json create mode 100644 public/files/resume.toml create mode 100644 src/components/PdfDownloadButton.tsx create mode 100644 src/pages/api/resume/pdf.ts diff --git a/package.json b/package.json index ea0b18c..b6afc0f 100644 --- a/package.json +++ b/package.json @@ -13,13 +13,16 @@ "@astrojs/node": "^9.2.2", "@astrojs/preact": "^4.1.0", "@astrojs/rss": "^4.0.12", + "@iarna/toml": "^2.2.5", "@preact/signals": "^2.2.0", "@tailwindcss/typography": "^0.5.16", "@tailwindcss/vite": "^4.1.10", + "@types/puppeteer": "^7.0.4", "astro": "^5.10.0", "astro-icon": "^1.1.5", "lucide-preact": "^0.518.0", "preact": "^10.26.9", + "puppeteer": "^24.10.2", "sharp": "^0.34.2", "tailwindcss": "^4.1.10" }, @@ -31,7 +34,8 @@ "pnpm": { "onlyBuiltDependencies": [ "esbuild", - "sharp" + "sharp", + "puppeteer" ] } -} \ No newline at end of file +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d2bb74a..0b4ad58 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -20,6 +20,9 @@ importers: '@astrojs/rss': specifier: ^4.0.12 version: 4.0.12 + '@iarna/toml': + specifier: ^2.2.5 + version: 2.2.5 '@preact/signals': specifier: ^2.2.0 version: 2.2.0(preact@10.26.9) @@ -29,6 +32,9 @@ importers: '@tailwindcss/vite': specifier: ^4.1.10 version: 4.1.10(vite@6.3.5(@types/node@24.0.3)(jiti@2.4.2)(lightningcss@1.30.1)) + '@types/puppeteer': + specifier: ^7.0.4 + version: 7.0.4(typescript@5.8.3) astro: specifier: ^5.10.0 version: 5.10.0(@types/node@24.0.3)(jiti@2.4.2)(lightningcss@1.30.1)(rollup@4.44.0)(typescript@5.8.3) @@ -41,6 +47,9 @@ importers: preact: specifier: ^10.26.9 version: 10.26.9 + puppeteer: + specifier: ^24.10.2 + version: 24.10.2(typescript@5.8.3) sharp: specifier: ^0.34.2 version: 0.34.2 @@ -352,6 +361,9 @@ packages: cpu: [x64] os: [win32] + '@iarna/toml@2.2.5': + resolution: {integrity: sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg==} + '@iconify-json/mdi@1.2.3': resolution: {integrity: sha512-O3cLwbDOK7NNDf2ihaQOH5F9JglnulNDFV7WprU2dSoZu3h3cWH//h74uQAB87brHmvFVxIOkuBX2sZSzYhScg==} @@ -647,6 +659,11 @@ packages: preact: ^10.4.0 vite: '>=2.0.0' + '@puppeteer/browsers@2.10.5': + resolution: {integrity: sha512-eifa0o+i8dERnngJwKrfp3dEq7ia5XFyoqB17S4gK8GhsQE4/P8nxOfQSE0zQHxzzLo/cmF+7+ywEQ7wK7Fb+w==} + engines: {node: '>=18'} + hasBin: true + '@rollup/pluginutils@4.2.1': resolution: {integrity: sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ==} engines: {node: '>= 8.0.0'} @@ -879,6 +896,9 @@ packages: peerDependencies: vite: ^5.2.0 || ^6 + '@tootallnate/quickjs-emscripten@0.23.0': + resolution: {integrity: sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==} + '@trysound/sax@0.2.0': resolution: {integrity: sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==} engines: {node: '>=10.13.0'} @@ -913,6 +933,10 @@ packages: '@types/node@24.0.3': resolution: {integrity: sha512-R4I/kzCYAdRLzfiCabn9hxWfbuHS573x+r0dJMkkzThEa7pbrcDWK+9zu3e7aBOouf+rQAciqPFMnxwr0aWgKg==} + '@types/puppeteer@7.0.4': + resolution: {integrity: sha512-ja78vquZc8y+GM2al07GZqWDKQskQXygCDiu0e3uO0DMRKqE0MjrFBFmTulfPYzLB6WnL7Kl2tFPy0WXSpPomg==} + deprecated: This is a stub types definition. puppeteer provides its own type definitions, so you do not need this installed. + '@types/tar@6.1.13': resolution: {integrity: sha512-IznnlmU5f4WcGTh2ltRu/Ijpmk8wiWXfF0VA4s+HPjHZgvFggk1YaIkbo5krX/zUCzWF8N/l4+W/LNxnvAJ8nw==} @@ -938,6 +962,10 @@ packages: engines: {node: '>=0.4.0'} hasBin: true + agent-base@7.1.3: + resolution: {integrity: sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==} + engines: {node: '>= 14'} + ansi-align@3.0.1: resolution: {integrity: sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==} @@ -949,6 +977,10 @@ packages: resolution: {integrity: sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==} engines: {node: '>=12'} + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + ansi-styles@6.2.1: resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} engines: {node: '>=12'} @@ -967,6 +999,10 @@ packages: array-iterate@2.0.1: resolution: {integrity: sha512-I1jXZMjAgCMmxT4qxXfPXa6SthSoE8h6gkSI9BGGNv8mP8G/v0blc+qFnZu6K42vTOiuME596QaLO0TP3Lk0xg==} + ast-types@0.13.4: + resolution: {integrity: sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==} + engines: {node: '>=4'} + astring@1.9.0: resolution: {integrity: sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg==} hasBin: true @@ -989,6 +1025,9 @@ packages: resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==} engines: {node: '>= 0.4'} + b4a@1.6.7: + resolution: {integrity: sha512-OnAYlL5b7LEkALw87fUVafQw5rVR9RjwGd4KUwNQ6DrrNmaVaUCgLipfVlzrPQ4tWOR9P0IXGNOx50jYCCdSJg==} + babel-plugin-transform-hook-names@1.0.2: resolution: {integrity: sha512-5gafyjyyBTTdX/tQQ0hRgu4AhNHG/hqWi0ZZmg2xvs2FgRkJXzDNKBZCyoYqgFkovfDrgM8OoKg8karoUvWeCw==} peerDependencies: @@ -997,12 +1036,46 @@ packages: bail@2.0.2: resolution: {integrity: sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==} + bare-events@2.5.4: + resolution: {integrity: sha512-+gFfDkR8pj4/TrWCGUGWmJIkBwuxPS5F+a5yWjOHQt2hHvNZd5YLzadjmDUtFmMM4y429bnKLa8bYBMHcYdnQA==} + + bare-fs@4.1.5: + resolution: {integrity: sha512-1zccWBMypln0jEE05LzZt+V/8y8AQsQQqxtklqaIyg5nu6OAYFhZxPXinJTSG+kU5qyNmeLgcn9AW7eHiCHVLA==} + engines: {bare: '>=1.16.0'} + peerDependencies: + bare-buffer: '*' + peerDependenciesMeta: + bare-buffer: + optional: true + + bare-os@3.6.1: + resolution: {integrity: sha512-uaIjxokhFidJP+bmmvKSgiMzj2sV5GPHaZVAIktcxcpCyBFFWO+YlikVAdhmUo2vYFvFhOXIAlldqV29L8126g==} + engines: {bare: '>=1.14.0'} + + bare-path@3.0.0: + resolution: {integrity: sha512-tyfW2cQcB5NN8Saijrhqn0Zh7AnFNsnczRcuWODH0eYAXBsJ5gVxAUuNr7tsHSC6IZ77cA0SitzT+s47kot8Mw==} + + bare-stream@2.6.5: + resolution: {integrity: sha512-jSmxKJNJmHySi6hC42zlZnq00rga4jjxcgNZjY9N5WlOe/iOoGRtdwGsHzQv2RlH2KOYMwGUXhf2zXd32BA9RA==} + peerDependencies: + bare-buffer: '*' + bare-events: '*' + peerDependenciesMeta: + bare-buffer: + optional: true + bare-events: + optional: true + base-64@1.0.0: resolution: {integrity: sha512-kwDPIFCGx0NZHog36dj+tHiwP4QMzsZ3AgMViUBKI0+V5n4U0ufTCUMhnQ04diaRI8EX/QcPfql7zlhZ7j4zgg==} base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + basic-ftp@5.0.5: + resolution: {integrity: sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg==} + engines: {node: '>=10.0.0'} + blob-to-buffer@1.2.9: resolution: {integrity: sha512-BF033y5fN6OCofD3vgHmNtwZWRcq9NLyyxyILx9hfMy1sXYy4ojFl765hJ2lP0YaN2fuxPaLO2Vzzoxy0FLFFA==} @@ -1028,6 +1101,10 @@ packages: resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} engines: {node: '>= 0.4'} + callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + camelcase@8.0.0: resolution: {integrity: sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA==} engines: {node: '>=16'} @@ -1073,6 +1150,11 @@ packages: resolution: {integrity: sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==} engines: {node: '>=18'} + chromium-bidi@5.1.0: + resolution: {integrity: sha512-9MSRhWRVoRPDG0TgzkHrshFSJJNZzfY5UFqUMuksg7zL1yoZIZ3jLB0YAgHclbiAxPI86pBnwDX1tbzoiV8aFw==} + peerDependencies: + devtools-protocol: '*' + ci-info@4.2.0: resolution: {integrity: sha512-cYY9mypksY8NRqgDB1XD1RiJL338v/551niynFTGkZOO2LHuB2OmOYxDIe/ttN9AHwrqdum1360G3ald0W9kCg==} engines: {node: '>=8'} @@ -1081,6 +1163,10 @@ packages: resolution: {integrity: sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==} engines: {node: '>=10'} + cliui@8.0.1: + resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} + engines: {node: '>=12'} + clone@2.1.2: resolution: {integrity: sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==} engines: {node: '>=0.8'} @@ -1136,6 +1222,15 @@ packages: resolution: {integrity: sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==} engines: {node: '>=18'} + cosmiconfig@9.0.0: + resolution: {integrity: sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==} + engines: {node: '>=14'} + peerDependencies: + typescript: '>=4.9.5' + peerDependenciesMeta: + typescript: + optional: true + cross-fetch@3.2.0: resolution: {integrity: sha512-Q+xVJLoGOeIMXZmbUK4HYk+69cQH6LudR0Vu/pRm2YlU/hDV9CiS0gKUMaWY5f2NeUH9C1nV3bsTlCo0FsTV1Q==} @@ -1173,6 +1268,10 @@ packages: daisyui@5.0.43: resolution: {integrity: sha512-2pshHJ73vetSpsbAyaOncGnNYL0mwvgseS1EWy1I9Qpw8D11OuBoDNIWrPIME4UFcq2xuff3A9x+eXbuFR9fUQ==} + data-uri-to-buffer@6.0.2: + resolution: {integrity: sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==} + engines: {node: '>= 14'} + debug@4.4.1: resolution: {integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==} engines: {node: '>=6.0'} @@ -1188,6 +1287,10 @@ packages: defu@6.1.4: resolution: {integrity: sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==} + degenerator@5.0.1: + resolution: {integrity: sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==} + engines: {node: '>= 14'} + delayed-stream@1.0.0: resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} engines: {node: '>=0.4.0'} @@ -1217,6 +1320,9 @@ packages: devlop@1.1.0: resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} + devtools-protocol@0.0.1452169: + resolution: {integrity: sha512-FOFDVMGrAUNp0dDKsAU1TorWJUx2JOU1k9xdgBKKJF3IBh/Uhl2yswG5r3TEAOrCiGY2QRp1e6LVDQrCsTKO4g==} + dfa@1.2.0: resolution: {integrity: sha512-ED3jP8saaweFTjeGX8HQPjeC1YYyZs98jGNZx6IiBvxW7JG5v492kamAQB3m2wop07CvU/RQmzcKr6bgcC5D/Q==} @@ -1282,6 +1388,13 @@ packages: resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==} engines: {node: '>=0.12'} + env-paths@2.2.1: + resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} + engines: {node: '>=6'} + + error-ex@1.3.2: + resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} + es-define-property@1.0.1: resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} engines: {node: '>= 0.4'} @@ -1323,6 +1436,20 @@ packages: resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==} engines: {node: '>=12'} + escodegen@2.1.0: + resolution: {integrity: sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==} + engines: {node: '>=6.0'} + hasBin: true + + esprima@4.0.1: + resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} + engines: {node: '>=4'} + hasBin: true + + estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + estree-util-attach-comments@3.0.0: resolution: {integrity: sha512-cKUwm/HUcTDsYh/9FgnuFqpfquUbwIqwKM26BVCGDPVgvaCl/nDCCjUfiLlx6lsEZ3Z4RFxNbOQ60pkaEwFxGw==} @@ -1347,6 +1474,10 @@ packages: estree-walker@3.0.3: resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + etag@1.8.1: resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} engines: {node: '>= 0.6'} @@ -1368,6 +1499,9 @@ packages: fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + fast-fifo@1.3.2: + resolution: {integrity: sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==} + fast-xml-parser@5.2.5: resolution: {integrity: sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ==} hasBin: true @@ -1426,6 +1560,10 @@ packages: resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} engines: {node: '>=6.9.0'} + get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + get-east-asian-width@1.3.0: resolution: {integrity: sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ==} engines: {node: '>=18'} @@ -1442,6 +1580,10 @@ packages: resolution: {integrity: sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==} engines: {node: '>=8'} + get-uri@6.0.4: + resolution: {integrity: sha512-E1b1lFFLvLgak2whF2xDBcOy6NLVGZBqqjJjsIhvopKfWWEi64pLVTWWehV8KlLerZkfNTA95sTe2OdJKm1OzQ==} + engines: {node: '>= 14'} + github-slugger@2.0.0: resolution: {integrity: sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw==} @@ -1531,10 +1673,22 @@ packages: resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} engines: {node: '>= 0.8'} + http-proxy-agent@7.0.2: + resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==} + engines: {node: '>= 14'} + + https-proxy-agent@7.0.6: + resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} + engines: {node: '>= 14'} + iconv-lite@0.6.3: resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} engines: {node: '>=0.10.0'} + import-fresh@3.3.1: + resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} + engines: {node: '>=6'} + import-meta-resolve@4.1.0: resolution: {integrity: sha512-I6fiaX09Xivtk+THaMfAwnA3MVA5Big1WHF1Dfx9hFuvNIWpXnorlkzhcQf6ehrqQiiZECRt1poOAkPmer3ruw==} @@ -1544,6 +1698,10 @@ packages: inline-style-parser@0.2.4: resolution: {integrity: sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q==} + ip-address@9.0.5: + resolution: {integrity: sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==} + engines: {node: '>= 12'} + iron-webcrypto@1.2.1: resolution: {integrity: sha512-feOM6FaSr6rEABp/eDfVseKyTMDt+KGpeB35SkVn9Tyn0CqvVsY3EwI0v5i8nMHyJnzCIQf7nsy3p41TPkJZhg==} @@ -1553,6 +1711,9 @@ packages: is-alphanumerical@2.0.1: resolution: {integrity: sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==} + is-arrayish@0.2.1: + resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} + is-arrayish@0.3.2: resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==} @@ -1595,11 +1756,17 @@ packages: resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} hasBin: true + jsbn@1.1.0: + resolution: {integrity: sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==} + jsesc@3.1.0: resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} engines: {node: '>=6'} hasBin: true + json-parse-even-better-errors@2.3.1: + resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + json5@2.2.3: resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} engines: {node: '>=6'} @@ -1680,6 +1847,9 @@ packages: resolution: {integrity: sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg==} engines: {node: '>= 12.0.0'} + lines-and-columns@1.2.4: + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + local-pkg@0.5.1: resolution: {integrity: sha512-9rrA30MRRP3gBD3HTGnC6cDFpaE1kVDWxWgqWJUN0RvDNAo+Nz/9GxB+nHOH0ifbVFy0hSA1V6vFDvnx54lTEQ==} engines: {node: '>=14'} @@ -1706,6 +1876,10 @@ packages: lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + lru-cache@7.18.3: + resolution: {integrity: sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==} + engines: {node: '>=12'} + lucide-preact@0.518.0: resolution: {integrity: sha512-aMMvKFAVI/xhGXjZUMUwEc2fsVQViHcmrXQ1CvTCv4KHUaC0RCb/v61ztMiEWJqdvDxvP/39FO8DRfvRn4oHgg==} peerDependencies: @@ -1933,6 +2107,9 @@ packages: resolution: {integrity: sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA==} engines: {node: '>= 18'} + mitt@3.0.1: + resolution: {integrity: sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==} + mkdirp@1.0.4: resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} engines: {node: '>=10'} @@ -1962,6 +2139,10 @@ packages: resolution: {integrity: sha512-Z4SmBUweYa09+o6pG+eASabEpP6QkQ70yHj351pQoEXIs8uHbaU2DWVmzBANKgflPa47A50PtB2+NgRpQvr7vA==} engines: {node: '>= 10'} + netmask@2.0.2: + resolution: {integrity: sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==} + engines: {node: '>= 0.4.0'} + nlcst-to-string@4.0.0: resolution: {integrity: sha512-YKLBCcUYKAg0FNlOBT6aI91qFmSiFKiluk655WzPF+DDMA02qIyy8uiRqI8QXtcFpEvll12LpL5MXqEmAZ+dcA==} @@ -2024,15 +2205,31 @@ packages: resolution: {integrity: sha512-MyIV3ZA/PmyBN/ud8vV9XzwTrNtR4jFrObymZYnZqMmW0zA8Z17vnT0rBgFE/TlohB+YCHqXMgZzb3Csp49vqg==} engines: {node: '>=14.16'} + pac-proxy-agent@7.2.0: + resolution: {integrity: sha512-TEB8ESquiLMc0lV8vcd5Ql/JAKAoyzHFXaStwjkzpOpC5Yv+pIzLfHvjTSdf3vpa2bMiUQrg9i6276yn8666aA==} + engines: {node: '>= 14'} + + pac-resolver@7.0.1: + resolution: {integrity: sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==} + engines: {node: '>= 14'} + package-manager-detector@1.3.0: resolution: {integrity: sha512-ZsEbbZORsyHuO00lY1kV3/t72yp6Ysay6Pd17ZAlNGuGwmWDLCJxFpRs0IzfXfj1o4icJOkUEioexFHzyPurSQ==} pako@0.2.9: resolution: {integrity: sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA==} + parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + parse-entities@4.0.2: resolution: {integrity: sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==} + parse-json@5.2.0: + resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} + engines: {node: '>=8'} + parse-latin@7.0.0: resolution: {integrity: sha512-mhHgobPPua5kZ98EF4HWiH167JWBfl4pvAIXXdbaVohtK7a6YBOy56kvhCqduqyo/f3yrHFWmqmiMg/BkBkYYQ==} @@ -2091,6 +2288,10 @@ packages: resolution: {integrity: sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==} engines: {node: '>=6'} + progress@2.0.3: + resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==} + engines: {node: '>=0.4.0'} + prompts@2.4.2: resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==} engines: {node: '>= 6'} @@ -2101,12 +2302,25 @@ packages: property-information@7.1.0: resolution: {integrity: sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==} + proxy-agent@6.5.0: + resolution: {integrity: sha512-TmatMXdr2KlRiA2CyDu8GqR8EjahTG3aY3nXjdzFyoZbmB8hrBsTyMezhULIXKnC0jpfjlmiZ3+EaCzoInSu/A==} + engines: {node: '>= 14'} + proxy-from-env@1.1.0: resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} pump@3.0.3: resolution: {integrity: sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==} + puppeteer-core@24.10.2: + resolution: {integrity: sha512-CnzhOgrZj8DvkDqI+Yx+9or33i3Y9uUYbKyYpP4C13jWwXx/keQ38RMTMmxuLCWQlxjZrOH0Foq7P2fGP7adDQ==} + engines: {node: '>=18'} + + puppeteer@24.10.2: + resolution: {integrity: sha512-+k26rCz6akFZntx0hqUoFjCojgOLIxZs6p2k53LmEicwsT8F/FMBKfRfiBw1sitjiCvlR/15K7lBqfjXa251FA==} + engines: {node: '>=18'} + hasBin: true + quansync@0.2.10: resolution: {integrity: sha512-t41VRkMYbkHyCYmOvx/6URnN80H7k4X0lLdBMGsz+maAwrJQYB1djpV6vHrQIBE0WBSGqhtEHrK9U3DWWH8v7A==} @@ -2176,6 +2390,14 @@ packages: remark-stringify@11.0.0: resolution: {integrity: sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==} + require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + + resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + restructure@3.0.2: resolution: {integrity: sha512-gSfoiOEA0VPE6Tukkrr7I0RBdE0s7H1eFCDBk05l1KIQT1UIKNc5JZy6jdyW6eYH3aR3g5b3PuL77rq0hvwtAw==} @@ -2238,14 +2460,30 @@ packages: sisteransi@1.0.5: resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} + smart-buffer@4.2.0: + resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==} + engines: {node: '>= 6.0.0', npm: '>= 3.0.0'} + smol-toml@1.3.4: resolution: {integrity: sha512-UOPtVuYkzYGee0Bd2Szz8d2G3RfMfJ2t3qVdZUAozZyAk+a0Sxa+QKix0YCwjL/A1RR0ar44nCxaoN9FxdJGwA==} engines: {node: '>= 18'} + socks-proxy-agent@8.0.5: + resolution: {integrity: sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==} + engines: {node: '>= 14'} + + socks@2.8.5: + resolution: {integrity: sha512-iF+tNDQla22geJdTyJB1wM/qrX9DMRwWrciEPwWLPRWAUEM8sQiyxgckLxWT1f7+9VabJS0jTGGr4QgBuvi6Ww==} + engines: {node: '>= 10.0.0', npm: '>= 3.0.0'} + source-map-js@1.2.1: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} + source-map@0.6.1: + resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} + engines: {node: '>=0.10.0'} + source-map@0.7.4: resolution: {integrity: sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==} engines: {node: '>= 8'} @@ -2253,6 +2491,9 @@ packages: space-separated-tokens@2.0.2: resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==} + sprintf-js@1.1.3: + resolution: {integrity: sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==} + stack-trace@1.0.0-pre2: resolution: {integrity: sha512-2ztBJRek8IVofG9DBJqdy2N5kulaacX30Nz7xmkYF6ale9WBVmIy6mFBchvGX7Vx/MyjBhx+Rcxqrj+dbOnQ6A==} engines: {node: '>=16'} @@ -2265,6 +2506,9 @@ packages: resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==} engines: {node: '>= 0.8'} + streamx@2.22.1: + resolution: {integrity: sha512-znKXEBxfatz2GBNK02kRnCXjV+AA4kjZIUxeWSr3UGirZMJfTE9uiwKHobnbgxWyL/JWro8tTq+vOqAK1/qbSA==} + string-width@4.2.3: resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} engines: {node: '>=8'} @@ -2305,6 +2549,12 @@ packages: resolution: {integrity: sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg==} engines: {node: '>=6'} + tar-fs@3.0.10: + resolution: {integrity: sha512-C1SwlQGNLe/jPNqapK8epDsXME7CAJR5RL3GcE6KWx1d9OUByzoHVcbu1VPI8tevg9H8Alae0AApHHFGzrD5zA==} + + tar-stream@3.1.7: + resolution: {integrity: sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==} + tar@6.2.1: resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==} engines: {node: '>=10'} @@ -2313,6 +2563,9 @@ packages: resolution: {integrity: sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==} engines: {node: '>=18'} + text-decoder@1.2.3: + resolution: {integrity: sha512-3/o9z3X0X0fTupwsYvR03pJ/DjWuqqrfwBgTQzdWDiQSm9KitAyz/9WqsT2JQW7KV2m+bC2ol/zqpW37NHxLaA==} + tiny-inflate@1.0.3: resolution: {integrity: sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==} @@ -2356,6 +2609,9 @@ packages: resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==} engines: {node: '>=16'} + typed-query-selector@2.12.0: + resolution: {integrity: sha512-SbklCd1F0EiZOyPiW192rrHZzZ5sBijB6xM+cpmrwDqObvdtunOHHIk9fCGsoK5JVIYXoyEp4iEdE3upFH3PAg==} + typescript@5.8.3: resolution: {integrity: sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==} engines: {node: '>=14.17'} @@ -2574,6 +2830,10 @@ packages: resolution: {integrity: sha512-c9bZp7b5YtRj2wOe6dlj32MK+Bx/M/d+9VB2SHM1OtsUHR0aV0tdP6DWh/iMt0kWi1t5g1Iudu6hQRNd1A4PVA==} engines: {node: '>=18'} + wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + wrap-ansi@9.0.0: resolution: {integrity: sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==} engines: {node: '>=18'} @@ -2581,9 +2841,25 @@ packages: wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + ws@8.18.2: + resolution: {integrity: sha512-DMricUmwGZUVr++AEAe2uiVM7UoO9MAVZMDu05UQOaUII0lp+zOzLLU4Xqh/JvTqklB1T4uELaaPBKyjE1r4fQ==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + xxhash-wasm@1.1.0: resolution: {integrity: sha512-147y/6YNh+tlp6nd/2pWq38i9h6mz/EuQ6njIrmW8D1BS5nCqs0P6DG+m6zTGnNz5I+uhZ0SHxBs9BsPrwcKDA==} + y18n@5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + yallist@3.1.1: resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} @@ -2598,6 +2874,10 @@ packages: resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} engines: {node: '>=12'} + yargs@17.7.2: + resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} + engines: {node: '>=12'} + yauzl@2.10.0: resolution: {integrity: sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==} @@ -2960,6 +3240,8 @@ snapshots: '@esbuild/win32-x64@0.25.5': optional: true + '@iarna/toml@2.2.5': {} + '@iconify-json/mdi@1.2.3': dependencies: '@iconify/types': 2.0.0 @@ -3252,6 +3534,19 @@ snapshots: transitivePeerDependencies: - supports-color + '@puppeteer/browsers@2.10.5': + dependencies: + debug: 4.4.1 + extract-zip: 2.0.1 + progress: 2.0.3 + proxy-agent: 6.5.0 + semver: 7.7.2 + tar-fs: 3.0.10 + yargs: 17.7.2 + transitivePeerDependencies: + - bare-buffer + - supports-color + '@rollup/pluginutils@4.2.1': dependencies: estree-walker: 2.0.2 @@ -3441,6 +3736,8 @@ snapshots: tailwindcss: 4.1.10 vite: 6.3.5(@types/node@24.0.3)(jiti@2.4.2)(lightningcss@1.30.1) + '@tootallnate/quickjs-emscripten@0.23.0': {} + '@trysound/sax@0.2.0': {} '@types/debug@4.1.12': @@ -3477,6 +3774,16 @@ snapshots: dependencies: undici-types: 7.8.0 + '@types/puppeteer@7.0.4(typescript@5.8.3)': + dependencies: + puppeteer: 24.10.2(typescript@5.8.3) + transitivePeerDependencies: + - bare-buffer + - bufferutil + - supports-color + - typescript + - utf-8-validate + '@types/tar@6.1.13': dependencies: '@types/node': 24.0.3 @@ -3499,6 +3806,8 @@ snapshots: acorn@8.15.0: {} + agent-base@7.1.3: {} + ansi-align@3.0.1: dependencies: string-width: 4.2.3 @@ -3507,6 +3816,10 @@ snapshots: ansi-regex@6.1.0: {} + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + ansi-styles@6.2.1: {} anymatch@3.1.3: @@ -3520,6 +3833,10 @@ snapshots: array-iterate@2.0.1: {} + ast-types@0.13.4: + dependencies: + tslib: 2.8.1 + astring@1.9.0: {} astro-icon@1.1.5: @@ -3643,16 +3960,45 @@ snapshots: axobject-query@4.1.0: {} + b4a@1.6.7: {} + babel-plugin-transform-hook-names@1.0.2(@babel/core@7.27.4): dependencies: '@babel/core': 7.27.4 bail@2.0.2: {} + bare-events@2.5.4: + optional: true + + bare-fs@4.1.5: + dependencies: + bare-events: 2.5.4 + bare-path: 3.0.0 + bare-stream: 2.6.5(bare-events@2.5.4) + optional: true + + bare-os@3.6.1: + optional: true + + bare-path@3.0.0: + dependencies: + bare-os: 3.6.1 + optional: true + + bare-stream@2.6.5(bare-events@2.5.4): + dependencies: + streamx: 2.22.1 + optionalDependencies: + bare-events: 2.5.4 + optional: true + base-64@1.0.0: {} base64-js@1.5.1: {} + basic-ftp@5.0.5: {} + blob-to-buffer@1.2.9: {} boolbase@1.0.0: {} @@ -3686,6 +4032,8 @@ snapshots: es-errors: 1.3.0 function-bind: 1.1.2 + callsites@3.1.0: {} + camelcase@8.0.0: {} caniuse-lite@1.0.30001724: {} @@ -3733,10 +4081,22 @@ snapshots: chownr@3.0.0: {} + chromium-bidi@5.1.0(devtools-protocol@0.0.1452169): + dependencies: + devtools-protocol: 0.0.1452169 + mitt: 3.0.1 + zod: 3.25.67 + ci-info@4.2.0: {} cli-boxes@3.0.0: {} + cliui@8.0.1: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + clone@2.1.2: {} clsx@2.1.1: {} @@ -3779,6 +4139,15 @@ snapshots: cookie@1.0.2: {} + cosmiconfig@9.0.0(typescript@5.8.3): + dependencies: + env-paths: 2.2.1 + import-fresh: 3.3.1 + js-yaml: 4.1.0 + parse-json: 5.2.0 + optionalDependencies: + typescript: 5.8.3 + cross-fetch@3.2.0: dependencies: node-fetch: 2.7.0 @@ -3822,6 +4191,8 @@ snapshots: daisyui@5.0.43: {} + data-uri-to-buffer@6.0.2: {} + debug@4.4.1: dependencies: ms: 2.1.3 @@ -3832,6 +4203,12 @@ snapshots: defu@6.1.4: {} + degenerator@5.0.1: + dependencies: + ast-types: 0.13.4 + escodegen: 2.1.0 + esprima: 4.0.1 + delayed-stream@1.0.0: {} depd@2.0.0: {} @@ -3852,6 +4229,8 @@ snapshots: dependencies: dequal: 2.0.3 + devtools-protocol@0.0.1452169: {} + dfa@1.2.0: {} diff@5.2.0: {} @@ -3912,6 +4291,12 @@ snapshots: entities@6.0.1: {} + env-paths@2.2.1: {} + + error-ex@1.3.2: + dependencies: + is-arrayish: 0.2.1 + es-define-property@1.0.1: {} es-errors@1.3.0: {} @@ -3977,6 +4362,18 @@ snapshots: escape-string-regexp@5.0.0: {} + escodegen@2.1.0: + dependencies: + esprima: 4.0.1 + estraverse: 5.3.0 + esutils: 2.0.3 + optionalDependencies: + source-map: 0.6.1 + + esprima@4.0.1: {} + + estraverse@5.3.0: {} + estree-util-attach-comments@3.0.0: dependencies: '@types/estree': 1.0.8 @@ -4012,6 +4409,8 @@ snapshots: dependencies: '@types/estree': 1.0.8 + esutils@2.0.3: {} + etag@1.8.1: {} eventemitter3@5.0.1: {} @@ -4032,6 +4431,8 @@ snapshots: fast-deep-equal@3.1.3: {} + fast-fifo@1.3.2: {} + fast-xml-parser@5.2.5: dependencies: strnum: 2.1.1 @@ -4086,6 +4487,8 @@ snapshots: gensync@1.0.0-beta.2: {} + get-caller-file@2.0.5: {} + get-east-asian-width@1.3.0: {} get-intrinsic@1.3.0: @@ -4110,6 +4513,14 @@ snapshots: dependencies: pump: 3.0.3 + get-uri@6.0.4: + dependencies: + basic-ftp: 5.0.5 + data-uri-to-buffer: 6.0.2 + debug: 4.4.1 + transitivePeerDependencies: + - supports-color + github-slugger@2.0.0: {} globals@11.12.0: {} @@ -4293,16 +4704,40 @@ snapshots: statuses: 2.0.1 toidentifier: 1.0.1 + http-proxy-agent@7.0.2: + dependencies: + agent-base: 7.1.3 + debug: 4.4.1 + transitivePeerDependencies: + - supports-color + + https-proxy-agent@7.0.6: + dependencies: + agent-base: 7.1.3 + debug: 4.4.1 + transitivePeerDependencies: + - supports-color + iconv-lite@0.6.3: dependencies: safer-buffer: 2.1.2 + import-fresh@3.3.1: + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + import-meta-resolve@4.1.0: {} inherits@2.0.4: {} inline-style-parser@0.2.4: {} + ip-address@9.0.5: + dependencies: + jsbn: 1.1.0 + sprintf-js: 1.1.3 + iron-webcrypto@1.2.1: {} is-alphabetical@2.0.1: {} @@ -4312,6 +4747,8 @@ snapshots: is-alphabetical: 2.0.1 is-decimal: 2.0.1 + is-arrayish@0.2.1: {} + is-arrayish@0.3.2: {} is-decimal@2.0.1: {} @@ -4340,8 +4777,12 @@ snapshots: dependencies: argparse: 2.0.1 + jsbn@1.1.0: {} + jsesc@3.1.0: {} + json-parse-even-better-errors@2.3.1: {} + json5@2.2.3: {} kleur@3.0.3: {} @@ -4395,6 +4836,8 @@ snapshots: lightningcss-win32-arm64-msvc: 1.30.1 lightningcss-win32-x64-msvc: 1.30.1 + lines-and-columns@1.2.4: {} + local-pkg@0.5.1: dependencies: mlly: 1.7.4 @@ -4420,6 +4863,8 @@ snapshots: dependencies: yallist: 3.1.1 + lru-cache@7.18.3: {} + lucide-preact@0.518.0(preact@10.26.9): dependencies: preact: 10.26.9 @@ -4910,6 +5355,8 @@ snapshots: dependencies: minipass: 7.1.2 + mitt@3.0.1: {} + mkdirp@1.0.4: {} mkdirp@3.0.1: {} @@ -4929,6 +5376,8 @@ snapshots: neotraverse@0.6.18: {} + netmask@2.0.2: {} + nlcst-to-string@4.0.0: dependencies: '@types/nlcst': 2.0.3 @@ -4989,10 +5438,32 @@ snapshots: p-timeout@6.1.4: {} + pac-proxy-agent@7.2.0: + dependencies: + '@tootallnate/quickjs-emscripten': 0.23.0 + agent-base: 7.1.3 + debug: 4.4.1 + get-uri: 6.0.4 + http-proxy-agent: 7.0.2 + https-proxy-agent: 7.0.6 + pac-resolver: 7.0.1 + socks-proxy-agent: 8.0.5 + transitivePeerDependencies: + - supports-color + + pac-resolver@7.0.1: + dependencies: + degenerator: 5.0.1 + netmask: 2.0.2 + package-manager-detector@1.3.0: {} pako@0.2.9: {} + parent-module@1.0.1: + dependencies: + callsites: 3.1.0 + parse-entities@4.0.2: dependencies: '@types/unist': 2.0.11 @@ -5003,6 +5474,13 @@ snapshots: is-decimal: 2.0.1 is-hexadecimal: 2.0.1 + parse-json@5.2.0: + dependencies: + '@babel/code-frame': 7.27.1 + error-ex: 1.3.2 + json-parse-even-better-errors: 2.3.1 + lines-and-columns: 1.2.4 + parse-latin@7.0.0: dependencies: '@types/nlcst': 2.0.3 @@ -5068,6 +5546,8 @@ snapshots: prismjs@1.30.0: {} + progress@2.0.3: {} + prompts@2.4.2: dependencies: kleur: 3.0.3 @@ -5077,6 +5557,19 @@ snapshots: property-information@7.1.0: {} + proxy-agent@6.5.0: + dependencies: + agent-base: 7.1.3 + debug: 4.4.1 + http-proxy-agent: 7.0.2 + https-proxy-agent: 7.0.6 + lru-cache: 7.18.3 + pac-proxy-agent: 7.2.0 + proxy-from-env: 1.1.0 + socks-proxy-agent: 8.0.5 + transitivePeerDependencies: + - supports-color + proxy-from-env@1.1.0: {} pump@3.0.3: @@ -5084,6 +5577,35 @@ snapshots: end-of-stream: 1.4.5 once: 1.4.0 + puppeteer-core@24.10.2: + dependencies: + '@puppeteer/browsers': 2.10.5 + chromium-bidi: 5.1.0(devtools-protocol@0.0.1452169) + debug: 4.4.1 + devtools-protocol: 0.0.1452169 + typed-query-selector: 2.12.0 + ws: 8.18.2 + transitivePeerDependencies: + - bare-buffer + - bufferutil + - supports-color + - utf-8-validate + + puppeteer@24.10.2(typescript@5.8.3): + dependencies: + '@puppeteer/browsers': 2.10.5 + chromium-bidi: 5.1.0(devtools-protocol@0.0.1452169) + cosmiconfig: 9.0.0(typescript@5.8.3) + devtools-protocol: 0.0.1452169 + puppeteer-core: 24.10.2 + typed-query-selector: 2.12.0 + transitivePeerDependencies: + - bare-buffer + - bufferutil + - supports-color + - typescript + - utf-8-validate + quansync@0.2.10: {} radix3@1.1.2: {} @@ -5213,6 +5735,10 @@ snapshots: mdast-util-to-markdown: 2.1.2 unified: 11.0.5 + require-directory@2.1.1: {} + + resolve-from@4.0.0: {} + restructure@3.0.2: {} retext-latin@4.0.0: @@ -5368,20 +5894,47 @@ snapshots: sisteransi@1.0.5: {} + smart-buffer@4.2.0: {} + smol-toml@1.3.4: {} + socks-proxy-agent@8.0.5: + dependencies: + agent-base: 7.1.3 + debug: 4.4.1 + socks: 2.8.5 + transitivePeerDependencies: + - supports-color + + socks@2.8.5: + dependencies: + ip-address: 9.0.5 + smart-buffer: 4.2.0 + source-map-js@1.2.1: {} + source-map@0.6.1: + optional: true + source-map@0.7.4: {} space-separated-tokens@2.0.2: {} + sprintf-js@1.1.3: {} + stack-trace@1.0.0-pre2: {} statuses@2.0.1: {} statuses@2.0.2: {} + streamx@2.22.1: + dependencies: + fast-fifo: 1.3.2 + text-decoder: 1.2.3 + optionalDependencies: + bare-events: 2.5.4 + string-width@4.2.3: dependencies: emoji-regex: 8.0.0 @@ -5431,6 +5984,22 @@ snapshots: tapable@2.2.2: {} + tar-fs@3.0.10: + dependencies: + pump: 3.0.3 + tar-stream: 3.1.7 + optionalDependencies: + bare-fs: 4.1.5 + bare-path: 3.0.0 + transitivePeerDependencies: + - bare-buffer + + tar-stream@3.1.7: + dependencies: + b4a: 1.6.7 + fast-fifo: 1.3.2 + streamx: 2.22.1 + tar@6.2.1: dependencies: chownr: 2.0.0 @@ -5449,6 +6018,10 @@ snapshots: mkdirp: 3.0.1 yallist: 5.0.0 + text-decoder@1.2.3: + dependencies: + b4a: 1.6.7 + tiny-inflate@1.0.3: {} tinyexec@0.3.2: {} @@ -5476,6 +6049,8 @@ snapshots: type-fest@4.41.0: {} + typed-query-selector@2.12.0: {} + typescript@5.8.3: {} ufo@1.6.1: {} @@ -5642,6 +6217,12 @@ snapshots: dependencies: string-width: 7.2.0 + wrap-ansi@7.0.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi@9.0.0: dependencies: ansi-styles: 6.2.1 @@ -5650,8 +6231,12 @@ snapshots: wrappy@1.0.2: {} + ws@8.18.2: {} + xxhash-wasm@1.1.0: {} + y18n@5.0.8: {} + yallist@3.1.1: {} yallist@4.0.0: {} @@ -5660,6 +6245,16 @@ snapshots: yargs-parser@21.1.1: {} + yargs@17.7.2: + 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 + yauzl@2.10.0: dependencies: buffer-crc32: 0.2.13 diff --git a/public/files/resume.json b/public/files/resume.json deleted file mode 100644 index 8b6d345..0000000 --- a/public/files/resume.json +++ /dev/null @@ -1,483 +0,0 @@ -{ - "basics": { - "name": "Atridad Lahiji", - "headline": "", - "email": "me@atri.dad", - "phone": "", - "location": "", - "url": { - "label": "", - "href": "https://atri.dad" - }, - "customFields": [], - "picture": { - "url": "", - "size": 64, - "aspectRatio": 1, - "borderRadius": 0, - "effects": { - "hidden": false, - "border": false, - "grayscale": false - } - } - }, - "sections": { - "summary": { - "name": "Summary", - "columns": 1, - "separateLinks": true, - "visible": true, - "id": "summary", - "content": "

I am a full-stack web developer and researcher with a background maintaining and developing for large-scale enterprise software systems. I am in the process of completing my Master of Science in Computer Science under the supervision of Dr. Nathaniel Osgood at the University of Saskatchewan. I have completed my course work and am now moving into writing my thesis which can be done asynchronously.

" - }, - "awards": { - "name": "Awards", - "columns": 1, - "separateLinks": true, - "visible": true, - "id": "awards", - "items": [] - }, - "certifications": { - "name": "Certifications", - "columns": 1, - "separateLinks": true, - "visible": true, - "id": "certifications", - "items": [] - }, - "education": { - "name": "Education", - "columns": 1, - "separateLinks": true, - "visible": true, - "id": "education", - "items": [ - { - "id": "xtkfnu2zq3myh09pumehphx9", - "visible": true, - "institution": "University of Saskatchewan", - "studyType": "Masters", - "area": "Computer Science", - "score": "", - "date": "2024 – Present", - "summary": "

Supervisor: Dr. Nathaniel Osgood

", - "url": { - "label": "", - "href": "" - } - }, - { - "id": "o4my8au0d7c6bf09vlqwxvyw", - "visible": true, - "institution": "University of Saskatchewan", - "studyType": "Bachelors (3 Year)", - "area": "Computer Science", - "score": "", - "date": "2016 – 2019", - "summary": "", - "url": { - "label": "", - "href": "" - } - }, - { - "id": "pnwpsei7ag1yldmtv9f4kt4e", - "visible": true, - "institution": "University of Saskatchewan", - "studyType": "Bachelors", - "area": "Computer Engineering", - "score": "", - "date": "2012 – 2017", - "summary": "", - "url": { - "label": "", - "href": "" - } - } - ] - }, - "experience": { - "name": "Experience", - "columns": 1, - "separateLinks": true, - "visible": true, - "id": "experience", - "items": [ - { - "id": "gn67fi9oygi5tz1x3p3r7mbf", - "visible": true, - "company": "Atash Consulting", - "position": "Owner/Developer", - "location": "Edmonton, Alberta", - "date": "June 2019 – Present", - "summary": "", - "url": { - "label": "", - "href": "https://atash.dev" - } - }, - { - "id": "x8ok2hutceh7lroyhwa7kj0h", - "visible": true, - "company": "University of Saskatchewan CEPHIL Lab", - "position": "Research Technician", - "location": "Saskatoon, Saskatchewan", - "date": "November 2023 – Present", - "summary": "", - "url": { - "label": "", - "href": "" - } - }, - { - "id": "f0kyaxcy3syb8wazs3ye662i", - "visible": true, - "company": "Alberta Motor Association", - "position": "Software Developer II", - "location": "Edmonton, Alberta", - "date": "August 2021 – November 2023", - "summary": "", - "url": { - "label": "", - "href": "" - } - }, - { - "id": "yikqef72i068lfiy8iiwjm45", - "visible": true, - "company": "University of Alberta IST", - "position": "Software Developer", - "location": "Edmonton, Alberta", - "date": "October 2019 – August 2021", - "summary": "", - "url": { - "label": "", - "href": "" - } - }, - { - "id": "wzqfv3h8rxs6574z5hlvrhm7", - "visible": true, - "company": "University of Alberta IST", - "position": "Support Analyst", - "location": "Edmonton, Alberta", - "date": "July 2017 – October 2019", - "summary": "", - "url": { - "label": "", - "href": "" - } - } - ] - }, - "volunteer": { - "name": "Volunteering", - "columns": 1, - "separateLinks": true, - "visible": true, - "id": "volunteer", - "items": [ - { - "id": "xhg1p7exqggrjkldszplj1wk", - "visible": true, - "organization": "Big Brother Big Sisters", - "position": "Mentor", - "location": "", - "date": "2021 – 2022", - "summary": "", - "url": { - "label": "", - "href": "" - } - } - ] - }, - "interests": { - "name": "Interests", - "columns": 1, - "separateLinks": true, - "visible": true, - "id": "interests", - "items": [] - }, - "languages": { - "name": "Languages", - "columns": 1, - "separateLinks": true, - "visible": true, - "id": "languages", - "items": [] - }, - "profiles": { - "name": "Profiles", - "columns": 1, - "separateLinks": true, - "visible": true, - "id": "profiles", - "items": [ - { - "id": "zuto1s9atwo6tdx9qfa9ggug", - "visible": true, - "network": "GitHub", - "username": "atridadl", - "icon": "github", - "url": { - "label": "", - "href": "https://github.com/atridadl" - } - }, - { - "id": "satbehrw5da07dmi8y8j70kl", - "visible": true, - "network": "linkedin", - "username": "atridadl", - "icon": "linkedin", - "url": { - "label": "", - "href": "https://www.linkedin.com/in/atridadl/" - } - }, - { - "id": "yorfn8ku98u5o0jzvumo9q2v", - "visible": true, - "network": "Gitea", - "username": "atridad", - "icon": "gitea", - "url": { - "label": "", - "href": "https://git.atri.dad/atridad" - } - } - ] - }, - "projects": { - "name": "Projects", - "columns": 1, - "separateLinks": true, - "visible": true, - "id": "projects", - "items": [] - }, - "publications": { - "name": "Publications", - "columns": 1, - "separateLinks": true, - "visible": true, - "id": "publications", - "items": [] - }, - "references": { - "name": "References", - "columns": 1, - "separateLinks": true, - "visible": true, - "id": "references", - "items": [] - }, - "skills": { - "name": "Skills", - "columns": 1, - "separateLinks": true, - "visible": true, - "id": "skills", - "items": [ - { - "id": "lpwyb43emmmukje3c49yupu7", - "visible": true, - "name": "HTML + CSS + JavaScript", - "description": "", - "level": 5, - "keywords": [] - }, - { - "id": "c5qu0q3wct06oj1wa3u3tkar", - "visible": true, - "name": "Typescrpt", - "description": "", - "level": 5, - "keywords": [] - }, - { - "id": "qtq2qfeoa0bskykwhfzmlpng", - "visible": true, - "name": "Vitest, Jest, and Playwright", - "description": "", - "level": 4, - "keywords": [] - }, - { - "id": "b6k6q4r592uesacsz03dtvyk", - "visible": true, - "name": "Docker + Docker Compose", - "description": "", - "level": 5, - "keywords": [] - }, - { - "id": "lc3eu9r8vvqhsst1mkeqxgse", - "visible": true, - "name": "Go (Golang)", - "description": "", - "level": 4, - "keywords": [] - }, - { - "id": "lme3ob0kfpe5hgsuar42nmi6", - "visible": true, - "name": "SQL (PostgreSQL, MySQL, SQLite)", - "description": "", - "level": 4, - "keywords": [] - }, - { - "id": "f58rq48rtsgdftfcbpt785is", - "visible": true, - "name": "Python", - "description": "", - "level": 4, - "keywords": [] - }, - { - "id": "ht9fn1i89gm0e3gf5mfde0os", - "visible": true, - "name": "SCRUM", - "description": "", - "level": 5, - "keywords": [] - }, - { - "id": "vtpxeg6r0os9ygjmg384wo7f", - "visible": true, - "name": "Amazon Web Services (AWS)", - "description": "", - "level": 4, - "keywords": [] - }, - { - "id": "tk3i1xdw92vny0fk7001rrj7", - "visible": true, - "name": "Ruby", - "description": "", - "level": 2, - "keywords": [] - }, - { - "id": "jqy6vkxl8hed4vgow0z12vwy", - "visible": true, - "name": "Test Driven Development", - "description": "", - "level": 3, - "keywords": [] - }, - { - "id": "oalwcevey6plalwasugwf4q7", - "visible": true, - "name": "C#", - "description": "", - "level": 3, - "keywords": [] - }, - { - "id": "skhsek829sf8012wbwd38fl8", - "visible": true, - "name": "PHP", - "description": "", - "level": 2, - "keywords": [] - }, - { - "id": "feotadkdeli1ukx3u3ix86ig", - "visible": true, - "name": "Time Management", - "description": "", - "level": 4, - "keywords": [] - }, - { - "id": "a993l06kuyinj9l88uz3ztux", - "visible": true, - "name": "Problem Solving", - "description": "", - "level": 5, - "keywords": [] - }, - { - "id": "rhyu2toaznnidknrz244klqq", - "visible": true, - "name": "Attention to Detail", - "description": "", - "level": 5, - "keywords": [] - } - ] - }, - "custom": { - "b5li7wh27iylvqlsmeavvkzh": { - "name": "Custom Section", - "columns": 1, - "separateLinks": true, - "visible": true, - "id": "b5li7wh27iylvqlsmeavvkzh", - "items": [] - } - } - }, - "metadata": { - "template": "glalie", - "layout": [ - [ - [ - "summary", - "education", - "experience", - "projects", - "references", - "custom.b5li7wh27iylvqlsmeavvkzh" - ], - [ - "profiles", - "skills", - "volunteer", - "interests", - "certifications", - "awards", - "publications", - "languages" - ] - ] - ], - "css": { - "value": ".text-2xl {\n\tfont-size: 30px;\n}", - "visible": true - }, - "page": { - "margin": 16, - "format": "letter", - "options": { - "breakLine": true, - "pageNumbers": true - } - }, - "theme": { - "background": "#ffffff", - "text": "#000000", - "primary": "#0284c7" - }, - "typography": { - "font": { - "family": "Lato", - "subset": "latin", - "variants": [ - "regular" - ], - "size": 14 - }, - "lineHeight": 0.95, - "hideIcons": false, - "underlineLinks": true - }, - "notes": "" - } -} \ No newline at end of file diff --git a/public/files/resume.toml b/public/files/resume.toml new file mode 100644 index 0000000..eaa26b7 --- /dev/null +++ b/public/files/resume.toml @@ -0,0 +1,174 @@ +[basics] +name = "Atridad Lahiji" +email = "me@atri.dad" +website = "https://atri.dad" + +[[basics.profiles]] +network = "GitHub" +username = "atridadl" +url = "https://github.com/atridadl" + +[[basics.profiles]] +network = "LinkedIn" +username = "atridadl" +url = "https://www.linkedin.com/in/atridadl/" + +[[basics.profiles]] +network = "Gitea" +username = "atridad" +url = "https://git.atri.dad/atridad" + +[summary] +content = "I am a full-stack web developer and researcher with a background maintaining and developing for large-scale enterprise software systems." + +[[experience]] +company = "Atash Consulting" +position = "Owner/Developer" +location = "Edmonton, Alberta" +date = "June 2019 – Present" +description = [ + "Builds mobile and web applications for small-medium sized businesses", + "Provides consulting on application development, system architecture, DevOps, etc", + "Hosting websites for small-medium sized businesses", +] +url = "https://atash.dev" + +[[experience]] +company = "University of Saskatchewan CEPHIL Lab" +position = "Research Technician" +location = "Saskatoon, Saskatchewan" +date = "November 2023 – Present" +description = [ + "Developing mobile and web applications", + "Coordinating with other grant researchers to deliver a minimum viable product", + "Gathering requirements from stakeholders to craft a product timeline", + "Acting as a technical lead and supervisor to a developer intern", +] + +[[experience]] +company = "Alberta Motor Association" +position = "Software Developer II" +location = "Edmonton, Alberta" +date = "August 2021 – November 2023" +description = [ + "Developed and maintained internal enterprise-level business applications leveraging Amazon Web Services (AWS)", + "Used React and Create React App (CRA) for standalone applications and micro-front-ends", + "Developed an in-house payment gateway for all AMA services that integrates with Stripe", + "Provided tier 3 support for internal services", + "Participated in a bi-monthly 24/7 on-call rotation", + "Mentored students in the organization's Developer in Training program", +] + +[[experience]] +company = "University of Alberta IST" +position = "Software Developer" +location = "Edmonton, Alberta" +date = "October 2019 – August 2021" +description = [ + "Front-end development of web applications using Vue.js", + "Leveraged Amazon Web Services to adopt a serverless architecture", + "Maintained a secure exam application developed in-house", + "Monitored and maintained an exam scheduling system hosted on-premises", +] + +[[experience]] +company = "University of Alberta IST" +position = "Support Analyst" +location = "Edmonton, Alberta" +date = "July 2017 – October 2019" +description = [ + "Provided support for our Moodle installation to students, faculty, and staff", + "Front-end development of web applications using Vue.js", +] + +[[education]] +institution = "University of Saskatchewan" +degree = "Masters" +field = "Computer Science" +date = "2024 – Present" +details = [ + "Supervisor: Dr. Nathaniel Osgood", + "CMPT 838: Computer Security", + "CMPT 815: Computer Systems and Performance Evaluation", +] + +[[education]] +institution = "University of Saskatchewan" +degree = "Bachelors (3 Year)" +field = "Computer Science" +date = "2016 – 2019" + +[[education]] +institution = "University of Saskatchewan" +degree = "Bachelors" +field = "Computer Engineering" +date = "2012 – 2017" + +[[skills]] +name = "HTML + CSS + JavaScript" +level = 5 + +[[skills]] +name = "TypeScript" +level = 5 + +[[skills]] +name = "Vitest, Jest, and Playwright" +level = 4 + +[[skills]] +name = "Docker + Docker Compose" +level = 5 + +[[skills]] +name = "Go (Golang)" +level = 4 + +[[skills]] +name = "SQL (PostgreSQL, MySQL, SQLite)" +level = 4 + +[[skills]] +name = "Python" +level = 4 + +[[skills]] +name = "SCRUM" +level = 5 + +[[skills]] +name = "Amazon Web Services (AWS)" +level = 4 + +[[skills]] +name = "Ruby" +level = 2 + +[[skills]] +name = "Test Driven Development" +level = 3 + +[[skills]] +name = "C#" +level = 3 + +[[skills]] +name = "PHP" +level = 2 + +[[skills]] +name = "Time Management" +level = 4 + +[[skills]] +name = "Problem Solving" +level = 5 + +[[skills]] +name = "Attention to Detail" +level = 5 + +[[volunteer]] +organization = "Big Brother Big Sisters" +position = "Mentor" +date = "2021 – 2022" diff --git a/src/components/PdfDownloadButton.tsx b/src/components/PdfDownloadButton.tsx new file mode 100644 index 0000000..eeb16a5 --- /dev/null +++ b/src/components/PdfDownloadButton.tsx @@ -0,0 +1,77 @@ +import { useState } from "preact/hooks"; + +interface PdfDownloadButtonProps { + className?: string; +} + +export default function PdfDownloadButton({ + className = "", +}: PdfDownloadButtonProps) { + const [isLoading, setIsLoading] = useState(false); + const [error, setError] = useState(null); + + const handleDownload = async () => { + setIsLoading(true); + setError(null); + + try { + const response = await fetch("/api/resume/pdf"); + + if (!response.ok) { + throw new Error( + `Failed to generate PDF: ${response.status} ${response.statusText}`, + ); + } + + const blob = await response.blob(); + const url = window.URL.createObjectURL(blob); + + // Create a temporary link element and trigger download + const link = document.createElement("a"); + link.href = url; + link.download = "Atridad_Lahiji_Resume.pdf"; + document.body.appendChild(link); + link.click(); + + // Clean up + document.body.removeChild(link); + window.URL.revokeObjectURL(url); + } catch (err) { + console.error("Error downloading PDF:", err); + setError(err instanceof Error ? err.message : "Failed to download PDF"); + } finally { + setIsLoading(false); + } + }; + + return ( +
+ + {error &&
{error}
} +
+ ); +} diff --git a/src/config/data.ts b/src/config/data.ts index 335ddea..7be6c76 100644 --- a/src/config/data.ts +++ b/src/config/data.ts @@ -62,7 +62,7 @@ export const homepageSections: HomepageSections = { // Resume Configuration export const resumeConfig: ResumeConfig = { - jsonFile: "/files/resume.json", + jsonFile: "/files/resume.toml", pdfFile: { path: "/files/Atridad_Lahiji_Resume.pdf", filename: "Atridad_Lahiji_Resume.pdf", diff --git a/src/pages/api/resume/pdf.ts b/src/pages/api/resume/pdf.ts new file mode 100644 index 0000000..c8c1dd7 --- /dev/null +++ b/src/pages/api/resume/pdf.ts @@ -0,0 +1,321 @@ +import type { APIRoute } from "astro"; +import puppeteer from "puppeteer"; +import { siteConfig } from "../../../config/data"; +import * as TOML from "@iarna/toml"; + +interface ResumeData { + basics: { + name: string; + email: string; + website?: string; + profiles: { + network: string; + username: string; + url: string; + }[]; + }; + summary: { + content: string; + }; + experience: { + company: string; + position: string; + location: string; + date: string; + description: string[]; + url?: string; + }[]; + education: { + institution: string; + degree: string; + field: string; + date: string; + details?: string[]; + }[]; + skills: { + name: string; + level: number; + }[]; + volunteer: { + organization: string; + position: string; + date: string; + }[]; +} + +const generateResumeHTML = (data: ResumeData): string => { + const resumeConfig = siteConfig.resume; + + const skillsHTML = + data.skills + ?.map((skill) => { + const progressValue = skill.level * 20; + return ` +
+
+ ${skill.name} + ${skill.level}/5 +
+
+
+
+
+ `; + }) + .join("") || ""; + + const experienceHTML = + data.experience + ?.map((exp) => { + const descriptionList = exp.description + .map((item) => `
  • ${item}
  • `) + .join(""); + + return ` +
    +

    ${exp.position}

    +
    + ${exp.company} + + ${exp.date} + + ${exp.location} +
    +
      + ${descriptionList} +
    +
    + `; + }) + .join("") || ""; + + const educationHTML = + data.education + ?.map((edu) => { + const detailsList = edu.details + ? edu.details + .map((detail) => `
  • ${detail}
  • `) + .join("") + : ""; + + return ` +
    +

    ${edu.institution}

    +
    + ${edu.degree} in ${edu.field} + + ${edu.date} +
    + ${detailsList ? `
      ${detailsList}
    ` : ""} +
    + `; + }) + .join("") || ""; + + const volunteerHTML = + data.volunteer + ?.map((vol) => { + return ` +
    +

    ${vol.organization}

    +
    + ${vol.position} + + ${vol.date} +
    +
    + `; + }) + .join("") || ""; + + return ` + + + + + + ${data.basics.name} - Resume + + + + +
    +
    +

    ${data.basics.name}

    +
    + ${data.basics.email ? `
    📧 ${data.basics.email}
    ` : ""} + ${ + data.basics.profiles?.find((p) => p.network === "GitHub") + ? `
    🔗 github.com/${data.basics.profiles.find((p) => p.network === "GitHub")?.username}
    ` + : "" + } + ${ + data.basics.profiles?.find((p) => p.network === "LinkedIn") + ? `
    💼 linkedin.com/in/${data.basics.profiles.find((p) => p.network === "LinkedIn")?.username}
    ` + : "" + } +
    +
    + + ${ + data.summary && resumeConfig.sections.summary?.enabled + ? ` +
    +

    + ${resumeConfig.sections.summary.title || "Summary"} +

    +
    ${data.summary.content}
    +
    + ` + : "" + } + +
    +
    + ${ + data.experience && + data.experience.length > 0 && + resumeConfig.sections.experience?.enabled + ? ` +
    +

    + ${resumeConfig.sections.experience.title || "Experience"} +

    +
    + ${experienceHTML} +
    +
    + ` + : "" + } + + ${ + data.volunteer && + data.volunteer.length > 0 && + resumeConfig.sections.volunteer?.enabled + ? ` +
    +

    + ${resumeConfig.sections.volunteer.title || "Volunteer Work"} +

    +
    + ${volunteerHTML} +
    +
    + ` + : "" + } +
    + +
    + ${ + data.skills && + data.skills.length > 0 && + resumeConfig.sections.skills?.enabled + ? ` +
    +

    + ${resumeConfig.sections.skills.title || "Skills"} +

    +
    + ${skillsHTML} +
    +
    + ` + : "" + } + + ${ + data.education && + data.education.length > 0 && + resumeConfig.sections.education?.enabled + ? ` +
    +

    + ${resumeConfig.sections.education.title || "Education"} +

    +
    + ${educationHTML} +
    +
    + ` + : "" + } +
    +
    +
    + + + `; +}; + +export const GET: APIRoute = async ({ request }) => { + try { + if (!siteConfig.resume.jsonFile || !siteConfig.resume.jsonFile.trim()) { + return new Response("Resume not configured", { status: 404 }); + } + + const url = new URL(request.url); + const baseUrl = `${url.protocol}//${url.host}`; + + const response = await fetch(`${baseUrl}${siteConfig.resume.jsonFile}`); + + if (!response.ok) { + throw new Error( + `Failed to fetch resume: ${response.status} ${response.statusText}`, + ); + } + + const tomlContent = await response.text(); + const resumeData: ResumeData = TOML.parse(tomlContent) as unknown as ResumeData; + + const htmlContent = generateResumeHTML(resumeData); + + const browser = await puppeteer.launch({ + headless: true, + args: ["--no-sandbox", "--disable-setuid-sandbox"], + }); + + const page = await browser.newPage(); + + await page.setContent(htmlContent, { waitUntil: "networkidle0" }); + + const pdfBuffer = await page.pdf({ + format: "A4", + margin: { + top: "0.2in", + bottom: "0.2in", + left: "0.2in", + right: "0.2in", + }, + printBackground: true, + preferCSSPageSize: false, + scale: 0.9, + }); + + await browser.close(); + + return new Response(pdfBuffer, { + headers: { + "Content-Type": "application/pdf", + "Content-Disposition": `attachment; filename="Atridad_Lahiji_Resume.pdf"`, + "Cache-Control": "public, max-age=3600", + }, + }); + } catch (error) { + console.error("Error generating PDF:", error); + return new Response("Error generating PDF", { status: 500 }); + } +}; diff --git a/src/pages/resume.astro b/src/pages/resume.astro index 916d069..81f1783 100644 --- a/src/pages/resume.astro +++ b/src/pages/resume.astro @@ -2,62 +2,49 @@ import { Icon } from "astro-icon/components"; import Layout from "../layouts/Layout.astro"; import ResumeSkills from "../components/ResumeSkills"; +import PdfDownloadButton from "../components/PdfDownloadButton"; import { siteConfig } from "../config/data"; import "../styles/global.css"; +import * as TOML from "@iarna/toml"; interface ResumeData { basics: { name: string; email: string; - url?: { href: string }; - }; - sections: { - summary: { name: string; content: string }; + website?: string; profiles: { - name: string; - items: { - network: string; - username: string; - url: { href: string }; - }[]; - }; - skills: { - name: string; - items: { id: string; name: string; level: number }[]; - }; - experience: { - name: string; - items: { - id: string; - company: string; - position: string; - date: string; - location: string; - summary: string; - url?: { href: string }; - }[]; - }; - education: { - name: string; - items: { - id: string; - institution: string; - studyType: string; - area: string; - date: string; - summary: string; - }[]; - }; - volunteer: { - name: string; - items: { - id: string; - organization: string; - position: string; - date: string; - }[]; - }; + network: string; + username: string; + url: string; + }[]; }; + summary: { + content: string; + }; + experience: { + company: string; + position: string; + location: string; + date: string; + description: string[]; + url?: string; + }[]; + education: { + institution: string; + degree: string; + field: string; + date: string; + details?: string[]; + }[]; + skills: { + name: string; + level: number; + }[]; + volunteer: { + organization: string; + position: string; + date: string; + }[]; } let resumeData: ResumeData | undefined = undefined; @@ -65,14 +52,14 @@ let fetchError: string | null = null; // Check if resume JSON file is configured before attempting to fetch if (!siteConfig.resume.jsonFile || !siteConfig.resume.jsonFile.trim()) { - return Astro.redirect('/'); + return Astro.redirect("/"); } try { // Get the base URL const baseUrl = Astro.url.origin; - // Fetch the JSON file from the public directory + // Fetch the TOML file from the public directory const response = await fetch(`${baseUrl}${siteConfig.resume.jsonFile}`); if (!response.ok) { @@ -81,23 +68,12 @@ try { ); } - resumeData = await response.json(); - - if (resumeData && resumeData.sections && resumeData.sections.skills) { - const resumeSkills = resumeData.sections.skills; - if (resumeSkills.items) { - const tsSkill = resumeSkills.items.find( - (s) => s.name === "Typescrpt", - ); - if (tsSkill) { - tsSkill.name = "Typescript"; - } - } - } + const tomlContent = await response.text(); + resumeData = TOML.parse(tomlContent) as unknown as ResumeData; } catch (error) { console.error("Error loading resume data:", error); // Return to home page when resume data cannot be loaded - return Astro.redirect('/'); + return Astro.redirect("/"); } const data = resumeData; @@ -105,7 +81,7 @@ const resumeConfig = siteConfig.resume; // At this point, data is guaranteed to exist since we redirect on error if (!data) { - return Astro.redirect('/'); + return Astro.redirect("/"); } --- @@ -115,91 +91,87 @@ if (!data) { {data.basics.name} -
    - {data.basics.email && ( - - {data.basics.email} - - )} - {data.sections.profiles.items.find( - (p) => p.network === "GitHub", - ) && ( - p.network === "GitHub", - )!.url.href - } - target="_blank" - rel="noopener noreferrer" - class="link link-hover inline-flex items-center gap-1 text-sm sm:text-base" - > - GitHub - - )} - {data.sections.profiles.items.find( - (p) => p.network === "linkedin", - ) && ( - p.network === "linkedin", - )!.url.href - } - target="_blank" - rel="noopener noreferrer" - class="link link-hover inline-flex items-center gap-1 text-sm sm:text-base" - > - LinkedIn - - )} + - {resumeConfig.pdfFile?.path && ( - - )} + - {data.sections.summary && resumeConfig.sections.summary?.enabled && ( -
    -
    -

    - {resumeConfig.sections.summary.title || data.sections.summary.name || "Summary"} -

    -
    -
    -
    - )} - - {data.sections.profiles && - data.sections.profiles.items && - data.sections.profiles.items.length > 0 && - resumeConfig.sections.profiles?.enabled && ( + { + data.summary && resumeConfig.sections.summary?.enabled && (

    - {resumeConfig.sections.profiles.title || data.sections.profiles.name || "Profiles"} + {resumeConfig.sections.summary.title || "Summary"}

    -
    - {data.sections.profiles.items.map( - (profile) => { +
    {data.summary.content}
    +
    +
    + ) + } + + { + data.basics.profiles && + data.basics.profiles.length > 0 && + resumeConfig.sections.profiles?.enabled && ( +
    +
    +

    + {resumeConfig.sections.profiles.title || + "Profiles"} +

    +
    + {data.basics.profiles.map((profile) => { // Use Simple Icons directly based on network name // Convert network name to lowercase and use simple-icons format const iconName = `simple-icons:${profile.network.toLowerCase()}`; return ( ); - }, - )} + })} +
    -
    - )} + ) + } - {data.sections.skills && - data.sections.skills.items && - data.sections.skills.items.length > 0 && - resumeConfig.sections.skills?.enabled && ( -
    -
    -

    - {resumeConfig.sections.skills.title || data.sections.skills.name || "Skills"} -

    - + { + data.skills && + data.skills.length > 0 && + resumeConfig.sections.skills?.enabled && ( +
    +
    +

    + {resumeConfig.sections.skills.title || "Skills"} +

    + ({ + id: `skill-${index}`, + name: skill.name, + level: skill.level, + }))} + client:load + /> +
    -
    - )} + ) + } - {data.sections.experience && - data.sections.experience.items && - data.sections.experience.items.length > 0 && - resumeConfig.sections.experience?.enabled && ( -
    -
    -

    - {resumeConfig.sections.experience.title || data.sections.experience.name || "Experience"} -

    -
    - )} + ) + } - {data.sections.education && - data.sections.education.items && - data.sections.education.items.length > 0 && - resumeConfig.sections.education?.enabled && ( -
    -
    -

    - {resumeConfig.sections.education.title || data.sections.education.name || "Education"} -

    -
    - {data.sections.education.items.map( - (education) => ( + { + data.education && + data.education.length > 0 && + resumeConfig.sections.education?.enabled && ( +
    +
    +

    + {resumeConfig.sections.education.title || + "Education"} +

    +
    + {data.education.map((education) => (

    {education.institution}

    - {education.studyType} in{" "} - {education.area} + {education.degree} in{" "} + {education.field} {education.date}
    - {education.summary && ( -
    + {education.details && ( +
      + {education.details.map( + (detail) => ( +
    • {detail}
    • + ), + )} +
    )}
    - ), - )} + ))} +
    -
    - )} + ) + } - {data.sections.volunteer && - data.sections.volunteer.items && - data.sections.volunteer.items.length > 0 && - resumeConfig.sections.volunteer?.enabled && ( -
    -
    -

    - {resumeConfig.sections.volunteer.title || data.sections.volunteer.name || "Volunteer Work"} -

    -
    - {data.sections.volunteer.items.map( - (volunteer) => ( + { + data.volunteer && + data.volunteer.length > 0 && + resumeConfig.sections.volunteer?.enabled && ( +
    +
    +

    + {resumeConfig.sections.volunteer.title || + "Volunteer Work"} +

    +
    + {data.volunteer.map((volunteer) => (

    {volunteer.organization} @@ -344,11 +325,11 @@ if (!data) {

    - ), - )} + ))} +
    -
    - )} + ) + }