├── .gitignore ├── .vscode └── settings.json ├── README.md ├── app ├── head.tsx ├── layout.tsx ├── page.tsx └── posts │ └── [slug] │ └── page.tsx ├── components ├── PostMetadata.ts ├── PostPreview.tsx └── getPostMetadata.ts ├── next.config.js ├── package-lock.json ├── package.json ├── postcss.config.js ├── posts ├── aws-quickstart.md ├── gpt-3-generated-poetry.md ├── qr-code-micro-app.md ├── retro-games-with-pico-8.md ├── saas-starter-app.md └── wolfhunter.md ├── public ├── favicon.ico ├── images │ ├── classic_nes_controller.jpg │ ├── jelpi_demo.gif │ ├── linked_list.jpg │ ├── pico-8-code-editor.png │ ├── pico-8-draw-sprite.png │ ├── pico-8-movement.gif │ ├── pico-8-sprite-editor.png │ ├── pokemon_fight_scene.png │ ├── wolfhunter_gameplay.gif │ └── wolfhunter_title.png └── logo.png ├── styles └── globals.css ├── tailwind.config.js └── tsconfig.json /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | .pnpm-debug.log* 27 | 28 | # local env files 29 | .env*.local 30 | 31 | # vercel 32 | .vercel 33 | 34 | # typescript 35 | *.tsbuildinfo 36 | next-env.d.ts 37 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "typescript.tsdk": "node_modules\\typescript\\lib", 3 | "typescript.enablePromptUseWorkspaceTsdk": true 4 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). 2 | 3 | ## Getting Started 4 | 5 | First, run the development server: 6 | 7 | ```bash 8 | npm run dev 9 | # or 10 | yarn dev 11 | ``` 12 | 13 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. 14 | 15 | You can start editing the page by modifying `pages/index.tsx`. The page auto-updates as you edit the file. 16 | 17 | [API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/hello.ts`. 18 | 19 | The `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages. 20 | 21 | ## Learn More 22 | 23 | To learn more about Next.js, take a look at the following resources: 24 | 25 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. 26 | - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. 27 | 28 | You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome! 29 | 30 | ## Deploy on Vercel 31 | 32 | The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. 33 | 34 | Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details. 35 | -------------------------------------------------------------------------------- /app/head.tsx: -------------------------------------------------------------------------------- 1 | export default function Head() { 2 | return ( 3 | <> 4 | Jack's Blog 5 | 6 | 7 | 8 | ); 9 | } 10 | -------------------------------------------------------------------------------- /app/layout.tsx: -------------------------------------------------------------------------------- 1 | import Image from "next/image"; 2 | import Link from "next/link"; 3 | import "../styles/globals.css"; 4 | 5 | export default function RootLayout({ 6 | children, 7 | }: { 8 | children: React.ReactNode; 9 | }) { 10 | const header = ( 11 |
12 |
13 | {"logo"} 20 | 21 |

Jack's Blog

22 | 23 |

🤟 Welcome to my tech blog. 💻

