diff --git a/.gitea/workflows/publish.yml b/.gitea/workflows/publish.yml index 6b804c1..17d0569 100644 --- a/.gitea/workflows/publish.yml +++ b/.gitea/workflows/publish.yml @@ -13,7 +13,7 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: - node-version: 20 + node-version-file: '.nvmrc' cache: 'npm' - name: Install dependencies diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 3504055..305cc2b 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -11,10 +11,10 @@ jobs: steps: - uses: actions/checkout@v4 - - name: Use Node.js 20 + - name: Use Node.js from .nvmrc uses: actions/setup-node@v4 with: - node-version: 20 + node-version-file: .nvmrc cache: npm - name: Install dependencies diff --git a/.nvmrc b/.nvmrc index c675bca..5bcf9c6 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -v20.20.2 +v24.18.0 diff --git a/README.md b/README.md index 33b7f22..4d53dd2 100644 --- a/README.md +++ b/README.md @@ -30,3 +30,14 @@ - Title - Title + +## 📰 Blog & Feed content + +There are two markdown-driven content sections on this site: + +- **Blog** (`content/blog/`) — long-form articles, product releases and case studies. Published at `/blog/`. +- **Feed** (`content/feed/`) — short announcements and social-media style updates. Published at `/feed/`. + +Both sections are combined into a single RSS feed at [`/rss.xml`](https://datacontroller.io/rss.xml). + +See [`docs/adding-feed-posts.md`](docs/adding-feed-posts.md) for a step-by-step guide on adding a new Feed post. diff --git a/content/feed/welcome-to-the-feed/index.md b/content/feed/welcome-to-the-feed/index.md new file mode 100644 index 0000000..81375d2 --- /dev/null +++ b/content/feed/welcome-to-the-feed/index.md @@ -0,0 +1,21 @@ +--- +title: "Welcome to the Data Controller Feed" +description: A new home for quick announcements, social media updates and other bite-sized news from the Data Controller team. +date: '2026-07-17 09:00:00' +author: 'Data Controller' +authorLink: https://www.linkedin.com/showcase/data_controller +tags: + - Announcements +--- + +# Welcome to the Feed + +We're launching a new **Feed** section on the Data Controller website! This is where you'll find: + +- Quick announcements +- Social media style updates +- Smaller news items that don't warrant a full blog post + +It sits alongside our [Blog](/blog/) and both are combined into a single [RSS feed](/rss.xml) so you can subscribe once and get everything in one place. + +Stay tuned for more updates! diff --git a/docs/adding-feed-posts.md b/docs/adding-feed-posts.md new file mode 100644 index 0000000..2d9c3b4 --- /dev/null +++ b/docs/adding-feed-posts.md @@ -0,0 +1,96 @@ +# Adding a Feed Post + +The **Feed** section (`/feed/`) is for short announcements and social-media +style updates — things that don't warrant a full [`content/blog/`](../content/blog) +article, but are still worth publishing and syndicating via RSS. + +Feed posts work exactly like blog posts (one markdown file per post, in its +own directory), but live under `content/feed/` instead of `content/blog/`, +and are published under `/feed/` instead of `/blog/`. + +Both sections share a single combined RSS feed at +[`/rss.xml`](https://datacontroller.io/rss.xml) (see `gatsby-plugin-feed` in +[`gatsby-config.js`](../gatsby-config.js)), so a new Feed post automatically +appears in the RSS feed the next time the site is built — no extra +configuration required. + +A visible "RSS Feed" link (pointing to `/rss.xml`) is rendered in two places +so visitors can discover and subscribe to it: + +- The site-wide [`Footer`](../src/components/footer/index.tsx), under + "Other Resources". +- The "Subscribe" section of the shared + [`SideBar`](../src/templates/sidebar.tsx), shown on Blog and Feed list/post + pages. + +## 1. Create a new post directory + +Create a new folder under `content/feed/`, named after the post slug (this +becomes the URL): + +``` +content/feed/my-new-announcement/index.md +``` + +## 2. Write the frontmatter + content + +```md +--- +title: 'My New Announcement' +description: A one or two sentence summary used for SEO and RSS. +date: '2024-06-01 09:00:00' +author: 'Your Name' +authorLink: https://www.linkedin.com/in/yourprofile/ +tags: + - Announcements +--- + +Your announcement content goes here, written in regular Markdown. +``` + +Notes on frontmatter fields: + +| Field | Required | Notes | +| ------------- | -------- | ---------------------------------------------------------------------- | +| `title` | Yes | Post title, used on the page, in the sidebar and in the RSS item. | +| `description` | Yes | Short summary, used for SEO meta tags and the RSS item description. | +| `date` | Yes | Format `'YYYY-MM-DD HH:MM:SS'`. Controls sort order and the archive. | +| `author` | Yes | Displayed under the post title. | +| `authorLink` | No | If set, the author name links out (e.g. to a LinkedIn profile). | +| `tags` | Yes | One or more tags. Used for the sidebar category list and RSS ``. | +| `previewImg` | No | Optional relative path to an image in the same folder (e.g. `'./cover.png'`). Feed posts commonly omit this since they're short-form. | + +## 3. Add any images (optional) + +If you use `previewImg` or reference images in the body, place the image +files in the same post directory and reference them with a relative path, +the same as for blog posts. + +## 4. Build / preview + +```shell +npm run develop +``` + +Your new post will be available at `/my-new-announcement/`, and will appear: + +- On the [`/feed/`](https://datacontroller.io/feed/) listing page (with + pagination, sidebar archives and tag categories under `/feed/...`). +- In the site search index. +- In the combined RSS feed at `/rss.xml`, alongside blog posts. + +## How it differs from a Blog post + +| Aspect | Blog | Feed | +| ------------------ | ------------------------------ | ----------------------------------- | +| Content directory | `content/blog/` | `content/feed/` | +| Listing page | `/blog/` | `/feed/` | +| Year archive | `/{year}/` | `/feed/{year}/` | +| Category page | `/category/{tag}/` | `/feed/category/{tag}/` | +| Template | `src/templates/blog-post.tsx`, `src/templates/blog-list.tsx` | `src/templates/feed-post.tsx`, `src/templates/feed-list.tsx` | +| RSS | Included in `/rss.xml` | Included in `/rss.xml` | + +Feed archive/category routes are prefixed with `/feed/` specifically to avoid +colliding with the equivalent Blog routes, since both content types are +rendered by the shared [`src/templates/sidebar.tsx`](../src/templates/sidebar.tsx) +component (via its `basePath` prop). diff --git a/gatsby-browser.js b/gatsby-browser.js index f68fc25..7dc8fe1 100644 --- a/gatsby-browser.js +++ b/gatsby-browser.js @@ -1,4 +1,5 @@ import "bootstrap/dist/css/bootstrap.min.css"; +import "./src/styles/montserrat.css"; // Import all js dependencies. import "jquery/dist/jquery.min.js"; diff --git a/gatsby-config.d.ts b/gatsby-config.d.ts new file mode 100644 index 0000000..e44cc64 --- /dev/null +++ b/gatsby-config.d.ts @@ -0,0 +1,15 @@ +export interface SiteMetadata { + title: string + description: string + siteUrl: string + author: { + name: string + summary: string + } + social: { + linkedin: string + } +} + +export const siteMetadata: SiteMetadata +export const pathPrefix: string diff --git a/gatsby-config.js b/gatsby-config.js index 13ecd09..3782e24 100644 --- a/gatsby-config.js +++ b/gatsby-config.js @@ -23,7 +23,6 @@ module.exports = { name: `Data Controller | Flexible and Secure SAS® Data Modification`, short_name: `Data Controller`, description: `Data Controller for SAS® is dedicated to helping users, admins and developers manage their data. A zero code approach with Data Lineage, Catalog, Dictionary, Validation, Workflow, Alerts and more.`, - homepage_url: 'https://datacontroller.io/', start_url: '/', background_color: '#fff', theme_color: '#314351', @@ -51,6 +50,14 @@ module.exports = { }, __key: 'blog' }, + { + resolve: `gatsby-source-filesystem`, + options: { + path: `./content/feed`, + name: `feed` + }, + __key: 'feed' + }, { resolve: `gatsby-source-filesystem`, options: { @@ -103,13 +110,6 @@ module.exports = { }, __key: 'pages' }, - { - resolve: `gatsby-plugin-google-fonts`, - options: { - fonts: [`Montserrat\:300,400,500`], - display: 'swap' - } - }, { resolve: 'gatsby-plugin-local-search', options: { @@ -131,7 +131,7 @@ module.exports = { // required. query: ` { - remark: allMarkdownRemark (filter: {fileAbsolutePath: {regex: "/content/blog/"}}) { + remark: allMarkdownRemark (filter: {fileAbsolutePath: {regex: "/content\\/(blog|feed)\\//"}}) { posts: edges { post: node { id @@ -184,6 +184,63 @@ module.exports = { html: data.post.html })) } + }, + { + resolve: 'gatsby-plugin-feed', + options: { + query: ` + { + site { + siteMetadata { + title + description + siteUrl + } + } + } + `, + feeds: [ + { + serialize: ({ query: { site, allMarkdownRemark } }) => + allMarkdownRemark.nodes.map((node) => ({ + title: node.frontmatter.title, + description: node.frontmatter.description || '', + date: node.frontmatter.date, + url: site.siteMetadata.siteUrl + node.fields.slug, + guid: site.siteMetadata.siteUrl + node.fields.slug, + author: node.frontmatter.author, + categories: node.frontmatter.tags || [], + custom_elements: [{ 'content:encoded': node.html }] + })), + query: ` + { + allMarkdownRemark( + sort: { frontmatter: { date: DESC } } + filter: { + fileAbsolutePath: { regex: "/content\\/(blog|feed)\\//" } + } + ) { + nodes { + html + fields { + slug + } + frontmatter { + title + description + date + author + tags + } + } + } + } + `, + output: '/rss.xml', + title: 'Data Controller — Blog & Feed' + } + ] + } } ] } diff --git a/gatsby-node.js b/gatsby-node.js index c858544..878a702 100644 --- a/gatsby-node.js +++ b/gatsby-node.js @@ -6,6 +6,10 @@ const recentPosts = [] const archives = {} const tagsFrequent = [] +const feedRecentPosts = [] +const feedArchives = {} +const feedTagsFrequent = [] + exports.createPages = async ({ graphql, actions, reporter }) => { const { createPage } = actions @@ -14,6 +18,10 @@ exports.createPages = async ({ graphql, actions, reporter }) => { const blogListTemplate = path.resolve(`./src/templates/blog-list.tsx`) const blogSearchTemplate = path.resolve(`./src/templates/blog-search.tsx`) + // Define a template for feed post + const feedPostTemplate = path.resolve(`./src/templates/feed-post.tsx`) + const feedListTemplate = path.resolve(`./src/templates/feed-list.tsx`) + // Get all markdown blog posts sorted by date const result = await graphql( ` @@ -34,7 +42,10 @@ exports.createPages = async ({ graphql, actions, reporter }) => { } } } - tagsGroup: allMarkdownRemark(limit: 1000) { + tagsGroup: allMarkdownRemark( + limit: 1000 + filter: { fileAbsolutePath: { regex: "/content/blog/" } } + ) { group(field: { frontmatter: { tags: SELECT } }) { name: fieldValue totalCount @@ -176,6 +187,166 @@ exports.createPages = async ({ graphql, actions, reporter }) => { tags: tagsFrequent } }) + + // --------------------------------------------------------------------- + // Feed section (announcements / social updates) - mirrors the blog logic + // above but is scoped to /content/feed/ and lives under the /feed/ path + // --------------------------------------------------------------------- + + const feedResult = await graphql( + ` + { + allMarkdownRemark( + sort: { frontmatter: { date: DESC } } + limit: 1000 + filter: { fileAbsolutePath: { regex: "/content/feed/" } } + ) { + nodes { + id + fields { + slug + } + frontmatter { + title + date(formatString: "YYYY") + } + } + } + tagsGroup: allMarkdownRemark( + limit: 1000 + filter: { fileAbsolutePath: { regex: "/content/feed/" } } + ) { + group(field: { frontmatter: { tags: SELECT } }) { + name: fieldValue + totalCount + } + } + } + ` + ) + + if (feedResult.errors) { + reporter.panicOnBuild( + `There was an error loading your feed posts`, + feedResult.errors + ) + return + } + + const feedPosts = feedResult.data.allMarkdownRemark.nodes + feedRecentPosts.push( + ...feedPosts.slice(0, 10).map((p) => ({ + slug: p.fields.slug, + title: p.frontmatter.title + })) + ) + + const feedTags = feedResult.data.tagsGroup.group + feedTagsFrequent.push( + ...feedTags.sort((a, b) => b.totalCount - a.totalCount).slice(0, 10) + ) + + feedPosts.forEach((d) => { + if (feedArchives[d.frontmatter.date] == null) + feedArchives[d.frontmatter.date] = 0 + feedArchives[d.frontmatter.date]++ + }) + + // Create individual feed post pages + if (feedPosts.length > 0) { + feedPosts.forEach((post, index) => { + const previousPostId = index === 0 ? null : feedPosts[index - 1].id + const nextPostId = + index === feedPosts.length - 1 ? null : feedPosts[index + 1].id + + createPage({ + path: post.fields.slug, + component: feedPostTemplate, + context: { + id: post.id, + archives: feedArchives, + recentPosts: feedRecentPosts, + tags: feedTagsFrequent, + previousPostId, + nextPostId + } + }) + }) + } + + // Create feed-list pages + const feedNumPages = Math.ceil(feedPosts.length / postsPerPage) + Array.from({ length: feedNumPages }).forEach((_, i) => { + createPage({ + path: i === 0 ? `/feed` : `/feed/page/${i + 1}`, + component: feedListTemplate, + context: { + page: 'index', + archives: feedArchives, + recentPosts: feedRecentPosts, + tags: feedTagsFrequent, + filter: { fileAbsolutePath: { regex: '/content/feed/' } }, + limit: postsPerPage, + skip: i * postsPerPage, + numPages: feedNumPages, + currentPage: i + 1 + } + }) + }) + + for (year in feedArchives) { + const count = feedArchives[year] + const numPagesOfYear = Math.ceil(count / postsPerPage) + Array.from({ length: numPagesOfYear }).forEach((_, i) => { + createPage({ + path: + i === 0 ? `/feed/${year}/` : `/feed/${year}/page/${i + 1}`, + component: feedListTemplate, + context: { + page: 'year', + archives: feedArchives, + recentPosts: feedRecentPosts, + tags: feedTagsFrequent, + filter: { + frontmatter: { date: { gte: year, lt: year + 1 } }, + fileAbsolutePath: { regex: '/content/feed/' } + }, + limit: postsPerPage, + skip: i * postsPerPage, + numPages: numPagesOfYear, + currentPage: i + 1, + year: year + } + }) + }) + } + + feedTags.forEach((tag) => { + const count = tag.totalCount + const numPagesOfTag = Math.ceil(count / postsPerPage) + Array.from({ length: numPagesOfTag }).forEach((__, i) => { + const tagPath = `/feed/category/${_.kebabCase(tag.name)}/` + createPage({ + path: i === 0 ? tagPath : `${tagPath}page/${i + 1}`, + component: feedListTemplate, + context: { + page: 'category', + archives: feedArchives, + recentPosts: feedRecentPosts, + tags: feedTagsFrequent, + filter: { + frontmatter: { tags: { in: [tag.name] } }, + fileAbsolutePath: { regex: '/content/feed/' } + }, + limit: postsPerPage, + skip: i * postsPerPage, + numPages: numPagesOfTag, + currentPage: i + 1, + tag: tag.name + } + }) + }) + }) } exports.onCreatePage = ({ page, actions }) => { diff --git a/package-lock.json b/package-lock.json index 7010354..0878495 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,9 +12,10 @@ "@mdx-js/mdx": "^3.1.1", "@mdx-js/react": "^3.1.1", "babel-plugin-styled-components": "^2.1.4", + "baseline-browser-mapping": "^2.10.43", + "caniuse-lite": "^1.0.30001806", "gatsby": "^5.15.0", - "gatsby-plugin-google-analytics": "^5.13.1", - "gatsby-plugin-google-fonts": "^1.0.1", + "gatsby-plugin-feed": "^5.16.0", "gatsby-plugin-image": "^3.15.0", "gatsby-plugin-local-search": "^2.0.1", "gatsby-plugin-manifest": "^5.15.0", @@ -41,9 +42,19 @@ }, "devDependencies": { "@popperjs/core": "^2.9.2", + "@types/lodash": "^4.17.24", + "@types/node": "^20.19.43", + "@types/react": "^18.3.31", + "@types/react-dom": "^18.3.7", + "@types/react-helmet": "^6.1.11", "bootstrap": "^5.0.0-beta3", "cloudron-surfer": "^6.4.1", - "jquery": "^3.6.0" + "jquery": "^3.6.0", + "typescript": "^7.0.2" + }, + "engines": { + "node": ">=24.0.0", + "npm": ">=10.0.0" } }, "node_modules/@ampproject/remapping": { @@ -2165,6 +2176,23 @@ "node": ">= 4" } }, + "node_modules/@exodus/bytes": { + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/@exodus/bytes/-/bytes-1.15.1.tgz", + "integrity": "sha512-S6mL0yNB/Abt9Ei4tq8gDhcczc4S3+vQ4ra7vxnAf+YHC02srtqxKKZghx2Dq6p0e66THKwR6r8N6P95wEty7Q==", + "license": "MIT", + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + }, + "peerDependencies": { + "@noble/hashes": "^1.8.0 || ^2.0.0" + }, + "peerDependenciesMeta": { + "@noble/hashes": { + "optional": true + } + } + }, "node_modules/@expo/devcert": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/@expo/devcert/-/devcert-1.2.1.tgz", @@ -2771,72 +2799,78 @@ } }, "node_modules/@lmdb/lmdb-darwin-arm64": { - "version": "2.5.3", - "resolved": "https://registry.npmjs.org/@lmdb/lmdb-darwin-arm64/-/lmdb-darwin-arm64-2.5.3.tgz", - "integrity": "sha512-RXwGZ/0eCqtCY8FLTM/koR60w+MXyvBUpToXiIyjOcBnC81tAlTUHrRUavCEWPI9zc9VgvpK3+cbumPyR8BSuA==", + "version": "2.9.4", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-darwin-arm64/-/lmdb-darwin-arm64-2.9.4.tgz", + "integrity": "sha512-38XmbES/wVcvMXdwcM5QzL0cSaZu3VwE7mCd0I89eliHQTMQblgWXsr2HQoP9v0JnH6jVt7+E/TkeGNLmp4wzA==", "cpu": [ "arm64" ], + "license": "MIT", "optional": true, "os": [ "darwin" ] }, "node_modules/@lmdb/lmdb-darwin-x64": { - "version": "2.5.3", - "resolved": "https://registry.npmjs.org/@lmdb/lmdb-darwin-x64/-/lmdb-darwin-x64-2.5.3.tgz", - "integrity": "sha512-337dNzh5yCdNCTk8kPfoU7jR3otibSlPDGW0vKZT97rKnQMb9tNdto3RtWoGPsQ8hKmlRZpojOJtmwjncq1MoA==", + "version": "2.9.4", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-darwin-x64/-/lmdb-darwin-x64-2.9.4.tgz", + "integrity": "sha512-JkPrV8rEu88FzMcuouZeU2b/NuVC3KwQxKo5vKhNycBtsCn7KCWHalxL4sdTiHQ4xtgMca3mmeDAdxgqQqnDig==", "cpu": [ "x64" ], + "license": "MIT", "optional": true, "os": [ "darwin" ] }, "node_modules/@lmdb/lmdb-linux-arm": { - "version": "2.5.3", - "resolved": "https://registry.npmjs.org/@lmdb/lmdb-linux-arm/-/lmdb-linux-arm-2.5.3.tgz", - "integrity": "sha512-mU2HFJDGwECkoD9dHQEfeTG5mp8hNS2BCfwoiOpVPMeapjYpQz9Uw3FkUjRZ4dGHWKbin40oWHuL0bk2bCx+Sg==", + "version": "2.9.4", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-linux-arm/-/lmdb-linux-arm-2.9.4.tgz", + "integrity": "sha512-b3JZL5pLuvcGEbcsThUQPFlQdBFaBxImrlNbFUeJmzLwpdgGRi0RSQdZZ2PuIoZvpRj0tfNlhXQwXiYMz+9iTw==", "cpu": [ "arm" ], + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@lmdb/lmdb-linux-arm64": { - "version": "2.5.3", - "resolved": "https://registry.npmjs.org/@lmdb/lmdb-linux-arm64/-/lmdb-linux-arm64-2.5.3.tgz", - "integrity": "sha512-VJw60Mdgb4n+L0fO1PqfB0C7TyEQolJAC8qpqvG3JoQwvyOv6LH7Ib/WE3wxEW9nuHmVz9jkK7lk5HfWWgoO1Q==", + "version": "2.9.4", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-linux-arm64/-/lmdb-linux-arm64-2.9.4.tgz", + "integrity": "sha512-aIzmw0g4Wdd/w2rDppGfo1JEl4xWpg6HPbf5ZeuWXCjFms8oc8cazm6oBEAimiZEgYYBFPDPdM644xJcwuJbxQ==", "cpu": [ "arm64" ], + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@lmdb/lmdb-linux-x64": { - "version": "2.5.3", - "resolved": "https://registry.npmjs.org/@lmdb/lmdb-linux-x64/-/lmdb-linux-x64-2.5.3.tgz", - "integrity": "sha512-qaReO5aV8griBDsBr8uBF/faO3ieGjY1RY4p8JvTL6Mu1ylLrTVvOONqKFlNaCwrmUjWw5jnf7VafxDAeQHTow==", + "version": "2.9.4", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-linux-x64/-/lmdb-linux-x64-2.9.4.tgz", + "integrity": "sha512-Yj6Nb+/j+ZZ65oH/UCE0UfUu/6TO5wWLIeE2izGCpsCxcozZVbzwhzrCs0FUXf6lXv46DJteONosWH9o1XjzqQ==", "cpu": [ "x64" ], + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@lmdb/lmdb-win32-x64": { - "version": "2.5.3", - "resolved": "https://registry.npmjs.org/@lmdb/lmdb-win32-x64/-/lmdb-win32-x64-2.5.3.tgz", - "integrity": "sha512-cK+Elf3RjEzrm3SerAhrFWL5oQAsZSJ/LmjL1joIpTfEP1etJJ9CTRvdaV6XLYAxaEkfdhk/9hOvHLbR9yIhCA==", + "version": "2.9.4", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-win32-x64/-/lmdb-win32-x64-2.9.4.tgz", + "integrity": "sha512-0L6Tyun47/kQb+FzTDIumrfZgU6oEos0RgekKa/3YC7nsUY+ZASZHikzGgEZpMQHSz5YeR+DDUtOMSwqodWHDg==", "cpu": [ "x64" ], + "license": "MIT", "optional": true, "os": [ "win32" @@ -3107,112 +3141,6 @@ "@parcel/core": "^2.8.3" } }, - "node_modules/@parcel/cache/node_modules/@lmdb/lmdb-darwin-arm64": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/@lmdb/lmdb-darwin-arm64/-/lmdb-darwin-arm64-2.5.2.tgz", - "integrity": "sha512-+F8ioQIUN68B4UFiIBYu0QQvgb9FmlKw2ctQMSBfW2QBrZIxz9vD9jCGqTCPqZBRbPHAS/vG1zSXnKqnS2ch/A==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@parcel/cache/node_modules/@lmdb/lmdb-darwin-x64": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/@lmdb/lmdb-darwin-x64/-/lmdb-darwin-x64-2.5.2.tgz", - "integrity": "sha512-KvPH56KRLLx4KSfKBx0m1r7GGGUMXm0jrKmNE7plbHlesZMuPJICtn07HYgQhj1LNsK7Yqwuvnqh1QxhJnF1EA==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@parcel/cache/node_modules/@lmdb/lmdb-linux-arm": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/@lmdb/lmdb-linux-arm/-/lmdb-linux-arm-2.5.2.tgz", - "integrity": "sha512-5kQAP21hAkfW5Bl+e0P57dV4dGYnkNIpR7f/GAh6QHlgXx+vp/teVj4PGRZaKAvt0GX6++N6hF8NnGElLDuIDw==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@parcel/cache/node_modules/@lmdb/lmdb-linux-arm64": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/@lmdb/lmdb-linux-arm64/-/lmdb-linux-arm64-2.5.2.tgz", - "integrity": "sha512-aLl89VHL/wjhievEOlPocoefUyWdvzVrcQ/MHQYZm2JfV1jUsrbr/ZfkPPUFvZBf+VSE+Q0clWs9l29PCX1hTQ==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@parcel/cache/node_modules/@lmdb/lmdb-linux-x64": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/@lmdb/lmdb-linux-x64/-/lmdb-linux-x64-2.5.2.tgz", - "integrity": "sha512-xUdUfwDJLGjOUPH3BuPBt0NlIrR7f/QHKgu3GZIXswMMIihAekj2i97oI0iWG5Bok/b+OBjHPfa8IU9velnP/Q==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@parcel/cache/node_modules/@lmdb/lmdb-win32-x64": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/@lmdb/lmdb-win32-x64/-/lmdb-win32-x64-2.5.2.tgz", - "integrity": "sha512-zrBczSbXKxEyK2ijtbRdICDygRqWSRPpZMN5dD1T8VMEW5RIhIbwFWw2phDRXuBQdVDpSjalCIUMWMV2h3JaZA==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@parcel/cache/node_modules/lmdb": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/lmdb/-/lmdb-2.5.2.tgz", - "integrity": "sha512-V5V5Xa2Hp9i2XsbDALkBTeHXnBXh/lEmk9p22zdr7jtuOIY9TGhjK6vAvTpOOx9IKU4hJkRWZxn/HsvR1ELLtA==", - "hasInstallScript": true, - "license": "MIT", - "dependencies": { - "msgpackr": "^1.5.4", - "node-addon-api": "^4.3.0", - "node-gyp-build-optional-packages": "5.0.3", - "ordered-binary": "^1.2.4", - "weak-lru-cache": "^1.2.2" - }, - "optionalDependencies": { - "@lmdb/lmdb-darwin-arm64": "2.5.2", - "@lmdb/lmdb-darwin-x64": "2.5.2", - "@lmdb/lmdb-linux-arm": "2.5.2", - "@lmdb/lmdb-linux-arm64": "2.5.2", - "@lmdb/lmdb-linux-x64": "2.5.2", - "@lmdb/lmdb-win32-x64": "2.5.2" - } - }, - "node_modules/@parcel/cache/node_modules/node-addon-api": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-4.3.0.tgz", - "integrity": "sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ==", - "license": "MIT" - }, "node_modules/@parcel/codeframe": { "version": "2.8.3", "resolved": "https://registry.npmjs.org/@parcel/codeframe/-/codeframe-2.8.3.tgz", @@ -4706,6 +4634,13 @@ "@types/node": "*" } }, + "node_modules/@types/lodash": { + "version": "4.17.24", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.24.tgz", + "integrity": "sha512-gIW7lQLZbue7lRSWEFql49QJJWThrTFFeIMJdp3eH4tKoxm1OvEPg02rm4wCCSHS0cL3/Fizimb35b7k8atwsQ==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/mdast": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", @@ -4725,11 +4660,12 @@ "integrity": "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==" }, "node_modules/@types/node": { - "version": "20.14.5", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.5.tgz", - "integrity": "sha512-aoRR+fJkZT2l0aGOJhuA8frnCSoNX6W7U2mpNq63+BxBIj5BQFt8rHy627kijCmm63ijdSdwvGgpUsU6MBsZZA==", + "version": "20.19.43", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.43.tgz", + "integrity": "sha512-6oYBAi5ikg4Pl+kGsoYtawUMBT2zZMCvPNF7pVLnHZfd1zf38DRiWn/gT01RYCdUqkv7Fhr+C9ot4/tb+2sVvA==", + "license": "MIT", "dependencies": { - "undici-types": "~5.26.4" + "undici-types": "~6.21.0" } }, "node_modules/@types/parse-json": { @@ -4743,9 +4679,10 @@ "integrity": "sha512-kUNnecmtkunAoQ3CnjmMkzNU/gtxG8guhi+Fk2U/kOpIKjIMKnXGp4IJCgQJrXSgMsWYimYG4TGjz/UzbGEBTw==" }, "node_modules/@types/prop-types": { - "version": "15.7.12", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.12.tgz", - "integrity": "sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==" + "version": "15.7.15", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz", + "integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==", + "license": "MIT" }, "node_modules/@types/reach__router": { "version": "1.3.15", @@ -4757,14 +4694,41 @@ } }, "node_modules/@types/react": { - "version": "18.3.3", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.3.tgz", - "integrity": "sha512-hti/R0pS0q1/xx+TsI73XIqk26eBsISZ2R0wUijXIngRK9R/e7Xw/cXVxQK7R5JjW+SV4zGcn5hXjudkN/pLIw==", + "version": "18.3.31", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.31.tgz", + "integrity": "sha512-vfEqpXTvwT91yhmwdfouStN2hSKwTvyRs8qpLfADyrq/kxDw0hZM7Wk9Ug1FELj8hIby+S/+kQCSRFF32nv2Qw==", + "license": "MIT", "dependencies": { "@types/prop-types": "*", - "csstype": "^3.0.2" + "csstype": "^3.2.2" } }, + "node_modules/@types/react-dom": { + "version": "18.3.7", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.7.tgz", + "integrity": "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^18.0.0" + } + }, + "node_modules/@types/react-helmet": { + "version": "6.1.11", + "resolved": "https://registry.npmjs.org/@types/react-helmet/-/react-helmet-6.1.11.tgz", + "integrity": "sha512-0QcdGLddTERotCXo3VFlUSWO3ztraw8nZ6e3zJSgG7apwV5xt+pJUS8ewPBqT4NYB1optGLprNQzFleIY84u/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/react": "*" + } + }, + "node_modules/@types/react/node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "license": "MIT" + }, "node_modules/@types/responselike": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.3.tgz", @@ -4810,6 +4774,346 @@ "integrity": "sha512-S9q47ByT2pPvD65IvrWp7qppVMpk9WGMbVq9wbWZOHg6tnXSD4vyhao6nOSBwwfDdV2p3Kx9evA9vI+XWTfDvw==", "license": "MIT" }, + "node_modules/@typescript/typescript-aix-ppc64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-aix-ppc64/-/typescript-aix-ppc64-7.0.2.tgz", + "integrity": "sha512-MTKKkWB7p/0E9xi1d1tHtZ5PiLkGEMIq88pK2CubZjOsLtYTLqhgIgi6zepFa+9GHZ6h05NMCkQxGKiPXMxXtQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-darwin-arm64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-darwin-arm64/-/typescript-darwin-arm64-7.0.2.tgz", + "integrity": "sha512-gowzar9MwS/aRWp6f3a4KUqzRjAZjOsmGNCM6LcTgXum+dBfgsBVMN+AgvOCCbguXyick6LJhpBszxMebJ8syA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-darwin-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-darwin-x64/-/typescript-darwin-x64-7.0.2.tgz", + "integrity": "sha512-SZ9xZInqApNlNGc9s0W1VSsktYSOe9cFqNOIqmN1Gs8SmkjKZYFt017G4VwPxASInODuAdbTW7sXiFUf893RgA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-freebsd-arm64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-freebsd-arm64/-/typescript-freebsd-arm64-7.0.2.tgz", + "integrity": "sha512-W5NH4y/J0plIIS5b2xvTEkU7JFxyqdMAOgf+Ilhl0vHQXKO5dZoxd+C/jEtq56c4F3wk71RB4BMRQ2XdI+bwYQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-freebsd-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-freebsd-x64/-/typescript-freebsd-x64-7.0.2.tgz", + "integrity": "sha512-UMGDx5sTpzNw3WiPebH7l90IWfJggEd+egHt/q6p7/Cm3zqoV7VxkGXt+3DxPIw8CcmvAB0j3sVVfbhX+M4Tpw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-arm": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-arm/-/typescript-linux-arm-7.0.2.tgz", + "integrity": "sha512-gffT3xPz9sR7j/YJExkyPntrI0P2EP9XbOyWzth2/Gs0RstK+90RBcO0ncXoXy/beYll1SXw846Nf2zdnEz0QQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-arm64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-arm64/-/typescript-linux-arm64-7.0.2.tgz", + "integrity": "sha512-Qh4eU4/y3yDjnfjjyPYihMj5/ODIlmt+Bzu17OI+fiSRDW57QmU5SiN63exPRNJPKUzcc1INa1NXdrJ+MqHjUQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-loong64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-loong64/-/typescript-linux-loong64-7.0.2.tgz", + "integrity": "sha512-uEHck9i8hoAzXPiYRib1O7miOnz23SxIeVl6F4LXox+qov1K35jHcEW6VHKvZI+pyvl7fZEP4MCU5LYvIq1GuQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-mips64el": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-mips64el/-/typescript-linux-mips64el-7.0.2.tgz", + "integrity": "sha512-R4KvAMnE43W5Qeqb0Ly56O3mWMWIAgsMyz36DCaycd5nbg/9kzm0liw3JocfRqyJY0KPmzFjbswozXyW0DnIYA==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-ppc64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-ppc64/-/typescript-linux-ppc64-7.0.2.tgz", + "integrity": "sha512-DORx5b3sd/4S7eayxm4FQv+A7CrkUIGRaHiwI8oiHTAI1fAPWhF4J0vAlkC8biAlHSVVwxMQ3tjZ2/DVbnQiiA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-riscv64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-riscv64/-/typescript-linux-riscv64-7.0.2.tgz", + "integrity": "sha512-wf0jqEDOjrPRnKwYRyyJDRo11KMbvMFrU+q4zqKyChODBzvlkbhNQfKvLxQCcwTpdDaXSHZTVuh0JoCrKCUMHQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-s390x": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-s390x/-/typescript-linux-s390x-7.0.2.tgz", + "integrity": "sha512-IkwJc3L7yhytWd/ewjyxNDfOmswCm9GWMJT/ue/dU4aZNbwZeYAetq42VyLmsmSjvoX7z74X6ZaYCtzAr0EuGw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-x64/-/typescript-linux-x64-7.0.2.tgz", + "integrity": "sha512-EYdf2cNg7rgCWJnxCdJ+F3V39O8ihb37eHAu1LK8oAFizgTQbPOK7zHHXbPt8rX24COqODXeI3sIf0fCXG7H/A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-netbsd-arm64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-netbsd-arm64/-/typescript-netbsd-arm64-7.0.2.tgz", + "integrity": "sha512-+polYF4MF04aPpO5FTkHran9yUQDSXqy5GiSDKpsll5jy3l3+g9QLhpf39T+ePtefhXLOGrLl0QIjkQP6VnelA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-netbsd-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-netbsd-x64/-/typescript-netbsd-x64-7.0.2.tgz", + "integrity": "sha512-8YIT0EHM/3dq10ZOVF/A7pc/YSMtbcecct4rWtexrnSCHOPcpC2KTLXfTCR6vDpnSiY12heNb1GiN/wu+T/FyA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-openbsd-arm64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-openbsd-arm64/-/typescript-openbsd-arm64-7.0.2.tgz", + "integrity": "sha512-APT8+ClYnuYm1u9+kgGXoMj2VzWzcymwh2gNSQVySHfkRDGOTVkoWLjCmOQSaO+PoqQ57B0flRp9SA+7GnnkzQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-openbsd-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-openbsd-x64/-/typescript-openbsd-x64-7.0.2.tgz", + "integrity": "sha512-yX7s+Q0Dln0Dt9tEzZsAjXXR/+ytBM7AlglaqyeMPxQszJ1JhlJdZ6jLA+IzldHtflX81em7lDao1xXu+aRRkg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-sunos-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-sunos-x64/-/typescript-sunos-x64-7.0.2.tgz", + "integrity": "sha512-dLJDGaLZ1D4HPQn62u1n8mBDkJREwMsAkCdkwd4Ieqw+x3TUyTsqY0YiBCtE6H6OzzgGk3iuZ3vFWRS+E8/d1g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-win32-arm64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-win32-arm64/-/typescript-win32-arm64-7.0.2.tgz", + "integrity": "sha512-Gyl1Vy6OsWesLzmq+EP0Fb7b4Nid5232AvcA2SFcdYreldpNtYFFofPjnt62y9hQy7VTaZp65ICJjuAQRaVcIQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-win32-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-win32-x64/-/typescript-win32-x64-7.0.2.tgz", + "integrity": "sha512-0BQ3HkAHHlKLSp1qRvf3SUhGpGsDuhB/jgFw75guyqbxJqEaS0Cw/VFO8i2nHglJUzQCRtMMR/IBAKE3ETMC4g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=16.20.0" + } + }, "node_modules/@ungap/structured-clone": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", @@ -5914,12 +6218,15 @@ } }, "node_modules/baseline-browser-mapping": { - "version": "2.9.7", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.7.tgz", - "integrity": "sha512-k9xFKplee6KIio3IDbwj+uaCLpqzOwakOgmqzPezM0sFJlFKcg30vk2wOiAJtkTSfx0SSQDSe8q+mWA/fSH5Zg==", + "version": "2.10.43", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.43.tgz", + "integrity": "sha512-AjYpR78kDWAY3Efj+cDTFH9t9SCoL7OoTp1BOb0mQV7S+6CiLwnWM3FyxhJtdPufDFKzmCSFoUncKjWgJEZTCQ==", "license": "Apache-2.0", "bin": { - "baseline-browser-mapping": "dist/cli.js" + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" } }, "node_modules/better-opn": { @@ -6404,9 +6711,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001760", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001760.tgz", - "integrity": "sha512-7AAMPcueWELt1p3mi13HR/LHH0TJLT11cnwDJEs3xA4+CK/PLKeO9Kl1oru24htkyUKtkGCvAx4ohB0Ttry8Dw==", + "version": "1.0.30001806", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001806.tgz", + "integrity": "sha512-72Cuvd95zbSYPKq6Fhg8eDJRlzgWDf7/mtoZv6Qe/DYNCEBdNxoA3+rZAU2ZhGCpZlns3EssFavaZomckT5Uuw==", "funding": [ { "type": "opencollective", @@ -11077,9 +11384,9 @@ } }, "node_modules/gatsby-core-utils": { - "version": "4.15.0", - "resolved": "https://registry.npmjs.org/gatsby-core-utils/-/gatsby-core-utils-4.15.0.tgz", - "integrity": "sha512-fxzHbCvQm1u74DSjy7G4PVqEcWb7sOCFg3kFFVUqVKe3hDCm6s1M/gRSvpm3/3fm74HhgLsmQ54Qaf54Ckyn9w==", + "version": "4.16.0", + "resolved": "https://registry.npmjs.org/gatsby-core-utils/-/gatsby-core-utils-4.16.0.tgz", + "integrity": "sha512-QCZ9BmQp3YyYxH0Wf4bofayL3vJnayqSvsBUAhKXGh/Os0fn1KMNyAjPLnW+zrGFQaK05Vjdlp99I/Wnc3M33A==", "license": "MIT", "dependencies": { "@babel/runtime": "^7.20.13", @@ -11100,7 +11407,7 @@ "xdg-basedir": "^4.0.0" }, "engines": { - "node": ">=18.0.0" + "node": ">=18.0.0 <26" } }, "node_modules/gatsby-graphiql-explorer": { @@ -11199,29 +11506,28 @@ "@parcel/core": "^2.0.0" } }, - "node_modules/gatsby-plugin-google-analytics": { - "version": "5.13.1", - "resolved": "https://registry.npmjs.org/gatsby-plugin-google-analytics/-/gatsby-plugin-google-analytics-5.13.1.tgz", - "integrity": "sha512-6TzvUPW7CBpfpSIqpcvaRwRKTYgM3CKqSnfyRIY16vlAKYkAtZLXm3PkIbg0+mM+WzY6GRnAoeXqRkXJv0FK9A==", + "node_modules/gatsby-plugin-feed": { + "version": "5.16.0", + "resolved": "https://registry.npmjs.org/gatsby-plugin-feed/-/gatsby-plugin-feed-5.16.0.tgz", + "integrity": "sha512-oKRmIbZjUS6TRRZJVP3n79jlqY5B99n4ovLxOrdrkWobmyGpIxUBRwUcIx8mrKz/7k9wxci3cdbn7pY9K18p9A==", + "license": "MIT", "dependencies": { "@babel/runtime": "^7.20.13", - "minimatch": "^3.1.2", - "web-vitals": "^1.1.2" + "common-tags": "^1.8.2", + "fs-extra": "^11.2.0", + "gatsby-plugin-utils": "^4.16.0", + "lodash.merge": "^4.6.2", + "rss": "^1.2.2" }, "engines": { - "node": ">=18.0.0" + "node": ">=18.0.0 <26" }, "peerDependencies": { "gatsby": "^5.0.0-next", - "react": "^18.0.0 || ^0.0.0", - "react-dom": "^18.0.0 || ^0.0.0" + "react": "^18.0.0 || ^19.0.0 || ^0.0.0", + "react-dom": "^18.0.0 || ^19.0.0 || ^0.0.0" } }, - "node_modules/gatsby-plugin-google-fonts": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gatsby-plugin-google-fonts/-/gatsby-plugin-google-fonts-1.0.1.tgz", - "integrity": "sha512-p1NVkn27GUnDA5qHM+Z4cCcLCJIardzZXMon3640sT4xuL/AZJbsx3HEt2KY/5oZu0UXIkytkxzV2Da4rQeUIg==" - }, "node_modules/gatsby-plugin-image": { "version": "3.15.0", "resolved": "https://registry.npmjs.org/gatsby-plugin-image/-/gatsby-plugin-image-3.15.0.tgz", @@ -11465,23 +11771,23 @@ } }, "node_modules/gatsby-plugin-utils": { - "version": "4.15.0", - "resolved": "https://registry.npmjs.org/gatsby-plugin-utils/-/gatsby-plugin-utils-4.15.0.tgz", - "integrity": "sha512-6syfcye5eVYHXAU48G83mU2uXohxM3fO0BSGYYvGYlnM7FSV5HvwfFuksh3QEd0MKpE9gcQMVPvxa0pq2/PWdg==", + "version": "4.16.0", + "resolved": "https://registry.npmjs.org/gatsby-plugin-utils/-/gatsby-plugin-utils-4.16.0.tgz", + "integrity": "sha512-5a2ui0XQBNP8vln/UyfaGBYrVpZ0oeK3fY6LjL2zx9H6wVcO5HLdjmU9yDUwQJygi2GtYW7VGQVKHfBGXMHI6A==", "license": "MIT", "dependencies": { "@babel/runtime": "^7.20.13", "fastq": "^1.16.0", "fs-extra": "^11.2.0", - "gatsby-core-utils": "^4.15.0", - "gatsby-sharp": "^1.15.0", + "gatsby-core-utils": "^4.16.0", + "gatsby-sharp": "^1.16.0", "graphql-compose": "^9.0.10", "import-from": "^4.0.0", "joi": "^17.11.0", "mime": "^3.0.0" }, "engines": { - "node": ">=18.0.0" + "node": ">=18.0.0 <26" }, "peerDependencies": { "gatsby": "^5.0.0-next", @@ -11677,15 +11983,15 @@ } }, "node_modules/gatsby-sharp": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/gatsby-sharp/-/gatsby-sharp-1.15.0.tgz", - "integrity": "sha512-nl+dSQwXPmhkE0qcLVvkLdE8scQALYopuk25RkLCDfi1VTNz0g6wcZwkmbcNjlTCR4RkbvCt7MGgNC7CVH3Ddw==", + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/gatsby-sharp/-/gatsby-sharp-1.16.0.tgz", + "integrity": "sha512-Xh2MwKtr9UYQnhlv5xpLXADRG6j/9dPgTLf010jNKQmvPsETkwZ3TZwnOpIgbGYJjg50rYzSCQ9RAuYXlqLmMA==", "license": "MIT", "dependencies": { "sharp": "^0.32.6" }, "engines": { - "node": ">=18.0.0" + "node": ">=18.0.0 <26" } }, "node_modules/gatsby-source-filesystem": { @@ -14680,30 +14986,35 @@ "integrity": "sha512-kmsGcmpvjStZ0ATjuHycBujtNnXiZR28BTivEu0gAMDTT7GEyodcK6zSRtu6xsrdorrPZEIN380x7BD7xEYkew==" }, "node_modules/lmdb": { - "version": "2.5.3", - "resolved": "https://registry.npmjs.org/lmdb/-/lmdb-2.5.3.tgz", - "integrity": "sha512-iBA0cb13CobBSoGJLfZgnrykLlfJipDAnvtf+YwIqqzBEsTeQYsXrHaSBkaHd5wCWeabwrNvhjZoFMUrlo+eLw==", + "version": "2.9.4", + "resolved": "https://registry.npmjs.org/lmdb/-/lmdb-2.9.4.tgz", + "integrity": "sha512-Kri5TSKgpLk5q1VO7vYCcqAMyXTxmis6Et+6UARkU7ygvg3ZxUX2oEu/UwBkBskaS1d73effiBrTiHYyDppcBg==", "hasInstallScript": true, + "license": "MIT", "dependencies": { - "msgpackr": "^1.5.4", - "node-addon-api": "^4.3.0", - "node-gyp-build-optional-packages": "5.0.3", - "ordered-binary": "^1.2.4", + "msgpackr": "^1.9.9", + "node-addon-api": "^6.1.0", + "node-gyp-build-optional-packages": "5.1.1", + "ordered-binary": "^1.4.1", "weak-lru-cache": "^1.2.2" }, + "bin": { + "download-lmdb-prebuilds": "bin/download-prebuilds.js" + }, "optionalDependencies": { - "@lmdb/lmdb-darwin-arm64": "2.5.3", - "@lmdb/lmdb-darwin-x64": "2.5.3", - "@lmdb/lmdb-linux-arm": "2.5.3", - "@lmdb/lmdb-linux-arm64": "2.5.3", - "@lmdb/lmdb-linux-x64": "2.5.3", - "@lmdb/lmdb-win32-x64": "2.5.3" + "@lmdb/lmdb-darwin-arm64": "2.9.4", + "@lmdb/lmdb-darwin-x64": "2.9.4", + "@lmdb/lmdb-linux-arm": "2.9.4", + "@lmdb/lmdb-linux-arm64": "2.9.4", + "@lmdb/lmdb-linux-x64": "2.9.4", + "@lmdb/lmdb-win32-x64": "2.9.4" } }, "node_modules/lmdb/node_modules/node-addon-api": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-4.3.0.tgz", - "integrity": "sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ==" + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-6.1.0.tgz", + "integrity": "sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA==", + "license": "MIT" }, "node_modules/loader-runner": { "version": "4.3.0", @@ -17628,9 +17939,10 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "node_modules/msgpackr": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/msgpackr/-/msgpackr-1.10.2.tgz", - "integrity": "sha512-L60rsPynBvNE+8BWipKKZ9jHcSGbtyJYIwjRq0VrIvQ08cRjntGXJYW/tmciZ2IHWIY8WEW32Qa2xbh5+SKBZA==", + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/msgpackr/-/msgpackr-1.12.1.tgz", + "integrity": "sha512-4EUH9tQHnMmEgzW/MdAP0KIfa1T9AF+htl0ffe2n5vb2EKn9y2co8ccpgWko6S52Jy1PQZKwRnx5/KkYjtd9MQ==", + "license": "MIT", "optionalDependencies": { "msgpackr-extract": "^3.0.2" } @@ -17865,15 +18177,28 @@ } }, "node_modules/node-gyp-build-optional-packages": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.0.3.tgz", - "integrity": "sha512-k75jcVzk5wnnc/FMxsf4udAoTEUv2jY3ycfdSd3yWu6Cnd1oee6/CfZJApyscA4FJOmdoixWwiwOyf16RzD5JA==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.1.1.tgz", + "integrity": "sha512-+P72GAjVAbTxjjwUmwjVrqrdZROD4nf8KgpBoDxqXXTiYZZt/ud60dE5yvCSr9lRO8e8yv6kgJIC0K0PfZFVQw==", + "license": "MIT", + "dependencies": { + "detect-libc": "^2.0.1" + }, "bin": { "node-gyp-build-optional-packages": "bin.js", "node-gyp-build-optional-packages-optional": "optional.js", "node-gyp-build-optional-packages-test": "build-test.js" } }, + "node_modules/node-gyp-build-optional-packages/node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, "node_modules/node-html-parser": { "version": "5.4.2", "resolved": "https://registry.npmjs.org/node-html-parser/-/node-html-parser-5.4.2.tgz", @@ -18374,9 +18699,10 @@ } }, "node_modules/ordered-binary": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/ordered-binary/-/ordered-binary-1.5.1.tgz", - "integrity": "sha512-5VyHfHY3cd0iza71JepYG50My+YUbrFtGoUz2ooEydPyPM7Aai/JW098juLr+RG6+rDJuzNNTsEQu2DZa1A41A==" + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/ordered-binary/-/ordered-binary-1.6.1.tgz", + "integrity": "sha512-QkCdPooczexPLiXIrbVOPYkR3VO3T6v2OyKRkR1Xbhpy7/LAVXwahnRCgRp78Oe/Ehf0C/HATAxfSr6eA1oX+w==", + "license": "MIT" }, "node_modules/os-tmpdir": { "version": "1.0.2", @@ -21326,6 +21652,37 @@ "url": "https://opencollective.com/express" } }, + "node_modules/rss": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/rss/-/rss-1.2.2.tgz", + "integrity": "sha512-xUhRTgslHeCBeHAqaWSbOYTydN2f0tAzNXvzh3stjz7QDhQMzdgHf3pfgNIngeytQflrFPfy6axHilTETr6gDg==", + "license": "MIT", + "dependencies": { + "mime-types": "2.1.13", + "xml": "1.0.1" + } + }, + "node_modules/rss/node_modules/mime-db": { + "version": "1.25.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.25.0.tgz", + "integrity": "sha512-5k547tI4Cy+Lddr/hdjNbBEWBwSl8EBc5aSdKvedav8DReADgWJzcYiktaRIw3GtGC1jjwldXtTzvqJZmtvC7w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/rss/node_modules/mime-types": { + "version": "2.1.13", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.13.tgz", + "integrity": "sha512-ryBDp1Z/6X90UvjUK3RksH0IBPM137T7cmg4OgD5wQBojlAiUwuok0QeELkim/72EtcYuNlmbkrcGuxj3Kl0YQ==", + "license": "MIT", + "dependencies": { + "mime-db": "~1.25.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/run-async": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", @@ -22974,9 +23331,16 @@ } }, "node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-6.0.0.tgz", + "integrity": "sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==", + "license": "MIT", + "dependencies": { + "punycode": "^2.3.1" + }, + "engines": { + "node": ">=20" + } }, "node_modules/trim-lines": { "version": "3.0.1", @@ -23207,6 +23571,41 @@ "is-typedarray": "^1.0.0" } }, + "node_modules/typescript": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-7.0.2.tgz", + "integrity": "sha512-8FYau96o3NKOhbjKi/qNvG/W5jhzxkbdm5sj9AbZ/5T5sWqn3hJgLfGx27sRKZWTvyzCP8dLRBTf5tBTSRVUNA==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc" + }, + "engines": { + "node": ">=16.20.0" + }, + "optionalDependencies": { + "@typescript/typescript-aix-ppc64": "7.0.2", + "@typescript/typescript-darwin-arm64": "7.0.2", + "@typescript/typescript-darwin-x64": "7.0.2", + "@typescript/typescript-freebsd-arm64": "7.0.2", + "@typescript/typescript-freebsd-x64": "7.0.2", + "@typescript/typescript-linux-arm": "7.0.2", + "@typescript/typescript-linux-arm64": "7.0.2", + "@typescript/typescript-linux-loong64": "7.0.2", + "@typescript/typescript-linux-mips64el": "7.0.2", + "@typescript/typescript-linux-ppc64": "7.0.2", + "@typescript/typescript-linux-riscv64": "7.0.2", + "@typescript/typescript-linux-s390x": "7.0.2", + "@typescript/typescript-linux-x64": "7.0.2", + "@typescript/typescript-netbsd-arm64": "7.0.2", + "@typescript/typescript-netbsd-x64": "7.0.2", + "@typescript/typescript-openbsd-arm64": "7.0.2", + "@typescript/typescript-openbsd-x64": "7.0.2", + "@typescript/typescript-sunos-x64": "7.0.2", + "@typescript/typescript-win32-arm64": "7.0.2", + "@typescript/typescript-win32-x64": "7.0.2" + } + }, "node_modules/ua-parser-js": { "version": "1.0.38", "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.38.tgz", @@ -23286,9 +23685,10 @@ "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==" }, "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "license": "MIT" }, "node_modules/unherit": { "version": "1.1.3", @@ -23842,11 +24242,6 @@ "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/web-vitals": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/web-vitals/-/web-vitals-1.1.2.tgz", - "integrity": "sha512-PFMKIY+bRSXlMxVAQ+m2aw9c/ioUYfDgrYot0YUa+/xa0sakubWhSDyxAKwzymvXVdF4CZI71g06W+mqhzu6ig==" - }, "node_modules/webdav-server": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/webdav-server/-/webdav-server-2.6.2.tgz", @@ -23862,9 +24257,13 @@ } }, "node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-8.0.1.tgz", + "integrity": "sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=20" + } }, "node_modules/webpack": { "version": "5.98.0", @@ -23985,12 +24384,17 @@ } }, "node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "version": "17.1.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-17.1.0.tgz", + "integrity": "sha512-3GeworPmc2ZfEEHP7lEbUfBX/L75wdEsi0rLNhXcXxnoN5jyq0SL5gCy06SGW2cyTIZdTvWIDQNQoza++vKeaw==", + "license": "MIT", "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" + "@exodus/bytes": "^1.15.1", + "tr46": "^6.0.0", + "webidl-conversions": "^8.0.1" + }, + "engines": { + "node": "^22.14.0 || >=24.0.0" } }, "node_modules/which": { @@ -24219,6 +24623,12 @@ "node": ">=8" } }, + "node_modules/xml": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/xml/-/xml-1.0.1.tgz", + "integrity": "sha512-huCv9IH9Tcf95zuYCsQraZtWnJvBtLVE0QHMOs8bWyZAFZNDcYjsPq1nEx8jKA9y+Beo9v+7OBPRisQTjinQMw==", + "license": "MIT" + }, "node_modules/xml-js": { "version": "1.6.11", "resolved": "https://registry.npmjs.org/xml-js/-/xml-js-1.6.11.tgz", diff --git a/package.json b/package.json index b73c30c..0e40df8 100644 --- a/package.json +++ b/package.json @@ -14,16 +14,18 @@ "serve": "gatsby serve", "clean": "gatsby clean", "lint": "npx prettier --check \"src/**/*.+(ts|tsx|js|jsx|json|css|scss)\"", - "lint:fix": "npx prettier --write \"src/**/*.+(ts|tsx|js|jsx|json|css|scss)\" --ignore-path .gitignore" + "lint:fix": "npx prettier --write \"src/**/*.+(ts|tsx|js|jsx|json|css|scss)\" --ignore-path .gitignore", + "typecheck": "tsc --noEmit" }, "dependencies": { "@browniebroke/gatsby-image-gallery": "^8.2.0", "@mdx-js/mdx": "^3.1.1", "@mdx-js/react": "^3.1.1", "babel-plugin-styled-components": "^2.1.4", + "baseline-browser-mapping": "^2.10.43", + "caniuse-lite": "^1.0.30001806", "gatsby": "^5.15.0", - "gatsby-plugin-google-analytics": "^5.13.1", - "gatsby-plugin-google-fonts": "^1.0.1", + "gatsby-plugin-feed": "^5.16.0", "gatsby-plugin-image": "^3.15.0", "gatsby-plugin-local-search": "^2.0.1", "gatsby-plugin-manifest": "^5.15.0", @@ -50,8 +52,30 @@ }, "devDependencies": { "@popperjs/core": "^2.9.2", + "@types/lodash": "^4.17.24", + "@types/node": "^20.19.43", + "@types/react": "^18.3.31", + "@types/react-dom": "^18.3.7", + "@types/react-helmet": "^6.1.11", "bootstrap": "^5.0.0-beta3", "cloudron-surfer": "^6.4.1", - "jquery": "^3.6.0" + "jquery": "^3.6.0", + "typescript": "^7.0.2" + }, + "allowScripts": { + "lmdb@2.5.2": true, + "lmdb@2.5.3": true, + "msgpackr@1.10.2": true, + "@parcel/watcher@2.5.1": true + }, + "overrides": { + "msgpackr": "^1.12.1", + "lmdb": "^2.9.4", + "ordered-binary": "^1.6.1", + "whatwg-url": "^17.1.0" + }, + "engines": { + "node": ">=24.0.0", + "npm": ">=10.0.0" } } diff --git a/src/components/footer/index.tsx b/src/components/footer/index.tsx index 79b0dfb..6bfb4a4 100644 --- a/src/components/footer/index.tsx +++ b/src/components/footer/index.tsx @@ -1,7 +1,11 @@ import React from 'react' +import { FaRss } from 'react-icons/fa' import { Section } from '../shared' import { SolidButton } from '../shared/styledComponents' import { StyledHeading, StyledDesc, InputStyled, StyledAnchor } from './style' +import { siteMetadata } from '../../../gatsby-config' + +const rssFeedUrl = `${siteMetadata.siteUrl}rss.xml` const anchorStyles = { color: '#888' @@ -13,42 +17,39 @@ const Footer = () => (
Data Controller - Data Controller is the product of a UK company with a singular focus on{' '} - - SAS Web Apps - . + Data Controller is the product of a UK company with a singular focus + on SAS Web Apps + .
Source Code - All our source code can be found on our self-hosted{' '} - + Data Controller source is on our self-hosted{' '} + Gitea Repository - . + ; the underlying SASjs framework is on{' '} + GitHub.
Other Resources - Leverage our underlying tech stack on{' '} - - Github + Connect on{' '} + + LinkedIn + + , read the{' '} + + docs + + , or subscribe to the{' '} + + + RSS feed {' '} - and build your own SAS Powered Web Apps. + for updates.
diff --git a/src/components/footer/style.js b/src/components/footer/style.tsx similarity index 79% rename from src/components/footer/style.js rename to src/components/footer/style.tsx index 36c76ba..581af3f 100644 --- a/src/components/footer/style.js +++ b/src/components/footer/style.tsx @@ -1,6 +1,8 @@ -import React from 'react' +import * as React from 'react' import styled from 'styled-components' +import type { StyledAnchorProps } from '../../types' + export const StyledHeading = styled.h6` margin-bottom: 0.8rem; text-transform: uppercase; @@ -33,7 +35,10 @@ const Anchor = styled.a` } ` -export const StyledAnchor = ({ children, href }) => ( +export const StyledAnchor: React.FC = ({ + children, + href +}) => ( {children} diff --git a/src/components/herosection/index.tsx b/src/components/herosection/index.tsx index addbab0..0bef502 100644 --- a/src/components/herosection/index.tsx +++ b/src/components/herosection/index.tsx @@ -1,21 +1,15 @@ import React from 'react' -import { PageProps, Link } from 'gatsby' - -import styled from 'styled-components' +import { Link } from 'gatsby' import { Hero, HeroHeading, HeroDesc } from './style' import { BottomSectionArrow, OutlineButton } from '../shared/styledComponents' import { Container } from '../shared' -import { pathPrefix } from '../../../gatsby-config.js' +import { pathPrefix } from '../../../gatsby-config' -type DataProps = { - location: Location - heading: string - desc: string -} +import type { HeroSectionProps } from '../../types' -const HeroSection: React.FC> = ({ +const HeroSection: React.FC = ({ location, heading, desc diff --git a/src/components/herosection/style.js b/src/components/herosection/style.tsx similarity index 78% rename from src/components/herosection/style.js rename to src/components/herosection/style.tsx index 033e255..203f679 100644 --- a/src/components/herosection/style.js +++ b/src/components/herosection/style.tsx @@ -1,7 +1,7 @@ import styled from 'styled-components' import background from '../../images/home_hero_bg.png' -export const Hero = styled.main` +export const Hero = styled.main<{ bg?: boolean }>` position: relative; padding: 50px 0; color: white; @@ -13,7 +13,8 @@ export const Hero = styled.main` ` export const HeroHeading = styled.h1` - font-family: 'Montserrat', 'HelveticaNeue', 'Helvetica Neue', Helvetica, Arial, + font-family: + 'Montserrat', 'HelveticaNeue', 'Helvetica Neue', Helvetica, Arial, sans-serif; text-transform: uppercase; ` diff --git a/src/components/layout.tsx b/src/components/layout.tsx index 3f9f3fc..6944a0c 100644 --- a/src/components/layout.tsx +++ b/src/components/layout.tsx @@ -1,16 +1,12 @@ import React, { useEffect } from 'react' -import { PageProps } from 'gatsby' import Navibar from './navibar' import HeroSection from './herosection' import Footer from './footer' -type DataProps = { - children?: React.ReactNode - heroSection: boolean -} +import type { LayoutProps } from '../types' -const Layout: React.FC> = ({ +const Layout: React.FC = ({ location, children, heroSection = true, diff --git a/src/components/navibar/index.tsx b/src/components/navibar/index.tsx index 21c4a67..d99fcae 100644 --- a/src/components/navibar/index.tsx +++ b/src/components/navibar/index.tsx @@ -1,49 +1,34 @@ import React from 'react' -import { Link, PageProps } from 'gatsby' +import { Link } from 'gatsby' import dcLogo from '../../images/dclogo.png' import { Container } from '../shared' import { logoStyles, CustomNavBar, ulStyles, Li, StyledLink } from './style' -import { pathPrefix } from '../../../gatsby-config.js' +import { pathPrefix } from '../../../gatsby-config' -const naviLinks = [ - { - name: 'Home', - url: '/', - active: 'no' - }, - { - name: 'About', - url: '/about/', - active: 'no' - }, - { - name: 'Blog', - url: '/blog/', - active: 'no' - }, - { - name: 'FAQ', - url: '/faq/', - active: 'no' - }, +import type { NavibarProps } from '../../types' + +interface NaviLink { + name: string + url: string + active: 'yes' | 'no' +} + +const naviLinks: NaviLink[] = [ + { name: 'Home', url: '/', active: 'no' }, + { name: 'About', url: '/about/', active: 'no' }, + { name: 'Blog', url: '/blog/', active: 'no' }, + { name: 'Feed', url: '/feed/', active: 'no' }, + { name: 'FAQ', url: '/faq/', active: 'no' }, { name: 'Documentation', url: 'https://docs.datacontroller.io/', active: 'no' }, - { - name: 'Pricing', - url: '/pricing/', - active: 'no' - }, - { - name: 'Book Demo', - url: '/contact/', - active: 'no' - }, + { name: 'Pricing', url: '/pricing/', active: 'no' }, + { name: 'Book Demo', url: '/contact/', active: 'no' }, { name: 'Source Code', url: 'https://git.datacontroller.io/dc/dc', @@ -51,11 +36,7 @@ const naviLinks = [ } ] -type DataProps = { - location: Location -} - -const Navibar: React.FC> = ({ location }) => { +const Navibar: React.FC = ({ location }) => { naviLinks.forEach((link) => (link.active = 'no')) const currentLink = naviLinks.find( (link) => pathPrefix + link.url === location?.pathname diff --git a/src/components/navibar/style.js b/src/components/navibar/style.tsx similarity index 97% rename from src/components/navibar/style.js rename to src/components/navibar/style.tsx index 76ef0fb..617dba3 100644 --- a/src/components/navibar/style.js +++ b/src/components/navibar/style.tsx @@ -1,8 +1,7 @@ -import React from 'react' +import * as React from 'react' import styled, { css } from 'styled-components' import { Link } from 'gatsby' -// styles export const logoStyles = { height: '55px' } @@ -295,7 +294,6 @@ const LinkUnderlineStyles = css` transition: opacity 0.3s ease; ` -// styled components export const Li = styled.li` position: relative; @media (min-width: 992px) { @@ -315,7 +313,14 @@ export const Li = styled.li` } } ` -export const StyledLink = styled((props) => )` + +interface StyledLinkProps { + active?: string +} + +export const StyledLink = styled(({ active, ...rest }: any) => ( + +))` padding-right: 0.8rem !important; padding-left: 0.8rem !important; color: white !important; diff --git a/src/components/seo.tsx b/src/components/seo.tsx index ae40469..4dd6721 100644 --- a/src/components/seo.tsx +++ b/src/components/seo.tsx @@ -1,9 +1,16 @@ import * as React from 'react' -import PropTypes from 'prop-types' import { Helmet } from 'react-helmet' import { useStaticQuery, graphql } from 'gatsby' -const Seo = ({ description, lang, meta, title, previewImg = undefined }) => { +import type { SeoProps } from '../types' + +const Seo: React.FC = ({ + description = '', + lang = 'en', + meta = [], + title = '', + previewImg +}) => { const { site } = useStaticQuery(graphql` query { site { @@ -37,44 +44,36 @@ const Seo = ({ description, lang, meta, title, previewImg = undefined }) => { lang }} title={pageTitle} - meta={[ - { name: 'author', property: 'author', content: author }, + link={[ { - name: 'description', - property: 'og:description', - content: metaDescription - }, - // { name: 'facebook:site', content: '', }, - { name: 'image', property: 'og:image', content: image }, - { - name: `linkedin:site`, - content: site.siteMetadata?.social?.linkedin || `` - }, - { name: `twitter:card`, content: `summary` }, - // { name: `twitter:creator`, content: site.siteMetadata?.social?.twitter || `` }, - { name: `twitter:description`, content: metaDescription }, - // { name: 'twitter:site', content: `${site?.twitter}`, }, - { name: `twitter:title`, content: title }, - // { name: 'youtube:site', content: `${site?.youtube}`, }, - { property: `og:title`, content: title }, - { property: `og:type`, content: `website` } - ].concat(meta)} + rel: 'alternate', + type: 'application/rss+xml', + title: `${defaultTitle} RSS Feed`, + href: `${siteUrl}rss.xml` + } + ]} + meta={( + [ + { name: 'author', property: 'author', content: author }, + { + name: 'description', + property: 'og:description', + content: metaDescription + }, + { name: 'image', property: 'og:image', content: image }, + { + name: `linkedin:site`, + content: site.siteMetadata?.social?.linkedin || `` + }, + { name: `twitter:card`, content: `summary` }, + { name: `twitter:description`, content: metaDescription }, + { name: `twitter:title`, content: title }, + { property: `og:title`, content: title }, + { property: `og:type`, content: `website` } + ] as Array<{ name?: string; property?: string; content?: string }> + ).concat(meta)} /> ) } -Seo.defaultProps = { - lang: `en`, - meta: [], - description: ``, - title: `` -} - -Seo.propTypes = { - description: PropTypes.string, - lang: PropTypes.string, - meta: PropTypes.arrayOf(PropTypes.object), - title: PropTypes.string -} - export default Seo diff --git a/src/components/shared/container.tsx b/src/components/shared/container.tsx index 61f98c7..d209e68 100644 --- a/src/components/shared/container.tsx +++ b/src/components/shared/container.tsx @@ -1,10 +1,7 @@ -import React from 'react' +import * as React from 'react' import styled from 'styled-components' -import { PageProps } from 'gatsby' -type DataProps = { - children?: React.ReactNode -} +import type { ContainerProps } from '../../types' const StyledDiv = styled.div` @media (min-width: 576px) { @@ -13,6 +10,6 @@ const StyledDiv = styled.div` } ` -export const Container: React.FC> = ({ children }) => { +export const Container: React.FC = ({ children }) => { return {children} } diff --git a/src/components/shared/scheduleDemo.tsx b/src/components/shared/scheduleDemo.tsx index 06f43f3..fa7f9ff 100644 --- a/src/components/shared/scheduleDemo.tsx +++ b/src/components/shared/scheduleDemo.tsx @@ -4,7 +4,7 @@ import styled from 'styled-components' import { FaEnvelope } from 'react-icons/fa' -export const StyledLink = styled((props) => )` +export const StyledLink = styled(Link)` color: rgb(255, 255, 255); background-color: rgb(144, 196, 69); border-radius: 0px; diff --git a/src/components/shared/section.tsx b/src/components/shared/section.tsx index 9284755..0da0919 100644 --- a/src/components/shared/section.tsx +++ b/src/components/shared/section.tsx @@ -1,25 +1,19 @@ -import React from 'react' +import * as React from 'react' import styled from 'styled-components' -import { PageProps } from 'gatsby' import { BottomSectionArrow } from './styledComponents' import { Container } from './' -type DataProps = { - children?: React.ReactNode - color?: string - bgColor?: string - bottomArrow?: boolean -} +import type { SectionProps } from '../../types' -const StyledSection = styled.div` +const StyledSection = styled.div<{ color?: string; bgColor?: string }>` position: relative; padding: 50px 0; color: ${(props) => props.color || 'white'}; background-color: ${(props) => props.bgColor || '#314351'}; ` -export const Section: React.FC> = ({ +export const Section: React.FC = ({ children, bgColor, color, diff --git a/src/components/shared/styledComponents.js b/src/components/shared/styledComponents.tsx similarity index 76% rename from src/components/shared/styledComponents.js rename to src/components/shared/styledComponents.tsx index cb2adc4..4b78e3a 100644 --- a/src/components/shared/styledComponents.js +++ b/src/components/shared/styledComponents.tsx @@ -1,6 +1,8 @@ -import React from 'react' +import * as React from 'react' import styled from 'styled-components' +import type { SolidButtonProps, OutlineButtonProps } from '../../types' + const BottomArrow = styled.div` width: 50px; height: 50px; @@ -9,9 +11,6 @@ const BottomArrow = styled.div` background: inherit; transform: translateX(-50%) rotate(45deg); left: 50%; - // right: 0; - // margin-left: auto; - // margin-right: auto; z-index: 10; ` const BottomArrowWrapper = styled.div` @@ -29,16 +28,17 @@ export const BottomSectionArrow = () => ( ) -export const SectionHeading = styled.h2` +export const SectionHeading = styled.h2<{ center?: string }>` text-align: ${(props) => (props.center === 'no' ? 'left' : 'center')}; letter-spacing: 1px; font-weight: 400; - font-family: 'Montserrat', 'HelveticaNeue', 'Helvetica Neue', Helvetica, Arial, + font-family: + 'Montserrat', 'HelveticaNeue', 'Helvetica Neue', Helvetica, Arial, sans-serif; text-transform: uppercase; ` -export const SectionDesc = styled.p` +export const SectionDesc = styled.p<{ center?: string; opacity?: string }>` text-align: ${(props) => (props.center === 'no' ? 'left' : 'center')}; opacity: ${(props) => props.opacity ?? 0.6}; a { @@ -59,12 +59,12 @@ const StyledSolidButton = styled.button` } ` -export const SolidButton = ({ +export const SolidButton: React.FC = ({ children, theme = 'light', type = 'submit', disabled = false, - onClick = undefined + onClick }) => ( ( +export const OutlineButton: React.FC = ({ children }) => ( {children} diff --git a/src/controllers/NostrController.ts b/src/controllers/NostrController.ts index 678adf8..41273ed 100644 --- a/src/controllers/NostrController.ts +++ b/src/controllers/NostrController.ts @@ -34,7 +34,7 @@ export class NostrController extends EventEmitter { /** * Function will publish provided event to the provided relays */ - publishEvent = async (event: Event, relays: string[]) => { + publishEvent = async (event: Event, relays: string[]): Promise => { const simplePool = new SimplePool() const promises = simplePool.publish(relays, event) @@ -47,7 +47,7 @@ export class NostrController extends EventEmitter { }) if (publishedRelays.length === 0) { - const failedPublishes: any[] = [] + const failedPublishes: Array<{ relay: string; error: string }> = [] const fallbackRejectionReason = 'Attempt to publish an event has been rejected with unknown reason.' @@ -92,7 +92,7 @@ export class NostrController extends EventEmitter { return Promise.resolve(signedEvent) } - nip04Encrypt = async (receiver: string, content: string) => { + nip04Encrypt = async (receiver: string, content: string): Promise => { if (!this.generatedKeys) { throw new Error(`Private & public key pair is not found.`) } @@ -113,7 +113,7 @@ export class NostrController extends EventEmitter { * @param isSigner Boolean indicating whether the recipient is a signer or viewer. * @param setAuthUrl Function to set the authentication URL in the component state. */ - sendDM = async (pubkey: string, message: string) => { + sendDM = async (pubkey: string, message: string): Promise => { // Set up timeout promise to handle encryption timeout const timeoutPromise = new Promise((_, reject) => { setTimeout(() => { @@ -159,7 +159,7 @@ export class NostrController extends EventEmitter { * @param hexKey hex private or public key * @returns whether or not is key valid */ - validateHex = (hexKey: string) => { + validateHex = (hexKey: string): RegExpMatchArray | null => { return hexKey.match(/^[a-f0-9]{64}$/) } @@ -193,7 +193,7 @@ export class NostrController extends EventEmitter { return { private: nip19.nsecEncode(nsec), public: getPublicKey(nsec) } } - verifySignedEvent = (event: SignedEvent) => { + verifySignedEvent = (event: SignedEvent): void => { const isGood = verifyEvent(event) if (!isGood) { diff --git a/src/pages/404.tsx b/src/pages/404.tsx index 95c1ffb..1b37f53 100644 --- a/src/pages/404.tsx +++ b/src/pages/404.tsx @@ -1,5 +1,4 @@ -import React from 'react' -// import { Link } from 'gatsby' +import * as React from 'react' import Layout from '../components/layout' import Seo from '../components/seo' @@ -7,7 +6,13 @@ import Seo from '../components/seo' import { Section } from '../components/shared' import { SideBar } from '../templates/sidebar' -const NotFound = ({ data, location, pageContext }) => ( +import type { PageProps } from 'gatsby' +import type { PageContext } from '../types' + +const NotFound: React.FC> = ({ + location, + pageContext +}) => (
diff --git a/src/pages/about.tsx b/src/pages/about.tsx index a377a74..99d0769 100644 --- a/src/pages/about.tsx +++ b/src/pages/about.tsx @@ -1,5 +1,5 @@ -import { PageProps, Link, graphql } from 'gatsby' -import React from 'react' +import { PageProps, Link } from 'gatsby' +import * as React from 'react' import Layout from '../components/layout' import Seo from '../components/seo' @@ -10,17 +10,7 @@ import { SectionDesc } from '../components/shared/styledComponents' -type DataProps = { - site: { - meta: { - title: string - description: string - social: { linkedin: string } - } - } -} - -const About: React.FC> = ({ data, location }) => { +const About: React.FC> = ({ location }) => { return ( > = ({ data, location }) => { +const Contact: React.FC> = ({ location }) => { const nostrController = NostrController.getInstance() - const [name, setName] = useState() - const [email, setEmail] = useState() - const [emailError, setEmailError] = useState(false) - const [subject, setSubject] = useState() - const [message, setMessage] = useState() - const [notification, setNotification] = useState() + const [name, setName] = React.useState() + const [email, setEmail] = React.useState() + const [emailError, setEmailError] = React.useState(false) + const [subject, setSubject] = React.useState() + const [message, setMessage] = React.useState() + const [notification, setNotification] = React.useState() const handleEmailChange = (e: React.ChangeEvent) => { const value = e.target.value @@ -98,7 +88,7 @@ Email: ${email} Subject: ${subject} Message: ${message}` ) - .catch((err) => { + .catch((err: unknown) => { setNotification( `Something went wrong. Please check the console for more information. Please try one more time.` ) @@ -176,7 +166,7 @@ Message: ${message}` className="form-control contactFormStyles" id="mesage" name="message" - rows="5" + rows={5} rules="required|max:200" onChange={(evt) => { setMessage(evt.target.value) diff --git a/src/pages/faq.tsx b/src/pages/faq.tsx index c289d1f..a3116a7 100644 --- a/src/pages/faq.tsx +++ b/src/pages/faq.tsx @@ -1,5 +1,5 @@ -import { PageProps, Link, graphql } from 'gatsby' -import React from 'react' +import { PageProps, Link } from 'gatsby' +import * as React from 'react' import Layout from '../components/layout' import Seo from '../components/seo' @@ -13,17 +13,7 @@ import { import { FAQ } from '../styledComponents/faq' -type DataProps = { - site: { - meta: { - title: string - description: string - social: { linkedin: string } - } - } -} - -const Faq: React.FC> = ({ data, location }) => { +const Faq: React.FC> = ({ location }) => { return ( = ({ data, location }) => { +const CustomWrapper: React.FC<{ + children?: React.ReactNode + onClick?: () => void +}> = ({ children, onClick }) => ( + {children} +) + +const Home: React.FC> = ({ data, location }) => { const images = data.images.edges.map(({ node }) => ({ ...node.childImageSharp, - // Use original name as caption. - // The `originalName` is queried in a nested field, - // but the `Gallery` component expects `caption` at the top level. - caption: node.childImageSharp.meta.originalName + caption: node.name })) return ( @@ -231,13 +231,7 @@ const Home: React.FC = ({ data, location }) => {
See How it Looks - ( - {children} - )} - /> +
@@ -254,6 +248,7 @@ export const pageQuery = graphql` ) { edges { node { + name childImageSharp { thumb: gatsbyImageData( width: 300 @@ -261,9 +256,6 @@ export const pageQuery = graphql` placeholder: BLURRED ) full: gatsbyImageData(layout: FULL_WIDTH) - meta: fixed { - originalName - } } } } diff --git a/src/pages/{MarkdownRemark.frontmatter__slug}.tsx b/src/pages/{MarkdownRemark.frontmatter__slug}.tsx index 842edb0..bc82aa0 100644 --- a/src/pages/{MarkdownRemark.frontmatter__slug}.tsx +++ b/src/pages/{MarkdownRemark.frontmatter__slug}.tsx @@ -1,5 +1,5 @@ -import React from 'react' -import { graphql } from 'gatsby' +import * as React from 'react' +import { graphql, PageProps } from 'gatsby' import styled from 'styled-components' import Layout from '../components/layout' @@ -7,6 +7,8 @@ import Seo from '../components/seo' import { Section } from '../components/shared' +import type { PostNode, PageContext } from '../types' + const StyledMarkdown = styled.div` color: rgb(102, 102, 102); h2, @@ -18,13 +20,20 @@ const StyledMarkdown = styled.div` h2 { text-transform: uppercase; letter-spacing: 1px; - font-family: 'Montserrat', 'HelveticaNeue', 'Helvetica Neue', Helvetica, - Arial, sans-serif; + font-family: + 'Montserrat', 'HelveticaNeue', 'Helvetica Neue', Helvetica, Arial, + sans-serif; } ` -export default function Template({ data, location }) { - const { frontmatter, html } = data?.markdownRemark +interface MarkdownPageData { + markdownRemark: PostNode +} + +const MarkdownPageTemplate: React.FC< + PageProps +> = ({ data, location }) => { + const { frontmatter, html } = data.markdownRemark return ( ({ - alt: props.info || '', - title: props.info || '' -}))` - max-width: 100%; -` diff --git a/src/styledComponents/contact.tsx b/src/styledComponents/contact.tsx new file mode 100644 index 0000000..c469f75 --- /dev/null +++ b/src/styledComponents/contact.tsx @@ -0,0 +1,20 @@ +import styled from 'styled-components' + +export const StyledHeading = styled.h3` + font-family: + 'Montserrat', 'HelveticaNeue', 'Helvetica Neue', Helvetica, Arial, + sans-serif; +` + +export const StyledLabel = styled.label` + margin-bottom: 0; +` + +export const ContactBackground = styled.img.attrs<{ info?: string }>( + (props) => ({ + alt: props.info || '', + title: props.info || '' + }) +)` + max-width: 100%; +` diff --git a/src/styledComponents/faq.js b/src/styledComponents/faq.tsx similarity index 78% rename from src/styledComponents/faq.js rename to src/styledComponents/faq.tsx index 6dc879c..0ad8743 100644 --- a/src/styledComponents/faq.js +++ b/src/styledComponents/faq.tsx @@ -1,15 +1,18 @@ -import React from 'react' +import * as React from 'react' import styled from 'styled-components' import { FcFaq } from 'react-icons/fc' +import type { FaqProps } from '../types' + const StyledFaq = styled.div` margin-bottom: 20px; ` const StyledQuestion = styled.h5` color: #222; - font-family: 'Montserrat', 'HelveticaNeue', 'Helvetica Neue', Helvetica, Arial, + font-family: + 'Montserrat', 'HelveticaNeue', 'Helvetica Neue', Helvetica, Arial, sans-serif; text-transform: uppercase; ` @@ -33,7 +36,7 @@ const FaqDetails = styled.div` margin-bottom: 20px; ` -export const FAQ = ({ question, answer }) => ( +export const FAQ: React.FC = ({ question, answer }) => ( diff --git a/src/styledComponents/index.js b/src/styledComponents/index.tsx similarity index 73% rename from src/styledComponents/index.js rename to src/styledComponents/index.tsx index df47f3c..ca1fcd1 100644 --- a/src/styledComponents/index.js +++ b/src/styledComponents/index.tsx @@ -1,6 +1,9 @@ -import React from 'react' +import * as React from 'react' import styled from 'styled-components' import { ImArrowRight } from 'react-icons/im' +import type { IconType } from 'react-icons' + +import type { ArtProps, ReasonProps, FeatureProps } from '../types' export const CenteredText = styled.p` text-align: center; @@ -9,20 +12,19 @@ export const CenteredText = styled.p` margin: 0 auto; font-size: 1.2rem; line-height: 1; - font-family: 'Montserrat', 'HelveticaNeue', 'Helvetica Neue', Helvetica, Arial, + font-family: + 'Montserrat', 'HelveticaNeue', 'Helvetica Neue', Helvetica, Arial, sans-serif; ` -export const Art = styled.img.attrs((props) => ({ +export const Art = styled.img.attrs((props) => ({ alt: props.info || '', title: props.info || '' }))` max-width: 100%; - display: block; - margin: 0 auto; - padding: 20px; + height: auto; ` -const ArrowHolder = styled.div` +const ArrowHolder = styled.div<{ bgColor?: string }>` display: inline-block; border-radius: 50px; padding: 15px 17px; @@ -30,7 +32,7 @@ const ArrowHolder = styled.div` background-color: ${(props) => props.bgColor === 'red' ? '#ed1c1c' : '#90c445'}; ` -const RightArrow = ({ bgColor = '' }) => ( +const RightArrow: React.FC<{ bgColor?: string }> = ({ bgColor = '' }) => ( @@ -46,7 +48,7 @@ const ReasonDesc = styled.p` text-transform: uppercase; ` -export const Reason = ({ text, bgColor = '' }) => ( +export const Reason: React.FC = ({ text, bgColor = '' }) => ( <> {text} @@ -62,7 +64,7 @@ const IconHolderStyled = styled.div` vertical-align: top; ` -const IconHolder = ({ Icon }) => { +const IconHolder = ({ Icon }: { Icon: IconType }) => { return ( @@ -77,7 +79,8 @@ const FeatureDetails = styled.div` margin-bottom: 20px; ` const FeatureTitle = styled.h5` - font-family: 'Montserrat', 'HelveticaNeue', 'Helvetica Neue', Helvetica, Arial, + font-family: + 'Montserrat', 'HelveticaNeue', 'Helvetica Neue', Helvetica, Arial, sans-serif; text-transform: uppercase; ` @@ -86,7 +89,12 @@ const FeatureDesc = styled.p` font-size: 0.9rem; ` -export const Feature = ({ title, desc, Icon, className }) => ( +export const Feature: React.FC = ({ + title, + desc, + Icon, + className +}) => (
diff --git a/src/styles/montserrat.css b/src/styles/montserrat.css new file mode 100644 index 0000000..1829bc3 --- /dev/null +++ b/src/styles/montserrat.css @@ -0,0 +1,35 @@ +@font-face { + font-family: 'Montserrat'; + font-style: normal; + font-weight: 300; + font-display: swap; + src: url('/fonts/Montserrat-300.woff2') format('woff2'); + unicode-range: + U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, + U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, + U+2212, U+2215, U+FEFF, U+FFFD; +} + +@font-face { + font-family: 'Montserrat'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url('/fonts/Montserrat-400.woff2') format('woff2'); + unicode-range: + U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, + U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, + U+2212, U+2215, U+FEFF, U+FFFD; +} + +@font-face { + font-family: 'Montserrat'; + font-style: normal; + font-weight: 500; + font-display: swap; + src: url('/fonts/Montserrat-500.woff2') format('woff2'); + unicode-range: + U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, + U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, + U+2212, U+2215, U+FEFF, U+FFFD; +} diff --git a/src/templates/blog-list.tsx b/src/templates/blog-list.tsx index 1a3b506..d577fb7 100644 --- a/src/templates/blog-list.tsx +++ b/src/templates/blog-list.tsx @@ -1,5 +1,5 @@ -import { Link, graphql } from 'gatsby' -import React from 'react' +import { Link, graphql, PageProps } from 'gatsby' +import * as React from 'react' import kebabCase from 'lodash/kebabCase' import styled from 'styled-components' @@ -16,27 +16,39 @@ import { import Post from './postpreview' import { SideBar } from './sidebar' -const BlogListTemplate = ({ data, location, pageContext }) => { +import type { PostNode, PageContext } from '../types' + +interface BlogListData { + remark: { + posts: Array<{ post: PostNode }> + } +} + +const BlogListTemplate: React.FC> = ({ + data, + location, + pageContext +}) => { const posts = data.remark.posts - const iniPath = - pageContext.page == 'index' + const iniPath: string = + pageContext.page === 'index' ? `/blog/` - : pageContext.page == 'year' - ? `/${pageContext.year}/` - : pageContext.page == 'category' - ? `/category/${kebabCase(pageContext.tag)}/` - : null + : pageContext.page === 'year' + ? `/${pageContext.year}/` + : pageContext.page === 'category' + ? `/category/${kebabCase(pageContext.tag ?? '')}/` + : `/blog/` const pageInfo = `Page ${pageContext.currentPage} of ${pageContext.numPages}` - Array.from({ length: 5 }, (v, k) => k + 1) const paginationJSX = Array.from( - { length: pageContext.numPages }, + { length: pageContext.numPages ?? 0 }, (_, i) => i + 1 ).map((pageIndex) => { const link = pageIndex === 1 ? iniPath : `${iniPath}page/${pageIndex}` return ( {
- {posts.map((data, i) => ( - + {posts.map(({ post }, i) => ( + ))}
{paginationJSX} @@ -114,5 +126,3 @@ export const pageQuery = graphql` } } ` - -// tags diff --git a/src/templates/blog-post.tsx b/src/templates/blog-post.tsx index a20db6c..cdf336d 100644 --- a/src/templates/blog-post.tsx +++ b/src/templates/blog-post.tsx @@ -1,5 +1,5 @@ -import React from 'react' -import { Link, graphql } from 'gatsby' +import * as React from 'react' +import { graphql, PageProps } from 'gatsby' import Layout from '../components/layout' import Seo from '../components/seo' @@ -8,15 +8,19 @@ import { Section } from '../components/shared' import Post from './post' import { SideBar } from './sidebar' -const BlogPostTemplate = ({ data, location, pageContext }) => { +import type { PostNode, PageContext } from '../types' + +const BlogPostTemplate: React.FC< + PageProps<{ post: PostNode }, PageContext> +> = ({ data, location, pageContext }) => { const { post } = data const { previewImg } = post.frontmatter return ( { +import type { PageContext, PostNode, SearchPost } from '../types' + +interface SearchData { + localSearchBlog: { + index: string + store: Record + } +} + +const BlogSearchTemplate: React.FC> = ({ + location, + pageContext +}) => { const queryData = useStaticQuery(graphql` query { localSearchBlog { @@ -34,12 +44,12 @@ const BlogSearchTemplate = ({ location, pageContext }) => { query, queryData.localSearchBlog.index, queryData.localSearchBlog.store - ) - const postsJSX = [] + ) as SearchPost[] + const postsJSX: React.ReactElement[] = [] if (query) { posts.forEach((post, i) => { - const _post = { + const _post: PostNode = { id: post.id, html: post.html, fields: { @@ -48,7 +58,7 @@ const BlogSearchTemplate = ({ location, pageContext }) => { frontmatter: { title: post.title, date: post.date, - previewImg: post.previewImg + previewImg: post.previewImg as PostNode['frontmatter']['previewImg'] } } diff --git a/src/templates/feed-list.tsx b/src/templates/feed-list.tsx new file mode 100644 index 0000000..4eb2550 --- /dev/null +++ b/src/templates/feed-list.tsx @@ -0,0 +1,131 @@ +import { Link, graphql, PageProps } from 'gatsby' +import * as React from 'react' +import kebabCase from 'lodash/kebabCase' + +import Layout from '../components/layout' +import Seo from '../components/seo' + +import { Section } from '../components/shared' +import { + SectionHeading, + SectionDesc, + SolidButton +} from '../components/shared/styledComponents' + +import Post from './postpreview' +import { SideBar } from './sidebar' + +import type { PostNode, PageContext } from '../types' + +interface FeedListData { + remark: { + posts: Array<{ post: PostNode }> + } +} + +const FeedListTemplate: React.FC> = ({ + data, + location, + pageContext +}) => { + const posts = data.remark.posts + + const iniPath: string = + pageContext.page === 'index' + ? `/feed/` + : pageContext.page === 'year' + ? `/feed/${pageContext.year}/` + : pageContext.page === 'category' + ? `/feed/category/${kebabCase(pageContext.tag ?? '')}/` + : `/feed/` + + const pageInfo = `Page ${pageContext.currentPage} of ${pageContext.numPages}` + const paginationJSX = Array.from( + { length: pageContext.numPages ?? 0 }, + (_, i) => i + 1 + ).map((pageIndex) => { + const link = pageIndex === 1 ? iniPath : `${iniPath}page/${pageIndex}` + return ( + + {pageIndex} + + ) + }) + return ( + + +
+
+
+
+ {posts.map(({ post }, i) => ( + + ))} +
+ {paginationJSX} + {pageInfo} +
+
+ +
+
+
+
+ ) +} + +export default FeedListTemplate + +export const pageQuery = graphql` + query FeedListQuery( + $filter: MarkdownRemarkFilterInput! + $skip: Int! + $limit: Int! + ) { + remark: allMarkdownRemark( + filter: $filter + limit: $limit + skip: $skip + sort: { frontmatter: { date: DESC } } + ) { + posts: edges { + post: node { + html + fields { + slug + } + frontmatter { + title + date(formatString: "MMMM DD, YYYY") + author + authorLink + previewImg { + childImageSharp { + gatsbyImageData(layout: CONSTRAINED) + } + } + } + } + } + } + } +` diff --git a/src/templates/feed-post.tsx b/src/templates/feed-post.tsx new file mode 100644 index 0000000..cfc414b --- /dev/null +++ b/src/templates/feed-post.tsx @@ -0,0 +1,71 @@ +import * as React from 'react' +import { graphql, PageProps } from 'gatsby' + +import Layout from '../components/layout' +import Seo from '../components/seo' + +import { Section } from '../components/shared' +import Post from './post' +import { SideBar } from './sidebar' + +import type { PostNode, PageContext } from '../types' + +const FeedPostTemplate: React.FC< + PageProps<{ post: PostNode }, PageContext> +> = ({ data, location, pageContext }) => { + const { post } = data + const { previewImg } = post.frontmatter + + return ( + + +
+
+
+ +
+
+ +
+
+
+
+ ) +} + +export default FeedPostTemplate + +export const pageQuery = graphql` + query FeedPostByPath($id: String!) { + post: markdownRemark(id: { eq: $id }) { + id + html + fields { + slug + } + frontmatter { + title + description + date(formatString: "MMMM DD, YYYY") + author + authorLink + previewImg { + childImageSharp { + gatsbyImageData(layout: CONSTRAINED) + } + } + tags + } + } + } +` diff --git a/src/templates/post/index.tsx b/src/templates/post/index.tsx index 7e5d7b9..aac7291 100644 --- a/src/templates/post/index.tsx +++ b/src/templates/post/index.tsx @@ -1,12 +1,14 @@ -import React from 'react' +import * as React from 'react' import { Link } from 'gatsby' import kebabCase from 'lodash/kebabCase' -import { GatsbyImage } from 'gatsby-plugin-image' +import { GatsbyImage, IGatsbyImageData } from 'gatsby-plugin-image' import styled from 'styled-components' -export const StyledLink = styled((props) => )` +import type { PostNode } from '../../types' + +export const StyledLink = styled(Link)` text-decoration: none; color: black; &:hover { @@ -16,7 +18,8 @@ export const StyledLink = styled((props) => )` ` const StyledTitle = styled.h5` - font-family: 'Montserrat', 'HelveticaNeue', 'Helvetica Neue', Helvetica, Arial, + font-family: + 'Montserrat', 'HelveticaNeue', 'Helvetica Neue', Helvetica, Arial, sans-serif; margin-bottom: 0; ` @@ -45,7 +48,8 @@ const StyledContent = styled.div` padding: 15px 0; color: #666666; - font-family: 'Montserrat', 'HelveticaNeue', 'Helvetica Neue', Helvetica, Arial, + font-family: + 'Montserrat', 'HelveticaNeue', 'Helvetica Neue', Helvetica, Arial, sans-serif; a { color: black; @@ -116,7 +120,12 @@ const StyledContent = styled.div` } ` -const Post = ({ post, location }) => { +interface PostProps { + post: PostNode + location?: Location +} + +const Post: React.FC = ({ post }) => { const tagsJSX = (post.frontmatter?.tags || []).map((tag, index) => ( {index > 0 && ', '} @@ -134,14 +143,18 @@ const Post = ({ post, location }) => { ) : ( {post.frontmatter.author} ) + const previewImgData = post.frontmatter.previewImg?.childImageSharp + ?.gatsbyImageData as IGatsbyImageData | undefined return (
- + {previewImgData && ( + + )} {post.frontmatter.title} diff --git a/src/templates/postpreview/index.tsx b/src/templates/postpreview/index.tsx index e7be9a1..d0748bf 100644 --- a/src/templates/postpreview/index.tsx +++ b/src/templates/postpreview/index.tsx @@ -1,13 +1,15 @@ -import React from 'react' +import * as React from 'react' import { Link } from 'gatsby' -import { GatsbyImage } from 'gatsby-plugin-image' +import { GatsbyImage, IGatsbyImageData } from 'gatsby-plugin-image' import styled from 'styled-components' import getDescription from '../shared/getDescription' -export const StyledLink = styled((props) => )` +import type { PostNode } from '../../types' + +export const StyledLink = styled(Link)` text-decoration: none; color: black; &:hover { @@ -17,7 +19,8 @@ export const StyledLink = styled((props) => )` ` const StyledTitle = styled.h5` - font-family: 'Montserrat', 'HelveticaNeue', 'Helvetica Neue', Helvetica, Arial, + font-family: + 'Montserrat', 'HelveticaNeue', 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 1rem; margin-bottom: 0; @@ -34,21 +37,31 @@ const StyledDesc = styled.p` margin-bottom: 100px; ` -const Post = ({ post }) => ( -
- - +interface PostPreviewProps { + post: PostNode +} - {post.frontmatter.title} - {post.frontmatter.date} - {getDescription(post.html)} - -
-) +const PostPreview: React.FC = ({ post }) => { + const previewImgData = post.frontmatter.previewImg?.childImageSharp + ?.gatsbyImageData as IGatsbyImageData | undefined + return ( +
+ + {previewImgData && ( + + )} -export default Post + {post.frontmatter.title} + {post.frontmatter.date} + {getDescription(post.html)} + +
+ ) +} + +export default PostPreview diff --git a/src/templates/shared/getDescription.ts b/src/templates/shared/getDescription.ts index 9ad7784..9391c22 100644 --- a/src/templates/shared/getDescription.ts +++ b/src/templates/shared/getDescription.ts @@ -7,7 +7,7 @@ const extractContent = (s: string): string => { return s } const getDescription = (content: string): string => { - return extractContent(content).substr(0, 100) + '...' + return extractContent(content).slice(0, 100) + '...' } export default getDescription diff --git a/src/templates/sidebar.tsx b/src/templates/sidebar.tsx index b1f01cf..e64d44e 100644 --- a/src/templates/sidebar.tsx +++ b/src/templates/sidebar.tsx @@ -1,12 +1,17 @@ -import React, { useState, useEffect } from 'react' +import * as React from 'react' import { Link, navigate } from 'gatsby' import kebabCase from 'lodash/kebabCase' import styled, { css } from 'styled-components' -import { pathPrefix } from '../../gatsby-config.js' +import { FaRss } from 'react-icons/fa' +import { pathPrefix, siteMetadata } from '../../gatsby-config' import { StyledHeading } from '../styledComponents/blog' import { SolidButton } from '../components/shared/styledComponents' +import type { PageContext, RecentPost, TagCount } from '../types' + +const rssFeedUrl = `${siteMetadata.siteUrl}rss.xml` + const styles = css` color: #314351; opacity: 0.8; @@ -29,10 +34,16 @@ const linkStyles = css` text-decoration: underline; } ` -const ArchiveLink = styled((props) => )` +const ArchiveLink = styled(Link)` ${linkStyles} ` -const RecentPostLink = styled((props) => )` +const RssAnchor = styled.a` + ${linkStyles} + display: flex; + align-items: center; + gap: 6px; +` +const RecentPostLink = styled(Link)` ${linkStyles} padding: 3px; border-bottom: 3px solid #e1e1e1; @@ -59,30 +70,44 @@ const StyledPara = styled.p` ${styles} ` -const Archives = ({ archives }) => ( +interface ArchivesProps { + archives: Record + basePath?: string +} + +const Archives: React.FC = ({ archives, basePath = '' }) => ( <> {Object.keys(archives) .sort() .reverse() - .map((year, index) => ( - + .map((year) => ( + {year} ({archives[year]}) ))} ) -const Categories = ({ tags }) => ( +interface CategoriesProps { + tags: TagCount[] + basePath?: string +} + +const Categories: React.FC = ({ tags, basePath = '' }) => ( <> {tags.map((tag, i) => ( - + {tag.name} ({tag.totalCount}) ))} ) -const RecentPosts = ({ posts }) => ( +interface RecentPostsProps { + posts: RecentPost[] +} + +const RecentPosts: React.FC = ({ posts }) => ( <> {posts.map((post) => ( @@ -92,22 +117,34 @@ const RecentPosts = ({ posts }) => ( ) -export const SideBar = ({ pageContext, location, notFoundPage = false }) => { +interface SideBarProps { + pageContext: PageContext + location: Location + notFoundPage?: boolean + basePath?: string +} + +export const SideBar: React.FC = ({ + pageContext, + location, + notFoundPage = false, + basePath = '' +}) => { const params = new URLSearchParams(location.search.substring(1)) const queryUrl = params?.get('s') || '' - const [query, setQuery] = useState(queryUrl) - useEffect(() => { + const [query, setQuery] = React.useState(queryUrl) + React.useEffect(() => { setQuery(queryUrl) }, [queryUrl]) - const handleSubmit = (event) => { + const handleSubmit = (event: React.FormEvent) => { event.preventDefault() navigate('/search?s=' + query) } - const _handleKeyDown = (event) => { + const _handleKeyDown = (event: React.KeyboardEvent) => { if (event.key === 'Enter') { - handleSubmit(event) + navigate('/search?s=' + query) } } @@ -130,13 +167,16 @@ export const SideBar = ({ pageContext, location, notFoundPage = false }) => { type="text" value={query} onChange={(e) => setQuery(e.target.value)} - onKeyDown={(e) => _handleKeyDown(e)} + onKeyDown={_handleKeyDown} className="form-control" placeholder="Search" aria-label="Search" aria-describedby="button-addon2" /> - + navigate('/search?s=' + query)} + > Search
@@ -168,11 +208,17 @@ export const SideBar = ({ pageContext, location, notFoundPage = false }) => { <> Archives - + Categories - + + + + Subscribe + + RSS Feed + )} diff --git a/src/types/assets.d.ts b/src/types/assets.d.ts new file mode 100644 index 0000000..8b62da4 --- /dev/null +++ b/src/types/assets.d.ts @@ -0,0 +1,25 @@ +declare module '*.png' +declare module '*.jpg' +declare module '*.jpeg' +declare module '*.svg' +declare module '*.gif' +declare module '*.webp' +declare module '*.ico' +declare module '*.css' +declare module '*.scss' + +declare module 'react-use-flexsearch' { + export function useFlexSearch( + query: string | null, + index: string, + store: Record + ): Array> +} + +declare module '@browniebroke/gatsby-image-gallery' { + import * as React from 'react' + const Gallery: React.FC< + Record & { images: Array> } + > + export default Gallery +} diff --git a/src/types/declarations.d.ts b/src/types/declarations.d.ts new file mode 100644 index 0000000..ae088f7 --- /dev/null +++ b/src/types/declarations.d.ts @@ -0,0 +1,10 @@ +import 'react' + +declare module 'react' { + interface InputHTMLAttributes extends HTMLAttributes { + rules?: string + } + interface TextareaHTMLAttributes extends HTMLAttributes { + rules?: string + } +} diff --git a/src/types/index.ts b/src/types/index.ts index e69da0f..064ecff 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -1 +1,2 @@ export * from './nostr' +export * from './site' diff --git a/src/types/site.ts b/src/types/site.ts new file mode 100644 index 0000000..a8fd41a --- /dev/null +++ b/src/types/site.ts @@ -0,0 +1,139 @@ +import { IGatsbyImageData } from 'gatsby-plugin-image' +import * as React from 'react' +import type { IconType } from 'react-icons' + +export type PageType = 'index' | 'year' | 'category' | 'search' + +export interface RecentPost { + slug: string + title: string +} + +export interface TagCount { + name: string + totalCount: number +} + +export interface PageContext { + page: PageType + archives: Record + recentPosts: RecentPost[] + tags: TagCount[] + filter?: unknown + limit?: number + skip?: number + numPages?: number + currentPage?: number + year?: string + tag?: string +} + +export interface PostFrontmatter { + title: string + date: string + author?: string + authorLink?: string + description?: string + tags?: string[] + heading?: string + desc?: string + previewImg?: { + childImageSharp?: { + gatsbyImageData: IGatsbyImageData + } + } +} + +export interface PostNode { + id?: string + html: string + fields: { + slug: string + } + frontmatter: PostFrontmatter +} + +export interface LayoutProps { + location: Location + heroSection?: boolean + heading?: string + desc?: string + children?: React.ReactNode +} + +export interface HeroSectionProps { + location: Location + heading?: string + desc?: string +} + +export interface NavibarProps { + location: Location +} + +export interface ContainerProps { + children?: React.ReactNode +} + +export interface SectionProps { + children?: React.ReactNode + color?: string + bgColor?: string + bottomArrow?: boolean +} + +export interface SeoProps { + description?: string + lang?: string + meta?: Array<{ name?: string; property?: string; content?: string }> + title?: string + previewImg?: string +} + +export interface SolidButtonProps { + children: React.ReactNode + theme?: 'light' | 'dark' + type?: 'button' | 'submit' | 'reset' + disabled?: boolean + onClick?: React.MouseEventHandler +} + +export interface OutlineButtonProps { + children: React.ReactNode +} + +export interface FaqProps { + question: string + answer: string +} + +export interface FeatureProps { + title: string + desc: string + Icon: IconType + className?: string +} + +export interface ReasonProps { + text: string + bgColor?: string +} + +export interface ArtProps { + src: string + info?: string +} + +export interface StyledAnchorProps { + href: string + children?: React.ReactNode +} + +export type SearchPost = { + id: string + html: string + slug: string + title: string + date: string + previewImg?: PostFrontmatter['previewImg'] +} diff --git a/static/fonts/Montserrat-300.woff2 b/static/fonts/Montserrat-300.woff2 new file mode 100644 index 0000000..a83d284 Binary files /dev/null and b/static/fonts/Montserrat-300.woff2 differ diff --git a/static/fonts/Montserrat-400.woff2 b/static/fonts/Montserrat-400.woff2 new file mode 100644 index 0000000..6fbeafa Binary files /dev/null and b/static/fonts/Montserrat-400.woff2 differ diff --git a/static/fonts/Montserrat-500.woff2 b/static/fonts/Montserrat-500.woff2 new file mode 100644 index 0000000..429a87d Binary files /dev/null and b/static/fonts/Montserrat-500.woff2 differ diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..65f1972 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,20 @@ +{ + "compilerOptions": { + "target": "ESNext", + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, + "skipLibCheck": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "strict": true, + "forceConsistentCasingInFileNames": true, + "noEmit": true, + "module": "ESNext", + "moduleResolution": "bundler", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "react-jsx" + }, + "include": ["src/**/*", "gatsby-config.d.ts"], + "exclude": ["node_modules", "public", ".cache"] +}