diff --git a/lib/blog/index.ts b/lib/blog/index.ts new file mode 100644 index 0000000..fe9868b --- /dev/null +++ b/lib/blog/index.ts @@ -0,0 +1,2 @@ +export * from "./types"; +export { usePostData } from "./src/data"; diff --git a/lib/blog/next.config.js b/lib/blog/next.config.js new file mode 100644 index 0000000..48100d0 --- /dev/null +++ b/lib/blog/next.config.js @@ -0,0 +1,29 @@ +import webpack from "webpack"; + +/** + * + * @param {import("next").NextConfig} config + * @returns + */ +export function withBlog(config = {}) { + const originalWebpack = config.webpack; + /** + * @param {string} phase + * @param {{ defaultConfig: import("next").NextConfig }} options + */ + // @ts-ignore + return async (phase, options) => { + config.webpack = (config, context) => { + originalWebpack?.(config, context); + if (!context.isServer) { + config.plugins.push( + // Intentionally verbose and explicit to make it hard + // for this to match user code. + new webpack.IgnorePlugin({ resourceRegExp: /\/__server-only\// }), + ); + } + return config; + }; + return config; + }; +} diff --git a/lib/blog/page.tsx b/lib/blog/page.tsx new file mode 100644 index 0000000..eb18ca9 --- /dev/null +++ b/lib/blog/page.tsx @@ -0,0 +1,116 @@ +import { MDXRemote, MDXRemoteSerializeResult } from "next-mdx-remote"; +import { PageLayoutProps, PostDataStore, Post, MdxOptions } from "./types"; +import { usePostWatcher } from "./src/watcher-client"; +import { PostDataProvider } from "./src/data"; +import { FrontMatter } from "./src/internal-types"; +import { GetStaticPaths, GetStaticProps } from "next"; +import { DEFAULT_SLUG_PARTS } from "@alexharri/blog/src/constants"; + +export interface CreatePageOptions { + components: Record | ((props: any) => React.ReactNode)>; + Layout: React.ComponentType; +} + +interface PageProps { + source: MDXRemoteSerializeResult; + version: string; + slug: string; + data: PostDataStore; + postsPath: string; +} + +export function createPostPage(options: CreatePageOptions) { + function Page(props: PageProps) { + const source = usePostWatcher(props); + + // Ensure scope is present and 'FrontMatter' has title + let scope = ((source.scope as unknown) ?? {}) as FrontMatter; + if (!scope.title) scope = { ...scope, title: "Untitled post" }; + + const post: Post = { + title: scope.title, + slug: props.slug, + description: scope.description || "", + publishedAt: scope.publishedAt || "", + updatedAt: scope.updatedAt || "", + image: scope.image || "", + firstCodeSnippet: null, + }; + + return ( + + + + + + ); + } + return Page; +} + +type Params = Partial<{ [key: string]: string }>; + +interface CreateGetStaticPropsOptions { + mdxOptions?: MdxOptions; + slug?: string; + /** + * @default ["slug"] + */ + slugParts?: string[]; // TBD: Can this be inferred from call stack? + /** + * Path to directory containing your posts as '.md' or '.mdx', relative to 'process.cwd()' + * + * @default "posts/" + */ + postsPath?: string; +} + +export const createGetStaticProps = + (options: CreateGetStaticPropsOptions = {}): GetStaticProps => + async (ctx) => { + const blogPageUtils = await import("./src/__server-only/blogPageUtils"); + let slug = options.slug; + if (!slug) { + const slugParts = options.slugParts || DEFAULT_SLUG_PARTS; + slug = slugParts + .map((key) => { + const value = ctx.params?.[key]; + if (!value) throw new Error(`Missing context parameter '${key}'`); + return value; + }) + .join("/"); + } + return blogPageUtils.getPostProps(slug, options); + }; + +interface GetStaticPathsOptions { + /** + * If set to true, this page only renders posts that have not been published + * and returns 404 for published posts. + * + * @default false + */ + drafts?: boolean; + /** + * @default ["slug"] + */ + slugParts?: string[]; + /** + * Path to directory containing your posts as '.md' or '.mdx', relative to 'process.cwd()' + * + * @default "posts/" + */ + postsPath?: string; +} + +export const createGetStaticPaths = + (options: GetStaticPathsOptions = {}): GetStaticPaths => + async () => { + const blogPageUtils = await import("./src/__server-only/blogPageUtils"); + const { slugParts = DEFAULT_SLUG_PARTS, postsPath = "posts/" } = options; + const type = options.drafts ? "draft" : "published"; + return { + paths: blogPageUtils.getPostPaths({ type, slugParts, postsPath }), + fallback: false, + }; + }; diff --git a/lib/blog/posts.ts b/lib/blog/posts.ts new file mode 100644 index 0000000..89cc806 --- /dev/null +++ b/lib/blog/posts.ts @@ -0,0 +1 @@ +export { getPosts, getPostPaths } from "./src/__server-only/blogPageUtils.js"; diff --git a/lib/blog/src/__server-only/blogPageUtils.ts b/lib/blog/src/__server-only/blogPageUtils.ts new file mode 100644 index 0000000..073ee6e --- /dev/null +++ b/lib/blog/src/__server-only/blogPageUtils.ts @@ -0,0 +1,168 @@ +import fs from "fs"; +import path from "path"; +import matter from "gray-matter"; +import { findMdFiles } from "../md.js"; +import { Post, PostDataStore } from "../../types.js"; +import { FrontMatter, GetPostOptions, GetPostsOptions } from "../internal-types.js"; +import { DEFAULT_POSTS_PATH } from "../constants.js"; + +export const getPosts = (options: GetPostsOptions = {}) => { + const { type = "published", postsPath = DEFAULT_POSTS_PATH } = options; + + const posts: Post[] = []; + + for (const fileName of findMdFiles(postsPath)) { + const filePath = path.join(postsPath, fileName); + const fileContent = fs.readFileSync(filePath, "utf-8"); + + const { data } = matter(fileContent); + + const { + title, + description = "", + publishedAt = "", + updatedAt = "", + image = "", + include = {}, + } = data as FrontMatter; + + let firstCodeSnippet: Post["firstCodeSnippet"] = null; + if (include.firstCodeSnippet) { + const lines = fileContent.split("\n"); + const startLineIndex = lines.findIndex((line) => line.startsWith("```")); + let endLineIndex = -1; + for (let i = startLineIndex + 1; i < lines.length; i++) { + if (lines[i].startsWith("```")) { + endLineIndex = i; + break; + } + } + if (endLineIndex !== -1) { + const language = lines[startLineIndex].substring(3); + const text = lines.slice(startLineIndex + 1, endLineIndex).join("\n") + "\n"; + firstCodeSnippet = { text, language }; + } + } + + const slug = fileName.replace(/\.mdx?$/, ""); + + const includePost = publishedAt ? type === "published" : type === "draft"; + + if (includePost) { + posts.push({ title, description, slug, publishedAt, updatedAt, image, firstCodeSnippet }); + } + } + + return posts.sort((a, b) => { + return b.publishedAt.localeCompare(a.publishedAt); + }); +}; + +function customPageExists(slug: string, isDraftPost: boolean) { + const extensions = [".js", ".ts", ".jsx", ".tsx"]; + const roots = ["./src/pages", "./pages/"]; + const cwd = process.cwd(); + for (const root of roots) { + for (const ext of extensions) { + const pagePath = path.resolve( + cwd, + // TBD: /blog/ shouldn't be hardcoded + `${root}/blog/${isDraftPost ? "draft/" : ""}${slug}${ext}`, + ); + if (fs.existsSync(pagePath)) return true; + } + } + return false; +} + +interface GetPostPathsOptions { + type: "published" | "draft"; + slugParts: string[]; + postsPath: string; +} + +export function getPostPaths(options: GetPostPathsOptions) { + const draft = options.type === "draft"; + const postsPath = path.resolve(process.cwd(), options.postsPath); + + const paths = findMdFiles(postsPath) + .filter((filePath) => { + const fileContent = fs.readFileSync(path.resolve(postsPath, filePath)); + const isDraftPost = !matter(fileContent).data.publishedAt; + if (draft) return isDraftPost; + return !isDraftPost; + }) + .map((path) => path.replace(/\.mdx?$/, "")) + .filter((slug) => !customPageExists(slug, draft)) + .map((slug) => { + const slugParts = slug.split("/"); + const expectedLen = options.slugParts.length; + if (slugParts.length !== expectedLen) { + throw new Error( + `Expected number of slug parts to equal ${expectedLen}. Got ${slugParts.length}`, + ); + } + const params: Partial> = {}; + for (const [i, key] of options.slugParts.entries()) { + params[key] = slugParts[i]; + } + return { params }; + }); + + return [...paths]; +} + +function getPostData(slug: string): PostDataStore { + const dataDir = path.resolve(process.cwd(), `./public/data/${slug}`); + + if (!fs.existsSync(dataDir)) return {}; + + const out: PostDataStore = {}; + for (const fileName of fs.readdirSync(dataDir)) { + const filePath = path.resolve(dataDir, fileName); + const json = JSON.parse(fs.readFileSync(filePath, "utf-8")); + const dataSlug = fileName.split(".")[0]; + out[dataSlug] = json; + } + return out; +} + +export const getPostProps = async (slug: string, options: GetPostOptions = {}) => { + const { postsPath = DEFAULT_POSTS_PATH } = options; + let filePath = path.join(postsPath, `${slug}.mdx`); + if (!fs.existsSync(filePath)) { + filePath = path.join(postsPath, `${slug}.md`); + } + + if (!fs.existsSync(filePath)) { + return { notFound: true as const }; + } + + const fileContent = fs.readFileSync(filePath); + + const { content, data: scope } = matter(fileContent); + + let { mdxOptions } = options; + if (typeof mdxOptions === "function") mdxOptions = await mdxOptions(); + + const serialize = (await import("next-mdx-remote/serialize")).serialize; + const source = await serialize(content, { scope, mdxOptions }); + + let version = "0"; + + const versionFilePath = path.resolve(postsPath, "./.version", slug); + + if (fs.existsSync(versionFilePath)) { + version = fs.readFileSync(versionFilePath, "utf-8"); + } + + const data = getPostData(slug); + + return { props: { source, slug, data, version, postsPath } }; +}; + +export function getSlugFromFilePath(filePath: string) { + const fileName = filePath.split("/").at(-1)!; + const slug = fileName.split(".")[0]; + return slug; +} diff --git a/lib/blog/src/constants.ts b/lib/blog/src/constants.ts new file mode 100644 index 0000000..b16c243 --- /dev/null +++ b/lib/blog/src/constants.ts @@ -0,0 +1,6 @@ +import path from "path"; + +export const POSTS_PATH = path.resolve(process.cwd(), "./posts"); + +export const DEFAULT_SLUG_PARTS = ["slug"]; +export const DEFAULT_POSTS_PATH = "posts/"; diff --git a/src/data/DataProvider.tsx b/lib/blog/src/data.tsx similarity index 83% rename from src/data/DataProvider.tsx rename to lib/blog/src/data.tsx index e308f3a..5457238 100644 --- a/src/data/DataProvider.tsx +++ b/lib/blog/src/data.tsx @@ -1,5 +1,5 @@ import { createContext, useContext } from "react"; -import { PostDataStore } from "../types/Post"; +import { PostDataStore } from "../types"; const PostDataContext = createContext({}); @@ -12,6 +12,7 @@ export const PostDataProvider: React.FC<{ children: React.ReactNode; data: PostD export function usePostData(slug: string): T { const dataStore = useContext(PostDataContext); const data = dataStore[slug]; + // TBD: Improve error message. How do you add data for the post? if (!data) throw new Error(`No data with slug '${slug}'`); return data as T; } diff --git a/lib/blog/src/internal-types.ts b/lib/blog/src/internal-types.ts new file mode 100644 index 0000000..1fdc897 --- /dev/null +++ b/lib/blog/src/internal-types.ts @@ -0,0 +1,35 @@ +import { MdxOptions } from "../types"; + +export interface FrontMatter { + title: string; + description?: string; + image?: string; + publishedAt?: string; + updatedAt?: string; + include?: { + firstCodeSnippet?: boolean; + }; +} + +export interface GetPostsOptions { + /** + * @default "published" + */ + type?: "published" | "draft"; + /** + * Path to directory containing your posts as '.md' or '.mdx', relative to 'process.cwd()' + * + * @default "posts/" + */ + postsPath?: string; +} + +export interface GetPostOptions { + mdxOptions?: MdxOptions; + /** + * Path to directory containing your posts as '.md' or '.mdx', relative to 'process.cwd()' + * + * @default "posts/" + */ + postsPath?: string; +} diff --git a/src/utils/mdxUtils.ts b/lib/blog/src/md.ts similarity index 64% rename from src/utils/mdxUtils.ts rename to lib/blog/src/md.ts index 0230283..1c667ce 100644 --- a/src/utils/mdxUtils.ts +++ b/lib/blog/src/md.ts @@ -1,10 +1,7 @@ import fs from "fs"; import path from "path"; -export const POSTS_PATH = path.resolve(process.cwd(), "./posts"); -export const SNIPPETS_PATH = path.resolve(process.cwd(), "./snippets"); - -function findMdFiles(rootPath: string) { +export function findMdFiles(rootPath: string) { const out: string[] = []; const stack: string[] = []; @@ -27,7 +24,3 @@ function findMdFiles(rootPath: string) { dfs(rootPath); return out; } - -export const postFileNames = findMdFiles(POSTS_PATH); - -export const snippetFileNames = findMdFiles(SNIPPETS_PATH); diff --git a/lib/blog/src/time.ts b/lib/blog/src/time.ts new file mode 100644 index 0000000..6cb3873 --- /dev/null +++ b/lib/blog/src/time.ts @@ -0,0 +1,3 @@ +export function currentTime() { + return new Date().toLocaleTimeString(); +} diff --git a/lib/blog/src/watcher-client.ts b/lib/blog/src/watcher-client.ts new file mode 100644 index 0000000..3056d7f --- /dev/null +++ b/lib/blog/src/watcher-client.ts @@ -0,0 +1,115 @@ +import { io } from "socket.io-client"; +import { currentTime } from "./time"; +import { MDXRemoteSerializeResult } from "next-mdx-remote"; +import { useEffect, useRef, useState } from "react"; + +interface Options { + version: string; + source: MDXRemoteSerializeResult; + slug: string; + postsPath: string; +} + +export function usePostWatcher(options: Options) { + const { postsPath, slug } = options; + + const [source, setSource] = useState(options.source); + + useEffect(() => { + if (process.env.NODE_ENV !== "development") { + // Disable in non-dev environments + return () => {}; + } + + const socket = io("http://localhost:3000", { transports: ["websocket"] }); + + // Tell the server which post we want updates for + socket.io.on("open", () => socket.emit("path", postsPath + slug)); + + // Add handler for post updates + socket.on("post", (source) => { + setSource(source); + console.log(`[${currentTime()}] Reloaded post contents`); + }); + + // Error handler + // + // TODO: Make more sophisticated? + socket.io.on("error", () => { + console.log("Failed to connect to websocket for post hot reloader."); + }); + + return () => socket.disconnect(); + }, []); + + return source; +} + +export function usePostWatcherOld(options: Options) { + const { slug } = options; + + const [source, setSource] = useState(options.source); + const [version, setVersion] = useState(options.version); + + const versionRef = useRef(version); + versionRef.current = version; + + useEffect(() => { + let delaySeconds = 1; + let unmounted = false; + + function pollAfterDelay() { + if (process.env.NODE_ENV === "production") return; + + if (unmounted) return; + setTimeout(poll, delaySeconds * 1000); + } + + async function poll() { + try { + const versionData = await fetch(`/api/__postwatcher?return=version&slug=${slug}`).then( + (res) => res.json(), + ); + + if (versionData.version === versionRef.current) { + pollAfterDelay(); + return; + } + + const res = await fetch(`/api/__postwatcher?return=post&slug=${slug}`); + + if (res.status < 200 || res.status > 299) { + console.error(`[${currentTime()}] Refreshing post failed with status ${res.status}`); + pollAfterDelay(); + return; + } + + const postData = await res.json(); + + delaySeconds = 1; // Reset delay after a successful fetch + + if (unmounted) return; + + console.log(`[${currentTime()}] Refreshed post content`); + + setSource(postData.source); + setVersion(versionData.version); + + pollAfterDelay(); + } catch (e) { + // Progressively make the delay longer, up to a maximum of 8 seconds + if (delaySeconds < 8) delaySeconds *= 2; + console.warn(`[${currentTime()}] Failed to refresh post. Is your dev server running?`); + pollAfterDelay(); + } + } + + pollAfterDelay(); + + return () => { + unmounted = true; + }; + }, []); + + return source; +} diff --git a/lib/blog/types.ts b/lib/blog/types.ts new file mode 100644 index 0000000..5b71dd6 --- /dev/null +++ b/lib/blog/types.ts @@ -0,0 +1,27 @@ +import { SerializeOptions } from "next-mdx-remote/dist/types"; + +export interface Post { + title: string; + slug: string; + /** Empty string if not provided */ + description: string; + /** Empty string if not provided */ + publishedAt: string; + /** Empty string if not provided */ + updatedAt: string; + /** Empty string if not provided */ + image: string; + firstCodeSnippet: { text: string; language: string } | null; +} + +export type PostDataStore = Partial>; + +export interface PageLayoutProps { + post: Post; + children: React.ReactNode; +} + +export type MdxOptions = + | SerializeOptions["mdxOptions"] + | (() => SerializeOptions["mdxOptions"]) + | (() => Promise); diff --git a/lib/blog/watcher.ts b/lib/blog/watcher.ts new file mode 100644 index 0000000..25d52e7 --- /dev/null +++ b/lib/blog/watcher.ts @@ -0,0 +1,58 @@ +import path from "path"; +import fs from "fs/promises"; +import { Server } from "socket.io"; +import matter from "gray-matter"; +import { serialize } from "next-mdx-remote/serialize"; +import { MdxOptions } from "@alexharri/blog/types"; + +async function startWatcher(onUpdated: (slug: string) => void) { + const watcher = fs.watch(process.cwd(), { recursive: true }); + for await (const event of watcher) { + const { filename } = event; + if (/\.mdx?$/.test(filename)) onUpdated(filename); + } +} + +export function startWatcherServer(mdxOptions?: MdxOptions) { + const io = new Server(); + const PATH_KEY = "__path"; + + io.on("connection", (socket) => { + // When a new connection is established, we expect the client to let us know which post + // they're listening to updates for. They do that by sending the path to the post + // (relative to 'process.cwd()'). + socket.on("path", (slug: unknown) => { + if (typeof slug !== "string") { + console.error(`Expected 'path' to be a string. Got '${slug}'`); + return; + } + (socket as any)[PATH_KEY] = slug; + }); + }); + + async function getSource(fileName: string) { + const filePath = path.resolve(process.cwd(), fileName); + const fileContent = await fs.readFile(filePath, "utf-8"); + const { content, data } = matter(fileContent); + if (typeof mdxOptions === "function") mdxOptions = await mdxOptions(); + const source = await serialize(content, { scope: data, mdxOptions }); + return source; + } + + startWatcher(async (fileName: string) => { + const filePathRelative = fileName.split(".")[0]; + const source = await getSource(fileName); + let nSent = 0; + for (const socket of io.sockets.sockets.values()) { + if ((socket as any)[PATH_KEY] === filePathRelative) { + socket.emit("post", source); + nSent++; + } + } + console.log( + `Sent updated content for '${fileName}' to ${nSent} ${nSent === 1 ? "tab" : "tabs"}.`, + ); + }); + + io.listen(3000); +} diff --git a/next.config.mjs b/next.config.js similarity index 63% rename from next.config.mjs rename to next.config.js index bfde1d0..393c9f8 100644 --- a/next.config.mjs +++ b/next.config.js @@ -1,6 +1,7 @@ import MonacoEditorWebpackPlugin from "monaco-editor-webpack-plugin"; import TM from "next-transpile-modules"; import { execSync } from "child_process"; +import { withBlog } from "@alexharri/blog/next.config.js"; const withTM = TM([ // `monaco-editor` isn't published to npm correctly: it includes both CSS @@ -18,36 +19,25 @@ const nextConfig = { }, ], + typescript: { + tsconfigPath: "./tsconfig.next.json", + }, + reactStrictMode: true, swcMinify: true, // See https://github.com/vercel/next.js/issues/31692 outputFileTracing: false, - webpack: (config, { isServer }) => { + webpack: (_config, { isServer }) => { + const config = _config; if (isServer) { execSync("npm run generate-sitemap"); execSync("npm run generate-rss"); } - - const rule = config.module.rules - .find((rule) => rule.oneOf) - .oneOf.find( - (r) => - // Find the global CSS loader - r.issuer && r.issuer.include && r.issuer.include.includes("_app"), - ); - if (rule) { - rule.issuer.include = [ - rule.issuer.include, - // Allow `monaco-editor` to import global CSS: - /[\\/]node_modules[\\/]monaco-editor[\\/]/, - ]; - } - config.plugins.push(new MonacoEditorWebpackPlugin()); return config; }, }; -export default withTM(nextConfig); +export default withBlog(withTM(nextConfig)); diff --git a/package-lock.json b/package-lock.json index eb51a4b..678eba2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,7 @@ "name": "website", "version": "0.1.0", "dependencies": { + "@alexharri/blog": "file:/./lib/blog/", "@emotion/css": "^11.11.2", "@emotion/server": "^11.11.0", "@monaco-editor/react": "^4.4.6", @@ -35,15 +36,24 @@ "three": "^0.162.0" }, "devDependencies": { + "@swc/core": "^1.7.24", "@types/rss": "^0.0.32", "@types/three": "^0.162.0", + "concurrently": "^8.2.2", "monaco-editor-webpack-plugin": "^7.0.1", "next-transpile-modules": "^10.0.0", "prettier": "^2.8.0", - "ts-node": "^10.9.1", + "socket.io": "^4.7.5", + "socket.io-client": "^4.7.5", + "ts-node": "^10.9.2", "typescript": "^4.9.3" } }, + "lib/blog": {}, + "node_modules/@alexharri/blog": { + "resolved": "lib/blog", + "link": true + }, "node_modules/@asamuzakjp/dom-selector": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/@asamuzakjp/dom-selector/-/dom-selector-2.0.2.tgz", @@ -838,14 +848,22 @@ "loose-envify": "^1.1.0" } }, + "node_modules/@socket.io/component-emitter": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", + "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==", + "dev": true + }, "node_modules/@swc/core": { - "version": "1.3.67", - "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.3.67.tgz", - "integrity": "sha512-9DROjzfAEt0xt0CDkOYsWpkUPyne8fl5ggWGon049678BOM7p0R0dmaalZGAsKatG5vYP1IWSKWsKhJIubDCsQ==", + "version": "1.7.24", + "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.7.24.tgz", + "integrity": "sha512-FzJaai6z6DYdICAY1UKNN5pzTn296ksK2zzEjjaXlpZtoMkGktWT0ttS7hbdBCPGhLOu5Q9TA2zdPejKUFjgig==", "dev": true, "hasInstallScript": true, - "optional": true, - "peer": true, + "dependencies": { + "@swc/counter": "^0.1.3", + "@swc/types": "^0.1.12" + }, "engines": { "node": ">=10" }, @@ -854,19 +872,19 @@ "url": "https://opencollective.com/swc" }, "optionalDependencies": { - "@swc/core-darwin-arm64": "1.3.67", - "@swc/core-darwin-x64": "1.3.67", - "@swc/core-linux-arm-gnueabihf": "1.3.67", - "@swc/core-linux-arm64-gnu": "1.3.67", - "@swc/core-linux-arm64-musl": "1.3.67", - "@swc/core-linux-x64-gnu": "1.3.67", - "@swc/core-linux-x64-musl": "1.3.67", - "@swc/core-win32-arm64-msvc": "1.3.67", - "@swc/core-win32-ia32-msvc": "1.3.67", - "@swc/core-win32-x64-msvc": "1.3.67" + "@swc/core-darwin-arm64": "1.7.24", + "@swc/core-darwin-x64": "1.7.24", + "@swc/core-linux-arm-gnueabihf": "1.7.24", + "@swc/core-linux-arm64-gnu": "1.7.24", + "@swc/core-linux-arm64-musl": "1.7.24", + "@swc/core-linux-x64-gnu": "1.7.24", + "@swc/core-linux-x64-musl": "1.7.24", + "@swc/core-win32-arm64-msvc": "1.7.24", + "@swc/core-win32-ia32-msvc": "1.7.24", + "@swc/core-win32-x64-msvc": "1.7.24" }, "peerDependencies": { - "@swc/helpers": "^0.5.0" + "@swc/helpers": "*" }, "peerDependenciesMeta": { "@swc/helpers": { @@ -875,9 +893,9 @@ } }, "node_modules/@swc/core-darwin-arm64": { - "version": "1.3.67", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.3.67.tgz", - "integrity": "sha512-zCT2mCkOBVNf5uJDcQ3A9KDoO1OEaGdfjsRTZTo7sejDd9AXLfJg+xgyCBBrK2jNS/uWcT21IvSv3LqKp4K8pA==", + "version": "1.7.24", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.7.24.tgz", + "integrity": "sha512-s0k09qAcsoa8jIncwgRRd43VApYqXu28R4OmICtDffV4S01HtsRLRarXsMuLutoZk3tbxqitep+A8MPBuqNgdg==", "cpu": [ "arm64" ], @@ -886,15 +904,14 @@ "os": [ "darwin" ], - "peer": true, "engines": { "node": ">=10" } }, "node_modules/@swc/core-darwin-x64": { - "version": "1.3.67", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.3.67.tgz", - "integrity": "sha512-hXTVsfTatPEec5gFVyjGj3NccKZsYj/OXyHn6XA+l3Q76lZzGm2ISHdku//XNwXu8OmJ0HhS7LPsC4XXwxXQhg==", + "version": "1.7.24", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.7.24.tgz", + "integrity": "sha512-1dlsulJ/fiOoJoJyQgaCewIEaZ7Sh6aJN4r5Uhl4lIZuNWa27XOb28A3K29/6HDO9JML3IJrvXPnl5o0vxDQuQ==", "cpu": [ "x64" ], @@ -903,15 +920,14 @@ "os": [ "darwin" ], - "peer": true, "engines": { "node": ">=10" } }, "node_modules/@swc/core-linux-arm-gnueabihf": { - "version": "1.3.67", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.3.67.tgz", - "integrity": "sha512-l8AKL0RkDL5FRTeWMmjoz9zvAc37amxC+0rheaNwE+gZya7ObyNjnIYz5FwN+3y+z6JFU7LS2x/5f6iwruv6pg==", + "version": "1.7.24", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.7.24.tgz", + "integrity": "sha512-2ft1NmxyvHCu5CY4r2rNVybPqZtJaxpRSzvCcPlVjN/2D5Q3QgM5kBoo1t+0RCFfk4TS2V0KWJhtqKz0CNX62Q==", "cpu": [ "arm" ], @@ -920,15 +936,14 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">=10" } }, "node_modules/@swc/core-linux-arm64-gnu": { - "version": "1.3.67", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.3.67.tgz", - "integrity": "sha512-S8zOB1AXEpb7kmtgMaFNeLAj01VOky4B0RNZ+uJWigdrDiFT67FeZzNHUNmNSOU0QM79G+Lie/xD/beqEw0vDg==", + "version": "1.7.24", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.7.24.tgz", + "integrity": "sha512-v/Z8I9tUUNkNHKa1Sw4r1Q7Wp66ezbRhe6xMIxvPNKVJQFaMOsRpe0t8T5qbk5sV2hJGOCKpQynSpZqQXLcJDQ==", "cpu": [ "arm64" ], @@ -937,15 +952,14 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">=10" } }, "node_modules/@swc/core-linux-arm64-musl": { - "version": "1.3.67", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.3.67.tgz", - "integrity": "sha512-Fex8J8ASrt13pmOr2xWh41tEeKWwXYGk3sV8L/aGHiYtIJEUi2f+RtMx3jp7LIdOD8pQptor7i5WBlfR9jhp8A==", + "version": "1.7.24", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.7.24.tgz", + "integrity": "sha512-0jJx0IcajcyOXaJsx1jXy86lYVrbupyy2VUj/OiJux/ic4oBJLjfL+WOuc8T8/hZj2p6X0X4jvfSCqWSuic4kA==", "cpu": [ "arm64" ], @@ -954,15 +968,14 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">=10" } }, "node_modules/@swc/core-linux-x64-gnu": { - "version": "1.3.67", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.3.67.tgz", - "integrity": "sha512-9bz9/bMphrv5vDg0os/d8ve0QgFpDzJgZgHUaHiGwcmfnlgdOSAaYJLIvWdcGTjZuQeV4L0m+iru357D9TXEzA==", + "version": "1.7.24", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.7.24.tgz", + "integrity": "sha512-2+3aKQpSGjVnWKDTKUPuJzitQlTQrGorg+PVFMRkv6l+RcNCHZQNe/8VYpMhyBhxDMb3LUlbp7776FRevcruxg==", "cpu": [ "x64" ], @@ -971,15 +984,14 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">=10" } }, "node_modules/@swc/core-linux-x64-musl": { - "version": "1.3.67", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.3.67.tgz", - "integrity": "sha512-ED0H6oLvQmhgo9zs8usmEA/lcZPGTu7K9og9K871b7HhHX0h/R+Xg2pb5KD7S/GyUHpfuopxjVROm+h6X1jMUA==", + "version": "1.7.24", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.7.24.tgz", + "integrity": "sha512-PMQ6SkCtMoj0Ks77DiishpEmIuHpYjFLDuVOzzJCzGeGoii0yRP5lKy/VeglFYLPqJzmhK9BHlpVehVf/8ZpvA==", "cpu": [ "x64" ], @@ -988,15 +1000,14 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">=10" } }, "node_modules/@swc/core-win32-arm64-msvc": { - "version": "1.3.67", - "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.3.67.tgz", - "integrity": "sha512-J1yFDLgPFeRtA8t5E159OXX+ww1gbkFg70yr4OP7EsOkOD1uMkuTf9yK/woHfsaVJlUYjJHzw7MkUIEgQBucqQ==", + "version": "1.7.24", + "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.7.24.tgz", + "integrity": "sha512-SNdCa4DtGXNWrPVHqctVUxgEVZVETuqERpqF50KFHO0Bvf5V/m1IJ4hFr2BxXlrzgnIW4t1Dpi6YOJbcGbEmnA==", "cpu": [ "arm64" ], @@ -1005,15 +1016,14 @@ "os": [ "win32" ], - "peer": true, "engines": { "node": ">=10" } }, "node_modules/@swc/core-win32-ia32-msvc": { - "version": "1.3.67", - "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.3.67.tgz", - "integrity": "sha512-bK11/KtasewqHxzkjKUBXRE9MSAidbZCxrgJUd49bItG2N/DHxkwMYu8Xkh5VDHdTYWv/2idYtf/VM9Yi+53qw==", + "version": "1.7.24", + "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.7.24.tgz", + "integrity": "sha512-5p3olHqwibMfrVFg2yVuSIPh9HArDYYlJXNZ9JKqeZk23A19J1pl9MuPmXDw+sxsiPfYJ/nUedIGeUHPF/+EDw==", "cpu": [ "ia32" ], @@ -1022,15 +1032,14 @@ "os": [ "win32" ], - "peer": true, "engines": { "node": ">=10" } }, "node_modules/@swc/core-win32-x64-msvc": { - "version": "1.3.67", - "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.3.67.tgz", - "integrity": "sha512-GxzUU3+NA3cPcYxCxtfSQIS2ySD7Z8IZmKTVaWA9GOUQbKLyCE8H5js31u39+0op/1gNgxOgYFDoj2lUyvLCqw==", + "version": "1.7.24", + "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.7.24.tgz", + "integrity": "sha512-gRyPIxDznS8d2ClfmWbytjp2d48bij6swHnDLWhukNuOvXdQkEmaIzjEsionFG/zhcFLnz8zKfTvjEjInAMzxg==", "cpu": [ "x64" ], @@ -1039,11 +1048,16 @@ "os": [ "win32" ], - "peer": true, "engines": { "node": ">=10" } }, + "node_modules/@swc/counter": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", + "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==", + "dev": true + }, "node_modules/@swc/helpers": { "version": "0.5.1", "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.1.tgz", @@ -1055,6 +1069,15 @@ "tslib": "^2.4.0" } }, + "node_modules/@swc/types": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.12.tgz", + "integrity": "sha512-wBJA+SdtkbFhHjTMYH+dEH1y4VpfGdAc2Kw/LK09i9bXd/K6j6PkDcFCEzb6iVfZMkPRrl/q0e3toqTAJdkIVA==", + "dev": true, + "dependencies": { + "@swc/counter": "^0.1.3" + } + }, "node_modules/@tsconfig/node10": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", @@ -1092,6 +1115,21 @@ "@types/estree": "*" } }, + "node_modules/@types/cookie": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==", + "dev": true + }, + "node_modules/@types/cors": { + "version": "2.8.17", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.17.tgz", + "integrity": "sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/debug": { "version": "4.1.7", "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.7.tgz", @@ -1463,6 +1501,19 @@ "dev": true, "peer": true }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dev": true, + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/acorn": { "version": "8.8.1", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz", @@ -1539,6 +1590,15 @@ "ajv": "^6.9.1" } }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/ansi-styles": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", @@ -1628,6 +1688,15 @@ } ] }, + "node_modules/base64id": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", + "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", + "dev": true, + "engines": { + "node": "^4.5.0 || >= 5.9" + } + }, "node_modules/bidi-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/bidi-js/-/bidi-js-1.0.3.tgz", @@ -1891,6 +1960,20 @@ "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==" }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", @@ -1931,16 +2014,126 @@ "dev": true, "peer": true }, + "node_modules/concurrently": { + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-8.2.2.tgz", + "integrity": "sha512-1dP4gpXFhei8IOtlXRE/T/4H88ElHgTiUzh71YUmtjTEHMSRS2Z/fgOxHSxxusGHogsRfxNq1vyAwxSC+EVyDg==", + "dev": true, + "dependencies": { + "chalk": "^4.1.2", + "date-fns": "^2.30.0", + "lodash": "^4.17.21", + "rxjs": "^7.8.1", + "shell-quote": "^1.8.1", + "spawn-command": "0.0.2", + "supports-color": "^8.1.1", + "tree-kill": "^1.2.2", + "yargs": "^17.7.2" + }, + "bin": { + "conc": "dist/bin/concurrently.js", + "concurrently": "dist/bin/concurrently.js" + }, + "engines": { + "node": "^14.13.0 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/open-cli-tools/concurrently?sponsor=1" + } + }, + "node_modules/concurrently/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/concurrently/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/concurrently/node_modules/chalk/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/concurrently/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/concurrently/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, "node_modules/convert-source-map": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==" }, + "node_modules/cookie": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", + "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/core-util-is": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "dev": true, + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, "node_modules/cosmiconfig": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", @@ -2033,9 +2226,12 @@ } }, "node_modules/date-fns": { - "version": "2.29.3", - "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.29.3.tgz", - "integrity": "sha512-dDCnyH2WnnKusqvZZ6+jA1O51Ibt8ZMRNkDZdyAyK4YfbDwa/cEmuztzG5pk6hqlp9aSBPYcjOlktquahGwGeA==", + "version": "2.30.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz", + "integrity": "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==", + "dependencies": { + "@babel/runtime": "^7.21.0" + }, "engines": { "node": ">=0.11" }, @@ -2178,6 +2374,12 @@ "dev": true, "peer": true }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, "node_modules/emojis-list": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", @@ -2187,6 +2389,49 @@ "node": ">= 4" } }, + "node_modules/engine.io": { + "version": "6.5.5", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.5.5.tgz", + "integrity": "sha512-C5Pn8Wk+1vKBoHghJODM63yk8MvrO9EWZUfkAt5HAqIgPE4/8FF0PEGHXtEd40l223+cE5ABWuPzm38PHFXfMA==", + "dev": true, + "dependencies": { + "@types/cookie": "^0.4.1", + "@types/cors": "^2.8.12", + "@types/node": ">=10.0.0", + "accepts": "~1.3.4", + "base64id": "2.0.0", + "cookie": "~0.4.1", + "cors": "~2.8.5", + "debug": "~4.3.1", + "engine.io-parser": "~5.2.1", + "ws": "~8.17.1" + }, + "engines": { + "node": ">=10.2.0" + } + }, + "node_modules/engine.io-client": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.5.4.tgz", + "integrity": "sha512-GeZeeRjpD2qf49cZQ0Wvh/8NJNfeXkXXcoGh+F77oEAgo9gUHwT1fCRxSNU+YEEaysOJTnsFHmM5oAcPy4ntvQ==", + "dev": true, + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1", + "engine.io-parser": "~5.2.1", + "ws": "~8.17.1", + "xmlhttprequest-ssl": "~2.0.0" + } + }, + "node_modules/engine.io-parser": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz", + "integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==", + "dev": true, + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/enhanced-resolve": { "version": "5.12.0", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.12.0.tgz", @@ -2231,7 +2476,6 @@ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", "dev": true, - "peer": true, "engines": { "node": ">=6" } @@ -2495,6 +2739,15 @@ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, "node_modules/glob-parent": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", @@ -2574,7 +2827,6 @@ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, - "peer": true, "engines": { "node": ">=8" } @@ -2944,6 +3196,15 @@ "node": ">=0.10.0" } }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/is-glob": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", @@ -3187,6 +3448,12 @@ "node": ">=8.9.0" } }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, "node_modules/longest-streak": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.0.1.tgz", @@ -4772,6 +5039,15 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/neo-async": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", @@ -5461,6 +5737,15 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/require-from-string": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", @@ -5531,6 +5816,15 @@ "node": ">= 0.6" } }, + "node_modules/rxjs": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", + "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", + "dev": true, + "dependencies": { + "tslib": "^2.1.0" + } + }, "node_modules/sade": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/sade/-/sade-1.8.1.tgz", @@ -5663,6 +5957,71 @@ "node": ">=8" } }, + "node_modules/shell-quote": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.1.tgz", + "integrity": "sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/socket.io": { + "version": "4.7.5", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.7.5.tgz", + "integrity": "sha512-DmeAkF6cwM9jSfmp6Dr/5/mfMwb5Z5qRrSXLpo3Fq5SqyU8CMF15jIN4ZhfSwu35ksM1qmHZDQ/DK5XTccSTvA==", + "dev": true, + "dependencies": { + "accepts": "~1.3.4", + "base64id": "~2.0.0", + "cors": "~2.8.5", + "debug": "~4.3.2", + "engine.io": "~6.5.2", + "socket.io-adapter": "~2.5.2", + "socket.io-parser": "~4.2.4" + }, + "engines": { + "node": ">=10.2.0" + } + }, + "node_modules/socket.io-adapter": { + "version": "2.5.5", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.5.tgz", + "integrity": "sha512-eLDQas5dzPgOWCk9GuuJC2lBqItuhKI4uxGgo9aIV7MYbk2h9Q6uULEh8WBzThoI7l+qU9Ast9fVUmkqPP9wYg==", + "dev": true, + "dependencies": { + "debug": "~4.3.4", + "ws": "~8.17.1" + } + }, + "node_modules/socket.io-client": { + "version": "4.7.5", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.7.5.tgz", + "integrity": "sha512-sJ/tqHOCe7Z50JCBCXrsY3I2k03iOiUe+tj1OmKeD2lXPiGH/RUCdTZFoqVyN7l1MnpIzPrGtLcijffmeouNlQ==", + "dev": true, + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.2", + "engine.io-client": "~6.5.2", + "socket.io-parser": "~4.2.4" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/socket.io-parser": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", + "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", + "dev": true, + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/source-map": { "version": "0.7.4", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", @@ -5709,6 +6068,12 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/spawn-command": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/spawn-command/-/spawn-command-0.0.2.tgz", + "integrity": "sha512-zC8zGoGkmc8J9ndvml8Xksr1Amk9qBujgbF0JAIWO7kXr43w0h/0GJNM/Vustixu+YE8N/MTrQ7N31FvHUACxQ==", + "dev": true + }, "node_modules/speech-rule-engine": { "version": "4.0.7", "resolved": "https://registry.npmjs.org/speech-rule-engine/-/speech-rule-engine-4.0.7.tgz", @@ -5755,6 +6120,20 @@ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==" }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/stringify-entities": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.3.tgz", @@ -5768,6 +6147,18 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/strip-bom-string": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/strip-bom-string/-/strip-bom-string-1.0.0.tgz", @@ -5816,7 +6207,6 @@ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "dev": true, - "peer": true, "dependencies": { "has-flag": "^4.0.0" }, @@ -6012,6 +6402,15 @@ "node": ">=18" } }, + "node_modules/tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "dev": true, + "bin": { + "tree-kill": "cli.js" + } + }, "node_modules/trim-lines": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", @@ -6058,9 +6457,9 @@ } }, "node_modules/ts-node": { - "version": "10.9.1", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", - "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", "dev": true, "dependencies": { "@cspotcode/source-map-support": "^0.8.0", @@ -6426,6 +6825,15 @@ "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", "dev": true }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/vfile": { "version": "5.3.5", "resolved": "https://registry.npmjs.org/vfile/-/vfile-5.3.5.tgz", @@ -6648,10 +7056,60 @@ "resolved": "https://registry.npmjs.org/wicked-good-xpath/-/wicked-good-xpath-1.3.0.tgz", "integrity": "sha512-Gd9+TUn5nXdwj/hFsPVx5cuHHiF5Bwuc30jZ4+ronF1qHK5O7HD0sgmXWSEgwKquT3ClLoKPVbO6qGwVwLzvAw==" }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/wrap-ansi/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, "node_modules/ws": { - "version": "8.16.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.16.0.tgz", - "integrity": "sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==", + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", "engines": { "node": ">=10.0.0" }, @@ -6694,6 +7152,15 @@ "node": ">=0.1" } }, + "node_modules/xmlhttprequest-ssl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz", + "integrity": "sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/xtend": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-2.1.2.tgz", @@ -6705,6 +7172,15 @@ "node": ">=0.4" } }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, "node_modules/yaml": { "version": "1.10.2", "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", @@ -6713,6 +7189,33 @@ "node": ">= 6" } }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "engines": { + "node": ">=12" + } + }, "node_modules/yn": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", @@ -6749,6 +7252,9 @@ } }, "dependencies": { + "@alexharri/blog": { + "version": "file:lib/blog" + }, "@asamuzakjp/dom-selector": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/@asamuzakjp/dom-selector/-/dom-selector-2.0.2.tgz", @@ -7312,105 +7818,107 @@ } } }, + "@socket.io/component-emitter": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", + "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==", + "dev": true + }, "@swc/core": { - "version": "1.3.67", - "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.3.67.tgz", - "integrity": "sha512-9DROjzfAEt0xt0CDkOYsWpkUPyne8fl5ggWGon049678BOM7p0R0dmaalZGAsKatG5vYP1IWSKWsKhJIubDCsQ==", + "version": "1.7.24", + "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.7.24.tgz", + "integrity": "sha512-FzJaai6z6DYdICAY1UKNN5pzTn296ksK2zzEjjaXlpZtoMkGktWT0ttS7hbdBCPGhLOu5Q9TA2zdPejKUFjgig==", "dev": true, - "optional": true, - "peer": true, "requires": { - "@swc/core-darwin-arm64": "1.3.67", - "@swc/core-darwin-x64": "1.3.67", - "@swc/core-linux-arm-gnueabihf": "1.3.67", - "@swc/core-linux-arm64-gnu": "1.3.67", - "@swc/core-linux-arm64-musl": "1.3.67", - "@swc/core-linux-x64-gnu": "1.3.67", - "@swc/core-linux-x64-musl": "1.3.67", - "@swc/core-win32-arm64-msvc": "1.3.67", - "@swc/core-win32-ia32-msvc": "1.3.67", - "@swc/core-win32-x64-msvc": "1.3.67" + "@swc/core-darwin-arm64": "1.7.24", + "@swc/core-darwin-x64": "1.7.24", + "@swc/core-linux-arm-gnueabihf": "1.7.24", + "@swc/core-linux-arm64-gnu": "1.7.24", + "@swc/core-linux-arm64-musl": "1.7.24", + "@swc/core-linux-x64-gnu": "1.7.24", + "@swc/core-linux-x64-musl": "1.7.24", + "@swc/core-win32-arm64-msvc": "1.7.24", + "@swc/core-win32-ia32-msvc": "1.7.24", + "@swc/core-win32-x64-msvc": "1.7.24", + "@swc/counter": "^0.1.3", + "@swc/types": "^0.1.12" } }, "@swc/core-darwin-arm64": { - "version": "1.3.67", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.3.67.tgz", - "integrity": "sha512-zCT2mCkOBVNf5uJDcQ3A9KDoO1OEaGdfjsRTZTo7sejDd9AXLfJg+xgyCBBrK2jNS/uWcT21IvSv3LqKp4K8pA==", + "version": "1.7.24", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.7.24.tgz", + "integrity": "sha512-s0k09qAcsoa8jIncwgRRd43VApYqXu28R4OmICtDffV4S01HtsRLRarXsMuLutoZk3tbxqitep+A8MPBuqNgdg==", "dev": true, - "optional": true, - "peer": true + "optional": true }, "@swc/core-darwin-x64": { - "version": "1.3.67", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.3.67.tgz", - "integrity": "sha512-hXTVsfTatPEec5gFVyjGj3NccKZsYj/OXyHn6XA+l3Q76lZzGm2ISHdku//XNwXu8OmJ0HhS7LPsC4XXwxXQhg==", + "version": "1.7.24", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.7.24.tgz", + "integrity": "sha512-1dlsulJ/fiOoJoJyQgaCewIEaZ7Sh6aJN4r5Uhl4lIZuNWa27XOb28A3K29/6HDO9JML3IJrvXPnl5o0vxDQuQ==", "dev": true, - "optional": true, - "peer": true + "optional": true }, "@swc/core-linux-arm-gnueabihf": { - "version": "1.3.67", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.3.67.tgz", - "integrity": "sha512-l8AKL0RkDL5FRTeWMmjoz9zvAc37amxC+0rheaNwE+gZya7ObyNjnIYz5FwN+3y+z6JFU7LS2x/5f6iwruv6pg==", + "version": "1.7.24", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.7.24.tgz", + "integrity": "sha512-2ft1NmxyvHCu5CY4r2rNVybPqZtJaxpRSzvCcPlVjN/2D5Q3QgM5kBoo1t+0RCFfk4TS2V0KWJhtqKz0CNX62Q==", "dev": true, - "optional": true, - "peer": true + "optional": true }, "@swc/core-linux-arm64-gnu": { - "version": "1.3.67", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.3.67.tgz", - "integrity": "sha512-S8zOB1AXEpb7kmtgMaFNeLAj01VOky4B0RNZ+uJWigdrDiFT67FeZzNHUNmNSOU0QM79G+Lie/xD/beqEw0vDg==", + "version": "1.7.24", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.7.24.tgz", + "integrity": "sha512-v/Z8I9tUUNkNHKa1Sw4r1Q7Wp66ezbRhe6xMIxvPNKVJQFaMOsRpe0t8T5qbk5sV2hJGOCKpQynSpZqQXLcJDQ==", "dev": true, - "optional": true, - "peer": true + "optional": true }, "@swc/core-linux-arm64-musl": { - "version": "1.3.67", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.3.67.tgz", - "integrity": "sha512-Fex8J8ASrt13pmOr2xWh41tEeKWwXYGk3sV8L/aGHiYtIJEUi2f+RtMx3jp7LIdOD8pQptor7i5WBlfR9jhp8A==", + "version": "1.7.24", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.7.24.tgz", + "integrity": "sha512-0jJx0IcajcyOXaJsx1jXy86lYVrbupyy2VUj/OiJux/ic4oBJLjfL+WOuc8T8/hZj2p6X0X4jvfSCqWSuic4kA==", "dev": true, - "optional": true, - "peer": true + "optional": true }, "@swc/core-linux-x64-gnu": { - "version": "1.3.67", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.3.67.tgz", - "integrity": "sha512-9bz9/bMphrv5vDg0os/d8ve0QgFpDzJgZgHUaHiGwcmfnlgdOSAaYJLIvWdcGTjZuQeV4L0m+iru357D9TXEzA==", + "version": "1.7.24", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.7.24.tgz", + "integrity": "sha512-2+3aKQpSGjVnWKDTKUPuJzitQlTQrGorg+PVFMRkv6l+RcNCHZQNe/8VYpMhyBhxDMb3LUlbp7776FRevcruxg==", "dev": true, - "optional": true, - "peer": true + "optional": true }, "@swc/core-linux-x64-musl": { - "version": "1.3.67", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.3.67.tgz", - "integrity": "sha512-ED0H6oLvQmhgo9zs8usmEA/lcZPGTu7K9og9K871b7HhHX0h/R+Xg2pb5KD7S/GyUHpfuopxjVROm+h6X1jMUA==", + "version": "1.7.24", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.7.24.tgz", + "integrity": "sha512-PMQ6SkCtMoj0Ks77DiishpEmIuHpYjFLDuVOzzJCzGeGoii0yRP5lKy/VeglFYLPqJzmhK9BHlpVehVf/8ZpvA==", "dev": true, - "optional": true, - "peer": true + "optional": true }, "@swc/core-win32-arm64-msvc": { - "version": "1.3.67", - "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.3.67.tgz", - "integrity": "sha512-J1yFDLgPFeRtA8t5E159OXX+ww1gbkFg70yr4OP7EsOkOD1uMkuTf9yK/woHfsaVJlUYjJHzw7MkUIEgQBucqQ==", + "version": "1.7.24", + "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.7.24.tgz", + "integrity": "sha512-SNdCa4DtGXNWrPVHqctVUxgEVZVETuqERpqF50KFHO0Bvf5V/m1IJ4hFr2BxXlrzgnIW4t1Dpi6YOJbcGbEmnA==", "dev": true, - "optional": true, - "peer": true + "optional": true }, "@swc/core-win32-ia32-msvc": { - "version": "1.3.67", - "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.3.67.tgz", - "integrity": "sha512-bK11/KtasewqHxzkjKUBXRE9MSAidbZCxrgJUd49bItG2N/DHxkwMYu8Xkh5VDHdTYWv/2idYtf/VM9Yi+53qw==", + "version": "1.7.24", + "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.7.24.tgz", + "integrity": "sha512-5p3olHqwibMfrVFg2yVuSIPh9HArDYYlJXNZ9JKqeZk23A19J1pl9MuPmXDw+sxsiPfYJ/nUedIGeUHPF/+EDw==", "dev": true, - "optional": true, - "peer": true + "optional": true }, "@swc/core-win32-x64-msvc": { - "version": "1.3.67", - "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.3.67.tgz", - "integrity": "sha512-GxzUU3+NA3cPcYxCxtfSQIS2ySD7Z8IZmKTVaWA9GOUQbKLyCE8H5js31u39+0op/1gNgxOgYFDoj2lUyvLCqw==", + "version": "1.7.24", + "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.7.24.tgz", + "integrity": "sha512-gRyPIxDznS8d2ClfmWbytjp2d48bij6swHnDLWhukNuOvXdQkEmaIzjEsionFG/zhcFLnz8zKfTvjEjInAMzxg==", "dev": true, - "optional": true, - "peer": true + "optional": true + }, + "@swc/counter": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", + "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==", + "dev": true }, "@swc/helpers": { "version": "0.5.1", @@ -7423,6 +7931,15 @@ "tslib": "^2.4.0" } }, + "@swc/types": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.12.tgz", + "integrity": "sha512-wBJA+SdtkbFhHjTMYH+dEH1y4VpfGdAc2Kw/LK09i9bXd/K6j6PkDcFCEzb6iVfZMkPRrl/q0e3toqTAJdkIVA==", + "dev": true, + "requires": { + "@swc/counter": "^0.1.3" + } + }, "@tsconfig/node10": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", @@ -7460,6 +7977,21 @@ "@types/estree": "*" } }, + "@types/cookie": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==", + "dev": true + }, + "@types/cors": { + "version": "2.8.17", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.17.tgz", + "integrity": "sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, "@types/debug": { "version": "4.1.7", "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.7.tgz", @@ -7828,6 +8360,16 @@ "dev": true, "peer": true }, + "accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dev": true, + "requires": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + } + }, "acorn": { "version": "8.8.1", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz", @@ -7882,6 +8424,12 @@ "peer": true, "requires": {} }, + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, "ansi-styles": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", @@ -7940,6 +8488,12 @@ "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" }, + "base64id": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", + "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", + "dev": true + }, "bidi-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/bidi-js/-/bidi-js-1.0.3.tgz", @@ -8102,6 +8656,17 @@ "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==" }, + "cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + } + }, "color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", @@ -8135,16 +8700,96 @@ "dev": true, "peer": true }, + "concurrently": { + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-8.2.2.tgz", + "integrity": "sha512-1dP4gpXFhei8IOtlXRE/T/4H88ElHgTiUzh71YUmtjTEHMSRS2Z/fgOxHSxxusGHogsRfxNq1vyAwxSC+EVyDg==", + "dev": true, + "requires": { + "chalk": "^4.1.2", + "date-fns": "^2.30.0", + "lodash": "^4.17.21", + "rxjs": "^7.8.1", + "shell-quote": "^1.8.1", + "spawn-command": "0.0.2", + "supports-color": "^8.1.1", + "tree-kill": "^1.2.2", + "yargs": "^17.7.2" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "dependencies": { + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + } + } + }, "convert-source-map": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==" }, + "cookie": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", + "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", + "dev": true + }, "core-util-is": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" }, + "cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "dev": true, + "requires": { + "object-assign": "^4", + "vary": "^1" + } + }, "cosmiconfig": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", @@ -8213,9 +8858,12 @@ } }, "date-fns": { - "version": "2.29.3", - "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.29.3.tgz", - "integrity": "sha512-dDCnyH2WnnKusqvZZ6+jA1O51Ibt8ZMRNkDZdyAyK4YfbDwa/cEmuztzG5pk6hqlp9aSBPYcjOlktquahGwGeA==" + "version": "2.30.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz", + "integrity": "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==", + "requires": { + "@babel/runtime": "^7.21.0" + } }, "debounce": { "version": "1.2.1", @@ -8328,12 +8976,55 @@ "dev": true, "peer": true }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, "emojis-list": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", "dev": true }, + "engine.io": { + "version": "6.5.5", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.5.5.tgz", + "integrity": "sha512-C5Pn8Wk+1vKBoHghJODM63yk8MvrO9EWZUfkAt5HAqIgPE4/8FF0PEGHXtEd40l223+cE5ABWuPzm38PHFXfMA==", + "dev": true, + "requires": { + "@types/cookie": "^0.4.1", + "@types/cors": "^2.8.12", + "@types/node": ">=10.0.0", + "accepts": "~1.3.4", + "base64id": "2.0.0", + "cookie": "~0.4.1", + "cors": "~2.8.5", + "debug": "~4.3.1", + "engine.io-parser": "~5.2.1", + "ws": "~8.17.1" + } + }, + "engine.io-client": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.5.4.tgz", + "integrity": "sha512-GeZeeRjpD2qf49cZQ0Wvh/8NJNfeXkXXcoGh+F77oEAgo9gUHwT1fCRxSNU+YEEaysOJTnsFHmM5oAcPy4ntvQ==", + "dev": true, + "requires": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1", + "engine.io-parser": "~5.2.1", + "ws": "~8.17.1", + "xmlhttprequest-ssl": "~2.0.0" + } + }, + "engine.io-parser": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz", + "integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==", + "dev": true + }, "enhanced-resolve": { "version": "5.12.0", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.12.0.tgz", @@ -8368,8 +9059,7 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "dev": true, - "peer": true + "dev": true }, "escape-string-regexp": { "version": "4.0.0", @@ -8561,6 +9251,12 @@ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true + }, "glob-parent": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", @@ -8629,8 +9325,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "peer": true + "dev": true }, "hast-util-from-dom": { "version": "5.0.0", @@ -8905,6 +9600,12 @@ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==" }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, "is-glob": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", @@ -9088,6 +9789,12 @@ "json5": "^2.1.2" } }, + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, "longest-streak": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.0.1.tgz", @@ -10052,6 +10759,12 @@ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==" }, + "negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "dev": true + }, "neo-async": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", @@ -10562,6 +11275,12 @@ "unified": "^10.0.0" } }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true + }, "require-from-string": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", @@ -10616,6 +11335,15 @@ } } }, + "rxjs": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", + "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", + "dev": true, + "requires": { + "tslib": "^2.1.0" + } + }, "sade": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/sade/-/sade-1.8.1.tgz", @@ -10706,6 +11434,59 @@ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==" }, + "shell-quote": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.1.tgz", + "integrity": "sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==", + "dev": true + }, + "socket.io": { + "version": "4.7.5", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.7.5.tgz", + "integrity": "sha512-DmeAkF6cwM9jSfmp6Dr/5/mfMwb5Z5qRrSXLpo3Fq5SqyU8CMF15jIN4ZhfSwu35ksM1qmHZDQ/DK5XTccSTvA==", + "dev": true, + "requires": { + "accepts": "~1.3.4", + "base64id": "~2.0.0", + "cors": "~2.8.5", + "debug": "~4.3.2", + "engine.io": "~6.5.2", + "socket.io-adapter": "~2.5.2", + "socket.io-parser": "~4.2.4" + } + }, + "socket.io-adapter": { + "version": "2.5.5", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.5.tgz", + "integrity": "sha512-eLDQas5dzPgOWCk9GuuJC2lBqItuhKI4uxGgo9aIV7MYbk2h9Q6uULEh8WBzThoI7l+qU9Ast9fVUmkqPP9wYg==", + "dev": true, + "requires": { + "debug": "~4.3.4", + "ws": "~8.17.1" + } + }, + "socket.io-client": { + "version": "4.7.5", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.7.5.tgz", + "integrity": "sha512-sJ/tqHOCe7Z50JCBCXrsY3I2k03iOiUe+tj1OmKeD2lXPiGH/RUCdTZFoqVyN7l1MnpIzPrGtLcijffmeouNlQ==", + "dev": true, + "requires": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.2", + "engine.io-client": "~6.5.2", + "socket.io-parser": "~4.2.4" + } + }, + "socket.io-parser": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", + "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", + "dev": true, + "requires": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1" + } + }, "source-map": { "version": "0.7.4", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", @@ -10741,6 +11522,12 @@ "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.1.tgz", "integrity": "sha512-ekwEbFp5aqSPKaqeY1PGrlGQxPNaq+Cnx4+bE2D8sciBQrHpbwoBbawqTN2+6jPs9IdWxxiUcN0K2pkczD3zmw==" }, + "spawn-command": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/spawn-command/-/spawn-command-0.0.2.tgz", + "integrity": "sha512-zC8zGoGkmc8J9ndvml8Xksr1Amk9qBujgbF0JAIWO7kXr43w0h/0GJNM/Vustixu+YE8N/MTrQ7N31FvHUACxQ==", + "dev": true + }, "speech-rule-engine": { "version": "4.0.7", "resolved": "https://registry.npmjs.org/speech-rule-engine/-/speech-rule-engine-4.0.7.tgz", @@ -10783,6 +11570,17 @@ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==" }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, "stringify-entities": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.3.tgz", @@ -10792,6 +11590,15 @@ "character-entities-legacy": "^3.0.0" } }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + }, "strip-bom-string": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/strip-bom-string/-/strip-bom-string-1.0.0.tgz", @@ -10823,7 +11630,6 @@ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "dev": true, - "peer": true, "requires": { "has-flag": "^4.0.0" } @@ -10960,6 +11766,12 @@ "punycode": "^2.3.1" } }, + "tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "dev": true + }, "trim-lines": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", @@ -10993,9 +11805,9 @@ "integrity": "sha512-AqTiAOLcj85xS7vQ8QkAV41hPDIJ71XJB4RCUrzo/1GM2CQwhkJGaf9Hgr7BOugMRpgGUrqRg/DrBDl4H40+8g==" }, "ts-node": { - "version": "10.9.1", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", - "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", "dev": true, "requires": { "@cspotcode/source-map-support": "^0.8.0", @@ -11231,6 +12043,12 @@ "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", "dev": true }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "dev": true + }, "vfile": { "version": "5.3.5", "resolved": "https://registry.npmjs.org/vfile/-/vfile-5.3.5.tgz", @@ -11393,10 +12211,47 @@ "resolved": "https://registry.npmjs.org/wicked-good-xpath/-/wicked-good-xpath-1.3.0.tgz", "integrity": "sha512-Gd9+TUn5nXdwj/hFsPVx5cuHHiF5Bwuc30jZ4+ronF1qHK5O7HD0sgmXWSEgwKquT3ClLoKPVbO6qGwVwLzvAw==" }, + "wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + } + } + }, "ws": { - "version": "8.16.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.16.0.tgz", - "integrity": "sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==", + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", "requires": {} }, "xml": { @@ -11419,6 +12274,12 @@ "resolved": "https://registry.npmjs.org/xmldom-sre/-/xmldom-sre-0.1.31.tgz", "integrity": "sha512-f9s+fUkX04BxQf+7mMWAp5zk61pciie+fFLC9hX9UVvCeJQfNHRHXpeo5MPcR0EUf57PYLdt+ZO4f3Ipk2oZUw==" }, + "xmlhttprequest-ssl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz", + "integrity": "sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==", + "dev": true + }, "xtend": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-2.1.2.tgz", @@ -11427,11 +12288,38 @@ "object-keys": "~0.4.0" } }, + "y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true + }, "yaml": { "version": "1.10.2", "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==" }, + "yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "requires": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + } + }, + "yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true + }, "yn": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", diff --git a/package.json b/package.json index 9a727aa..9e70247 100644 --- a/package.json +++ b/package.json @@ -1,17 +1,22 @@ { "name": "website", "version": "0.1.0", + "type": "module", "scripts": { - "dev": "next dev -p 8080", + "dev": "concurrently \"npm run ws-only\" \"npm run dev-only\" --prefix none", + "dev-only": "next dev -p 8080", + "ws-only": "NODE_NO_WARNINGS=1 node --loader ts-node/esm -r ts-node/register src/watcher", + "foo": "ts-node --swc src/foo", "build": "next build", "start": "next start -p 8080", "lint": "next lint", "watcher": "ts-node -T ./watcher/postWatcher.ts", "typecheck": "tsc --noEmit --skipLibCheck", - "generate-sitemap": "ts-node -T ./scripts/generate-sitemap.ts", - "generate-rss": "ts-node -T ./scripts/generate-rss.ts" + "generate-sitemap": "NODE_NO_WARNINGS=1 node --loader ts-node/esm -r ts-node/register ./scripts/generate-sitemap", + "generate-rss": "NODE_NO_WARNINGS=1 node --loader ts-node/esm -r ts-node/register ./scripts/generate-rss.ts" }, "dependencies": { + "@alexharri/blog": "file:/./lib/blog/", "@emotion/css": "^11.11.2", "@emotion/server": "^11.11.0", "@monaco-editor/react": "^4.4.6", @@ -39,12 +44,16 @@ "three": "^0.162.0" }, "devDependencies": { + "@swc/core": "^1.7.24", "@types/rss": "^0.0.32", "@types/three": "^0.162.0", + "concurrently": "^8.2.2", "monaco-editor-webpack-plugin": "^7.0.1", "next-transpile-modules": "^10.0.0", "prettier": "^2.8.0", - "ts-node": "^10.9.1", + "socket.io": "^4.7.5", + "socket.io-client": "^4.7.5", + "ts-node": "^10.9.2", "typescript": "^4.9.3" } } diff --git a/posts/planes.md b/posts/planes.md index a52eb41..656cb25 100644 --- a/posts/planes.md +++ b/posts/planes.md @@ -13,7 +13,7 @@ Planes have loads of uses in applications that deal with 3D geometry. I've mostl Learning about planes felt abstract and non-intuitive to me. _“Sure, that's a plane equation, but what do I do with it? What does a plane look like?”_ It took some time for me to build an intuition for how to reason about and work with them. -In writing this, I want to provide you with an introduction that focuses on building a practical, intuitive understanding of planes. I hope to achieve this through the use of visual (and interactive!) explanations which will accompany us as we work through progressively more complex problems. +In writing this, I want to provide you with an introduction that focuses on building a practical, intuitive understanding of planes. I hope to achieve this through the use of visual (and interactive!) explanations which will accompany us as we work through progressively more complex problems foo. With that out of the way, let's get to it! diff --git a/scripts/generate-rss.ts b/scripts/generate-rss.ts index 063c5f7..86d594b 100644 --- a/scripts/generate-rss.ts +++ b/scripts/generate-rss.ts @@ -1,9 +1,9 @@ import fs from "fs"; -import { getPosts } from "../src/utils/blogPageUtils"; +import { getPosts } from "@alexharri/blog/posts.js"; import RSS from "rss"; function generateRSS() { - const posts = getPosts("published"); + const posts = getPosts({ type: "published" }); const url = "https://alexharri.com"; diff --git a/scripts/generate-sitemap.ts b/scripts/generate-sitemap.ts index 650ef77..c29a343 100644 --- a/scripts/generate-sitemap.ts +++ b/scripts/generate-sitemap.ts @@ -1,9 +1,9 @@ import fs from "fs"; -import { Post } from "../src/types/Post"; -import { getPosts } from "../src/utils/blogPageUtils"; +import { getPosts } from "@alexharri/blog/posts.js"; +import { Post } from "@alexharri/blog"; function generateSitemap() { - const posts = getPosts("published"); + const posts = getPosts({ type: "published" }); const postXML = (post: Post) => { return ` diff --git a/snippets/git/undo-commit.md b/snippets/git/undo-commit.md index 8d0e10c..74b6666 100644 --- a/snippets/git/undo-commit.md +++ b/snippets/git/undo-commit.md @@ -1,7 +1,8 @@ --- title: "Undo Git Commit" -showPreview: true publishedAt: "27-11-2022" +include: + firstCodeSnippet: true --- To undo a recent local commit, run the following: diff --git a/src/components.tsx b/src/components.tsx new file mode 100644 index 0000000..1acf4e7 --- /dev/null +++ b/src/components.tsx @@ -0,0 +1,41 @@ +import Head from "next/head"; +import { Link } from "./components/Link"; +import { withScriptedEditor } from "./components/ScriptedEditor/LazyScriptedEditor"; +import { SmallNote } from "./components/SmallNote/SmallNote"; +import { Pre, StaticCodeBlock } from "./components/StaticCodeBlock/StaticCodeBlock"; +import { RenderTextCommand } from "./components/ScriptedEditor/RenderCommand/RenderCommand"; +import { NotMacOs } from "./components/OperatingSystem/OperatingSystem"; +import { Image } from "./components/Image"; +import { SectionAnchor } from "./components/SectionAnchor/SectionAnchor"; +import { Note } from "./components/Note/Note"; +import { Scene } from "./threejs/scenes"; +import { BarChart } from "./components/BarChart/BarChart"; + +export const components = { + a: Link, + pre: withScriptedEditor(Pre, (props) => { + const language = props.children.props.className?.split("-")[1] ?? "text"; + return { code: props.children.props.children, language }; + }), + img: Image, + Image, + StaticCodeBlock: withScriptedEditor(StaticCodeBlock, (props) => ({ + code: props.children, + language: props.language, + })), + SmallNote, + CodeScript: (props: any) => ( +
+ ), + Command: RenderTextCommand, + SectionAnchor, + NotMacOs, + Head, + Note, + Scene, + BarChart, +}; diff --git a/src/components/BarChart/BarChart.tsx b/src/components/BarChart/BarChart.tsx index 94aad0b..87feb46 100644 --- a/src/components/BarChart/BarChart.tsx +++ b/src/components/BarChart/BarChart.tsx @@ -1,4 +1,5 @@ import "../../utils/chartjs"; +import { usePostData } from "@alexharri/blog"; import { Bar } from "react-chartjs-2"; import { useState } from "react"; import { ChartData, ChartDataset, GridLineOptions } from "chart.js"; @@ -6,7 +7,6 @@ import { useStyles } from "../../utils/styles"; import { BarChartStyles } from "./BarChart.styles"; import { colors, cssVariables } from "../../utils/cssVariables"; import { Toggle } from "../Toggle/Toggle"; -import { usePostData } from "../../data/DataProvider"; import { useIsomorphicViewportWidth } from "../../utils/hooks/useViewportWidth"; import { invLerp, lerp } from "../../math/lerp"; diff --git a/src/components/PostPageLayout/PostPageLayout.tsx b/src/components/PostPageLayout/PostPageLayout.tsx new file mode 100644 index 0000000..1964921 --- /dev/null +++ b/src/components/PostPageLayout/PostPageLayout.tsx @@ -0,0 +1,44 @@ +import { PageLayoutProps } from "@alexharri/blog"; +import { ThreeProvider } from "../../threejs/Components/ThreeProvider"; +import { formatDate } from "../../utils/formatDate"; +import { Layout } from "../Layout"; +import { Meta } from "../Meta/Meta"; +import { PostLayout } from "../PostLayout/PostLayout"; +import { FocusedScriptProvider } from "../ScriptedEditor/FocusedScriptContext/FocusedScriptContext"; +import { MonacoProvider } from "../ScriptedEditor/MonacoProvider"; +import { SubscribeToNewsletter } from "../SubscribeToNewsletter/SubscribeToNewsletter"; + +export const PostPageLayout: React.FC = (props) => { + const { post, children } = props; + return ( + <> + + + +
+

{post.title}

+ {post.publishedAt && ( +
+ +
+ )} +
+ + + {children} + + + + +
+
+ + ); +}; diff --git a/src/components/Snippet/Snippet.tsx b/src/components/Snippet/Snippet.tsx index 3faef9a..b6c0670 100644 --- a/src/components/Snippet/Snippet.tsx +++ b/src/components/Snippet/Snippet.tsx @@ -1,27 +1,27 @@ +import { Post } from "@alexharri/blog"; import Link from "next/link"; -import { Snippet } from "../../types/Snippet"; import { useStyles } from "../../utils/styles"; import { StaticCodeBlock } from "../StaticCodeBlock/StaticCodeBlock"; import { SnippetStyles } from "./Snippet.styles"; interface Props { - snippet: Snippet; + post: Post; } export const SnippetLink = (props: Props) => { const s = useStyles(SnippetStyles); - const { snippet } = props; + const { post } = props; return (
- -

{snippet.title}

- {snippet.description &&

{snippet.description}

} + +

{post.title}

+ {post.description &&

{post.description}

} - {snippet.snippet && ( - - {snippet.snippet.text} + {post.firstCodeSnippet && ( + + {post.firstCodeSnippet.text} )}
diff --git a/src/pages/api/blog/[slug]/post.ts b/src/pages/api/blog/[slug]/post.ts deleted file mode 100644 index f199d1a..0000000 --- a/src/pages/api/blog/[slug]/post.ts +++ /dev/null @@ -1,34 +0,0 @@ -import type { NextApiRequest, NextApiResponse } from "next"; -import fs from "fs"; -import path from "path"; -import { POSTS_PATH } from "../../../../utils/mdxUtils"; -import matter from "gray-matter"; -import { serialize } from "next-mdx-remote/serialize"; -import { getMdxOptions } from "../../../../utils/mdx"; - -export default async function getPost(req: NextApiRequest, res: NextApiResponse) { - const slug = req.query.slug as string; - - let fileContent: string | undefined; - - for (const ext of [".md", ".mdx"]) { - const filePath = path.resolve(POSTS_PATH, slug + ext); - console.log(filePath); - if (!fs.existsSync(filePath)) continue; - fileContent = fs.readFileSync(filePath, "utf-8"); - } - - if (!fileContent) { - res.status(404).end(); - return; - } - - const { content, data } = matter(fileContent); - - const source = await serialize(content, { - scope: data, - mdxOptions: await getMdxOptions(), - }); - - res.status(200).json({ source }); -} diff --git a/src/pages/api/blog/[slug]/version.ts b/src/pages/api/blog/[slug]/version.ts deleted file mode 100644 index 2720ddd..0000000 --- a/src/pages/api/blog/[slug]/version.ts +++ /dev/null @@ -1,17 +0,0 @@ -import type { NextApiRequest, NextApiResponse } from "next"; -import fs from "fs"; -import path from "path"; -import { POSTS_PATH } from "../../../../utils/mdxUtils"; - -export default function getPost(req: NextApiRequest, res: NextApiResponse) { - const slug = req.query.slug as string; - - let version = "0"; - - const filePath = path.resolve(POSTS_PATH, "./.version", slug); - if (fs.existsSync(filePath)) { - version = fs.readFileSync(filePath, "utf-8"); - } - - res.status(200).json({ version }); -} diff --git a/src/pages/blog/[slug].tsx b/src/pages/blog/[slug].tsx index 7413350..67e1f50 100644 --- a/src/pages/blog/[slug].tsx +++ b/src/pages/blog/[slug].tsx @@ -1,128 +1,13 @@ -import { GetStaticPaths, GetStaticProps } from "next"; -import { MDXRemote, MDXRemoteSerializeResult } from "next-mdx-remote"; -import Head from "next/head"; -import { Layout } from "../../components/Layout"; -import { Link } from "../../components/Link"; -import { Meta } from "../../components/Meta/Meta"; -import { FocusedScriptProvider } from "../../components/ScriptedEditor/FocusedScriptContext/FocusedScriptContext"; -import { withScriptedEditor } from "../../components/ScriptedEditor/LazyScriptedEditor"; -import { SmallNote } from "../../components/SmallNote/SmallNote"; -import { Pre, StaticCodeBlock } from "../../components/StaticCodeBlock/StaticCodeBlock"; -import { FrontMatter } from "../../types/FrontMatter"; -import { usePostWatcher } from "../../utils/hooks/usePostWatcher"; -import { MonacoProvider } from "../../components/ScriptedEditor/MonacoProvider"; -import { getPostPaths, getPostProps } from "../../utils/blogPageUtils"; -import { RenderTextCommand } from "../../components/ScriptedEditor/RenderCommand/RenderCommand"; -import { NotMacOs } from "../../components/OperatingSystem/OperatingSystem"; -import { Image } from "../../components/Image"; -import { SectionAnchor } from "../../components/SectionAnchor/SectionAnchor"; -import { formatDate } from "../../utils/formatDate"; -import { Note } from "../../components/Note/Note"; -import { PostLayout } from "../../components/PostLayout/PostLayout"; -import { Scene } from "../../threejs/scenes"; -import { ThreeProvider } from "../../threejs/Components/ThreeProvider"; -import { BarChart } from "../../components/BarChart/BarChart"; -import { PostDataProvider } from "../../data/DataProvider"; -import { PostDataStore } from "../../types/Post"; -import { SubscribeToNewsletter } from "../../components/SubscribeToNewsletter/SubscribeToNewsletter"; +import { createGetStaticPaths, createGetStaticProps, createPostPage } from "@alexharri/blog/page"; +import { PostPageLayout } from "../../components/PostPageLayout/PostPageLayout"; +import { components } from "../../components"; +import { mdxOptions } from "../../utils/mdxOptions"; -// Custom components/renderers to pass to MDX. -// Since the MDX files aren't loaded by webpack, they have no knowledge of how -// to handle import statements. Instead, you must include components in scope -// here. -const baseComponents = { - a: Link, - pre: withScriptedEditor(Pre, (props) => { - const language = props.children.props.className?.split("-")[1] ?? "text"; - return { code: props.children.props.children, language }; - }), - img: Image, - Image, - StaticCodeBlock: withScriptedEditor(StaticCodeBlock, (props) => ({ - code: props.children, - language: props.language, - })), - SmallNote, - CodeScript: (props: any) => ( -
- ), - Command: RenderTextCommand, - SectionAnchor, - NotMacOs, - Head, - Note, - Scene, - BarChart, -}; +export default createPostPage({ + components, + Layout: PostPageLayout, +}); -interface Props { - source: MDXRemoteSerializeResult; - version: string; - slug: string; - data: PostDataStore; -} +export const getStaticProps = createGetStaticProps({ mdxOptions }); -export function createPage(customComponents: Record>) { - const components = { - ...baseComponents, - ...customComponents, - }; - return function PostPage(props: Props) { - const source = usePostWatcher(props); - const scope = source.scope! as unknown as FrontMatter; - - const pathName = scope.publishedAt ? `/blog/${props.slug}` : `/blog/draft/${props.slug}`; - - return ( - <> - - - -
-

{scope.title}

- {scope.publishedAt && ( -
- -
- )} -
- - - - - - - - - - - -
-
- - ); - }; -} - -export default createPage({}); - -export type Params = { - slug: string; -}; - -export const getStaticProps: GetStaticProps = async (ctx) => getPostProps(ctx); - -export const getStaticPaths: GetStaticPaths = async () => { - return { paths: getPostPaths({ type: "published" }), fallback: false }; -}; +export const getStaticPaths = createGetStaticPaths(); diff --git a/src/pages/blog/draft/[slug].tsx b/src/pages/blog/draft/[slug].tsx index caa892c..fe0b489 100644 --- a/src/pages/blog/draft/[slug].tsx +++ b/src/pages/blog/draft/[slug].tsx @@ -1,9 +1,13 @@ -import { getPostPaths } from "../../../utils/blogPageUtils"; -import { GetStaticPaths } from "next"; -import { Params } from "../[slug]"; +import { createGetStaticPaths, createGetStaticProps, createPostPage } from "@alexharri/blog/page"; +import { components } from "../../../components"; +import { PostPageLayout } from "../../../components/PostPageLayout/PostPageLayout"; +import { mdxOptions } from "../../../utils/mdxOptions"; -export { default, getStaticProps } from "../[slug]"; +export default createPostPage({ + components, + Layout: PostPageLayout, +}); -export const getStaticPaths: GetStaticPaths = async () => { - return { paths: getPostPaths({ type: "draft" }), fallback: false }; -}; +export const getStaticProps = createGetStaticProps({ mdxOptions }); + +export const getStaticPaths = createGetStaticPaths({ drafts: true }); diff --git a/src/pages/blog/draft/stabilizing-noisy-inputs.tsx b/src/pages/blog/draft/stabilizing-noisy-inputs.tsx index 10b2d71..1b5b5fa 100644 --- a/src/pages/blog/draft/stabilizing-noisy-inputs.tsx +++ b/src/pages/blog/draft/stabilizing-noisy-inputs.tsx @@ -1,5 +1,6 @@ -import { createPage } from "../[slug]"; -import { getPostProps, getSlugFromFilePath } from "../../../utils/blogPageUtils"; +import { createGetStaticProps, createPostPage } from "@alexharri/blog/page"; +import { PostPageLayout } from "../../../components/PostPageLayout/PostPageLayout"; +import { components } from "../../../components"; import { NoNoise, NoNoiseShowBoundaries, @@ -13,8 +14,9 @@ import { OptionsSticky } from "../../../components/posts/stabilizing-noisy-input import { OptionsStickyGap } from "../../../components/posts/stabilizing-noisy-inputs/OptionsStickyGap"; import { OptionsStickyGapDynamic } from "../../../components/posts/stabilizing-noisy-inputs/OptionsStickyGapDynamic"; import { LargeStickinessFactors } from "../../../components/posts/stabilizing-noisy-inputs/LargeStickinessFactors"; +import { mdxOptions } from "../../../utils/mdxOptions"; -export default createPage({ +const customComponents = { NoNoise, NoNoiseShowBoundaries, SomeNoise, @@ -26,9 +28,17 @@ export default createPage({ OptionsStickyGap, OptionsStickyGapDynamic, LargeStickinessFactors, +}; + +export default createPostPage({ + components: { + ...components, + ...customComponents, + }, + Layout: PostPageLayout, }); -export const getStaticProps = async () => { - const slug = getSlugFromFilePath(import.meta.url); - return getPostProps({ params: { slug } }); -}; +export const getStaticProps = createGetStaticProps({ + mdxOptions, + slug: "stabilizing-noisy-inputs", +}); diff --git a/src/pages/blog/index.tsx b/src/pages/blog/index.tsx index 1769cfd..7e61d90 100644 --- a/src/pages/blog/index.tsx +++ b/src/pages/blog/index.tsx @@ -1,10 +1,10 @@ +import { getPosts } from "@alexharri/blog/posts"; import { GetStaticProps } from "next"; import Head from "next/head"; import { AboutMe } from "../../components/AboutMe/AboutMe"; import { Layout } from "../../components/Layout"; import { PostCard } from "../../components/PostCard/PostCard"; import { Post } from "../../types/Post"; -import { getPosts } from "../../utils/blogPageUtils"; import { StyleOptions, useStyles } from "../../utils/styles"; interface Props { diff --git a/src/pages/drafts.tsx b/src/pages/drafts.tsx index 04a212e..183c4c3 100644 --- a/src/pages/drafts.tsx +++ b/src/pages/drafts.tsx @@ -1,8 +1,8 @@ +import { getPosts } from "@alexharri/blog/posts"; import { GetStaticProps } from "next"; import { BlogPost } from "../components/BlogPost/BlogPost"; import { Layout } from "../components/Layout"; import { Post } from "../types/Post"; -import { getPosts } from "../utils/blogPageUtils"; interface Props { posts: Post[]; diff --git a/src/pages/index.tsx b/src/pages/index.tsx index 4cccfe6..c6fe5bf 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -6,7 +6,7 @@ import { Link } from "../components/Link"; import { PostCard } from "../components/PostCard/PostCard"; import { PostCarousel } from "../components/PostCarousel/PostCarousel"; import { Post } from "../types/Post"; -import { getPopularPosts } from "../utils/blogPageUtils"; +import { getPopularPosts } from "../utils/getPopularPosts"; import { StyleOptions, useStyles } from "../utils/styles"; const Styles = ({ styled }: StyleOptions) => ({ diff --git a/src/pages/snippets/[category]/[slug].tsx b/src/pages/snippets/[category]/[slug].tsx index bb01877..eb65457 100644 --- a/src/pages/snippets/[category]/[slug].tsx +++ b/src/pages/snippets/[category]/[slug].tsx @@ -1,10 +1,6 @@ -import fs from "fs"; -import matter from "gray-matter"; -import { GetStaticPaths, GetStaticProps } from "next"; +import { createGetStaticPaths, createGetStaticProps } from "@alexharri/blog/page"; import { MDXRemote, MDXRemoteSerializeResult } from "next-mdx-remote"; -import { serialize } from "next-mdx-remote/serialize"; import Head from "next/head"; -import path from "path"; import { Layout } from "../../../components/Layout"; import { Link } from "../../../components/Link"; import { Meta } from "../../../components/Meta/Meta"; @@ -13,20 +9,13 @@ import { SmallNote } from "../../../components/SmallNote/SmallNote"; import { SnippetTitle } from "../../../components/SnippetTitle/SnippetTitle"; import { Pre, StaticCodeBlock } from "../../../components/StaticCodeBlock/StaticCodeBlock"; import { FrontMatter } from "../../../types/FrontMatter"; -import { usePostWatcher } from "../../../utils/hooks/usePostWatcher"; -import { getMdxOptions } from "../../../utils/mdx"; -import { snippetFileNames, SNIPPETS_PATH } from "../../../utils/mdxUtils"; +import { mdxOptions } from "../../../utils/mdxOptions"; -// Custom components/renderers to pass to MDX. -// Since the MDX files aren't loaded by webpack, they have no knowledge of how -// to handle import statements. Instead, you must include components in scope -// here. const components = { a: Link, pre: Pre, StaticCodeBlock, SmallNote, - // ExampleComponent: dynamic(() => import("../../src/components/ExampleComponent")), Head, }; @@ -38,7 +27,7 @@ interface Props { } export default function SnippetPage(props: Props) { - const source = usePostWatcher(props); + const source = props.source; const scope = source.scope! as unknown as FrontMatter; return ( @@ -58,46 +47,13 @@ export default function SnippetPage(props: Props) { ); } -type Params = { - slug: string; - category: string; -}; - -export const getStaticProps: GetStaticProps = async (ctx) => { - const params = ctx.params!; - - let filePath = path.join(SNIPPETS_PATH, params.category, `${params.slug}.mdx`); - if (!fs.existsSync(filePath)) { - filePath = path.join(SNIPPETS_PATH, params.category, `${params.slug}.md`); - } - const fileContent = fs.readFileSync(filePath); - - const { content, data } = matter(fileContent); - - const source = await serialize(content, { - scope: data, - mdxOptions: await getMdxOptions(), - }); +const slugParts = ["category", "slug"]; +const postsPath = "snippets/"; - let version = "0"; +export const getStaticProps = createGetStaticProps({ + slugParts, + mdxOptions, + postsPath, +}); - const versionFilePath = path.resolve(SNIPPETS_PATH, "./.version", params.slug); - - if (fs.existsSync(versionFilePath)) { - version = fs.readFileSync(versionFilePath, "utf-8"); - } - - return { props: { source, slug: params.slug, category: params.category, version } }; -}; - -export const getStaticPaths: GetStaticPaths = async () => { - const paths = snippetFileNames - .map((path) => path.replace(/\.mdx?$/, "")) - .map((path) => path.split("/")) - .map(([category, slug]) => ({ params: { category, slug } })); - - return { - paths, - fallback: false, - }; -}; +export const getStaticPaths = createGetStaticPaths({ slugParts, postsPath }); diff --git a/src/pages/snippets/index.tsx b/src/pages/snippets/index.tsx index 9eafb01..9fa4256 100644 --- a/src/pages/snippets/index.tsx +++ b/src/pages/snippets/index.tsx @@ -1,15 +1,12 @@ -import fs from "fs"; -import matter from "gray-matter"; +import { Post } from "@alexharri/blog"; +import { getPosts } from "@alexharri/blog/posts"; import { GetStaticProps } from "next"; -import path from "path"; import { Layout } from "../../components/Layout"; import { SnippetLink } from "../../components/Snippet/Snippet"; import { SnippetList } from "../../components/SnippetList/SnippetList"; -import { Snippet } from "../../types/Snippet"; -import { snippetFileNames, SNIPPETS_PATH } from "../../utils/mdxUtils"; interface Props { - snippets: Snippet[]; + posts: Post[]; } export default function Page(props: Props) { @@ -17,8 +14,8 @@ export default function Page(props: Props) {

Snippets

- {props.snippets.map((snippet) => ( - + {props.posts.map((post) => ( + ))}
@@ -26,42 +23,50 @@ export default function Page(props: Props) { } export const getStaticProps: GetStaticProps = async () => { - const snippets: Snippet[] = []; + return { + props: { + posts: getPosts({ postsPath: "snippets/" }), + }, + }; +}; - for (const fileName of snippetFileNames) { - const filePath = path.join(SNIPPETS_PATH, fileName); - const fileContent = fs.readFileSync(filePath, "utf8"); +// export const getStaticProps: GetStaticProps = async () => { +// const snippets: Snippet[] = []; - const { data } = matter(fileContent); +// for (const fileName of snippetFileNames) { +// const filePath = path.join(SNIPPETS_PATH, fileName); +// const fileContent = fs.readFileSync(filePath, "utf8"); - const { - title, - description = "", - showPreview, - } = data as { - title: string; - description?: string; - showPreview?: boolean; - }; +// const { data } = matter(fileContent); - let snippet: Snippet["snippet"] = null; +// const { +// title, +// description = "", +// showPreview, +// } = data as { +// title: string; +// description?: string; +// showPreview?: boolean; +// }; - if (showPreview) { - const lines = fileContent.split("\n"); - const lineIndex = lines.findIndex((line) => line.startsWith("```")); - const language = lines[lineIndex].substring(3); - const after = lines.slice(lineIndex + 1); - const content = after.slice( - 0, - after.findIndex((line) => line.startsWith("```")), - ); - snippet = { text: content.join("\n") + "\n", language }; - } +// let snippet: Snippet["snippet"] = null; - const slug = fileName.replace(/\.mdx?$/, ""); +// if (showPreview) { +// const lines = fileContent.split("\n"); +// const lineIndex = lines.findIndex((line) => line.startsWith("```")); +// const language = lines[lineIndex].substring(3); +// const after = lines.slice(lineIndex + 1); +// const content = after.slice( +// 0, +// after.findIndex((line) => line.startsWith("```")), +// ); +// snippet = { text: content.join("\n") + "\n", language }; +// } - snippets.push({ title, description, slug, snippet }); - } +// const slug = fileName.replace(/\.mdx?$/, ""); - return { props: { snippets } }; -}; +// snippets.push({ title, description, slug, snippet }); +// } + +// return { props: { snippets } }; +// }; diff --git a/src/types/Post.ts b/src/types/Post.ts index cc46ef8..49222a9 100644 --- a/src/types/Post.ts +++ b/src/types/Post.ts @@ -7,5 +7,3 @@ export interface Post { publishedAt: string; updatedAt?: string; } - -export type PostDataStore = Partial>; diff --git a/src/utils/blogPageUtils.ts b/src/utils/blogPageUtils.ts deleted file mode 100644 index 766f8c2..0000000 --- a/src/utils/blogPageUtils.ts +++ /dev/null @@ -1,150 +0,0 @@ -import fs from "fs"; -import path from "path"; -import matter from "gray-matter"; -import { postFileNames, POSTS_PATH } from "./mdxUtils"; -import { Post, PostDataStore } from "../types/Post"; -import { getMdxOptions } from "./mdx"; - -export const getPosts = (type: "published" | "draft") => { - const posts: Post[] = []; - - for (const fileName of postFileNames) { - const filePath = path.join(POSTS_PATH, fileName); - const fileContent = fs.readFileSync(filePath); - - const { data } = matter(fileContent); - - const { - title, - description = "", - publishedAt = "", - } = data as { - title: string; - description?: string; - publishedAt?: string; - }; - - const slug = fileName.replace(/\.mdx?$/, ""); - - const includePost = publishedAt ? type === "published" : type === "draft"; - - if (includePost) { - posts.push({ title, description, slug, publishedAt }); - } - } - - return posts.sort((a, b) => { - return b.publishedAt.localeCompare(a.publishedAt); - }); -}; - -export function getPopularPosts() { - const postsBySlug = getPosts("published").reduce((acc, post) => { - acc[post.slug] = post; - return acc; - }, {} as Record); - - const popularPosts = [ - "planes", - "typescript-structural-typing", - "multi-cursor-code-editing-animated-introduction", - "vector-networks", - ]; - - for (const slug of popularPosts) { - if (!postsBySlug[slug]) { - throw new Error(`No post with slug '${slug}'`); - } - } - - return popularPosts.map((slug) => postsBySlug[slug]); -} - -export function getPostPaths(options: { type: "published" | "draft" }) { - const draft = options.type === "draft"; - const paths = postFileNames - .filter((filePath) => { - const fileContent = fs.readFileSync(path.resolve(POSTS_PATH, filePath)); - const { data } = matter(fileContent); - - if (draft) { - return !data.publishedAt; - } - return !!data.publishedAt; - }) - .map((path) => path.replace(/\.mdx?$/, "")) - .filter((slug) => { - const pagePath = path.resolve( - process.cwd(), - `./src/pages/blog/${draft ? "draft/" : ""}${slug}.tsx`, - ); - return !fs.existsSync(pagePath); - }) - .map((slug) => ({ params: { slug } })); - - return [...paths]; -} - -function getPostData(slug: string): PostDataStore { - const dataDir = path.resolve(process.cwd(), `./public/data/${slug}`); - - if (!fs.existsSync(dataDir)) return {}; - - const out: PostDataStore = {}; - for (const fileName of fs.readdirSync(dataDir)) { - const filePath = path.resolve(dataDir, fileName); - const json = JSON.parse(fs.readFileSync(filePath, "utf-8")); - const dataSlug = fileName.split(".")[0]; - out[dataSlug] = json; - } - return out; -} - -type Params = { - slug: string; -}; - -type Context = { - params?: Params; -}; - -export const getPostProps = async (ctx: Context) => { - const params = ctx.params!; - - let filePath = path.join(POSTS_PATH, `${params.slug}.mdx`); - if (!fs.existsSync(filePath)) { - filePath = path.join(POSTS_PATH, `${params.slug}.md`); - } - - if (!fs.existsSync(filePath)) { - return { notFound: true as const }; - } - - const fileContent = fs.readFileSync(filePath); - - const { content, data: scope } = matter(fileContent); - - const serialize = (await import("next-mdx-remote/serialize")).serialize; - const source = await serialize(content, { - scope, - mdxOptions: await getMdxOptions(), - }); - - let version = "0"; - - const versionFilePath = path.resolve(POSTS_PATH, "./.version", params.slug); - - if (fs.existsSync(versionFilePath)) { - version = fs.readFileSync(versionFilePath, "utf-8"); - } - - const data = getPostData(params.slug); - - return { props: { source, slug: params.slug, data, version } }; -}; - -export function getSlugFromFilePath(filePath: string) { - const fileName = filePath.split("/").at(-1)!; - const slug = fileName.split(".")[0]; - return slug; -} diff --git a/src/utils/getPopularPosts.ts b/src/utils/getPopularPosts.ts new file mode 100644 index 0000000..cd2e5ab --- /dev/null +++ b/src/utils/getPopularPosts.ts @@ -0,0 +1,25 @@ +import { Post } from "@alexharri/blog"; +import { getPosts } from "@alexharri/blog/posts"; + +export function getPopularPosts() { + const postsBySlug = getPosts("published").reduce((acc, post) => { + acc[post.slug] = post; + return acc; + }, {} as Record); + + const popularPosts = [ + "planes", + "typescript-structural-typing", + "multi-cursor-code-editing-animated-introduction", + "vector-networks", + ]; + + for (const slug of popularPosts) { + if (!postsBySlug[slug]) { + // TBD: Better error messages. What does the user do about this. + throw new Error(`No post with slug '${slug}'`); + } + } + + return popularPosts.map((slug) => postsBySlug[slug]); +} diff --git a/src/utils/hooks/usePostWatcher.ts b/src/utils/hooks/usePostWatcher.ts deleted file mode 100644 index 4263a16..0000000 --- a/src/utils/hooks/usePostWatcher.ts +++ /dev/null @@ -1,66 +0,0 @@ -import { MDXRemoteSerializeResult } from "next-mdx-remote"; -import { useEffect, useRef, useState } from "react"; - -interface Options { - version: string; - source: MDXRemoteSerializeResult; - slug: string; -} - -export function usePostWatcher(options: Options) { - const { slug } = options; - - const [source, setSource] = useState(options.source); - const [version, setVersion] = useState(options.version); - - const versionRef = useRef(version); - versionRef.current = version; - - useEffect(() => { - let unmounted = false; - - function pollAfterDelay() { - if (process.env.NODE_ENV === "production") return; - - if (unmounted) return; - setTimeout(poll, 1000); - } - - async function poll() { - try { - const versionData = await fetch(`/api/blog/${slug}/version`).then( - (res) => res.json() - ); - - if (versionData.version === versionRef.current) { - pollAfterDelay(); - return; - } - - const sourceData = await fetch(`/api/blog/${slug}/post`).then((res) => - res.json() - ); - - if (unmounted) return; - - console.log("Fetched post"); - - setSource(sourceData.source); - setVersion(versionData.version); - - pollAfterDelay(); - } catch (e) { - console.log(e); - pollAfterDelay(); - } - } - - pollAfterDelay(); - - return () => { - unmounted = true; - }; - }, []); - - return source; -} diff --git a/src/utils/mdx.ts b/src/utils/mdxOptions.ts similarity index 71% rename from src/utils/mdx.ts rename to src/utils/mdxOptions.ts index 3175523..95ece4f 100644 --- a/src/utils/mdx.ts +++ b/src/utils/mdxOptions.ts @@ -1,6 +1,6 @@ import { SerializeOptions } from "next-mdx-remote/dist/types"; -export const getMdxOptions = async (): Promise => ({ +export const mdxOptions = async (): Promise => ({ remarkPlugins: [(await import("remark-math")).default], rehypePlugins: [[(await import("rehype-mathjax/svg")).default, { svg: { scale: 1 } }]], }); diff --git a/src/watcher.ts b/src/watcher.ts new file mode 100644 index 0000000..faecbbc --- /dev/null +++ b/src/watcher.ts @@ -0,0 +1,4 @@ +import { startWatcherServer } from "@alexharri/blog/watcher.js"; +import { mdxOptions } from "./utils/mdxOptions.js"; + +startWatcherServer(mdxOptions); diff --git a/tsconfig.json b/tsconfig.json index 0980bb8..b809bd0 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -17,13 +17,18 @@ "resolveJsonModule": true, "isolatedModules": true, "jsx": "preserve", - "incremental": true + "incremental": true, + "paths": { + "@alexharri/blog": ["./lib/blog/index"], + "@alexharri/blog/*": ["./lib/blog/*"] + } }, - "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "**/*.js"], + "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "**/*.js", "src/watcher.mjs"], "exclude": ["node_modules"], "ts-node": { "compilerOptions": { - "module": "CommonJS" + "module": "ESNext", + "target": "ESNext" } } } diff --git a/tsconfig.next.json b/tsconfig.next.json new file mode 100644 index 0000000..98f3373 --- /dev/null +++ b/tsconfig.next.json @@ -0,0 +1,9 @@ +{ + "extends": "./tsconfig.json", + "ts-node": { + "compilerOptions": { + "module": "CommonJS", + "target": "ES2015" + } + } +} diff --git a/watcher/postWatcher.ts b/watcher/postWatcher.ts deleted file mode 100644 index 4573e13..0000000 --- a/watcher/postWatcher.ts +++ /dev/null @@ -1,38 +0,0 @@ -import fs from "fs"; -import path from "path"; -import fsPromises from "fs/promises"; -import { POSTS_PATH } from "../src/utils/mdxUtils"; - -console.log("Started watcher"); - -async function watch() { - const watcher = fsPromises.watch(POSTS_PATH, { recursive: true }); - - for await (const event of watcher) { - const { filename } = event; - - if (!/\.mdx?$/.test(filename)) { - continue; - } - - const slug = filename.split(".")[0]; - - const idFileName = path.resolve(POSTS_PATH, "./.version", slug); - const versionExists = fs.existsSync(idFileName); - - let version: number; - - if (versionExists) { - version = Number(fs.readFileSync(idFileName, "utf-8")); - if (!Number.isFinite(version)) { - version = 0; - } - } else { - version = 0; - } - - fs.writeFileSync(idFileName, String(version + 1), "utf-8"); - } -} - -watch();