24 |
25 |
26 | ); 27 | 28 | const footer = ( 29 | 34 | ); 35 | 36 | return ( 37 | 38 | 39 | 40 |
41 | {header} 42 | {children} 43 | {footer} 44 |
45 | 46 | 47 | ); 48 | } 49 | -------------------------------------------------------------------------------- /app/page.tsx: -------------------------------------------------------------------------------- 1 | import getPostMetadata from "../components/getPostMetadata"; 2 | import PostPreview from "../components/PostPreview"; 3 | 4 | const HomePage = () => { 5 | const postMetadata = getPostMetadata(); 6 | const postPreviews = postMetadata.map((post) => ( 7 | 8 | )); 9 | 10 | return ( 11 |
{postPreviews}
12 | ); 13 | }; 14 | 15 | export default HomePage; 16 | -------------------------------------------------------------------------------- /app/posts/[slug]/page.tsx: -------------------------------------------------------------------------------- 1 | import fs from "fs"; 2 | import Markdown from "markdown-to-jsx"; 3 | import matter from "gray-matter"; 4 | import getPostMetadata from "../../../components/getPostMetadata"; 5 | 6 | const getPostContent = (slug: string) => { 7 | const folder = "posts/"; 8 | const file = `${folder}${slug}.md`; 9 | const content = fs.readFileSync(file, "utf8"); 10 | const matterResult = matter(content); 11 | return matterResult; 12 | }; 13 | 14 | export const generateStaticParams = async () => { 15 | const posts = getPostMetadata(); 16 | return posts.map((post) => ({ 17 | slug: post.slug, 18 | })); 19 | }; 20 | 21 | const PostPage = (props: any) => { 22 | const slug = props.params.slug; 23 | const post = getPostContent(slug); 24 | return ( 25 |
26 |
27 |

{post.data.title}

28 |

{post.data.date}

29 |
30 | 31 |
32 | {post.content} 33 |
34 |
35 | ); 36 | }; 37 | 38 | export default PostPage; 39 | -------------------------------------------------------------------------------- /components/PostMetadata.ts: -------------------------------------------------------------------------------- 1 | export interface PostMetadata { 2 | title: string; 3 | date: string; 4 | subtitle: string; 5 | slug: string; 6 | } 7 | -------------------------------------------------------------------------------- /components/PostPreview.tsx: -------------------------------------------------------------------------------- 1 | import Link from "next/link"; 2 | import { PostMetadata } from "./PostMetadata"; 3 | 4 | const PostPreview = (props: PostMetadata) => { 5 | return ( 6 |
10 |

{props.date}

11 | 12 | 13 |

{props.title}

14 | 15 |

{props.subtitle}

16 |
17 | ); 18 | }; 19 | 20 | export default PostPreview; 21 | -------------------------------------------------------------------------------- /components/getPostMetadata.ts: -------------------------------------------------------------------------------- 1 | import fs from "fs"; 2 | import matter from "gray-matter"; 3 | import { PostMetadata } from "../components/PostMetadata"; 4 | 5 | const getPostMetadata = (): PostMetadata[] => { 6 | const folder = "posts/"; 7 | const files = fs.readdirSync(folder); 8 | const markdownPosts = files.filter((file) => file.endsWith(".md")); 9 | 10 | // Get gray-matter data from each file. 11 | const posts = markdownPosts.map((fileName) => { 12 | const fileContents = fs.readFileSync(`posts/${fileName}`, "utf8"); 13 | const matterResult = matter(fileContents); 14 | return { 15 | title: matterResult.data.title, 16 | date: matterResult.data.date, 17 | subtitle: matterResult.data.subtitle, 18 | slug: fileName.replace(".md", ""), 19 | }; 20 | }); 21 | 22 | return posts; 23 | }; 24 | 25 | export default getPostMetadata; 26 | -------------------------------------------------------------------------------- /next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = { 3 | reactStrictMode: true, 4 | swcMinify: true, 5 | experimental: { 6 | appDir: true, 7 | }, 8 | }; 9 | 10 | module.exports = nextConfig; 11 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nextjs-blog", 3 | "version": "0.1.0", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "nextjs-blog", 9 | "version": "0.1.0", 10 | "dependencies": { 11 | "@types/node": "18.11.9", 12 | "@types/react": "18.0.25", 13 | "@types/react-dom": "18.0.9", 14 | "gray-matter": "^4.0.3", 15 | "markdown-to-jsx": "^7.1.7", 16 | "next": "13.0.5", 17 | "react": "18.2.0", 18 | "react-dom": "18.2.0", 19 | "typescript": "4.9.3" 20 | }, 21 | "devDependencies": { 22 | "@tailwindcss/typography": "^0.5.8", 23 | "autoprefixer": "^10.4.13", 24 | "postcss": "^8.4.19", 25 | "tailwindcss": "^3.2.4" 26 | } 27 | }, 28 | "node_modules/@next/env": { 29 | "version": "13.0.5", 30 | "resolved": "https://registry.npmjs.org/@next/env/-/env-13.0.5.tgz", 31 | "integrity": "sha512-F3KLtiDrUslAZhTYTh8Zk5ZaavbYwLUn3NYPBnOjAXU8hWm0QVGVzKIOuURQ098ofRU4e9oglf3Sj9pFx5nI5w==" 32 | }, 33 | "node_modules/@next/swc-android-arm-eabi": { 34 | "version": "13.0.5", 35 | "resolved": "https://registry.npmjs.org/@next/swc-android-arm-eabi/-/swc-android-arm-eabi-13.0.5.tgz", 36 | "integrity": "sha512-YO691dxHlviy6H0eghgwqn+5kU9J3iQnKERHTDSppqjjGDBl6ab4wz9XfI5AhljjkaTg3TknHoIEWFDoZ4Ve8g==", 37 | "cpu": [ 38 | "arm" 39 | ], 40 | "optional": true, 41 | "os": [ 42 | "android" 43 | ], 44 | "engines": { 45 | "node": ">= 10" 46 | } 47 | }, 48 | "node_modules/@next/swc-android-arm64": { 49 | "version": "13.0.5", 50 | "resolved": "https://registry.npmjs.org/@next/swc-android-arm64/-/swc-android-arm64-13.0.5.tgz", 51 | "integrity": "sha512-ugbwffkUmp8cd2afehDC8LtQeFUxElRUBBngfB5UYSWBx18HW4OgzkPFIY8jUBH16zifvGZWXbICXJWDHrOLtw==", 52 | "cpu": [ 53 | "arm64" 54 | ], 55 | "optional": true, 56 | "os": [ 57 | "android" 58 | ], 59 | "engines": { 60 | "node": ">= 10" 61 | } 62 | }, 63 | "node_modules/@next/swc-darwin-arm64": { 64 | "version": "13.0.5", 65 | "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.0.5.tgz", 66 | "integrity": "sha512-mshlh8QOtOalfZbc17uNAftWgqHTKnrv6QUwBe+mpGz04eqsSUzVz1JGZEdIkmuDxOz00cK2NPoc+VHDXh99IQ==", 67 | "cpu": [ 68 | "arm64" 69 | ], 70 | "optional": true, 71 | "os": [ 72 | "darwin" 73 | ], 74 | "engines": { 75 | "node": ">= 10" 76 | } 77 | }, 78 | "node_modules/@next/swc-darwin-x64": { 79 | "version": "13.0.5", 80 | "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-13.0.5.tgz", 81 | "integrity": "sha512-SfigOKW4Z2UB3ruUPyvrlDIkcJq1hiw1wvYApWugD+tQsAkYZKEoz+/8emCmeYZ6Gwgi1WHV+z52Oj8u7bEHPg==", 82 | "cpu": [ 83 | "x64" 84 | ], 85 | "optional": true, 86 | "os": [ 87 | "darwin" 88 | ], 89 | "engines": { 90 | "node": ">= 10" 91 | } 92 | }, 93 | "node_modules/@next/swc-freebsd-x64": { 94 | "version": "13.0.5", 95 | "resolved": "https://registry.npmjs.org/@next/swc-freebsd-x64/-/swc-freebsd-x64-13.0.5.tgz", 96 | "integrity": "sha512-0NJg8HZr4yG8ynmMGFXQf+Mahvq4ZgBmUwSlLXXymgxEQgH17erH/LoR69uITtW+KTsALgk9axEt5AAabM4ucg==", 97 | "cpu": [ 98 | "x64" 99 | ], 100 | "optional": true, 101 | "os": [ 102 | "freebsd" 103 | ], 104 | "engines": { 105 | "node": ">= 10" 106 | } 107 | }, 108 | "node_modules/@next/swc-linux-arm-gnueabihf": { 109 | "version": "13.0.5", 110 | "resolved": "https://registry.npmjs.org/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-13.0.5.tgz", 111 | "integrity": "sha512-Cye+h3oDT3NDWjACMlRaolL8fokpKie34FlPj9nfoW7bYKmoMBY1d4IO/GgBF+5xEl7HkH0Ny/qex63vQ0pN+A==", 112 | "cpu": [ 113 | "arm" 114 | ], 115 | "optional": true, 116 | "os": [ 117 | "linux" 118 | ], 119 | "engines": { 120 | "node": ">= 10" 121 | } 122 | }, 123 | "node_modules/@next/swc-linux-arm64-gnu": { 124 | "version": "13.0.5", 125 | "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.0.5.tgz", 126 | "integrity": "sha512-5BfDS/VoRDR5QUGG9oedOCEZGmV2zxUVFYLUJVPMSMeIgqkjxWQBiG2BUHZI6/LGk9yvHmjx7BTvtBCLtRg6IQ==", 127 | "cpu": [ 128 | "arm64" 129 | ], 130 | "optional": true, 131 | "os": [ 132 | "linux" 133 | ], 134 | "engines": { 135 | "node": ">= 10" 136 | } 137 | }, 138 | "node_modules/@next/swc-linux-arm64-musl": { 139 | "version": "13.0.5", 140 | "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.0.5.tgz", 141 | "integrity": "sha512-xenvqlXz+KxVKAB1YR723gnVNszpsCvKZkiFFaAYqDGJ502YuqU2fwLsaSm/ASRizNcBYeo9HPLTyc3r/9cdMQ==", 142 | "cpu": [ 143 | "arm64" 144 | ], 145 | "optional": true, 146 | "os": [ 147 | "linux" 148 | ], 149 | "engines": { 150 | "node": ">= 10" 151 | } 152 | }, 153 | "node_modules/@next/swc-linux-x64-gnu": { 154 | "version": "13.0.5", 155 | "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.0.5.tgz", 156 | "integrity": "sha512-9Ahi1bbdXwhrWQmOyoTod23/hhK05da/FzodiNqd6drrMl1y7+RujoEcU8Dtw3H1mGWB+yuTlWo8B4Iba8hqiQ==", 157 | "cpu": [ 158 | "x64" 159 | ], 160 | "optional": true, 161 | "os": [ 162 | "linux" 163 | ], 164 | "engines": { 165 | "node": ">= 10" 166 | } 167 | }, 168 | "node_modules/@next/swc-linux-x64-musl": { 169 | "version": "13.0.5", 170 | "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.0.5.tgz", 171 | "integrity": "sha512-V+1mnh49qmS9fOZxVRbzjhBEz9IUGJ7AQ80JPWAYQM5LI4TxfdiF4APLPvJ52rOmNeTqnVz1bbKtVOso+7EZ4w==", 172 | "cpu": [ 173 | "x64" 174 | ], 175 | "optional": true, 176 | "os": [ 177 | "linux" 178 | ], 179 | "engines": { 180 | "node": ">= 10" 181 | } 182 | }, 183 | "node_modules/@next/swc-win32-arm64-msvc": { 184 | "version": "13.0.5", 185 | "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.0.5.tgz", 186 | "integrity": "sha512-wRE9rkp7I+/3Jf2T9PFIJOKq3adMWYEFkPOA7XAkUfYbQHlDJm/U5cVCWUsKByyQq5RThwufI91sgd19MfxRxg==", 187 | "cpu": [ 188 | "arm64" 189 | ], 190 | "optional": true, 191 | "os": [ 192 | "win32" 193 | ], 194 | "engines": { 195 | "node": ">= 10" 196 | } 197 | }, 198 | "node_modules/@next/swc-win32-ia32-msvc": { 199 | "version": "13.0.5", 200 | "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.0.5.tgz", 201 | "integrity": "sha512-Q1XQSLEhFuFhkKFdJIGt7cYQ4T3u6P5wrtUNreg5M+7P+fjSiC8+X+Vjcw+oebaacsdl0pWZlK+oACGafush1w==", 202 | "cpu": [ 203 | "ia32" 204 | ], 205 | "optional": true, 206 | "os": [ 207 | "win32" 208 | ], 209 | "engines": { 210 | "node": ">= 10" 211 | } 212 | }, 213 | "node_modules/@next/swc-win32-x64-msvc": { 214 | "version": "13.0.5", 215 | "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.0.5.tgz", 216 | "integrity": "sha512-t5gRblrwwiNZP6cT7NkxlgxrFgHWtv9ei5vUraCLgBqzvIsa7X+PnarZUeQCXqz6Jg9JSGGT9j8lvzD97UqeJQ==", 217 | "cpu": [ 218 | "x64" 219 | ], 220 | "optional": true, 221 | "os": [ 222 | "win32" 223 | ], 224 | "engines": { 225 | "node": ">= 10" 226 | } 227 | }, 228 | "node_modules/@nodelib/fs.scandir": { 229 | "version": "2.1.5", 230 | "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", 231 | "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", 232 | "dev": true, 233 | "dependencies": { 234 | "@nodelib/fs.stat": "2.0.5", 235 | "run-parallel": "^1.1.9" 236 | }, 237 | "engines": { 238 | "node": ">= 8" 239 | } 240 | }, 241 | "node_modules/@nodelib/fs.stat": { 242 | "version": "2.0.5", 243 | "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", 244 | "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", 245 | "dev": true, 246 | "engines": { 247 | "node": ">= 8" 248 | } 249 | }, 250 | "node_modules/@nodelib/fs.walk": { 251 | "version": "1.2.8", 252 | "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", 253 | "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", 254 | "dev": true, 255 | "dependencies": { 256 | "@nodelib/fs.scandir": "2.1.5", 257 | "fastq": "^1.6.0" 258 | }, 259 | "engines": { 260 | "node": ">= 8" 261 | } 262 | }, 263 | "node_modules/@swc/helpers": { 264 | "version": "0.4.14", 265 | "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.4.14.tgz", 266 | "integrity": "sha512-4C7nX/dvpzB7za4Ql9K81xK3HPxCpHMgwTZVyf+9JQ6VUbn9jjZVN7/Nkdz/Ugzs2CSjqnL/UPXroiVBVHUWUw==", 267 | "dependencies": { 268 | "tslib": "^2.4.0" 269 | } 270 | }, 271 | "node_modules/@tailwindcss/typography": { 272 | "version": "0.5.8", 273 | "resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.8.tgz", 274 | "integrity": "sha512-xGQEp8KXN8Sd8m6R4xYmwxghmswrd0cPnNI2Lc6fmrC3OojysTBJJGSIVwPV56q4t6THFUK3HJ0EaWwpglSxWw==", 275 | "dev": true, 276 | "dependencies": { 277 | "lodash.castarray": "^4.4.0", 278 | "lodash.isplainobject": "^4.0.6", 279 | "lodash.merge": "^4.6.2", 280 | "postcss-selector-parser": "6.0.10" 281 | }, 282 | "peerDependencies": { 283 | "tailwindcss": ">=3.0.0 || insiders" 284 | } 285 | }, 286 | "node_modules/@tailwindcss/typography/node_modules/postcss-selector-parser": { 287 | "version": "6.0.10", 288 | "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz", 289 | "integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==", 290 | "dev": true, 291 | "dependencies": { 292 | "cssesc": "^3.0.0", 293 | "util-deprecate": "^1.0.2" 294 | }, 295 | "engines": { 296 | "node": ">=4" 297 | } 298 | }, 299 | "node_modules/@types/node": { 300 | "version": "18.11.9", 301 | "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.9.tgz", 302 | "integrity": "sha512-CRpX21/kGdzjOpFsZSkcrXMGIBWMGNIHXXBVFSH+ggkftxg+XYP20TESbh+zFvFj3EQOl5byk0HTRn1IL6hbqg==" 303 | }, 304 | "node_modules/@types/prop-types": { 305 | "version": "15.7.5", 306 | "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", 307 | "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==" 308 | }, 309 | "node_modules/@types/react": { 310 | "version": "18.0.25", 311 | "resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.25.tgz", 312 | "integrity": "sha512-xD6c0KDT4m7n9uD4ZHi02lzskaiqcBxf4zi+tXZY98a04wvc0hi/TcCPC2FOESZi51Nd7tlUeOJY8RofL799/g==", 313 | "dependencies": { 314 | "@types/prop-types": "*", 315 | "@types/scheduler": "*", 316 | "csstype": "^3.0.2" 317 | } 318 | }, 319 | "node_modules/@types/react-dom": { 320 | "version": "18.0.9", 321 | "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.0.9.tgz", 322 | "integrity": "sha512-qnVvHxASt/H7i+XG1U1xMiY5t+IHcPGUK7TDMDzom08xa7e86eCeKOiLZezwCKVxJn6NEiiy2ekgX8aQssjIKg==", 323 | "dependencies": { 324 | "@types/react": "*" 325 | } 326 | }, 327 | "node_modules/@types/scheduler": { 328 | "version": "0.16.2", 329 | "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", 330 | "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==" 331 | }, 332 | "node_modules/acorn": { 333 | "version": "7.4.1", 334 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", 335 | "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", 336 | "dev": true, 337 | "bin": { 338 | "acorn": "bin/acorn" 339 | }, 340 | "engines": { 341 | "node": ">=0.4.0" 342 | } 343 | }, 344 | "node_modules/acorn-node": { 345 | "version": "1.8.2", 346 | "resolved": "https://registry.npmjs.org/acorn-node/-/acorn-node-1.8.2.tgz", 347 | "integrity": "sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A==", 348 | "dev": true, 349 | "dependencies": { 350 | "acorn": "^7.0.0", 351 | "acorn-walk": "^7.0.0", 352 | "xtend": "^4.0.2" 353 | } 354 | }, 355 | "node_modules/acorn-walk": { 356 | "version": "7.2.0", 357 | "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", 358 | "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", 359 | "dev": true, 360 | "engines": { 361 | "node": ">=0.4.0" 362 | } 363 | }, 364 | "node_modules/anymatch": { 365 | "version": "3.1.3", 366 | "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", 367 | "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", 368 | "dev": true, 369 | "dependencies": { 370 | "normalize-path": "^3.0.0", 371 | "picomatch": "^2.0.4" 372 | }, 373 | "engines": { 374 | "node": ">= 8" 375 | } 376 | }, 377 | "node_modules/arg": { 378 | "version": "5.0.2", 379 | "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", 380 | "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", 381 | "dev": true 382 | }, 383 | "node_modules/argparse": { 384 | "version": "1.0.10", 385 | "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", 386 | "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", 387 | "dependencies": { 388 | "sprintf-js": "~1.0.2" 389 | } 390 | }, 391 | "node_modules/autoprefixer": { 392 | "version": "10.4.13", 393 | "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.13.tgz", 394 | "integrity": "sha512-49vKpMqcZYsJjwotvt4+h/BCjJVnhGwcLpDt5xkcaOG3eLrG/HUYLagrihYsQ+qrIBgIzX1Rw7a6L8I/ZA1Atg==", 395 | "dev": true, 396 | "funding": [ 397 | { 398 | "type": "opencollective", 399 | "url": "https://opencollective.com/postcss/" 400 | }, 401 | { 402 | "type": "tidelift", 403 | "url": "https://tidelift.com/funding/github/npm/autoprefixer" 404 | } 405 | ], 406 | "dependencies": { 407 | "browserslist": "^4.21.4", 408 | "caniuse-lite": "^1.0.30001426", 409 | "fraction.js": "^4.2.0", 410 | "normalize-range": "^0.1.2", 411 | "picocolors": "^1.0.0", 412 | "postcss-value-parser": "^4.2.0" 413 | }, 414 | "bin": { 415 | "autoprefixer": "bin/autoprefixer" 416 | }, 417 | "engines": { 418 | "node": "^10 || ^12 || >=14" 419 | }, 420 | "peerDependencies": { 421 | "postcss": "^8.1.0" 422 | } 423 | }, 424 | "node_modules/binary-extensions": { 425 | "version": "2.2.0", 426 | "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", 427 | "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", 428 | "dev": true, 429 | "engines": { 430 | "node": ">=8" 431 | } 432 | }, 433 | "node_modules/braces": { 434 | "version": "3.0.2", 435 | "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", 436 | "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", 437 | "dev": true, 438 | "dependencies": { 439 | "fill-range": "^7.0.1" 440 | }, 441 | "engines": { 442 | "node": ">=8" 443 | } 444 | }, 445 | "node_modules/browserslist": { 446 | "version": "4.21.4", 447 | "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.4.tgz", 448 | "integrity": "sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==", 449 | "dev": true, 450 | "funding": [ 451 | { 452 | "type": "opencollective", 453 | "url": "https://opencollective.com/browserslist" 454 | }, 455 | { 456 | "type": "tidelift", 457 | "url": "https://tidelift.com/funding/github/npm/browserslist" 458 | } 459 | ], 460 | "dependencies": { 461 | "caniuse-lite": "^1.0.30001400", 462 | "electron-to-chromium": "^1.4.251", 463 | "node-releases": "^2.0.6", 464 | "update-browserslist-db": "^1.0.9" 465 | }, 466 | "bin": { 467 | "browserslist": "cli.js" 468 | }, 469 | "engines": { 470 | "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" 471 | } 472 | }, 473 | "node_modules/camelcase-css": { 474 | "version": "2.0.1", 475 | "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", 476 | "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", 477 | "dev": true, 478 | "engines": { 479 | "node": ">= 6" 480 | } 481 | }, 482 | "node_modules/caniuse-lite": { 483 | "version": "1.0.30001434", 484 | "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001434.tgz", 485 | "integrity": "sha512-aOBHrLmTQw//WFa2rcF1If9fa3ypkC1wzqqiKHgfdrXTWcU8C4gKVZT77eQAPWN1APys3+uQ0Df07rKauXGEYA==", 486 | "funding": [ 487 | { 488 | "type": "opencollective", 489 | "url": "https://opencollective.com/browserslist" 490 | }, 491 | { 492 | "type": "tidelift", 493 | "url": "https://tidelift.com/funding/github/npm/caniuse-lite" 494 | } 495 | ] 496 | }, 497 | "node_modules/chokidar": { 498 | "version": "3.5.3", 499 | "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", 500 | "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", 501 | "dev": true, 502 | "funding": [ 503 | { 504 | "type": "individual", 505 | "url": "https://paulmillr.com/funding/" 506 | } 507 | ], 508 | "dependencies": { 509 | "anymatch": "~3.1.2", 510 | "braces": "~3.0.2", 511 | "glob-parent": "~5.1.2", 512 | "is-binary-path": "~2.1.0", 513 | "is-glob": "~4.0.1", 514 | "normalize-path": "~3.0.0", 515 | "readdirp": "~3.6.0" 516 | }, 517 | "engines": { 518 | "node": ">= 8.10.0" 519 | }, 520 | "optionalDependencies": { 521 | "fsevents": "~2.3.2" 522 | } 523 | }, 524 | "node_modules/chokidar/node_modules/glob-parent": { 525 | "version": "5.1.2", 526 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", 527 | "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", 528 | "dev": true, 529 | "dependencies": { 530 | "is-glob": "^4.0.1" 531 | }, 532 | "engines": { 533 | "node": ">= 6" 534 | } 535 | }, 536 | "node_modules/client-only": { 537 | "version": "0.0.1", 538 | "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", 539 | "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==" 540 | }, 541 | "node_modules/color-name": { 542 | "version": "1.1.4", 543 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", 544 | "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", 545 | "dev": true 546 | }, 547 | "node_modules/cssesc": { 548 | "version": "3.0.0", 549 | "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", 550 | "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", 551 | "dev": true, 552 | "bin": { 553 | "cssesc": "bin/cssesc" 554 | }, 555 | "engines": { 556 | "node": ">=4" 557 | } 558 | }, 559 | "node_modules/csstype": { 560 | "version": "3.1.1", 561 | "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.1.tgz", 562 | "integrity": "sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==" 563 | }, 564 | "node_modules/defined": { 565 | "version": "1.0.1", 566 | "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.1.tgz", 567 | "integrity": "sha512-hsBd2qSVCRE+5PmNdHt1uzyrFu5d3RwmFDKzyNZMFq/EwDNJF7Ee5+D5oEKF0hU6LhtoUF1macFvOe4AskQC1Q==", 568 | "dev": true, 569 | "funding": { 570 | "url": "https://github.com/sponsors/ljharb" 571 | } 572 | }, 573 | "node_modules/detective": { 574 | "version": "5.2.1", 575 | "resolved": "https://registry.npmjs.org/detective/-/detective-5.2.1.tgz", 576 | "integrity": "sha512-v9XE1zRnz1wRtgurGu0Bs8uHKFSTdteYZNbIPFVhUZ39L/S79ppMpdmVOZAnoz1jfEFodc48n6MX483Xo3t1yw==", 577 | "dev": true, 578 | "dependencies": { 579 | "acorn-node": "^1.8.2", 580 | "defined": "^1.0.0", 581 | "minimist": "^1.2.6" 582 | }, 583 | "bin": { 584 | "detective": "bin/detective.js" 585 | }, 586 | "engines": { 587 | "node": ">=0.8.0" 588 | } 589 | }, 590 | "node_modules/didyoumean": { 591 | "version": "1.2.2", 592 | "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", 593 | "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", 594 | "dev": true 595 | }, 596 | "node_modules/dlv": { 597 | "version": "1.1.3", 598 | "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", 599 | "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", 600 | "dev": true 601 | }, 602 | "node_modules/electron-to-chromium": { 603 | "version": "1.4.284", 604 | "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz", 605 | "integrity": "sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA==", 606 | "dev": true 607 | }, 608 | "node_modules/escalade": { 609 | "version": "3.1.1", 610 | "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", 611 | "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", 612 | "dev": true, 613 | "engines": { 614 | "node": ">=6" 615 | } 616 | }, 617 | "node_modules/esprima": { 618 | "version": "4.0.1", 619 | "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", 620 | "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", 621 | "bin": { 622 | "esparse": "bin/esparse.js", 623 | "esvalidate": "bin/esvalidate.js" 624 | }, 625 | "engines": { 626 | "node": ">=4" 627 | } 628 | }, 629 | "node_modules/extend-shallow": { 630 | "version": "2.0.1", 631 | "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", 632 | "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", 633 | "dependencies": { 634 | "is-extendable": "^0.1.0" 635 | }, 636 | "engines": { 637 | "node": ">=0.10.0" 638 | } 639 | }, 640 | "node_modules/fast-glob": { 641 | "version": "3.2.12", 642 | "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", 643 | "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", 644 | "dev": true, 645 | "dependencies": { 646 | "@nodelib/fs.stat": "^2.0.2", 647 | "@nodelib/fs.walk": "^1.2.3", 648 | "glob-parent": "^5.1.2", 649 | "merge2": "^1.3.0", 650 | "micromatch": "^4.0.4" 651 | }, 652 | "engines": { 653 | "node": ">=8.6.0" 654 | } 655 | }, 656 | "node_modules/fast-glob/node_modules/glob-parent": { 657 | "version": "5.1.2", 658 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", 659 | "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", 660 | "dev": true, 661 | "dependencies": { 662 | "is-glob": "^4.0.1" 663 | }, 664 | "engines": { 665 | "node": ">= 6" 666 | } 667 | }, 668 | "node_modules/fastq": { 669 | "version": "1.13.0", 670 | "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", 671 | "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", 672 | "dev": true, 673 | "dependencies": { 674 | "reusify": "^1.0.4" 675 | } 676 | }, 677 | "node_modules/fill-range": { 678 | "version": "7.0.1", 679 | "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", 680 | "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", 681 | "dev": true, 682 | "dependencies": { 683 | "to-regex-range": "^5.0.1" 684 | }, 685 | "engines": { 686 | "node": ">=8" 687 | } 688 | }, 689 | "node_modules/fraction.js": { 690 | "version": "4.2.0", 691 | "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz", 692 | "integrity": "sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==", 693 | "dev": true, 694 | "engines": { 695 | "node": "*" 696 | }, 697 | "funding": { 698 | "type": "patreon", 699 | "url": "https://www.patreon.com/infusion" 700 | } 701 | }, 702 | "node_modules/fsevents": { 703 | "version": "2.3.2", 704 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", 705 | "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", 706 | "dev": true, 707 | "hasInstallScript": true, 708 | "optional": true, 709 | "os": [ 710 | "darwin" 711 | ], 712 | "engines": { 713 | "node": "^8.16.0 || ^10.6.0 || >=11.0.0" 714 | } 715 | }, 716 | "node_modules/function-bind": { 717 | "version": "1.1.1", 718 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", 719 | "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", 720 | "dev": true 721 | }, 722 | "node_modules/glob-parent": { 723 | "version": "6.0.2", 724 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", 725 | "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", 726 | "dev": true, 727 | "dependencies": { 728 | "is-glob": "^4.0.3" 729 | }, 730 | "engines": { 731 | "node": ">=10.13.0" 732 | } 733 | }, 734 | "node_modules/gray-matter": { 735 | "version": "4.0.3", 736 | "resolved": "https://registry.npmjs.org/gray-matter/-/gray-matter-4.0.3.tgz", 737 | "integrity": "sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q==", 738 | "dependencies": { 739 | "js-yaml": "^3.13.1", 740 | "kind-of": "^6.0.2", 741 | "section-matter": "^1.0.0", 742 | "strip-bom-string": "^1.0.0" 743 | }, 744 | "engines": { 745 | "node": ">=6.0" 746 | } 747 | }, 748 | "node_modules/has": { 749 | "version": "1.0.3", 750 | "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", 751 | "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", 752 | "dev": true, 753 | "dependencies": { 754 | "function-bind": "^1.1.1" 755 | }, 756 | "engines": { 757 | "node": ">= 0.4.0" 758 | } 759 | }, 760 | "node_modules/is-binary-path": { 761 | "version": "2.1.0", 762 | "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", 763 | "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", 764 | "dev": true, 765 | "dependencies": { 766 | "binary-extensions": "^2.0.0" 767 | }, 768 | "engines": { 769 | "node": ">=8" 770 | } 771 | }, 772 | "node_modules/is-core-module": { 773 | "version": "2.11.0", 774 | "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", 775 | "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", 776 | "dev": true, 777 | "dependencies": { 778 | "has": "^1.0.3" 779 | }, 780 | "funding": { 781 | "url": "https://github.com/sponsors/ljharb" 782 | } 783 | }, 784 | "node_modules/is-extendable": { 785 | "version": "0.1.1", 786 | "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", 787 | "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", 788 | "engines": { 789 | "node": ">=0.10.0" 790 | } 791 | }, 792 | "node_modules/is-extglob": { 793 | "version": "2.1.1", 794 | "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", 795 | "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", 796 | "dev": true, 797 | "engines": { 798 | "node": ">=0.10.0" 799 | } 800 | }, 801 | "node_modules/is-glob": { 802 | "version": "4.0.3", 803 | "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", 804 | "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", 805 | "dev": true, 806 | "dependencies": { 807 | "is-extglob": "^2.1.1" 808 | }, 809 | "engines": { 810 | "node": ">=0.10.0" 811 | } 812 | }, 813 | "node_modules/is-number": { 814 | "version": "7.0.0", 815 | "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", 816 | "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", 817 | "dev": true, 818 | "engines": { 819 | "node": ">=0.12.0" 820 | } 821 | }, 822 | "node_modules/js-tokens": { 823 | "version": "4.0.0", 824 | "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", 825 | "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" 826 | }, 827 | "node_modules/js-yaml": { 828 | "version": "3.14.1", 829 | "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", 830 | "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", 831 | "dependencies": { 832 | "argparse": "^1.0.7", 833 | "esprima": "^4.0.0" 834 | }, 835 | "bin": { 836 | "js-yaml": "bin/js-yaml.js" 837 | } 838 | }, 839 | "node_modules/kind-of": { 840 | "version": "6.0.3", 841 | "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", 842 | "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", 843 | "engines": { 844 | "node": ">=0.10.0" 845 | } 846 | }, 847 | "node_modules/lilconfig": { 848 | "version": "2.0.6", 849 | "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.6.tgz", 850 | "integrity": "sha512-9JROoBW7pobfsx+Sq2JsASvCo6Pfo6WWoUW79HuB1BCoBXD4PLWJPqDF6fNj67pqBYTbAHkE57M1kS/+L1neOg==", 851 | "dev": true, 852 | "engines": { 853 | "node": ">=10" 854 | } 855 | }, 856 | "node_modules/lodash.castarray": { 857 | "version": "4.4.0", 858 | "resolved": "https://registry.npmjs.org/lodash.castarray/-/lodash.castarray-4.4.0.tgz", 859 | "integrity": "sha512-aVx8ztPv7/2ULbArGJ2Y42bG1mEQ5mGjpdvrbJcJFU3TbYybe+QlLS4pst9zV52ymy2in1KpFPiZnAOATxD4+Q==", 860 | "dev": true 861 | }, 862 | "node_modules/lodash.isplainobject": { 863 | "version": "4.0.6", 864 | "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", 865 | "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", 866 | "dev": true 867 | }, 868 | "node_modules/lodash.merge": { 869 | "version": "4.6.2", 870 | "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", 871 | "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", 872 | "dev": true 873 | }, 874 | "node_modules/loose-envify": { 875 | "version": "1.4.0", 876 | "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", 877 | "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", 878 | "dependencies": { 879 | "js-tokens": "^3.0.0 || ^4.0.0" 880 | }, 881 | "bin": { 882 | "loose-envify": "cli.js" 883 | } 884 | }, 885 | "node_modules/markdown-to-jsx": { 886 | "version": "7.1.7", 887 | "resolved": "https://registry.npmjs.org/markdown-to-jsx/-/markdown-to-jsx-7.1.7.tgz", 888 | "integrity": "sha512-VI3TyyHlGkO8uFle0IOibzpO1c1iJDcXcS/zBrQrXQQvJ2tpdwVzVZ7XdKsyRz1NdRmre4dqQkMZzUHaKIG/1w==", 889 | "engines": { 890 | "node": ">= 10" 891 | }, 892 | "peerDependencies": { 893 | "react": ">= 0.14.0" 894 | } 895 | }, 896 | "node_modules/merge2": { 897 | "version": "1.4.1", 898 | "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", 899 | "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", 900 | "dev": true, 901 | "engines": { 902 | "node": ">= 8" 903 | } 904 | }, 905 | "node_modules/micromatch": { 906 | "version": "4.0.5", 907 | "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", 908 | "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", 909 | "dev": true, 910 | "dependencies": { 911 | "braces": "^3.0.2", 912 | "picomatch": "^2.3.1" 913 | }, 914 | "engines": { 915 | "node": ">=8.6" 916 | } 917 | }, 918 | "node_modules/minimist": { 919 | "version": "1.2.7", 920 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz", 921 | "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==", 922 | "dev": true, 923 | "funding": { 924 | "url": "https://github.com/sponsors/ljharb" 925 | } 926 | }, 927 | "node_modules/nanoid": { 928 | "version": "3.3.4", 929 | "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", 930 | "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==", 931 | "bin": { 932 | "nanoid": "bin/nanoid.cjs" 933 | }, 934 | "engines": { 935 | "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" 936 | } 937 | }, 938 | "node_modules/next": { 939 | "version": "13.0.5", 940 | "resolved": "https://registry.npmjs.org/next/-/next-13.0.5.tgz", 941 | "integrity": "sha512-awpc3DkphyKydwCotcBnuKwh6hMqkT5xdiBK4OatJtOZurDPBYLP62jtM2be/4OunpmwIbsS0Eyv+ZGU97ciEg==", 942 | "dependencies": { 943 | "@next/env": "13.0.5", 944 | "@swc/helpers": "0.4.14", 945 | "caniuse-lite": "^1.0.30001406", 946 | "postcss": "8.4.14", 947 | "styled-jsx": "5.1.0" 948 | }, 949 | "bin": { 950 | "next": "dist/bin/next" 951 | }, 952 | "engines": { 953 | "node": ">=14.6.0" 954 | }, 955 | "optionalDependencies": { 956 | "@next/swc-android-arm-eabi": "13.0.5", 957 | "@next/swc-android-arm64": "13.0.5", 958 | "@next/swc-darwin-arm64": "13.0.5", 959 | "@next/swc-darwin-x64": "13.0.5", 960 | "@next/swc-freebsd-x64": "13.0.5", 961 | "@next/swc-linux-arm-gnueabihf": "13.0.5", 962 | "@next/swc-linux-arm64-gnu": "13.0.5", 963 | "@next/swc-linux-arm64-musl": "13.0.5", 964 | "@next/swc-linux-x64-gnu": "13.0.5", 965 | "@next/swc-linux-x64-musl": "13.0.5", 966 | "@next/swc-win32-arm64-msvc": "13.0.5", 967 | "@next/swc-win32-ia32-msvc": "13.0.5", 968 | "@next/swc-win32-x64-msvc": "13.0.5" 969 | }, 970 | "peerDependencies": { 971 | "fibers": ">= 3.1.0", 972 | "node-sass": "^6.0.0 || ^7.0.0", 973 | "react": "^18.2.0", 974 | "react-dom": "^18.2.0", 975 | "sass": "^1.3.0" 976 | }, 977 | "peerDependenciesMeta": { 978 | "fibers": { 979 | "optional": true 980 | }, 981 | "node-sass": { 982 | "optional": true 983 | }, 984 | "sass": { 985 | "optional": true 986 | } 987 | } 988 | }, 989 | "node_modules/next/node_modules/postcss": { 990 | "version": "8.4.14", 991 | "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.14.tgz", 992 | "integrity": "sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig==", 993 | "funding": [ 994 | { 995 | "type": "opencollective", 996 | "url": "https://opencollective.com/postcss/" 997 | }, 998 | { 999 | "type": "tidelift", 1000 | "url": "https://tidelift.com/funding/github/npm/postcss" 1001 | } 1002 | ], 1003 | "dependencies": { 1004 | "nanoid": "^3.3.4", 1005 | "picocolors": "^1.0.0", 1006 | "source-map-js": "^1.0.2" 1007 | }, 1008 | "engines": { 1009 | "node": "^10 || ^12 || >=14" 1010 | } 1011 | }, 1012 | "node_modules/node-releases": { 1013 | "version": "2.0.6", 1014 | "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz", 1015 | "integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==", 1016 | "dev": true 1017 | }, 1018 | "node_modules/normalize-path": { 1019 | "version": "3.0.0", 1020 | "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", 1021 | "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", 1022 | "dev": true, 1023 | "engines": { 1024 | "node": ">=0.10.0" 1025 | } 1026 | }, 1027 | "node_modules/normalize-range": { 1028 | "version": "0.1.2", 1029 | "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", 1030 | "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", 1031 | "dev": true, 1032 | "engines": { 1033 | "node": ">=0.10.0" 1034 | } 1035 | }, 1036 | "node_modules/object-hash": { 1037 | "version": "3.0.0", 1038 | "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", 1039 | "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", 1040 | "dev": true, 1041 | "engines": { 1042 | "node": ">= 6" 1043 | } 1044 | }, 1045 | "node_modules/path-parse": { 1046 | "version": "1.0.7", 1047 | "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", 1048 | "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", 1049 | "dev": true 1050 | }, 1051 | "node_modules/picocolors": { 1052 | "version": "1.0.0", 1053 | "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", 1054 | "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" 1055 | }, 1056 | "node_modules/picomatch": { 1057 | "version": "2.3.1", 1058 | "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", 1059 | "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", 1060 | "dev": true, 1061 | "engines": { 1062 | "node": ">=8.6" 1063 | }, 1064 | "funding": { 1065 | "url": "https://github.com/sponsors/jonschlinkert" 1066 | } 1067 | }, 1068 | "node_modules/pify": { 1069 | "version": "2.3.0", 1070 | "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", 1071 | "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", 1072 | "dev": true, 1073 | "engines": { 1074 | "node": ">=0.10.0" 1075 | } 1076 | }, 1077 | "node_modules/postcss": { 1078 | "version": "8.4.19", 1079 | "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.19.tgz", 1080 | "integrity": "sha512-h+pbPsyhlYj6N2ozBmHhHrs9DzGmbaarbLvWipMRO7RLS+v4onj26MPFXA5OBYFxyqYhUJK456SwDcY9H2/zsA==", 1081 | "dev": true, 1082 | "funding": [ 1083 | { 1084 | "type": "opencollective", 1085 | "url": "https://opencollective.com/postcss/" 1086 | }, 1087 | { 1088 | "type": "tidelift", 1089 | "url": "https://tidelift.com/funding/github/npm/postcss" 1090 | } 1091 | ], 1092 | "dependencies": { 1093 | "nanoid": "^3.3.4", 1094 | "picocolors": "^1.0.0", 1095 | "source-map-js": "^1.0.2" 1096 | }, 1097 | "engines": { 1098 | "node": "^10 || ^12 || >=14" 1099 | } 1100 | }, 1101 | "node_modules/postcss-import": { 1102 | "version": "14.1.0", 1103 | "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-14.1.0.tgz", 1104 | "integrity": "sha512-flwI+Vgm4SElObFVPpTIT7SU7R3qk2L7PyduMcokiaVKuWv9d/U+Gm/QAd8NDLuykTWTkcrjOeD2Pp1rMeBTGw==", 1105 | "dev": true, 1106 | "dependencies": { 1107 | "postcss-value-parser": "^4.0.0", 1108 | "read-cache": "^1.0.0", 1109 | "resolve": "^1.1.7" 1110 | }, 1111 | "engines": { 1112 | "node": ">=10.0.0" 1113 | }, 1114 | "peerDependencies": { 1115 | "postcss": "^8.0.0" 1116 | } 1117 | }, 1118 | "node_modules/postcss-js": { 1119 | "version": "4.0.0", 1120 | "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.0.tgz", 1121 | "integrity": "sha512-77QESFBwgX4irogGVPgQ5s07vLvFqWr228qZY+w6lW599cRlK/HmnlivnnVUxkjHnCu4J16PDMHcH+e+2HbvTQ==", 1122 | "dev": true, 1123 | "dependencies": { 1124 | "camelcase-css": "^2.0.1" 1125 | }, 1126 | "engines": { 1127 | "node": "^12 || ^14 || >= 16" 1128 | }, 1129 | "funding": { 1130 | "type": "opencollective", 1131 | "url": "https://opencollective.com/postcss/" 1132 | }, 1133 | "peerDependencies": { 1134 | "postcss": "^8.3.3" 1135 | } 1136 | }, 1137 | "node_modules/postcss-load-config": { 1138 | "version": "3.1.4", 1139 | "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-3.1.4.tgz", 1140 | "integrity": "sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==", 1141 | "dev": true, 1142 | "dependencies": { 1143 | "lilconfig": "^2.0.5", 1144 | "yaml": "^1.10.2" 1145 | }, 1146 | "engines": { 1147 | "node": ">= 10" 1148 | }, 1149 | "funding": { 1150 | "type": "opencollective", 1151 | "url": "https://opencollective.com/postcss/" 1152 | }, 1153 | "peerDependencies": { 1154 | "postcss": ">=8.0.9", 1155 | "ts-node": ">=9.0.0" 1156 | }, 1157 | "peerDependenciesMeta": { 1158 | "postcss": { 1159 | "optional": true 1160 | }, 1161 | "ts-node": { 1162 | "optional": true 1163 | } 1164 | } 1165 | }, 1166 | "node_modules/postcss-nested": { 1167 | "version": "6.0.0", 1168 | "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.0.0.tgz", 1169 | "integrity": "sha512-0DkamqrPcmkBDsLn+vQDIrtkSbNkv5AD/M322ySo9kqFkCIYklym2xEmWkwo+Y3/qZo34tzEPNUw4y7yMCdv5w==", 1170 | "dev": true, 1171 | "dependencies": { 1172 | "postcss-selector-parser": "^6.0.10" 1173 | }, 1174 | "engines": { 1175 | "node": ">=12.0" 1176 | }, 1177 | "funding": { 1178 | "type": "opencollective", 1179 | "url": "https://opencollective.com/postcss/" 1180 | }, 1181 | "peerDependencies": { 1182 | "postcss": "^8.2.14" 1183 | } 1184 | }, 1185 | "node_modules/postcss-selector-parser": { 1186 | "version": "6.0.11", 1187 | "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.11.tgz", 1188 | "integrity": "sha512-zbARubNdogI9j7WY4nQJBiNqQf3sLS3wCP4WfOidu+p28LofJqDH1tcXypGrcmMHhDk2t9wGhCsYe/+szLTy1g==", 1189 | "dev": true, 1190 | "dependencies": { 1191 | "cssesc": "^3.0.0", 1192 | "util-deprecate": "^1.0.2" 1193 | }, 1194 | "engines": { 1195 | "node": ">=4" 1196 | } 1197 | }, 1198 | "node_modules/postcss-value-parser": { 1199 | "version": "4.2.0", 1200 | "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", 1201 | "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", 1202 | "dev": true 1203 | }, 1204 | "node_modules/queue-microtask": { 1205 | "version": "1.2.3", 1206 | "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", 1207 | "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", 1208 | "dev": true, 1209 | "funding": [ 1210 | { 1211 | "type": "github", 1212 | "url": "https://github.com/sponsors/feross" 1213 | }, 1214 | { 1215 | "type": "patreon", 1216 | "url": "https://www.patreon.com/feross" 1217 | }, 1218 | { 1219 | "type": "consulting", 1220 | "url": "https://feross.org/support" 1221 | } 1222 | ] 1223 | }, 1224 | "node_modules/quick-lru": { 1225 | "version": "5.1.1", 1226 | "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", 1227 | "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", 1228 | "dev": true, 1229 | "engines": { 1230 | "node": ">=10" 1231 | }, 1232 | "funding": { 1233 | "url": "https://github.com/sponsors/sindresorhus" 1234 | } 1235 | }, 1236 | "node_modules/react": { 1237 | "version": "18.2.0", 1238 | "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", 1239 | "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", 1240 | "dependencies": { 1241 | "loose-envify": "^1.1.0" 1242 | }, 1243 | "engines": { 1244 | "node": ">=0.10.0" 1245 | } 1246 | }, 1247 | "node_modules/react-dom": { 1248 | "version": "18.2.0", 1249 | "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", 1250 | "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", 1251 | "dependencies": { 1252 | "loose-envify": "^1.1.0", 1253 | "scheduler": "^0.23.0" 1254 | }, 1255 | "peerDependencies": { 1256 | "react": "^18.2.0" 1257 | } 1258 | }, 1259 | "node_modules/read-cache": { 1260 | "version": "1.0.0", 1261 | "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", 1262 | "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", 1263 | "dev": true, 1264 | "dependencies": { 1265 | "pify": "^2.3.0" 1266 | } 1267 | }, 1268 | "node_modules/readdirp": { 1269 | "version": "3.6.0", 1270 | "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", 1271 | "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", 1272 | "dev": true, 1273 | "dependencies": { 1274 | "picomatch": "^2.2.1" 1275 | }, 1276 | "engines": { 1277 | "node": ">=8.10.0" 1278 | } 1279 | }, 1280 | "node_modules/resolve": { 1281 | "version": "1.22.1", 1282 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", 1283 | "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", 1284 | "dev": true, 1285 | "dependencies": { 1286 | "is-core-module": "^2.9.0", 1287 | "path-parse": "^1.0.7", 1288 | "supports-preserve-symlinks-flag": "^1.0.0" 1289 | }, 1290 | "bin": { 1291 | "resolve": "bin/resolve" 1292 | }, 1293 | "funding": { 1294 | "url": "https://github.com/sponsors/ljharb" 1295 | } 1296 | }, 1297 | "node_modules/reusify": { 1298 | "version": "1.0.4", 1299 | "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", 1300 | "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", 1301 | "dev": true, 1302 | "engines": { 1303 | "iojs": ">=1.0.0", 1304 | "node": ">=0.10.0" 1305 | } 1306 | }, 1307 | "node_modules/run-parallel": { 1308 | "version": "1.2.0", 1309 | "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", 1310 | "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", 1311 | "dev": true, 1312 | "funding": [ 1313 | { 1314 | "type": "github", 1315 | "url": "https://github.com/sponsors/feross" 1316 | }, 1317 | { 1318 | "type": "patreon", 1319 | "url": "https://www.patreon.com/feross" 1320 | }, 1321 | { 1322 | "type": "consulting", 1323 | "url": "https://feross.org/support" 1324 | } 1325 | ], 1326 | "dependencies": { 1327 | "queue-microtask": "^1.2.2" 1328 | } 1329 | }, 1330 | "node_modules/scheduler": { 1331 | "version": "0.23.0", 1332 | "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", 1333 | "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", 1334 | "dependencies": { 1335 | "loose-envify": "^1.1.0" 1336 | } 1337 | }, 1338 | "node_modules/section-matter": { 1339 | "version": "1.0.0", 1340 | "resolved": "https://registry.npmjs.org/section-matter/-/section-matter-1.0.0.tgz", 1341 | "integrity": "sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA==", 1342 | "dependencies": { 1343 | "extend-shallow": "^2.0.1", 1344 | "kind-of": "^6.0.0" 1345 | }, 1346 | "engines": { 1347 | "node": ">=4" 1348 | } 1349 | }, 1350 | "node_modules/source-map-js": { 1351 | "version": "1.0.2", 1352 | "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", 1353 | "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", 1354 | "engines": { 1355 | "node": ">=0.10.0" 1356 | } 1357 | }, 1358 | "node_modules/sprintf-js": { 1359 | "version": "1.0.3", 1360 | "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", 1361 | "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" 1362 | }, 1363 | "node_modules/strip-bom-string": { 1364 | "version": "1.0.0", 1365 | "resolved": "https://registry.npmjs.org/strip-bom-string/-/strip-bom-string-1.0.0.tgz", 1366 | "integrity": "sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g==", 1367 | "engines": { 1368 | "node": ">=0.10.0" 1369 | } 1370 | }, 1371 | "node_modules/styled-jsx": { 1372 | "version": "5.1.0", 1373 | "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.0.tgz", 1374 | "integrity": "sha512-/iHaRJt9U7T+5tp6TRelLnqBqiaIT0HsO0+vgyj8hK2KUk7aejFqRrumqPUlAqDwAj8IbS/1hk3IhBAAK/FCUQ==", 1375 | "dependencies": { 1376 | "client-only": "0.0.1" 1377 | }, 1378 | "engines": { 1379 | "node": ">= 12.0.0" 1380 | }, 1381 | "peerDependencies": { 1382 | "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0" 1383 | }, 1384 | "peerDependenciesMeta": { 1385 | "@babel/core": { 1386 | "optional": true 1387 | }, 1388 | "babel-plugin-macros": { 1389 | "optional": true 1390 | } 1391 | } 1392 | }, 1393 | "node_modules/supports-preserve-symlinks-flag": { 1394 | "version": "1.0.0", 1395 | "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", 1396 | "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", 1397 | "dev": true, 1398 | "engines": { 1399 | "node": ">= 0.4" 1400 | }, 1401 | "funding": { 1402 | "url": "https://github.com/sponsors/ljharb" 1403 | } 1404 | }, 1405 | "node_modules/tailwindcss": { 1406 | "version": "3.2.4", 1407 | "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.2.4.tgz", 1408 | "integrity": "sha512-AhwtHCKMtR71JgeYDaswmZXhPcW9iuI9Sp2LvZPo9upDZ7231ZJ7eA9RaURbhpXGVlrjX4cFNlB4ieTetEb7hQ==", 1409 | "dev": true, 1410 | "dependencies": { 1411 | "arg": "^5.0.2", 1412 | "chokidar": "^3.5.3", 1413 | "color-name": "^1.1.4", 1414 | "detective": "^5.2.1", 1415 | "didyoumean": "^1.2.2", 1416 | "dlv": "^1.1.3", 1417 | "fast-glob": "^3.2.12", 1418 | "glob-parent": "^6.0.2", 1419 | "is-glob": "^4.0.3", 1420 | "lilconfig": "^2.0.6", 1421 | "micromatch": "^4.0.5", 1422 | "normalize-path": "^3.0.0", 1423 | "object-hash": "^3.0.0", 1424 | "picocolors": "^1.0.0", 1425 | "postcss": "^8.4.18", 1426 | "postcss-import": "^14.1.0", 1427 | "postcss-js": "^4.0.0", 1428 | "postcss-load-config": "^3.1.4", 1429 | "postcss-nested": "6.0.0", 1430 | "postcss-selector-parser": "^6.0.10", 1431 | "postcss-value-parser": "^4.2.0", 1432 | "quick-lru": "^5.1.1", 1433 | "resolve": "^1.22.1" 1434 | }, 1435 | "bin": { 1436 | "tailwind": "lib/cli.js", 1437 | "tailwindcss": "lib/cli.js" 1438 | }, 1439 | "engines": { 1440 | "node": ">=12.13.0" 1441 | }, 1442 | "peerDependencies": { 1443 | "postcss": "^8.0.9" 1444 | } 1445 | }, 1446 | "node_modules/to-regex-range": { 1447 | "version": "5.0.1", 1448 | "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", 1449 | "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", 1450 | "dev": true, 1451 | "dependencies": { 1452 | "is-number": "^7.0.0" 1453 | }, 1454 | "engines": { 1455 | "node": ">=8.0" 1456 | } 1457 | }, 1458 | "node_modules/tslib": { 1459 | "version": "2.4.1", 1460 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz", 1461 | "integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==" 1462 | }, 1463 | "node_modules/typescript": { 1464 | "version": "4.9.3", 1465 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.3.tgz", 1466 | "integrity": "sha512-CIfGzTelbKNEnLpLdGFgdyKhG23CKdKgQPOBc+OUNrkJ2vr+KSzsSV5kq5iWhEQbok+quxgGzrAtGWCyU7tHnA==", 1467 | "bin": { 1468 | "tsc": "bin/tsc", 1469 | "tsserver": "bin/tsserver" 1470 | }, 1471 | "engines": { 1472 | "node": ">=4.2.0" 1473 | } 1474 | }, 1475 | "node_modules/update-browserslist-db": { 1476 | "version": "1.0.10", 1477 | "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", 1478 | "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==", 1479 | "dev": true, 1480 | "funding": [ 1481 | { 1482 | "type": "opencollective", 1483 | "url": "https://opencollective.com/browserslist" 1484 | }, 1485 | { 1486 | "type": "tidelift", 1487 | "url": "https://tidelift.com/funding/github/npm/browserslist" 1488 | } 1489 | ], 1490 | "dependencies": { 1491 | "escalade": "^3.1.1", 1492 | "picocolors": "^1.0.0" 1493 | }, 1494 | "bin": { 1495 | "browserslist-lint": "cli.js" 1496 | }, 1497 | "peerDependencies": { 1498 | "browserslist": ">= 4.21.0" 1499 | } 1500 | }, 1501 | "node_modules/util-deprecate": { 1502 | "version": "1.0.2", 1503 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", 1504 | "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", 1505 | "dev": true 1506 | }, 1507 | "node_modules/xtend": { 1508 | "version": "4.0.2", 1509 | "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", 1510 | "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", 1511 | "dev": true, 1512 | "engines": { 1513 | "node": ">=0.4" 1514 | } 1515 | }, 1516 | "node_modules/yaml": { 1517 | "version": "1.10.2", 1518 | "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", 1519 | "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", 1520 | "dev": true, 1521 | "engines": { 1522 | "node": ">= 6" 1523 | } 1524 | } 1525 | }, 1526 | "dependencies": { 1527 | "@next/env": { 1528 | "version": "13.0.5", 1529 | "resolved": "https://registry.npmjs.org/@next/env/-/env-13.0.5.tgz", 1530 | "integrity": "sha512-F3KLtiDrUslAZhTYTh8Zk5ZaavbYwLUn3NYPBnOjAXU8hWm0QVGVzKIOuURQ098ofRU4e9oglf3Sj9pFx5nI5w==" 1531 | }, 1532 | "@next/swc-android-arm-eabi": { 1533 | "version": "13.0.5", 1534 | "resolved": "https://registry.npmjs.org/@next/swc-android-arm-eabi/-/swc-android-arm-eabi-13.0.5.tgz", 1535 | "integrity": "sha512-YO691dxHlviy6H0eghgwqn+5kU9J3iQnKERHTDSppqjjGDBl6ab4wz9XfI5AhljjkaTg3TknHoIEWFDoZ4Ve8g==", 1536 | "optional": true 1537 | }, 1538 | "@next/swc-android-arm64": { 1539 | "version": "13.0.5", 1540 | "resolved": "https://registry.npmjs.org/@next/swc-android-arm64/-/swc-android-arm64-13.0.5.tgz", 1541 | "integrity": "sha512-ugbwffkUmp8cd2afehDC8LtQeFUxElRUBBngfB5UYSWBx18HW4OgzkPFIY8jUBH16zifvGZWXbICXJWDHrOLtw==", 1542 | "optional": true 1543 | }, 1544 | "@next/swc-darwin-arm64": { 1545 | "version": "13.0.5", 1546 | "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.0.5.tgz", 1547 | "integrity": "sha512-mshlh8QOtOalfZbc17uNAftWgqHTKnrv6QUwBe+mpGz04eqsSUzVz1JGZEdIkmuDxOz00cK2NPoc+VHDXh99IQ==", 1548 | "optional": true 1549 | }, 1550 | "@next/swc-darwin-x64": { 1551 | "version": "13.0.5", 1552 | "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-13.0.5.tgz", 1553 | "integrity": "sha512-SfigOKW4Z2UB3ruUPyvrlDIkcJq1hiw1wvYApWugD+tQsAkYZKEoz+/8emCmeYZ6Gwgi1WHV+z52Oj8u7bEHPg==", 1554 | "optional": true 1555 | }, 1556 | "@next/swc-freebsd-x64": { 1557 | "version": "13.0.5", 1558 | "resolved": "https://registry.npmjs.org/@next/swc-freebsd-x64/-/swc-freebsd-x64-13.0.5.tgz", 1559 | "integrity": "sha512-0NJg8HZr4yG8ynmMGFXQf+Mahvq4ZgBmUwSlLXXymgxEQgH17erH/LoR69uITtW+KTsALgk9axEt5AAabM4ucg==", 1560 | "optional": true 1561 | }, 1562 | "@next/swc-linux-arm-gnueabihf": { 1563 | "version": "13.0.5", 1564 | "resolved": "https://registry.npmjs.org/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-13.0.5.tgz", 1565 | "integrity": "sha512-Cye+h3oDT3NDWjACMlRaolL8fokpKie34FlPj9nfoW7bYKmoMBY1d4IO/GgBF+5xEl7HkH0Ny/qex63vQ0pN+A==", 1566 | "optional": true 1567 | }, 1568 | "@next/swc-linux-arm64-gnu": { 1569 | "version": "13.0.5", 1570 | "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.0.5.tgz", 1571 | "integrity": "sha512-5BfDS/VoRDR5QUGG9oedOCEZGmV2zxUVFYLUJVPMSMeIgqkjxWQBiG2BUHZI6/LGk9yvHmjx7BTvtBCLtRg6IQ==", 1572 | "optional": true 1573 | }, 1574 | "@next/swc-linux-arm64-musl": { 1575 | "version": "13.0.5", 1576 | "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.0.5.tgz", 1577 | "integrity": "sha512-xenvqlXz+KxVKAB1YR723gnVNszpsCvKZkiFFaAYqDGJ502YuqU2fwLsaSm/ASRizNcBYeo9HPLTyc3r/9cdMQ==", 1578 | "optional": true 1579 | }, 1580 | "@next/swc-linux-x64-gnu": { 1581 | "version": "13.0.5", 1582 | "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.0.5.tgz", 1583 | "integrity": "sha512-9Ahi1bbdXwhrWQmOyoTod23/hhK05da/FzodiNqd6drrMl1y7+RujoEcU8Dtw3H1mGWB+yuTlWo8B4Iba8hqiQ==", 1584 | "optional": true 1585 | }, 1586 | "@next/swc-linux-x64-musl": { 1587 | "version": "13.0.5", 1588 | "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.0.5.tgz", 1589 | "integrity": "sha512-V+1mnh49qmS9fOZxVRbzjhBEz9IUGJ7AQ80JPWAYQM5LI4TxfdiF4APLPvJ52rOmNeTqnVz1bbKtVOso+7EZ4w==", 1590 | "optional": true 1591 | }, 1592 | "@next/swc-win32-arm64-msvc": { 1593 | "version": "13.0.5", 1594 | "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.0.5.tgz", 1595 | "integrity": "sha512-wRE9rkp7I+/3Jf2T9PFIJOKq3adMWYEFkPOA7XAkUfYbQHlDJm/U5cVCWUsKByyQq5RThwufI91sgd19MfxRxg==", 1596 | "optional": true 1597 | }, 1598 | "@next/swc-win32-ia32-msvc": { 1599 | "version": "13.0.5", 1600 | "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.0.5.tgz", 1601 | "integrity": "sha512-Q1XQSLEhFuFhkKFdJIGt7cYQ4T3u6P5wrtUNreg5M+7P+fjSiC8+X+Vjcw+oebaacsdl0pWZlK+oACGafush1w==", 1602 | "optional": true 1603 | }, 1604 | "@next/swc-win32-x64-msvc": { 1605 | "version": "13.0.5", 1606 | "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.0.5.tgz", 1607 | "integrity": "sha512-t5gRblrwwiNZP6cT7NkxlgxrFgHWtv9ei5vUraCLgBqzvIsa7X+PnarZUeQCXqz6Jg9JSGGT9j8lvzD97UqeJQ==", 1608 | "optional": true 1609 | }, 1610 | "@nodelib/fs.scandir": { 1611 | "version": "2.1.5", 1612 | "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", 1613 | "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", 1614 | "dev": true, 1615 | "requires": { 1616 | "@nodelib/fs.stat": "2.0.5", 1617 | "run-parallel": "^1.1.9" 1618 | } 1619 | }, 1620 | "@nodelib/fs.stat": { 1621 | "version": "2.0.5", 1622 | "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", 1623 | "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", 1624 | "dev": true 1625 | }, 1626 | "@nodelib/fs.walk": { 1627 | "version": "1.2.8", 1628 | "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", 1629 | "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", 1630 | "dev": true, 1631 | "requires": { 1632 | "@nodelib/fs.scandir": "2.1.5", 1633 | "fastq": "^1.6.0" 1634 | } 1635 | }, 1636 | "@swc/helpers": { 1637 | "version": "0.4.14", 1638 | "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.4.14.tgz", 1639 | "integrity": "sha512-4C7nX/dvpzB7za4Ql9K81xK3HPxCpHMgwTZVyf+9JQ6VUbn9jjZVN7/Nkdz/Ugzs2CSjqnL/UPXroiVBVHUWUw==", 1640 | "requires": { 1641 | "tslib": "^2.4.0" 1642 | } 1643 | }, 1644 | "@tailwindcss/typography": { 1645 | "version": "0.5.8", 1646 | "resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.8.tgz", 1647 | "integrity": "sha512-xGQEp8KXN8Sd8m6R4xYmwxghmswrd0cPnNI2Lc6fmrC3OojysTBJJGSIVwPV56q4t6THFUK3HJ0EaWwpglSxWw==", 1648 | "dev": true, 1649 | "requires": { 1650 | "lodash.castarray": "^4.4.0", 1651 | "lodash.isplainobject": "^4.0.6", 1652 | "lodash.merge": "^4.6.2", 1653 | "postcss-selector-parser": "6.0.10" 1654 | }, 1655 | "dependencies": { 1656 | "postcss-selector-parser": { 1657 | "version": "6.0.10", 1658 | "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz", 1659 | "integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==", 1660 | "dev": true, 1661 | "requires": { 1662 | "cssesc": "^3.0.0", 1663 | "util-deprecate": "^1.0.2" 1664 | } 1665 | } 1666 | } 1667 | }, 1668 | "@types/node": { 1669 | "version": "18.11.9", 1670 | "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.9.tgz", 1671 | "integrity": "sha512-CRpX21/kGdzjOpFsZSkcrXMGIBWMGNIHXXBVFSH+ggkftxg+XYP20TESbh+zFvFj3EQOl5byk0HTRn1IL6hbqg==" 1672 | }, 1673 | "@types/prop-types": { 1674 | "version": "15.7.5", 1675 | "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", 1676 | "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==" 1677 | }, 1678 | "@types/react": { 1679 | "version": "18.0.25", 1680 | "resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.25.tgz", 1681 | "integrity": "sha512-xD6c0KDT4m7n9uD4ZHi02lzskaiqcBxf4zi+tXZY98a04wvc0hi/TcCPC2FOESZi51Nd7tlUeOJY8RofL799/g==", 1682 | "requires": { 1683 | "@types/prop-types": "*", 1684 | "@types/scheduler": "*", 1685 | "csstype": "^3.0.2" 1686 | } 1687 | }, 1688 | "@types/react-dom": { 1689 | "version": "18.0.9", 1690 | "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.0.9.tgz", 1691 | "integrity": "sha512-qnVvHxASt/H7i+XG1U1xMiY5t+IHcPGUK7TDMDzom08xa7e86eCeKOiLZezwCKVxJn6NEiiy2ekgX8aQssjIKg==", 1692 | "requires": { 1693 | "@types/react": "*" 1694 | } 1695 | }, 1696 | "@types/scheduler": { 1697 | "version": "0.16.2", 1698 | "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", 1699 | "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==" 1700 | }, 1701 | "acorn": { 1702 | "version": "7.4.1", 1703 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", 1704 | "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", 1705 | "dev": true 1706 | }, 1707 | "acorn-node": { 1708 | "version": "1.8.2", 1709 | "resolved": "https://registry.npmjs.org/acorn-node/-/acorn-node-1.8.2.tgz", 1710 | "integrity": "sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A==", 1711 | "dev": true, 1712 | "requires": { 1713 | "acorn": "^7.0.0", 1714 | "acorn-walk": "^7.0.0", 1715 | "xtend": "^4.0.2" 1716 | } 1717 | }, 1718 | "acorn-walk": { 1719 | "version": "7.2.0", 1720 | "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", 1721 | "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", 1722 | "dev": true 1723 | }, 1724 | "anymatch": { 1725 | "version": "3.1.3", 1726 | "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", 1727 | "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", 1728 | "dev": true, 1729 | "requires": { 1730 | "normalize-path": "^3.0.0", 1731 | "picomatch": "^2.0.4" 1732 | } 1733 | }, 1734 | "arg": { 1735 | "version": "5.0.2", 1736 | "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", 1737 | "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", 1738 | "dev": true 1739 | }, 1740 | "argparse": { 1741 | "version": "1.0.10", 1742 | "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", 1743 | "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", 1744 | "requires": { 1745 | "sprintf-js": "~1.0.2" 1746 | } 1747 | }, 1748 | "autoprefixer": { 1749 | "version": "10.4.13", 1750 | "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.13.tgz", 1751 | "integrity": "sha512-49vKpMqcZYsJjwotvt4+h/BCjJVnhGwcLpDt5xkcaOG3eLrG/HUYLagrihYsQ+qrIBgIzX1Rw7a6L8I/ZA1Atg==", 1752 | "dev": true, 1753 | "requires": { 1754 | "browserslist": "^4.21.4", 1755 | "caniuse-lite": "^1.0.30001426", 1756 | "fraction.js": "^4.2.0", 1757 | "normalize-range": "^0.1.2", 1758 | "picocolors": "^1.0.0", 1759 | "postcss-value-parser": "^4.2.0" 1760 | } 1761 | }, 1762 | "binary-extensions": { 1763 | "version": "2.2.0", 1764 | "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", 1765 | "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", 1766 | "dev": true 1767 | }, 1768 | "braces": { 1769 | "version": "3.0.2", 1770 | "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", 1771 | "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", 1772 | "dev": true, 1773 | "requires": { 1774 | "fill-range": "^7.0.1" 1775 | } 1776 | }, 1777 | "browserslist": { 1778 | "version": "4.21.4", 1779 | "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.4.tgz", 1780 | "integrity": "sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==", 1781 | "dev": true, 1782 | "requires": { 1783 | "caniuse-lite": "^1.0.30001400", 1784 | "electron-to-chromium": "^1.4.251", 1785 | "node-releases": "^2.0.6", 1786 | "update-browserslist-db": "^1.0.9" 1787 | } 1788 | }, 1789 | "camelcase-css": { 1790 | "version": "2.0.1", 1791 | "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", 1792 | "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", 1793 | "dev": true 1794 | }, 1795 | "caniuse-lite": { 1796 | "version": "1.0.30001434", 1797 | "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001434.tgz", 1798 | "integrity": "sha512-aOBHrLmTQw//WFa2rcF1If9fa3ypkC1wzqqiKHgfdrXTWcU8C4gKVZT77eQAPWN1APys3+uQ0Df07rKauXGEYA==" 1799 | }, 1800 | "chokidar": { 1801 | "version": "3.5.3", 1802 | "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", 1803 | "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", 1804 | "dev": true, 1805 | "requires": { 1806 | "anymatch": "~3.1.2", 1807 | "braces": "~3.0.2", 1808 | "fsevents": "~2.3.2", 1809 | "glob-parent": "~5.1.2", 1810 | "is-binary-path": "~2.1.0", 1811 | "is-glob": "~4.0.1", 1812 | "normalize-path": "~3.0.0", 1813 | "readdirp": "~3.6.0" 1814 | }, 1815 | "dependencies": { 1816 | "glob-parent": { 1817 | "version": "5.1.2", 1818 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", 1819 | "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", 1820 | "dev": true, 1821 | "requires": { 1822 | "is-glob": "^4.0.1" 1823 | } 1824 | } 1825 | } 1826 | }, 1827 | "client-only": { 1828 | "version": "0.0.1", 1829 | "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", 1830 | "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==" 1831 | }, 1832 | "color-name": { 1833 | "version": "1.1.4", 1834 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", 1835 | "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", 1836 | "dev": true 1837 | }, 1838 | "cssesc": { 1839 | "version": "3.0.0", 1840 | "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", 1841 | "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", 1842 | "dev": true 1843 | }, 1844 | "csstype": { 1845 | "version": "3.1.1", 1846 | "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.1.tgz", 1847 | "integrity": "sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==" 1848 | }, 1849 | "defined": { 1850 | "version": "1.0.1", 1851 | "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.1.tgz", 1852 | "integrity": "sha512-hsBd2qSVCRE+5PmNdHt1uzyrFu5d3RwmFDKzyNZMFq/EwDNJF7Ee5+D5oEKF0hU6LhtoUF1macFvOe4AskQC1Q==", 1853 | "dev": true 1854 | }, 1855 | "detective": { 1856 | "version": "5.2.1", 1857 | "resolved": "https://registry.npmjs.org/detective/-/detective-5.2.1.tgz", 1858 | "integrity": "sha512-v9XE1zRnz1wRtgurGu0Bs8uHKFSTdteYZNbIPFVhUZ39L/S79ppMpdmVOZAnoz1jfEFodc48n6MX483Xo3t1yw==", 1859 | "dev": true, 1860 | "requires": { 1861 | "acorn-node": "^1.8.2", 1862 | "defined": "^1.0.0", 1863 | "minimist": "^1.2.6" 1864 | } 1865 | }, 1866 | "didyoumean": { 1867 | "version": "1.2.2", 1868 | "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", 1869 | "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", 1870 | "dev": true 1871 | }, 1872 | "dlv": { 1873 | "version": "1.1.3", 1874 | "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", 1875 | "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", 1876 | "dev": true 1877 | }, 1878 | "electron-to-chromium": { 1879 | "version": "1.4.284", 1880 | "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz", 1881 | "integrity": "sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA==", 1882 | "dev": true 1883 | }, 1884 | "escalade": { 1885 | "version": "3.1.1", 1886 | "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", 1887 | "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", 1888 | "dev": true 1889 | }, 1890 | "esprima": { 1891 | "version": "4.0.1", 1892 | "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", 1893 | "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" 1894 | }, 1895 | "extend-shallow": { 1896 | "version": "2.0.1", 1897 | "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", 1898 | "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", 1899 | "requires": { 1900 | "is-extendable": "^0.1.0" 1901 | } 1902 | }, 1903 | "fast-glob": { 1904 | "version": "3.2.12", 1905 | "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", 1906 | "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", 1907 | "dev": true, 1908 | "requires": { 1909 | "@nodelib/fs.stat": "^2.0.2", 1910 | "@nodelib/fs.walk": "^1.2.3", 1911 | "glob-parent": "^5.1.2", 1912 | "merge2": "^1.3.0", 1913 | "micromatch": "^4.0.4" 1914 | }, 1915 | "dependencies": { 1916 | "glob-parent": { 1917 | "version": "5.1.2", 1918 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", 1919 | "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", 1920 | "dev": true, 1921 | "requires": { 1922 | "is-glob": "^4.0.1" 1923 | } 1924 | } 1925 | } 1926 | }, 1927 | "fastq": { 1928 | "version": "1.13.0", 1929 | "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", 1930 | "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", 1931 | "dev": true, 1932 | "requires": { 1933 | "reusify": "^1.0.4" 1934 | } 1935 | }, 1936 | "fill-range": { 1937 | "version": "7.0.1", 1938 | "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", 1939 | "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", 1940 | "dev": true, 1941 | "requires": { 1942 | "to-regex-range": "^5.0.1" 1943 | } 1944 | }, 1945 | "fraction.js": { 1946 | "version": "4.2.0", 1947 | "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz", 1948 | "integrity": "sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==", 1949 | "dev": true 1950 | }, 1951 | "fsevents": { 1952 | "version": "2.3.2", 1953 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", 1954 | "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", 1955 | "dev": true, 1956 | "optional": true 1957 | }, 1958 | "function-bind": { 1959 | "version": "1.1.1", 1960 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", 1961 | "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", 1962 | "dev": true 1963 | }, 1964 | "glob-parent": { 1965 | "version": "6.0.2", 1966 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", 1967 | "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", 1968 | "dev": true, 1969 | "requires": { 1970 | "is-glob": "^4.0.3" 1971 | } 1972 | }, 1973 | "gray-matter": { 1974 | "version": "4.0.3", 1975 | "resolved": "https://registry.npmjs.org/gray-matter/-/gray-matter-4.0.3.tgz", 1976 | "integrity": "sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q==", 1977 | "requires": { 1978 | "js-yaml": "^3.13.1", 1979 | "kind-of": "^6.0.2", 1980 | "section-matter": "^1.0.0", 1981 | "strip-bom-string": "^1.0.0" 1982 | } 1983 | }, 1984 | "has": { 1985 | "version": "1.0.3", 1986 | "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", 1987 | "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", 1988 | "dev": true, 1989 | "requires": { 1990 | "function-bind": "^1.1.1" 1991 | } 1992 | }, 1993 | "is-binary-path": { 1994 | "version": "2.1.0", 1995 | "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", 1996 | "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", 1997 | "dev": true, 1998 | "requires": { 1999 | "binary-extensions": "^2.0.0" 2000 | } 2001 | }, 2002 | "is-core-module": { 2003 | "version": "2.11.0", 2004 | "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", 2005 | "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", 2006 | "dev": true, 2007 | "requires": { 2008 | "has": "^1.0.3" 2009 | } 2010 | }, 2011 | "is-extendable": { 2012 | "version": "0.1.1", 2013 | "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", 2014 | "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==" 2015 | }, 2016 | "is-extglob": { 2017 | "version": "2.1.1", 2018 | "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", 2019 | "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", 2020 | "dev": true 2021 | }, 2022 | "is-glob": { 2023 | "version": "4.0.3", 2024 | "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", 2025 | "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", 2026 | "dev": true, 2027 | "requires": { 2028 | "is-extglob": "^2.1.1" 2029 | } 2030 | }, 2031 | "is-number": { 2032 | "version": "7.0.0", 2033 | "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", 2034 | "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", 2035 | "dev": true 2036 | }, 2037 | "js-tokens": { 2038 | "version": "4.0.0", 2039 | "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", 2040 | "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" 2041 | }, 2042 | "js-yaml": { 2043 | "version": "3.14.1", 2044 | "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", 2045 | "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", 2046 | "requires": { 2047 | "argparse": "^1.0.7", 2048 | "esprima": "^4.0.0" 2049 | } 2050 | }, 2051 | "kind-of": { 2052 | "version": "6.0.3", 2053 | "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", 2054 | "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==" 2055 | }, 2056 | "lilconfig": { 2057 | "version": "2.0.6", 2058 | "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.6.tgz", 2059 | "integrity": "sha512-9JROoBW7pobfsx+Sq2JsASvCo6Pfo6WWoUW79HuB1BCoBXD4PLWJPqDF6fNj67pqBYTbAHkE57M1kS/+L1neOg==", 2060 | "dev": true 2061 | }, 2062 | "lodash.castarray": { 2063 | "version": "4.4.0", 2064 | "resolved": "https://registry.npmjs.org/lodash.castarray/-/lodash.castarray-4.4.0.tgz", 2065 | "integrity": "sha512-aVx8ztPv7/2ULbArGJ2Y42bG1mEQ5mGjpdvrbJcJFU3TbYybe+QlLS4pst9zV52ymy2in1KpFPiZnAOATxD4+Q==", 2066 | "dev": true 2067 | }, 2068 | "lodash.isplainobject": { 2069 | "version": "4.0.6", 2070 | "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", 2071 | "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", 2072 | "dev": true 2073 | }, 2074 | "lodash.merge": { 2075 | "version": "4.6.2", 2076 | "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", 2077 | "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", 2078 | "dev": true 2079 | }, 2080 | "loose-envify": { 2081 | "version": "1.4.0", 2082 | "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", 2083 | "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", 2084 | "requires": { 2085 | "js-tokens": "^3.0.0 || ^4.0.0" 2086 | } 2087 | }, 2088 | "markdown-to-jsx": { 2089 | "version": "7.1.7", 2090 | "resolved": "https://registry.npmjs.org/markdown-to-jsx/-/markdown-to-jsx-7.1.7.tgz", 2091 | "integrity": "sha512-VI3TyyHlGkO8uFle0IOibzpO1c1iJDcXcS/zBrQrXQQvJ2tpdwVzVZ7XdKsyRz1NdRmre4dqQkMZzUHaKIG/1w==", 2092 | "requires": {} 2093 | }, 2094 | "merge2": { 2095 | "version": "1.4.1", 2096 | "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", 2097 | "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", 2098 | "dev": true 2099 | }, 2100 | "micromatch": { 2101 | "version": "4.0.5", 2102 | "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", 2103 | "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", 2104 | "dev": true, 2105 | "requires": { 2106 | "braces": "^3.0.2", 2107 | "picomatch": "^2.3.1" 2108 | } 2109 | }, 2110 | "minimist": { 2111 | "version": "1.2.7", 2112 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz", 2113 | "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==", 2114 | "dev": true 2115 | }, 2116 | "nanoid": { 2117 | "version": "3.3.4", 2118 | "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", 2119 | "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==" 2120 | }, 2121 | "next": { 2122 | "version": "13.0.5", 2123 | "resolved": "https://registry.npmjs.org/next/-/next-13.0.5.tgz", 2124 | "integrity": "sha512-awpc3DkphyKydwCotcBnuKwh6hMqkT5xdiBK4OatJtOZurDPBYLP62jtM2be/4OunpmwIbsS0Eyv+ZGU97ciEg==", 2125 | "requires": { 2126 | "@next/env": "13.0.5", 2127 | "@next/swc-android-arm-eabi": "13.0.5", 2128 | "@next/swc-android-arm64": "13.0.5", 2129 | "@next/swc-darwin-arm64": "13.0.5", 2130 | "@next/swc-darwin-x64": "13.0.5", 2131 | "@next/swc-freebsd-x64": "13.0.5", 2132 | "@next/swc-linux-arm-gnueabihf": "13.0.5", 2133 | "@next/swc-linux-arm64-gnu": "13.0.5", 2134 | "@next/swc-linux-arm64-musl": "13.0.5", 2135 | "@next/swc-linux-x64-gnu": "13.0.5", 2136 | "@next/swc-linux-x64-musl": "13.0.5", 2137 | "@next/swc-win32-arm64-msvc": "13.0.5", 2138 | "@next/swc-win32-ia32-msvc": "13.0.5", 2139 | "@next/swc-win32-x64-msvc": "13.0.5", 2140 | "@swc/helpers": "0.4.14", 2141 | "caniuse-lite": "^1.0.30001406", 2142 | "postcss": "8.4.14", 2143 | "styled-jsx": "5.1.0" 2144 | }, 2145 | "dependencies": { 2146 | "postcss": { 2147 | "version": "8.4.14", 2148 | "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.14.tgz", 2149 | "integrity": "sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig==", 2150 | "requires": { 2151 | "nanoid": "^3.3.4", 2152 | "picocolors": "^1.0.0", 2153 | "source-map-js": "^1.0.2" 2154 | } 2155 | } 2156 | } 2157 | }, 2158 | "node-releases": { 2159 | "version": "2.0.6", 2160 | "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz", 2161 | "integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==", 2162 | "dev": true 2163 | }, 2164 | "normalize-path": { 2165 | "version": "3.0.0", 2166 | "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", 2167 | "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", 2168 | "dev": true 2169 | }, 2170 | "normalize-range": { 2171 | "version": "0.1.2", 2172 | "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", 2173 | "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", 2174 | "dev": true 2175 | }, 2176 | "object-hash": { 2177 | "version": "3.0.0", 2178 | "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", 2179 | "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", 2180 | "dev": true 2181 | }, 2182 | "path-parse": { 2183 | "version": "1.0.7", 2184 | "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", 2185 | "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", 2186 | "dev": true 2187 | }, 2188 | "picocolors": { 2189 | "version": "1.0.0", 2190 | "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", 2191 | "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" 2192 | }, 2193 | "picomatch": { 2194 | "version": "2.3.1", 2195 | "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", 2196 | "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", 2197 | "dev": true 2198 | }, 2199 | "pify": { 2200 | "version": "2.3.0", 2201 | "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", 2202 | "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", 2203 | "dev": true 2204 | }, 2205 | "postcss": { 2206 | "version": "8.4.19", 2207 | "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.19.tgz", 2208 | "integrity": "sha512-h+pbPsyhlYj6N2ozBmHhHrs9DzGmbaarbLvWipMRO7RLS+v4onj26MPFXA5OBYFxyqYhUJK456SwDcY9H2/zsA==", 2209 | "dev": true, 2210 | "requires": { 2211 | "nanoid": "^3.3.4", 2212 | "picocolors": "^1.0.0", 2213 | "source-map-js": "^1.0.2" 2214 | } 2215 | }, 2216 | "postcss-import": { 2217 | "version": "14.1.0", 2218 | "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-14.1.0.tgz", 2219 | "integrity": "sha512-flwI+Vgm4SElObFVPpTIT7SU7R3qk2L7PyduMcokiaVKuWv9d/U+Gm/QAd8NDLuykTWTkcrjOeD2Pp1rMeBTGw==", 2220 | "dev": true, 2221 | "requires": { 2222 | "postcss-value-parser": "^4.0.0", 2223 | "read-cache": "^1.0.0", 2224 | "resolve": "^1.1.7" 2225 | } 2226 | }, 2227 | "postcss-js": { 2228 | "version": "4.0.0", 2229 | "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.0.tgz", 2230 | "integrity": "sha512-77QESFBwgX4irogGVPgQ5s07vLvFqWr228qZY+w6lW599cRlK/HmnlivnnVUxkjHnCu4J16PDMHcH+e+2HbvTQ==", 2231 | "dev": true, 2232 | "requires": { 2233 | "camelcase-css": "^2.0.1" 2234 | } 2235 | }, 2236 | "postcss-load-config": { 2237 | "version": "3.1.4", 2238 | "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-3.1.4.tgz", 2239 | "integrity": "sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==", 2240 | "dev": true, 2241 | "requires": { 2242 | "lilconfig": "^2.0.5", 2243 | "yaml": "^1.10.2" 2244 | } 2245 | }, 2246 | "postcss-nested": { 2247 | "version": "6.0.0", 2248 | "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.0.0.tgz", 2249 | "integrity": "sha512-0DkamqrPcmkBDsLn+vQDIrtkSbNkv5AD/M322ySo9kqFkCIYklym2xEmWkwo+Y3/qZo34tzEPNUw4y7yMCdv5w==", 2250 | "dev": true, 2251 | "requires": { 2252 | "postcss-selector-parser": "^6.0.10" 2253 | } 2254 | }, 2255 | "postcss-selector-parser": { 2256 | "version": "6.0.11", 2257 | "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.11.tgz", 2258 | "integrity": "sha512-zbARubNdogI9j7WY4nQJBiNqQf3sLS3wCP4WfOidu+p28LofJqDH1tcXypGrcmMHhDk2t9wGhCsYe/+szLTy1g==", 2259 | "dev": true, 2260 | "requires": { 2261 | "cssesc": "^3.0.0", 2262 | "util-deprecate": "^1.0.2" 2263 | } 2264 | }, 2265 | "postcss-value-parser": { 2266 | "version": "4.2.0", 2267 | "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", 2268 | "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", 2269 | "dev": true 2270 | }, 2271 | "queue-microtask": { 2272 | "version": "1.2.3", 2273 | "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", 2274 | "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", 2275 | "dev": true 2276 | }, 2277 | "quick-lru": { 2278 | "version": "5.1.1", 2279 | "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", 2280 | "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", 2281 | "dev": true 2282 | }, 2283 | "react": { 2284 | "version": "18.2.0", 2285 | "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", 2286 | "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", 2287 | "requires": { 2288 | "loose-envify": "^1.1.0" 2289 | } 2290 | }, 2291 | "react-dom": { 2292 | "version": "18.2.0", 2293 | "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", 2294 | "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", 2295 | "requires": { 2296 | "loose-envify": "^1.1.0", 2297 | "scheduler": "^0.23.0" 2298 | } 2299 | }, 2300 | "read-cache": { 2301 | "version": "1.0.0", 2302 | "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", 2303 | "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", 2304 | "dev": true, 2305 | "requires": { 2306 | "pify": "^2.3.0" 2307 | } 2308 | }, 2309 | "readdirp": { 2310 | "version": "3.6.0", 2311 | "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", 2312 | "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", 2313 | "dev": true, 2314 | "requires": { 2315 | "picomatch": "^2.2.1" 2316 | } 2317 | }, 2318 | "resolve": { 2319 | "version": "1.22.1", 2320 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", 2321 | "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", 2322 | "dev": true, 2323 | "requires": { 2324 | "is-core-module": "^2.9.0", 2325 | "path-parse": "^1.0.7", 2326 | "supports-preserve-symlinks-flag": "^1.0.0" 2327 | } 2328 | }, 2329 | "reusify": { 2330 | "version": "1.0.4", 2331 | "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", 2332 | "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", 2333 | "dev": true 2334 | }, 2335 | "run-parallel": { 2336 | "version": "1.2.0", 2337 | "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", 2338 | "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", 2339 | "dev": true, 2340 | "requires": { 2341 | "queue-microtask": "^1.2.2" 2342 | } 2343 | }, 2344 | "scheduler": { 2345 | "version": "0.23.0", 2346 | "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", 2347 | "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", 2348 | "requires": { 2349 | "loose-envify": "^1.1.0" 2350 | } 2351 | }, 2352 | "section-matter": { 2353 | "version": "1.0.0", 2354 | "resolved": "https://registry.npmjs.org/section-matter/-/section-matter-1.0.0.tgz", 2355 | "integrity": "sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA==", 2356 | "requires": { 2357 | "extend-shallow": "^2.0.1", 2358 | "kind-of": "^6.0.0" 2359 | } 2360 | }, 2361 | "source-map-js": { 2362 | "version": "1.0.2", 2363 | "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", 2364 | "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==" 2365 | }, 2366 | "sprintf-js": { 2367 | "version": "1.0.3", 2368 | "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", 2369 | "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" 2370 | }, 2371 | "strip-bom-string": { 2372 | "version": "1.0.0", 2373 | "resolved": "https://registry.npmjs.org/strip-bom-string/-/strip-bom-string-1.0.0.tgz", 2374 | "integrity": "sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g==" 2375 | }, 2376 | "styled-jsx": { 2377 | "version": "5.1.0", 2378 | "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.0.tgz", 2379 | "integrity": "sha512-/iHaRJt9U7T+5tp6TRelLnqBqiaIT0HsO0+vgyj8hK2KUk7aejFqRrumqPUlAqDwAj8IbS/1hk3IhBAAK/FCUQ==", 2380 | "requires": { 2381 | "client-only": "0.0.1" 2382 | } 2383 | }, 2384 | "supports-preserve-symlinks-flag": { 2385 | "version": "1.0.0", 2386 | "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", 2387 | "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", 2388 | "dev": true 2389 | }, 2390 | "tailwindcss": { 2391 | "version": "3.2.4", 2392 | "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.2.4.tgz", 2393 | "integrity": "sha512-AhwtHCKMtR71JgeYDaswmZXhPcW9iuI9Sp2LvZPo9upDZ7231ZJ7eA9RaURbhpXGVlrjX4cFNlB4ieTetEb7hQ==", 2394 | "dev": true, 2395 | "requires": { 2396 | "arg": "^5.0.2", 2397 | "chokidar": "^3.5.3", 2398 | "color-name": "^1.1.4", 2399 | "detective": "^5.2.1", 2400 | "didyoumean": "^1.2.2", 2401 | "dlv": "^1.1.3", 2402 | "fast-glob": "^3.2.12", 2403 | "glob-parent": "^6.0.2", 2404 | "is-glob": "^4.0.3", 2405 | "lilconfig": "^2.0.6", 2406 | "micromatch": "^4.0.5", 2407 | "normalize-path": "^3.0.0", 2408 | "object-hash": "^3.0.0", 2409 | "picocolors": "^1.0.0", 2410 | "postcss": "^8.4.18", 2411 | "postcss-import": "^14.1.0", 2412 | "postcss-js": "^4.0.0", 2413 | "postcss-load-config": "^3.1.4", 2414 | "postcss-nested": "6.0.0", 2415 | "postcss-selector-parser": "^6.0.10", 2416 | "postcss-value-parser": "^4.2.0", 2417 | "quick-lru": "^5.1.1", 2418 | "resolve": "^1.22.1" 2419 | } 2420 | }, 2421 | "to-regex-range": { 2422 | "version": "5.0.1", 2423 | "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", 2424 | "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", 2425 | "dev": true, 2426 | "requires": { 2427 | "is-number": "^7.0.0" 2428 | } 2429 | }, 2430 | "tslib": { 2431 | "version": "2.4.1", 2432 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz", 2433 | "integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==" 2434 | }, 2435 | "typescript": { 2436 | "version": "4.9.3", 2437 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.3.tgz", 2438 | "integrity": "sha512-CIfGzTelbKNEnLpLdGFgdyKhG23CKdKgQPOBc+OUNrkJ2vr+KSzsSV5kq5iWhEQbok+quxgGzrAtGWCyU7tHnA==" 2439 | }, 2440 | "update-browserslist-db": { 2441 | "version": "1.0.10", 2442 | "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", 2443 | "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==", 2444 | "dev": true, 2445 | "requires": { 2446 | "escalade": "^3.1.1", 2447 | "picocolors": "^1.0.0" 2448 | } 2449 | }, 2450 | "util-deprecate": { 2451 | "version": "1.0.2", 2452 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", 2453 | "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", 2454 | "dev": true 2455 | }, 2456 | "xtend": { 2457 | "version": "4.0.2", 2458 | "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", 2459 | "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", 2460 | "dev": true 2461 | }, 2462 | "yaml": { 2463 | "version": "1.10.2", 2464 | "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", 2465 | "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", 2466 | "dev": true 2467 | } 2468 | } 2469 | } 2470 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nextjs-blog", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start", 9 | "lint": "next lint" 10 | }, 11 | "dependencies": { 12 | "@types/node": "18.11.9", 13 | "@types/react": "18.0.25", 14 | "@types/react-dom": "18.0.9", 15 | "gray-matter": "^4.0.3", 16 | "markdown-to-jsx": "^7.1.7", 17 | "next": "13.0.5", 18 | "react": "18.2.0", 19 | "react-dom": "18.2.0", 20 | "typescript": "4.9.3" 21 | }, 22 | "devDependencies": { 23 | "@tailwindcss/typography": "^0.5.8", 24 | "autoprefixer": "^10.4.13", 25 | "postcss": "^8.4.19", 26 | "tailwindcss": "^3.2.4" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /posts/aws-quickstart.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Getting Started with AWS" 3 | subtitle: "Create an AWS account and set up CLI/SDK access." 4 | date: "2020-12-27" 5 | --- 6 | 7 | "Cloud computing" plays a vital role in the creation of software products and services. It's also one of the most highly sought-after skills in the tech industry. 8 | 9 | In fact, most of the projects on this site will require cloud interaction of some sort—particularly with AWS's serverless products. 10 | 11 | ## Getting Started 12 | 13 | > To use AWS in these projects, we'll need to set up an account, the CLI, and the SDK. 14 | 15 | ### Create an account 16 | 17 | If you don't already have an account then [sign up here](https://portal.aws.amazon.com/billing/signup#/start). 18 | 19 | Once you are signed up, you should be able to log in to the [AWS Console](https://aws.amazon.com/console/). It might look overwhelming if you're seeing for the first time. 20 | 21 | ### Install the CLI 22 | 23 | The AWS CLI is a command-line application that lets you interact with your AWS account from the terminal. It's available on all platforms. 24 | 25 | If you are a proficient Python user, you can just install it with `pip`. 26 | 27 | ```bash 28 | pip install awscli 29 | ``` 30 | 31 | Otherwise, check out the [official instructions](https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-install.html). 32 | 33 | Once installed, you should be able to run this command from the terminal to see its version. 34 | 35 | ```bash 36 | aws --version 37 | ``` 38 | 39 | ### Create an IAM user 40 | 41 | The CLI will access your AWS account via an "IAM user." You can create one from the **Users** page in your [IAM console](https://console.aws.amazon.com/iam). 42 | 43 | Once the user is created, you'll need to generate access keys (passwords, essentially) for it. 44 | 45 | * [Creating an Admin IAM User](https://docs.aws.amazon.com/IAM/latest/UserGuide/getting-started_create-admin-group.html) 46 | * [Creating access keys for a user](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_access-keys.html#Using_CreateAccessKey) 47 | 48 | Your access keys should look something like this: 49 | 50 | ``` 51 | Access key ID: AKIAIOSFODNN7EXAMPLE 52 | Secret access key: wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY 53 | ``` 54 | 55 | 56 | ### Configure the CLI 57 | 58 | Next you need to [configure the CLI](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-quickstart.html) so that it can access your AWS account via the IAM user. 59 | 60 | Basically, just run this command and paste in your access keys. 61 | 62 | ```bash 63 | aws configure 64 | ``` 65 | 66 | Additionally, you'll also be asked for a [default region](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-quickstart.html#cli-configure-quickstart-region) and [default output format](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-quickstart.html#cli-configure-quickstart-format). 67 | 68 | You may leave them empty—but generally I like to use: 69 | 70 | ``` 71 | Default region name [None]: us-east-1 72 | Default output format [None]: json 73 | ``` 74 | 75 | ### Configuration files 76 | 77 | Once configured, the AWS CLI [saves the credentials](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-files.html) and region/format profiles to your computer. They are typically in these locations: 78 | 79 | ``` 80 | ~/.aws/credentials 81 | ~/.aws/config 82 | ``` 83 | 84 | You can open them up and edit them if you like or just run `aws configure` again to change them. 85 | 86 | ### Test the CLI! 87 | 88 | Now you should be able to use your CLI to access AWS. For example, I should be able to see the S3 buckets I have in `us-east-1`: 89 | 90 | ```bash 91 | aws s3 ls 92 | 93 | 2020-12-09 22:36:32 blog.pixegami.com 94 | 2020-12-27 00:04:52 cloud-archiver.5dac84a54677.archivetest 95 | ``` 96 | 97 | Generally, everything that can be done in the console can also be done in the CLI. Check out the [full reference guide here](https://docs.aws.amazon.com/cli/latest/index.html). 98 | 99 | ### AWS SDK 100 | 101 | Finally, to use AWS directly from your application code, you need to download the [SDK](https://aws.amazon.com/tools/) for the language you work with. 102 | 103 | The SDKs can be configured in different ways as well, but by default it usually uses the same profiles and credentials stored by your `aws configure`. 104 | 105 | 106 | ### That's it! 107 | 108 | You're all set to start using AWS. 109 | 110 | ## Why AWS? 111 | 112 | When we bring "the cloud" into a project, it's usually because there's some capability we'd like to add. 113 | 114 | * Hosting for a website or service. 115 | * File or data storage. 116 | * On-demand computation. 117 | * Authentication. 118 | 119 | And there's many viable solutions to choose from—[Azure](https://azure.microsoft.com/en-au/), [Google Cloud](https://cloud.google.com), [Firebase](https://firebase.google.com), [Digital Ocean](https://try.digitalocean.com). 120 | 121 | So why could you choose [AWS](https://aws.amazon.com/what-is-aws/) over any of these alternatives? From a new user's perspective: 122 | 123 | * **Largest marketshare (at 30%)** which roughly translates to lots of community resources and job opportunities. 124 | 125 | * **Most services available (175+)** which means more tools at your disposal, well-integrated under one umbrella. 126 | 127 | On the flip-side, the biggest drawback is its upfront complexity. 128 | 129 | Personally though, the reason I use AWS is because it's the technology I'm most familiar with. 130 | 131 | 132 | ## Why Serverless? 133 | 134 | **It's cheaper.** Most cloud "getting started" guides will show you how to spin up a server—a mercenary rented computer that stays online 24/7 to do your bidding. 135 | 136 | But for most of my projects, I'm going utilize technology that doesn't require a hosted server. In particular: 137 | 138 | | Service | Purpose | 139 | | --- | --- | 140 | | S3 | File storage | 141 | | DynamoDB | Database | 142 | | Lambda | Compute engine | 143 | 144 | Their on-demand pricing means the cost scales with usage. There is a [free tier](https://aws.amazon.com/free/?all-free-tier.sort-by=item.additionalFields.SortRank&all-free-tier.sort-order=asc), and it only begins to cost money if usage exceeds a certain amount. 145 | 146 | For small projects with light traffic, this usually translates to monthly costs of **less than a dollar** (if not completely free). 147 | 148 | In comparison, the price of hosting a server typically starts at **$5.00 per month**. 149 | 150 | ## Continue Learning 151 | 152 | * [Official documentation](https://aws.amazon.com/getting-started/) 153 | * [Free YouTube videos](https://www.youtube.com/watch?v=ubCNZRNjhyo) 154 | * [Udemy Courses](https://www.udemy.com/course/aws-certified-developer-associate/) 155 | -------------------------------------------------------------------------------- /posts/gpt-3-generated-poetry.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Using AI to generate poetry" 3 | subtitle: "I used GPT-3 to generate poetry and other creative content." 4 | date: "2021-08-27" 5 | --- 6 | 7 | I was recently granted access to the [OpenAI GPT-3 Beta](https://beta.openai.com/), a very powerful language model that can be used to do all sorts of magic like chat bot generation, article summary, and a [lot more cool stuff](https://beta.openai.com/examples). 8 | 9 | But the application that really excited me was creative writing (stuff like SEO generation is cool and all, but a also a bit dry for my taste). 10 | 11 | I had previously seen [AI Dungeon](https://play.aidungeon.io/) use GPT to create an interactive text adventure game (try it, it's fun!) and that got me curious about what else the AI was capable of — especially when elements of both _creativity_ and _structure_ were involved. 12 | 13 | I decided to test it against poetry and story generation, and this is the resulting write-up of those experiments. 14 | 15 | ## What is GPT-3? 16 | 17 | [GPT-3](https://arxiv.org/abs/2005.14165) is a language model developed by [OpenAI](https://beta.openai.com/). To the average developer (like me), it's basically a magical black-box that is extremely powerful at natural language use-cases. 18 | 19 | It was trained on an estimated **45 TB** of text data (pretty much anything that is publicly available on the internet)! 20 | 21 | We can literally ask it to do anything. For example, if I ask for steps to bake a cake it gives me: 22 | 23 | ``` 24 | 1. Preheat oven to 350°F. 25 | 2. Grease and flour a 9-inch round cake pan. 26 | 3. Stir cake mix, oil, eggs, and water in a large bowl with a spoon until blended. 27 | 4. Pour batter into prepared pan. 28 | ``` 29 | 30 | Alternatively, I can provide it with an example of what I want. Such as: 31 | 32 | ``` 33 | [15 mins] Walk or jog lightlyy to warm up. 34 | [10 mins] 3 sets of Squats 35 | ``` 36 | 37 | And it will understand that we're writing a work-out routine, and continue in that style — responding with something like this: 38 | 39 | ``` 40 | [10 mins] 3 sets of Push ups (use the bench/steps if you can't do regular ones) 41 | [10 mins] 3 sets of Lunges 42 | [10 mins] 3 sets of Sit ups 43 | [10 mins] 3 sets of Tricep dips 44 | [10 mins] 3 sets of 45 | ``` 46 | 47 | It can also do some pretty zany stuff, like translating from English to emoji. 48 | 49 | ``` 50 | I love pizza and beer. 51 | 😀❤️🍕🍺 52 | 53 | I flew to Japan and ate some Sushi. 54 | ✈️🇯🇵😋 🍣 55 | 56 | I was snowboarding in France. 57 | 🏂❄️🇫🇷 58 | ``` 59 | 60 | So as you can see, there's just endless amount of ways you can interact with the AI. 61 | 62 | It's up to us to experiment with it and see how it can be useful for any particular problem. Let's give it a go! 63 | 64 | ## 💡 Method 65 | 66 | Today, I want to see if this AI is able to understand and generate poetry. Why poetry? Because I think it's a relatively simple example, and has three distinct dimensions that we can easily assess it on: 67 | 68 | - Tone 69 | - Content 70 | - Structure 71 | 72 | OpenAI lets customers interact with GPT-3 via an API, so that's how I'll be using it as well. 73 | 74 | I'll be providing both direct requests and examples (with increasing complexity), and let the AI come up with **20 responses** for each prompt. 75 | 76 | Additionally, for my example prompts, I'll avoid using existing text—the AI may have already seen it in the training data and that might skew the results. 77 | 78 | I'll then talk about the best samples in each batch and anything I find interesting about them. 79 | 80 | Also I've pushed the [full set of results on GitHub](https://github.com/pixegami/gpt-3-poetry-results) if you wanted to check out the rest of the samples. 81 | 82 | ## 🧪 Results 83 | 84 | ### ✍ Short poem 85 | 86 | As a baseline, I want to know how much the AI understands about the tone, structure and content of a 'poem.' 87 | 88 | ``` 89 | [Prompt] 90 | Write a short poem. 91 | 92 | [Sample AI Response] 93 | Snow is falling, 94 | 95 | silently, 96 | 97 | the ground a white blanket. 98 | Heroes dream in peace, 99 | where they sleep. 100 | ``` 101 | 102 | It's a pretty good start! It's structured but it doesn't rhyme. The language and story also have a 'poetic' feel to it. Not bad. 103 | 104 | Amongst the other 20 samples were also hilarious rubbish like this one 🤡 : 105 | 106 | ``` 107 | [Sample AI Response] 108 | Life is like a beach 109 | Where we take our coconut and whack it 110 | or chew on it 111 | 112 | There are also other beaches 113 | Where we can drink some sand and ruminate 114 | ``` 115 | 116 | Not quite hitting the mark, but still pretty interesting. 117 | 118 | ### ✍ Short poem with theme 119 | 120 | Next, I wanted to extend the prompt with some theme, just to see how it is able to assimilate that into its response. 121 | 122 | I picked a theme that isn't typically associated with poetry: **Space Exploration** (also because I've been watching a lot of old-school sci-fi stuff recently). 123 | 124 | ``` 125 | [Prompt] 126 | Write a short poem about space exploration. 127 | 128 | [Sample AI Response] 129 | I hope the stars align 130 | For I dream and explore 131 | Beyond what we see, 132 | With the most amazing views 133 | ``` 134 | 135 | It's definitely found a way to put the space theme in there — not too unnaturally either, because it still links it to 'emotional' concepts like 'dream', 'hope' and 'explore.' 136 | 137 | ### ✍ Short poem with 2x themes 138 | 139 | Now I know it can handle one theme, let's add another one to the mix and see what happens. Something _not_ commonly associated with either poetry nor space (we want to challenge the AI right?) — how about... 140 | 141 | ``` 142 | [Prompt] 143 | Write a short poem about space exploration and parkour. 144 | 145 | [Sample AI Response] 146 | Taking the stars and reaching for the moon 147 | Afraid to look down 148 | She jumps, leaps, and lands 149 | ``` 150 | 151 | Not bad. Here's another sample. 152 | 153 | ``` 154 | [Sample AI Response] 155 | Parkour through the cosmos 156 | jump to the moon and land with a moonwalk 157 | push off from the Earth, do a headspin 158 | jump to Mars, make it look easy 159 | ``` 160 | 161 | I'm impressed that instead of just shoe-horning in the parkour theme, it's found words and phrases that have a natural connection with the space theme — like 'leap' and 'moonwalk.' 162 | 163 | It was at this point that the [vector aspect](https://jalammar.github.io/illustrated-gpt2/) of the model started to really click for me. 164 | 165 | It's as if it found the [cross-product](https://en.wikipedia.org/wiki/Cross_product) between the 'space' vector and the 'parkour' vector (this is a gross over-simplification), and applied that to the poem. 166 | 167 | ### ✍ Children's Story (Rhyming Verse) 168 | 169 | My next task was to try something a bit more directed. This time I will give it a concrete example (with a tone, structure and story) to continue with. 170 | 171 | To keep it (relatively) easy for the AI, I started with a rhyming verse, like something from a Dr Seuss book: 172 | 173 | ``` 174 | [Prompt] 175 | It's a sunny day. 176 | And Milky wants to play! 177 | He looks for his ball, 178 | but it's not in the hall. 179 | ``` 180 | 181 | > By the way Milky's the name of my dog, and this literally is the story of his life every morning. 182 | 183 | Some of the results from this were super interesting. My favorite one is: 184 | 185 | ``` 186 | [Sample AI Response] 187 | It's not on the stairs. 188 | It's not in the yard. 189 | Maybe it's in the attic, 190 | somewhere really dark. 191 | ``` 192 | 193 | This one nails the tone — notice that it repeats the "It's not at \_\_\_" three times? That's just like what a children's book would do! 194 | 195 | It also introduces some progression and drama into the story. Now our character has an idea where the ball is (the attic), but there is a sense of danger and risk in the way (somewhere really dark). 196 | 197 | Structurally, "yard" and "dark" is also partial rhyme, but I think that was probably a fluke (out of the 20 samples, maybe only 2-3 managed to rhyme). 198 | 199 | Still, I was really impressed with this result. Here's something else that I observed as well: 200 | 201 | ``` 202 | [Sample AI Response] 203 | Then in through the window 204 | stumbles a dog with a bark. 205 | It's Billy, his brother. 206 | ``` 207 | 208 | _Dog!_ It figured out that Milky in this story is a dog, even though that was never explicitly mentioned in the prompt. It might be a small detail, but I was low-key amazed by that. 209 | 210 | ### ✍ Fantasy Fiction 211 | 212 | Next let's test GPT-3's ability for creative story-telling as well. Here's a rhyming story with a bit of drama, written in a high-fantasy style: 213 | 214 | ``` 215 | [Prompt] 216 | A mysterious curse sweeps the land, 217 | and into townsfolk's hearts expand. 218 | One broken link and night shall fall, 219 | eternal sleep will enslave them all. 220 | 221 | But there is hope 222 | ``` 223 | 224 | I ended the prompt with `But there is hope` because I was interested to see if the AI can progress (and negate!) the verse before it, rather than extend the example (which we already know it can do). 225 | 226 | The results for this prompt were probably the most entertaining for me to read. My favorite: 227 | 228 | ``` 229 | [Sample AI Response] 230 | a prophecy is spoken, 231 | Which tells of one who will restore the sun. 232 | She comes with a prince and shall break the spell, 233 | and together save the land from hell. 234 | ``` 235 | 236 | So many things I love about this response. 237 | 238 | - It introduces a _heroine_ to the story (+1 for gender diversity!) 239 | - It writes a sensible continuation of the prompt. 240 | - It even manages rhyming (between 'one/sun', and 'spell/hell'). 241 | 242 | A lot of the other samples were also quite interesting (check them out [here](https://github.com/pixegami/gpt-3-poetry-results)) and full of creative ideas. 243 | 244 | ### ✍ Hacker Rap 245 | 246 | And finally just for a bit of fun, let's see what it can do with hacker rap 🤣 : 247 | 248 | ``` 249 | [Prompt] 250 | When I'm tappin' I'm trapped in the screen 251 | I escape when the codebase is clean 252 | ``` 253 | 254 | I loved it's work here as well. It did a good job picking up on both structure (rhyme) and content. 255 | 256 | ``` 257 | [Sample AI Response] 258 | I can write code, I can code all night 259 | You are my code and I am your light 260 | We're in this together from dusk 'til dawn. 261 | We got the skill to make things run 262 | ``` 263 | 264 | I also found this particular choice of words interesting: 265 | 266 | ``` 267 | The refactoring was a success, great! 268 | And I will not commit the same crime 269 | So I'm testin', testin', testin, testin' again 270 | Just when I've seen enough, it happens again 271 | ``` 272 | 273 | "Commit the same crime" — does it know the significance of the word _commit_ for developers? 🤯 274 | 275 | ## Wrap Up 276 | 277 | That's pretty much all I had to share this time around. GPT-3 has definitely impressed me with it's capabilities, and now I have a stronger understanding how this power can be wielded. 278 | 279 | In a batch of 20 samples, not all of them are great—but the good ones are sometimes _really_ good. 280 | 281 | If I wanted to use it in this way, I'd need a process that allows a human (me) to select and use the best samples. 282 | 283 | I hope this was interesting! I also covered everything here in a video as well (which I geared towards less technical viewers) which you can check out [here](https://www.youtube.com/watch?v=r3zKcL0iGeA&t=47s). 284 | 285 | Thanks for reading! 286 | -------------------------------------------------------------------------------- /posts/qr-code-micro-app.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Building a QR code micro-app" 3 | subtitle: "Build a serverless 'hello-world' QR code app with Python and AWS." 4 | date: "2021-08-14" 5 | --- 6 | 7 | Ever since the pandemic started, I've noticed QR codes creeping into my daily life. Venue check-ins, digital menus at restaurants, and online payments. 8 | 9 | I thought it'd be fun to explore the technology a little bit, so I've built a micro web-app that lets you save messages and view them later using a QR code. Sort of a "Hello World" QR code project. 10 | 11 | In this post, I'll be sharing how I built this using Python and AWS free tier. 12 | 13 | You can try out the [app itself here](https://qr.pixegami.com/). The [source code](https://github.com/pixegami/qr-code-webapp) is also available on GitHub. 14 | 15 | ![images/qr-code-demo.gif](https://cdn.hashnode.com/res/hashnode/image/upload/v1628899108564/NGWiGc3-w.gif) 16 | 17 | ## What is a QR Code? 18 | 19 | We've all seen and used them before, but how does a QR code actually work? The first thing to understand is that it is an [internationally standardized specification](https://www.iso.org/obp/ui/#iso:std:iso-iec:18004:ed-3:v1:en): 20 | 21 | > This International Standard [...] specifies the QR Code symbology characteristics, data character encoding methods, symbol formats, dimensional characteristics, error correction rules [...] 22 | 23 | As long an image respects these standard, it is a "QR code" and can be understood by most smartphone cameras. The image itself also has a kind of anatomy: 24 | 25 | ![images/qr_code_anatomy.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1628900340423/NSYNLARYu.png) 26 | 27 | It is quite interesting, but ultimately these were the three things I cared about: 28 | 29 | - We can store up to around 4000 alpha-numeric symbols in a standard sized QR code. 30 | - QR codes can have a "URL" format, which will prompt phones to open it up in a browser when scanned. 31 | - QR codes have error-correction capabilities, which allow it to remain function even if some parts of it is covered or removed—_I did not know this!_ 32 | 33 | ## Architecture 34 | 35 | I want to make an app that lets me author some arbitrary content (in this case a short text message), have it persisted somewhere, and generate a QR code that loads the content when I scan it with the phone. 36 | 37 | So after breaking these requirements into technical tasks, here is the strategy: 38 | 39 | - **Author some arbitrary content**: I'll use a static React front-end to let the user write their message. 40 | - **Persist the content**: I'll have a serverless API (using AWS Lambda and API Gateway) for the front-end to use. It will receive the message (a string) and put it into database for storage. I'll generate a unique ID and use this as the `tag` for the object. 41 | - **Generate a QR Code**: I'll first come up with a URL string I want to encode into the QR image (using the `tag` I made above). Then I'll find a Python library that lets me turn this URL string into a QR image. I'll make the image accessible to the user. 42 | - **Load the content**: Now I'll implement the URL endpoint that the QR code re-directs to. It will probably have the `tag` as a query parameter, so I'll just use that to look up the message in my table and send it back to the page. 43 | 44 | ### Overall Stack 45 | 46 | - **Frontend**: React (Typescript) 47 | - **Backend**: Python code with AWS Lambda and API Gateway 48 | - **Database**: DynamoDB 49 | - **Image Storage**: Amazon S3 50 | 51 | ## Implementation 52 | 53 | Most of the app's 'meaty' logic lives in the [`qr-code-infrastructure/compute/api`](https://github.com/pixegami/qr-code-webapp/tree/main/qr-code-infrastructure/compute/api) folder, as a bunch 54 | of Python functions. 55 | 56 | ### Generating a `tag` and a URL 57 | 58 | When a user sends a message, it generates a random tag using `uuid4` (which I truncated to 12 59 | characters to keep it a bit shorter). A URL to view this message will then be used to create a QR code. 60 | 61 | ```python 62 | 63 | # uuid is a built-in Python library to generate random IDs with, with low chance of collision. 64 | qr_id = uuid.uuid4().hex[:12] 65 | qr_tag = f"qr-{qr_id}" 66 | 67 | # We'll later have to implement this page so that it can load our message with the given tag. 68 | content = f"https://qr.pixegami.com/view?tag={qr_tag}" 69 | ``` 70 | 71 | ### Generating the QR code image 72 | 73 | One of the things I really love about Python is how there's a library for everything. I just typed in "qr code" into PyPI and found this [library](https://pypi.org/project/qrcode/), which took a minute to install and use. 74 | 75 | Using the `qrcode` library, I create an image. It's a one-liner. 76 | 77 | ```python 78 | qr_image = qrcode.make(content) 79 | ``` 80 | 81 | But now we need to save this image somewhere. Since this function is running on AWS Lambda, we can't just [save it anywhere](https://aws.amazon.com/lambda/faqs/). 82 | 83 | > Each Lambda function receives 500MB of non-persistent disk space in its own /tmp directory. 84 | 85 | This must be saved into `/tmp` folder on Lambda 86 | since that is the only folder that is writable (hence why we need to pass down a `path`). 87 | 88 | ```python 89 | image_path = os.path.join(path, f"{qr_tag}.png") 90 | qr_image.save(image_path) 91 | ``` 92 | 93 | ### Showing the QR image to the user 94 | 95 | Now in our Lambda runtime we have a `.png` file at `image_path`. We need to find a way to get it to the user. We'll do this by storing the image somewhere permanent, and then generate a URL for that image. 96 | 97 | Let's upload it to an [S3 bucket](https://aws.amazon.com/s3/?nc2=h_ql_prod_fs_s3). 98 | 99 | ```python 100 | bucket_name = os.environ["IMAGE_BUCKET_NAME"] 101 | key = f"{qr_result.tag}.png" 102 | s3client = boto3.client("s3") 103 | s3client.upload_file(qr_result.image_path, bucket_name, key) 104 | ``` 105 | 106 | I then create a [pre-signed URL](https://docs.aws.amazon.com/AmazonS3/latest/userguide/ShareObjectPreSignedURL.html) from the bucket so that I can tell the frontend where to load the 107 | QR code image from. 108 | 109 | > All objects by default are private. Only the object owner has permission to access these objects. However, the object owner can optionally share objects with others by creating a presigned URL, using their own security credentials, to grant time-limited permission to download the objects. 110 | 111 | It expires in `3600` seconds, but that's fine because I don't need the image 112 | itself to be long lived. 113 | 114 | ```python 115 | presigned_url = s3client.generate_presigned_url( 116 | "get_object", 117 | Params={"Bucket": bucket_name, "Key": key}, 118 | ExpiresIn=3600, 119 | ) 120 | ``` 121 | 122 | This URL is sent back to the front-end for display. 123 | 124 | ![images/qr-code.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1628903696663/uU7ksdnrL.png) 125 | 126 | ### Persisting the `tag` and the message 127 | 128 | Finally I need to write an entry into the [DynamoDB table](https://aws.amazon.com/dynamodb/) so that when a user scans a QR code, we can fetch the message later. 129 | 130 | The only important thing to know about DynamoDB here is that it acts as a simple key-value store, and it is also serverless. 131 | 132 | ```python 133 | item = QrItem() 134 | item.pk = qr_result.tag # This is the UUID we generated above. 135 | item.message = message # This is the message body. 136 | self.database.put_item(item) 137 | ``` 138 | 139 | ### Scanning the QR image to load the content 140 | 141 | On the front-end, it's simple just a page for this URL we generated earlier `https://qr.pixegami.com/view?tag={qr_tag}` to look up the table value for the item with that `tag`. 142 | 143 | I [used](https://github.com/pixegami/qr-code-webapp/blob/main/qr-code-site/src/components/pages/ViewPage.tsx#L19) a [React `useEffect` hook](https://reactjs.org/docs/hooks-effect.html), which lets me make an API call once the page loads. 144 | 145 | I have another API on the back-end, which receives this `tag` and looks up the saved message. It then sends it back for the front-end to display. 146 | 147 | ```python 148 | serialized_item = self.database.get_item(QrItem(tag)) 149 | item = QrItem().deserialize(serialized_item) 150 | message = item.message 151 | ``` 152 | 153 | ![images/qr-result.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1628903629263/V2Jg0j8r3.png) 154 | 155 | ## Wrap Up 156 | 157 | That's pretty much it! Now if I ever need to use QR codes as part of an application in the future I'll just dig this up 😅 158 | 159 | And if you're keen to try this out yourself, feel free to check out the [source](https://github.com/pixegami/qr-code-webapp). It's a small project so you can probably build it out in a few hours (or a few days, if you're new to AWS as well). 160 | -------------------------------------------------------------------------------- /posts/retro-games-with-pico-8.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Create retro games with PICO-8" 3 | subtitle: "8-bit games are a great way to learn and expand your portfolio." 4 | date: "2020-12-19" 5 | cr: ["https://www.artstation.com/shizuorin"] 6 | --- 7 | 8 | After watching [High Score](https://www.youtube.com/watch?v=B4jopG1wX88) on Netflix, I was suddenly inspired to develop a retro-style game. I think it was a combination of the aesthetics and nostalgia that motivated me. 9 | 10 | It turned out to be a pretty fun experience, and something I'd recommend as a weekend project to any developer. With tools like [PICO-8](https://www.lexaloffle.com/pico-8.php) (a NES-like virtual console), it's really easy to get started — even if you're a beginner to programming. 11 | 12 | In this post, I'll be discussing why you should try your hand at retro-game development, and explain some of the technical limitations you'll be facing. 13 | 14 | ![images/pico-8 demo](/images/jelpi_demo.gif) 15 | 16 | ## Why you should build a retro game 17 | 18 | For new programmers especially, a retro game project is: 19 | 20 | - A great way to learn programming. 21 | - An excellent addition to your portfolio. 22 | - An effective way of prototyping new ideas (see [CELESTE]()). 23 | 24 | The constraints of using "retro technology" creates a harsh environment to develop in. 25 | 26 | But at the same time, you won't have to worry about learning a hundred different libraries, monetization funnels, or how to get it running on four different platforms. 27 | 28 | Essentially, you get to focus purely on the technical and creative problems of the game itself. 29 | 30 | ## What is PICO-8? 31 | 32 | From the [PICO-8](https://www.lexaloffle.com/pico-8.php) official website: 33 | 34 | > PICO-8 is a [fantasy console](https://www.lexaloffle.com/pico-8.php?page=faq) for making, sharing and playing tiny games and other computer programs. It _feels_ like a regular console, but runs on Windows / Mac / Linux. 35 | 36 | Basically, it's a program that pretends to be a console. It costs $15 to buy. You can use it to both play _and_ create your games. You can, of course, use an external editor (like [VSCode](https://code.visualstudio.com/)) with it as well. 37 | 38 | You write code for it in [Lua](https://www.lua.org/) (if you haven't used that language before, don't worry — you'll pick it up in a day). The art and sound can be created directly in the console's editor, to be used in your game. 39 | 40 | ![images/pico-8-code-editor](/images/pico-8-code-editor.png) 41 | 42 | Finally, you can even export them to HTML so your friends (and recruiters?) can check it out from their phone. 43 | 44 | I'd say the learning curve from zero to [Pong](https://en.wikipedia.org/wiki/Pong) is just a matter of hours (or days, at most). 45 | 46 | The best way to get started is to follow the [official manual](https://www.lexaloffle.com/pico-8.php?page=manual), or [watch a video](https://www.youtube.com/watch?v=K5RXMuH54iw). 47 | 48 | ## PICO-8's technical specs 49 | 50 | You only have a palette of `16` colours, your canvas is `128` pixels wide and your whole program needs to fit within `65536` characters. There's almost no framework library to learn — aside from a handful of helper functions that would probably fit on a [napkin if written out](https://www.lexaloffle.com/bbs/files/16585/PICO-8_Cheat-Sheet_0-9-2.png). 51 | 52 | Comparing it to actual retro console specs, it's somewhere between a [NES](https://en.wikipedia.org/wiki/Nintendo_Entertainment_System) and an [Atari](https://en.wikipedia.org/wiki/Atari). 53 | 54 | | | PICO-8 | Atari | NES | 55 | | ------------------ | --------- | --------- | --------- | 56 | | **Year** | 2015 | 1977 | 1985 | 57 | | **Resolution** | 128 x 128 | 160 x 192 | 256 x 240 | 58 | | **Colors** | 16 | 128 | 52 | 59 | | **Cartridge Size** | 32 kB | 4 kB | 128 kB | 60 | 61 | ## Code examples 62 | 63 | Here's a couple of snippets of PICO-8 code to give you an idea of what development looks like. 64 | 65 | ### Game Loop 66 | 67 | The first thing to note is that the game has a special function called `_update()` which is invoked at 30 FPS. This will probably be the main driving force behind your game logic. In this snippet, we create a variable `f`, which increases by `1` each update — effectively counting the number of frames since the game loaded. 68 | 69 | ```lua 70 | -- this is a global variable 71 | f = 0 72 | 73 | -- this is a special function that pico-8 invokes 30 times per second. 74 | function _update() 75 | f += 1 76 | end 77 | ``` 78 | 79 | ### Rendering a sprite 80 | 81 | You can draw sprites (images) with the pixel art editor directly in the console. In PICO-8, you have 16 colors to choose from. 82 | 83 | ![images/pico-8-sprite-editor](/images/pico-8-sprite-editor.png) 84 | 85 | Each sprite has an ID, which can then be used to render it on the screen at the x and y position you specify. Another special in-built function of PICO-8 is `_draw()`, which also executes at 30 FPS, but is guaranteed to execute after `_update()`. 86 | 87 | ```lua 88 | x = 64 89 | y = 64 90 | 91 | function _draw() 92 | cls(0) -- clear the screen and set it to color 0 (black). 93 | spr(1, x, y) -- draw the sprite ID 1 at (x, y) 94 | end 95 | ``` 96 | 97 | This will draw the above sprite (ID 1) at (64, 64) at the centre of the screen. 98 | 99 | ![images/pico-8-draw-sprite](/images/pico-8-draw-sprite.png) 100 | 101 | ### Capturing player input 102 | 103 | PICO-8 detects user input via the `btn(k)` function, which returns true with the button with ID `k` is being pressed by the player. `k` ranges from 0 to 6 for a single player, and each number represents either the arrow keys, or two arbitrary game-play buttons like the `A` and `B` on a NES controller. 104 | 105 | ![images/nes_controller](/images/classic_nes_controller.jpg) 106 | 107 | Adding this snippet to the rendering one above will allow us to move the character. 108 | 109 | ```lua 110 | function _update() 111 | if btn(0) then x -= 2 end -- move left 112 | if btn(1) then x -+ 2 end -- move right 113 | end 114 | ``` 115 | 116 | ![images/pico-8-movement](/images/pico-8-movement.gif) 117 | 118 | ## Ideas to get started 119 | 120 | So, if you like the sound of creating your own retro-game from scratch — either to pad your CV with an extra project, or just to learn and have fun, head over to [PICO-8](https://www.lexaloffle.com/pico-8.php) to get started! I recommend first just following the [manual](https://www.lexaloffle.com/pico-8.php?page=manual). 121 | 122 | Once you've nailed the basics, here are some classic titles you could try to implement (and possibly extend): 123 | 124 | - [Pong](https://en.wikipedia.org/wiki/Pong) (1972) 125 | - [Space Invaders](https://en.wikipedia.org/wiki/Space_Invaders) (1978) 126 | - [Pac-Man](https://en.wikipedia.org/wiki/Pac-Man) (1980) 127 | - [Snake]() (1997) 128 | 129 | Or if you're feeling more ambitious, you could even try to implement a [Mario](https://en.wikipedia.org/wiki/Super_Mario_Bros.) clone! 130 | -------------------------------------------------------------------------------- /posts/saas-starter-app.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Building a serverless SaaS product" 3 | subtitle: "A full-stack SaaS project with authentication and payments." 4 | date: "2021-07-12" 5 | --- 6 | 7 | The [SaaS (software as a service)](https://en.wikipedia.org/wiki/Software_as_a_service) model underpins many of today's successful new businesses. Knowing how to build one from start to finish is probably a useful addition to any software developer's skill set. 8 | 9 | But even when you strip a SaaS product of its business logic, there's still a non-trivial amount of work and trade-offs to consider. 10 | 11 | In this project, my goal was to build a fully serverless SaaS web-app with authentication and payments — the two vital organs of any business. 12 | 13 | My implementation is opinionated (as you'll see), and intended to serve as a starting point for new SaaS ideas in the future. Here's what's included: 14 | 15 | - [Authentication](#authentication) 16 | - [Payments (Stripe)](#payments-stripe) 17 | - [Frontend (React)](#frontend-react) 18 | - [Backend API](#backend-api) 19 | - [Serverless architecture](#serverless-architecture) 20 | - [Infrastructure as code](#infrastructure-as-code) 21 | - [CRUD operations](#crud-operations) 22 | - [Lessons Learnt](#lessons-learnt) 23 | 24 | You can view the example at https://saas-starter-stack.com/app/ and the source on [GitHub](https://github.com/pixegami/saas-starter). In this post, I'll be reflecting on my choices and experience for each of the above features. 25 | 26 | ### Authentication 27 | 28 | **Don't roll your own auth!** It's hard, and mistakes can be devastating to a business. With that said, I did it anyway — mostly to learn from it. Here's also some [discussion on Hackernews](https://news.ycombinator.com/item?id=22001918) on why you might want to build your own auth. 29 | 30 | I used [bcrypt](https://codahale.com/how-to-safely-store-a-password/) and [JSON Web Tokens](https://jwt.io/), and stored credentials on DynamoDB. That part wasn't so bad. The real grind came from building things like exponential back-offs for failed attempts, account verification and reset mechanisms, and patching all the security edge cases. 31 | 32 | I got it to a roughly working state, and then called it a day. If this was a production system, I'd probably look into something like [Cognito](https://aws.amazon.com/cognito/), [Firebase](https://firebase.google.com/products/auth) or [Okta](https://www.okta.com/). 33 | 34 | ### Payments (Stripe) 35 | 36 | From payments integration, [Stripe](https://stripe.com) was an easy choice. No prominent alternative come to mind, and I've heard high praises about Stripe's developer onboarding experience. 37 | 38 | I set up [subscription payment](https://stripe.com/en-au/billing) integration with the project, and I think the developer experience lives up to expectations. The tutorials were well structured and concise. 39 | 40 | But the little thing that impressed me the most was when I typed in 'test card' in a [search box](https://stripe.com/docs/testing), it actually just straight up gave me a card-number I could copy straight to my clipboard. Whoever thought of that just saved me a click, and I'm grateful. 41 | 42 | ### Frontend (React) 43 | 44 | The frontend is a responsive web-app build with [React](https://reactjs.org/). It seems like React is still the dominant technology is the area, although I've yet to try its main competitors like [Vue](https://vuejs.org/) or [Svelte](https://svelte.dev/). 45 | 46 | I used [TailWindCSS](https://tailwindcss.com/) for styling, and prefer to anything I've tried in the past (Boostrap CSS, Semantic UI and just vanilla CSS). 47 | 48 | I then used [Gatsby](https://www.gatsbyjs.com/) to optimize the static site rendering — but I'm not sure if the extra steps are worth it at this stage. It's better for SEO and performance, but costs extra development cycles. 49 | 50 | Overall though, I was quite satisfied with this stack for the frontend, and would be happy to use it for production. 51 | 52 | ### Backend API 53 | 54 | The backend is a serverless REST API implemented in Python and hosted as [Lambda functions](https://aws.amazon.com/lambda/) behind API Gateway. 55 | 56 | My main challenge here was to abstract away the lower level things (like CORS, HTTP response formatting, database access) as much as possible. I did this via [Lambda layers](https://docs.aws.amazon.com/lambda/latest/dg/configuration-layers.html), which allowed me to group a bunch of Python packages and common scripts together. 57 | 58 | This allowed me to implement handlers that are quite short and readable, which is think is key to maintainability. 59 | 60 | ### Serverless architecture 61 | 62 | Why serverless? I think for a lot of businesses it simply wins out from a cost and scaling perspective. I could probably serve north of 500k API requests for [less than a dollar](https://aws.amazon.com/lambda/pricing/). 63 | 64 | However, this implies that the choice of database must be serverless as well. I chose [DynamoDB](https://aws.amazon.com/dynamodb/) just for the ease of integration. But if I had different data modeling requires (for which the DynamoDB architecture might be unfit), I might look into [Aurora](https://aws.amazon.com/rds/aurora/) or [Fauna](https://fauna.com/). 65 | 66 | ### Infrastructure as code 67 | 68 | Configuring infrastructure is time-consuming and error prone. If I want to be able to deploy a copy of this service quickly, I'd have to [model it as code (IaC)](https://en.wikipedia.org/wiki/Infrastructure_as_code). In keeping theme with my AWS integration so far, I've modeled this project with [AWS CDK](https://docs.aws.amazon.com/cdk/latest/guide/home.html) in Typescript. 69 | 70 | With this the entire frontend and backend can be deployed to a brand new account or domain in less than 30 minutes with just a [few configuration changes](https://github.com/pixegami/saas-starter/blob/master/saas-infrastructure/service.config.json). 71 | 72 | ### CRUD operations 73 | 74 | Finally, I've added some simple Twitter-like posting capabilities to the project just as a stub for the actual business logic. It has ways to interact with the authentication API, and find out whether a user is verified, and if they are a paying subscriber. 75 | 76 | ## Closing Thoughts 77 | 78 | Honestly, I'm so tired of this project already. It was a lot more complex than I expected — especially for an app that really doesn't do _anything_! But I did learn a lot along the way though, and will probably be faster the second time around. 79 | 80 | My top three takeaways are: 81 | 82 | - Don't build your own auth. 83 | - You'll probably rebuild the project at least once or twice, so design things to be flexible. 84 | - Having integration tests really paid off. 85 | -------------------------------------------------------------------------------- /posts/wolfhunter.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Wolfhunter: A PICO-8 RPG" 3 | subtitle: "Implementing Pokemon-like combat mechanics in PICO-8." 4 | date: "2020-12-22" 5 | --- 6 | 7 | In a [previous post](./retro-games-with-pico-8) I talked about how making retro 8-bit games is a great way for fledgling developers to learn new things whilst expanding their portfolio. 8 | 9 | I recently gave it a go myself, making a small RPG combat game called "Wolfhunter". 10 | 11 | I spent two weekends on the project, which—despite my prior experience in games development—still managed to surprise me with some interesting challenges. 12 | 13 | [Source (GitHib)](https://github.com/pixegami/wolfhunter) | [Play the Game! (Web, Mobile)](https://pixegami.github.io/wolfhunter/) 14 | 15 | ![images/wolfhunter_title](/images/wolfhunter_title.png) 16 | 17 | ## Concept 18 | 19 | Wolfhunter is an 8-bit RPG where you play as a hunter facing off against a werewolf, in a Pokemon-like combat sequence. 20 | 21 | You have magic spells and single-use items at your disposal, but the wolf is very powerful. The only way to kill it is to weaken it with your attacks, then finish it off with a silver-knife (which you only have one shot at). 22 | 23 | It is made with [PICO-8](https://www.lexaloffle.com/pico-8.php), and can be played directly in your browser [here](https://pixegami.github.io/wolfhunter/). 24 | 25 | > The motif and design of the game was based on the word "Moonshot", which was the theme of [GitHub's GameOff 2020 game jam](https://github.blog/2020-11-01-github-game-off-2020-theme-announcement/). 26 | 27 | ## Gameplay 28 | 29 | ![images/wolfhunter_gameplay](/images/wolfhunter_gameplay.gif) 30 | 31 | I set out to make a game loop that felt similar to the combat in [Pokemon Red/Blue](https://en.wikipedia.org/wiki/Pok%C3%A9mon_Red_and_Blue), which was the most popular title on the [original Nintendo Gameboy](https://en.wikipedia.org/wiki/Game_Boy). 32 | 33 | The characters stand opposite each other, and take turns to fight. There's a bunch of other mechanics too, like strengths/weaknesses, status effects, and switching characters. Here's a scene from it: 34 | 35 | ![images/pokemon_red_fight_scene](/images/pokemon_fight_scene.png) 36 | 37 | For Wolfhunter, I copied the scene layout, the turn based fighting, and the status effects. I didn't have a chance to implement other aspects of Pokemon — so instead I doubled down on the mechanics I did implement. 38 | 39 | ### Core Mechanics 40 | 41 | The units take turns to fight. The game ends when one of the character's HP is reduced to 0. 42 | 43 | Each turn, the player has two basic abilities that can be used for free. 44 | 45 | | Ability | Effect | 46 | | ------- | ------------------------------ | 47 | | Attack | Deal 15 damage. | 48 | | Defend | Blocks 15 damage for one turn. | 49 | 50 | ### Magic 51 | 52 | The basic abilities were good to establish some baseline gameplay, but not enough to be interesting. I also added '"magic", which costs **1 mana** to use (you start with **5 mana**, and cannot cast spells if you run out). 53 | 54 | | Spell | Effect | 55 | | -------- | -------------------------------------------------------------------------- | 56 | | Fireball | Deals 40 damage. Ignores defence. | 57 | | Spark | Deals 12 damage and **blinds** the enemy, causing the next attack to miss. | 58 | | Heal | Recovers 35 HP and clears **bleed** effects. | 59 | 60 | Magic is more powerful than just attacking or defending, but are in limited supply, and designed to _interact/react_ against enemy abilities. 61 | 62 | ### Items 63 | 64 | Items are even more powerful than magic, but only have **1 use** per fight. 65 | 66 | | Item | Effect | 67 | | ------------ | --------------------------------------------------------- | 68 | | Crossbow | Deals 25% of the enemy's HP as damage. | 69 | | Elixir | Recover 5 mana. | 70 | | Silver Knife | Deals 5 damage, but is the only way to kill the werewolf. | 71 | 72 | ### Enemy 73 | 74 | Finally, to tie the gameplay together, the enemy must interact well with the player's abilities, and provide some kind of strategic challenge. 75 | 76 | | Ability | Effect | 77 | | ------------- | ------------------------------------------------------------------------------------ | 78 | | Ravage | Deals 8 damage and causes **bleeding**. | 79 | | Strong Defend | Blocks 20 damage for one turn. | 80 | | Dark Flight | Skips a turn, then deals 64 damage. | 81 | | Raging Strike | Deals 18 damage but becomes **vulnerable** after, taking double damage for one turn. | 82 | 83 | These enemy is strong, but the player has ways to mitigate each of its threats. Pretty much the point of the entire game here is figuring out what move to use next. 84 | 85 | ## Event Sequence 86 | 87 | From an implementation standpoint, the most important part of the game is the "event sequence". This system will control how the game flows, and sets the framework for everything else to follow. 88 | 89 | I define an "event" as **something atomic that happens in the game**. It usually displays some description to the player, and requires the player to acknowledge. Examples: 90 | 91 | - A character uses an ability. 92 | - Damage is taken. 93 | - Character is defeated. 94 | - Turns switched. 95 | - Status effect is applied. 96 | 97 | This is pretty much the back-bone of a turn-based system. Everything in the game—from the menu selection, to mana consumption, to damage/block resolution—can be modelled with events. 98 | 99 | And in this game, events are always sequential and happening in order. But new events can be added in the middle of the sequence—for example if an attack is blocked, we need a "block" event to resolve. 100 | 101 | ![images/linked_list](/images/linked_list.jpg) 102 | 103 | This is pretty much a [linked list](https://en.wikipedia.org/wiki/Linked_list), and that's how I implemented it. The events are the nodes. Additionally, when each event resolves, it can modify the nodes directly ahead of it, or at the tail of the list. 104 | 105 | ### Sequence 106 | 107 | This is pretty much a linked list implementation. We start it off with an `new_info_event` (display some text to the player) that says "it's your turn to move." 108 | 109 | The `head` of the sequence is the event we are currently processing in the game. 110 | 111 | ```lua 112 | function new_sequence() 113 | 114 | local first_event = new_info_event("it's your turn to move!") 115 | local sequence = { 116 | head = first_event, 117 | tail = first_event 118 | } 119 | 120 | -- move sequence cursor to the next event. 121 | sequence.next = function(this) 122 | this.head = this.head.next 123 | end 124 | 125 | -- add an event to the end of the sequence. 126 | sequence.add = function(this, e) 127 | this.tail.next = e 128 | this.tail = e 129 | end 130 | 131 | -- add an event after the head of the sequence. 132 | sequence.insert = function(this, e) 133 | e:get_tail().next = this.head.next 134 | this.head.next = e 135 | end 136 | 137 | return sequence 138 | end 139 | ``` 140 | 141 | ### Damage Event 142 | 143 | Here's an example of an event that causes damage to a unit (character). The `action` is an anonymous function that executes _once_, when the event is processed. 144 | 145 | This includes playing sound effects, animations, and actually modifying the HP values. In this case, if at run-time the damage causes a unit to die, then a `new_end_combat_event` is added to the sequence, along with a `new_info_event` to display some informative text to the player. 146 | 147 | ```lua 148 | function new_damage_event(unit, value) 149 | 150 | local desc = unit.name.." takes "..value.." damage!" 151 | local dmg_event = new_event("damage", desc, true) 152 | 153 | dmg_event.action = function(this) 154 | unit.hp -= value 155 | unit:animate(new_hit_animation()) 156 | sfx(3) 157 | if unit.hp <= 0 then 158 | unit.hp = 0 159 | sequence:insert(new_end_combat_event(unit.name)) 160 | sequence:insert(new_info_event("the fight has ended!")) 161 | end 162 | end 163 | 164 | return dmg_event 165 | end 166 | ``` 167 | 168 | ### Sequence Control 169 | 170 | In the `_update()` function of the game, we process the `head` event and show the appropriate information. Special event types, like `menu` will control what UI is shown. 171 | 172 | Some events (`auto`) will be processed immediately—but most will require interaction from the player to progress. 173 | 174 | ```lua 175 | -- update the menu if we are showing one. 176 | if event.type == "menu" then combat_menu:update() end 177 | if event.type == "magic" then magic_menu:update() end 178 | if event.type == "items" then items_menu:update() end 179 | 180 | -- each time we press x, the sequence progresses. 181 | if btnp(5) or event.type == "auto" then sequence:next() end 182 | if btnp(5) and (event.type == "info" or event.type == "damage") then sfx(1) end 183 | ``` 184 | 185 | ## Items and Magic 186 | 187 | I think items and magic are an interesting one. Instead of being their own event types, I think it was a good chance to apply the [decorator pattern](https://en.wikipedia.org/wiki/Decorator_pattern). 188 | 189 | That's because items and magic can be thought of as regular abilities—wrapped with some extra conditions! 190 | 191 | Namely—we want to wrap the concept of **mana consumption** onto magic, and **single-use** onto items. Aside from those things, they still do anything else an ability can do. So what I'm after here, is a function that can take in an **ability** (an arbitrary event), and turn it into a magic spell, or an item. 192 | 193 | Consider we have this 'heal' event. As is, this will recover 35 HP, and doesn't have any other cost or conditions associated with it. 194 | 195 | ```lua 196 | function new_heal_event(name, unit, value) 197 | -- restore 35 hp! 198 | end 199 | ``` 200 | 201 | Now, I have a decorator function, which accepts an `event`, and adds mana usage to it. 202 | 203 | ```lua 204 | -- wrap an event as a spell, so it costs mana to use. 205 | function as_spell(unit, event) 206 | local spell_event = new_event("auto", "", true) 207 | spell_event.action = function(this) 208 | if unit.mana > 0 then 209 | unit.mana -= 1 210 | sequence:insert(event) 211 | else 212 | sequence:insert(new_event("menu")) 213 | sequence:insert(new_info_event("you don't have enough mana to cast this spell.")) 214 | end 215 | end 216 | return spell_event 217 | end 218 | ``` 219 | 220 | This will check if I have **enough mana** to use this event—and either cast the spell and deplete the mana, or cancel the event and inform me that I don't have enough mana. 221 | 222 | Now I can wrap my `heal` event with `as_spell` and turn it into a magic spell! 223 | 224 | ```lua 225 | as_spell(unit, new_heal_event(event_id, unit, 35)) 226 | ``` 227 | 228 | ## Animation 229 | 230 | [PICO-8](https://www.lexaloffle.com/pico-8.php) doesn't really give us an animation framework. We can draw sprites at arbitrary locations and change some colour palettes, but aside from that—we're pretty much on our own. 231 | 232 | Thankfully, in a turn-based RPG, the animations aren't very complex. I have 'units' on two corners of the screen, and they flicker and bounce around a bit when hit. 233 | 234 | To achieve, this, assume we have a `unit` object with some `x` and `y` position—its absolute position on the screen. 235 | 236 | ```lua 237 | function draw_unit(unit) 238 | local x = 128 - spr_size 239 | local y = 0 240 | 241 | -- draw the unit 242 | spr(1, x, y) 243 | end 244 | ``` 245 | 246 | I can move the unit by changing `x` and `y`, but what if I cancel an animation half way through? I don't want the animation state to be 'baked' into the unit's actual position, because it will be harder for me to reset it. 247 | 248 | My solution was to create an 'animation' object, which keeps an independent frame count `n` and transformation `x, y`, which can be then _applied_ to the unit at render time. 249 | 250 | ```lua 251 | function new_animation() 252 | local animation = { 253 | name = "default", 254 | n = 0, 255 | frames_left = 15, 256 | x = 0, 257 | y = 0, 258 | color = 0, 259 | } 260 | } 261 | ``` 262 | 263 | Now each update, we can also `update()` the animation and apply its transform to the unit. If the animation runs out of frames, it will disappear and things will be back to normal. 264 | 265 | ```lua 266 | -- apply unit animation 267 | if unit.animation then 268 | unit.animation:update(x, y) 269 | anim_spr_x = x + unit.animation.x 270 | anim_spr_y = y + unit.animation.y 271 | if unit.animation:has_ended() then unit.animation = nil end 272 | end 273 | 274 | -- draw the unit 275 | spr(1, anim_spr_x, anim_spr_y) 276 | ``` 277 | 278 | However, we can also just cancel the animation at any time we want, no matter what frame it is on. This gave me a lot of modular control over the animation, but also prevented any part of the animation system having an impact on the logical game state. 279 | 280 | ## Tools 281 | 282 | Here's some of the main tools/software I used for the job. 283 | 284 | - [PICO-8](https://www.lexaloffle.com/pico-8.php): Virtual console, and framework to develop and run the game. 285 | - [VSCode](https://code.visualstudio.com/): Code editor. 286 | - [Pixaki](https://rizer.co/): iPad pixel art editor to draw and touch up the sprites (which to be honest, I mostly traced from Pokemon). 287 | 288 | ## Summary 289 | 290 | That pretty much sums up my experience of implementing RPG combat in [PICO-8](https://www.lexaloffle.com/pico-8.php). Even with the lo-fi, 80's era constraints, I found that the core problems—like game design and event modelling—are still timeless. 291 | 292 | With a bit of work, I think it'd be possible to implement a more Pokemon-like RPG in PICO-8, but probably on a smaller scale, because of the memory and size limits (I was almost at 30% capacity with just this). 293 | 294 | At some point though, dealing with over 1,000 lines of code in a single `lua` run-time becomes unwieldy. So whilst something like [Unity](https://unity.com/) is better suited for serious projects, PICO-8 is still a great way to learn and prototype ideas. 295 | 296 | > Wolfhunter's [source code](https://github.com/pixegami/wolfhunter) is public, and you can play the game directly in the browser (both web and mobile) [here](https://pixegami.github.io/wolfhunter/). 297 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pixegami/nextjs-blog-tutorial/5b5f57af74f4e6f18a3b105e299f9c861d1f99c1/public/favicon.ico -------------------------------------------------------------------------------- /public/images/classic_nes_controller.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pixegami/nextjs-blog-tutorial/5b5f57af74f4e6f18a3b105e299f9c861d1f99c1/public/images/classic_nes_controller.jpg -------------------------------------------------------------------------------- /public/images/jelpi_demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pixegami/nextjs-blog-tutorial/5b5f57af74f4e6f18a3b105e299f9c861d1f99c1/public/images/jelpi_demo.gif -------------------------------------------------------------------------------- /public/images/linked_list.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pixegami/nextjs-blog-tutorial/5b5f57af74f4e6f18a3b105e299f9c861d1f99c1/public/images/linked_list.jpg -------------------------------------------------------------------------------- /public/images/pico-8-code-editor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pixegami/nextjs-blog-tutorial/5b5f57af74f4e6f18a3b105e299f9c861d1f99c1/public/images/pico-8-code-editor.png -------------------------------------------------------------------------------- /public/images/pico-8-draw-sprite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pixegami/nextjs-blog-tutorial/5b5f57af74f4e6f18a3b105e299f9c861d1f99c1/public/images/pico-8-draw-sprite.png -------------------------------------------------------------------------------- /public/images/pico-8-movement.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pixegami/nextjs-blog-tutorial/5b5f57af74f4e6f18a3b105e299f9c861d1f99c1/public/images/pico-8-movement.gif -------------------------------------------------------------------------------- /public/images/pico-8-sprite-editor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pixegami/nextjs-blog-tutorial/5b5f57af74f4e6f18a3b105e299f9c861d1f99c1/public/images/pico-8-sprite-editor.png -------------------------------------------------------------------------------- /public/images/pokemon_fight_scene.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pixegami/nextjs-blog-tutorial/5b5f57af74f4e6f18a3b105e299f9c861d1f99c1/public/images/pokemon_fight_scene.png -------------------------------------------------------------------------------- /public/images/wolfhunter_gameplay.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pixegami/nextjs-blog-tutorial/5b5f57af74f4e6f18a3b105e299f9c861d1f99c1/public/images/wolfhunter_gameplay.gif -------------------------------------------------------------------------------- /public/images/wolfhunter_title.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pixegami/nextjs-blog-tutorial/5b5f57af74f4e6f18a3b105e299f9c861d1f99c1/public/images/wolfhunter_title.png -------------------------------------------------------------------------------- /public/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pixegami/nextjs-blog-tutorial/5b5f57af74f4e6f18a3b105e299f9c861d1f99c1/public/logo.png -------------------------------------------------------------------------------- /styles/globals.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | module.exports = { 3 | content: ["./app/**/*.{js,ts,jsx,tsx}", "./components/**/*.{js,ts,jsx,tsx}"], 4 | theme: { 5 | extend: {}, 6 | }, 7 | plugins: [require("@tailwindcss/typography")], 8 | }; 9 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": [ 5 | "dom", 6 | "dom.iterable", 7 | "esnext" 8 | ], 9 | "allowJs": true, 10 | "skipLibCheck": true, 11 | "strict": true, 12 | "forceConsistentCasingInFileNames": true, 13 | "noEmit": true, 14 | "esModuleInterop": true, 15 | "module": "esnext", 16 | "moduleResolution": "node", 17 | "resolveJsonModule": true, 18 | "isolatedModules": true, 19 | "jsx": "preserve", 20 | "incremental": true, 21 | "plugins": [ 22 | { 23 | "name": "next" 24 | } 25 | ] 26 | }, 27 | "include": [ 28 | "next-env.d.ts", 29 | "**/*.ts", 30 | "**/*.tsx", 31 | ".next/types/**/*.ts" 32 | ], 33 | "exclude": [ 34 | "node_modules" 35 | ] 36 | } 37 | --------------------------------------------------------------------------------