From e8341de5a14f10a5bdcc97962394e022d13bb859 Mon Sep 17 00:00:00 2001 From: Tekiter Date: Fri, 21 Jul 2023 01:26:53 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=EB=B8=94=EB=A1=9C=EA=B7=B8=20=EA=B8=B0?= =?UTF-8?q?=EC=B4=88=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84=20(#5)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 블로그 데이터 함수 추가 * feat: 블로그 글 리스트 추가 * feat: 블로그 글 보기 구현 --- apps/ssr-gateway/.dev.vars.example | 2 + apps/ssr-gateway/package.json | 6 +- apps/ssr-gateway/src/notion/index.ts | 4 +- apps/ssr-gateway/src/notion/property.ts | 10 +- apps/ssr-gateway/src/notion/types.ts | 7 +- apps/ssr-gateway/src/router/blog.ts | 107 ++++++++++++ apps/ssr-gateway/src/router/index.ts | 2 + apps/ssr-gateway/src/trpc/context.ts | 4 + apps/ssr-gateway/src/worker.ts | 34 +++- apps/web/package.json | 14 +- apps/web/src/app/blog/article/[id]/page.tsx | 19 +++ .../src/app/blog/category/[category]/page.tsx | 19 +++ apps/web/src/app/blog/page.tsx | 17 ++ .../web/src/components/blog/ArticleDetail.tsx | 65 ++++++++ apps/web/src/components/blog/ArticleList.tsx | 93 +++++++++++ apps/web/tailwind.config.js | 26 +++ yarn.lock | 153 ++++++++++-------- 17 files changed, 493 insertions(+), 89 deletions(-) create mode 100644 apps/ssr-gateway/src/router/blog.ts create mode 100644 apps/web/src/app/blog/article/[id]/page.tsx create mode 100644 apps/web/src/app/blog/category/[category]/page.tsx create mode 100644 apps/web/src/app/blog/page.tsx create mode 100644 apps/web/src/components/blog/ArticleDetail.tsx create mode 100644 apps/web/src/components/blog/ArticleList.tsx diff --git a/apps/ssr-gateway/.dev.vars.example b/apps/ssr-gateway/.dev.vars.example index 21d1840..7d798e7 100644 --- a/apps/ssr-gateway/.dev.vars.example +++ b/apps/ssr-gateway/.dev.vars.example @@ -1,3 +1,5 @@ INTERNAL_API_KEY=TEST_API_KEY_ONLY_FOR_DEVELOPMENT RECRUIT_NOTION_API_KEY= RECRUIT_NOTION_PAGE_ID= +BLOG_NOTION_API_KEY= +BLOG_NOTION_DB_ID= diff --git a/apps/ssr-gateway/package.json b/apps/ssr-gateway/package.json index 28a2b12..cdb2a19 100644 --- a/apps/ssr-gateway/package.json +++ b/apps/ssr-gateway/package.json @@ -11,8 +11,8 @@ "typecheck": "tsc --noEmit" }, "dependencies": { - "@trpc/server": "^10.33.1", - "superjson": "^1.12.4", + "@trpc/server": "^10.35.0", + "superjson": "^1.13.1", "zod": "^3.21.4" }, "devDependencies": { @@ -21,6 +21,6 @@ "eslint": "^8.44.0", "eslint-config-custom": "*", "typescript": "^5.1.6", - "wrangler": "^3.1.1" + "wrangler": "^3.3.0" } } diff --git a/apps/ssr-gateway/src/notion/index.ts b/apps/ssr-gateway/src/notion/index.ts index d46f576..938ebfc 100644 --- a/apps/ssr-gateway/src/notion/index.ts +++ b/apps/ssr-gateway/src/notion/index.ts @@ -1,5 +1,5 @@ import { createRawNotionAPIClient } from './api'; -import { NotionBlock, PageObjectResponse } from './types'; +import { NotionBlock, NotionPage } from './types'; export function createNotionClient(notionApiKey: string) { const notionRawAPI = createRawNotionAPIClient(notionApiKey); @@ -7,7 +7,7 @@ export function createNotionClient(notionApiKey: string) { async function getDatabaseContents(id: string) { const dbData = await notionRawAPI.databaseRetrieve(id); - const objects = dbData.results.filter((result): result is PageObjectResponse => 'properties' in result); + const objects = dbData.results.filter((result): result is NotionPage => 'properties' in result); return objects; } diff --git a/apps/ssr-gateway/src/notion/property.ts b/apps/ssr-gateway/src/notion/property.ts index 1988dc1..2953252 100644 --- a/apps/ssr-gateway/src/notion/property.ts +++ b/apps/ssr-gateway/src/notion/property.ts @@ -1,17 +1,17 @@ -import type { PageObjectResponse } from '@notionhq/client/build/src/api-endpoints'; +import { NotionPage } from './types'; -export function propertyResolver(properties: PageObjectResponse['properties']) { - function getTypedProperty( +export function propertyResolver(properties: NotionPage['properties']) { + function getTypedProperty( name: string, type: T, - ): PageObjectResponse['properties'][string] & { type: T } { + ): NotionPage['properties'][string] & { type: T } { const property = properties[name]; if (property.type !== type) { throw new Error(`${name} 필드는 ${type} 타입의 속성이 아닙니다.`); } - return property as PageObjectResponse['properties'][string] & { type: T }; + return property as NotionPage['properties'][string] & { type: T }; } return { diff --git a/apps/ssr-gateway/src/notion/types.ts b/apps/ssr-gateway/src/notion/types.ts index 693733b..616b80f 100644 --- a/apps/ssr-gateway/src/notion/types.ts +++ b/apps/ssr-gateway/src/notion/types.ts @@ -1,6 +1,9 @@ -import type { BlockObjectResponse as NotionBlock, PageObjectResponse } from '@notionhq/client/build/src/api-endpoints'; +import type { + BlockObjectResponse as NotionBlock, + PageObjectResponse as NotionPage, +} from '@notionhq/client/build/src/api-endpoints'; -export type { NotionBlock, PageObjectResponse }; +export type { NotionBlock, NotionPage }; export type PickNotionBlock = Extract; diff --git a/apps/ssr-gateway/src/router/blog.ts b/apps/ssr-gateway/src/router/blog.ts new file mode 100644 index 0000000..932d544 --- /dev/null +++ b/apps/ssr-gateway/src/router/blog.ts @@ -0,0 +1,107 @@ +import { z } from 'zod'; + +import { propertyResolver } from '../notion/property'; +import { NotionPage } from '../notion/types'; +import { internalProcedure, router } from '../trpc/stub'; + +export const blogRouter = router({ + list: internalProcedure + .input( + z.object({ + category: z.string().optional(), + }), + ) + .query(async ({ input, ctx }) => { + const pages = await (async () => { + const cacheKey = () => `cache:blog:list`; + + const cached = await ctx.kv.get(cacheKey(), 'json'); + if (cached) { + return cached as PagesType; + } + + const pages = await ctx.blog.notion.getDatabaseContents(ctx.blog.databaseId); + type PagesType = typeof pages; + + await ctx.kv.put(cacheKey(), JSON.stringify(pages)); + + return pages; + })(); + + const articles = pages.map((page) => { + const properties = extractArticleProperties(page.properties); + + return { + ...properties, + id: page.id, + }; + }); + const categories = articles + .map((article) => article.category) + .filter((category): category is string => !!category); + + const filtered = articles.filter((article) => !input.category || article.category === input.category); + + const data = { + articles: filtered, + categories, + }; + + return data; + }), + invalidateList: internalProcedure.mutation(async ({ ctx }) => { + await ctx.kv.delete('cache:blog:list'); + }), + article: internalProcedure.input(z.object({ id: z.string() })).query(async ({ input, ctx }) => { + const cached = await ctx.kv.get(`cache:blog:article:${input.id}`, 'json'); + if (cached) { + return cached as ArticleData; + } + + const [page, blocks] = await Promise.all([ctx.blog.notion.getPage(input.id), ctx.blog.notion.getBlocks(input.id)]); + const properties = extractArticleProperties(page.properties); + + const articleData = { ...properties, blocks }; + type ArticleData = typeof articleData; + + await ctx.kv.put(`cache:blog:article:${input.id}`, JSON.stringify(articleData)); + + return articleData; + }), + invalidateArticle: internalProcedure.input(z.object({ id: z.string() })).mutation(async ({ input, ctx }) => { + await ctx.kv.delete(`cache:blog:article:${input.id}`); + }), + invalidateAllArticles: internalProcedure.mutation(async ({ ctx }) => { + const { keys } = await ctx.kv.list({ prefix: 'cache:blog:article:' }); + + await Promise.all(keys.map(async ({ name }) => ctx.kv.delete(name))); + }), +}); + +function extractArticleProperties(properties: NotionPage['properties']) { + const resolver = propertyResolver(properties); + + const title = resolver.title('title'); + const editors = resolver.multiSelect('editors').map((raw) => { + const [name, role = undefined] = raw.split('|'); + + return { + name, + role, + }; + }); + const publishedAt = resolver.date('publishedAt'); + const category = resolver.select('category'); + const thumbnailFiles = resolver.files('thumbnail'); + const thumbnail = thumbnailFiles.length > 0 ? thumbnailFiles[0] : null; + const publish = resolver.checkbox('publish'); + + return { + title, + editors, + publishedAt, + category, + thumbnail, + publish, + }; +} diff --git a/apps/ssr-gateway/src/router/index.ts b/apps/ssr-gateway/src/router/index.ts index d7ff7ba..421a0b1 100644 --- a/apps/ssr-gateway/src/router/index.ts +++ b/apps/ssr-gateway/src/router/index.ts @@ -1,10 +1,12 @@ import { router } from '../trpc/stub'; +import { blogRouter } from './blog'; import { internalRouter } from './internal'; import { recruitRouter } from './recruit'; export const appRouter = router({ internal: internalRouter, recruit: recruitRouter, + blog: blogRouter, }); // export type definition of API diff --git a/apps/ssr-gateway/src/trpc/context.ts b/apps/ssr-gateway/src/trpc/context.ts index 1f527b9..b90980b 100644 --- a/apps/ssr-gateway/src/trpc/context.ts +++ b/apps/ssr-gateway/src/trpc/context.ts @@ -11,6 +11,10 @@ interface ContextDeps { waitUntil: (promise: Promise) => void; checkApiKey: (apiKey: string) => boolean; recruitNotionClient: NotionClient; + blog: { + notion: NotionClient; + databaseId: string; + }; kv: KVNamespace; } diff --git a/apps/ssr-gateway/src/worker.ts b/apps/ssr-gateway/src/worker.ts index d250e2a..0fd443b 100644 --- a/apps/ssr-gateway/src/worker.ts +++ b/apps/ssr-gateway/src/worker.ts @@ -34,15 +34,22 @@ export interface Env { INTERNAL_API_KEY?: string; RECRUIT_NOTION_API_KEY?: string; RECRUIT_NOTION_PAGE_ID?: string; + BLOG_NOTION_API_KEY?: string; + BLOG_NOTION_DB_ID?: string; } export default { async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise { - if (!env.RECRUIT_NOTION_API_KEY) { - return new Response('Invalid RECRUIT_NOTION_API_KEY', { status: 500 }); - } - if (!env.RECRUIT_NOTION_PAGE_ID) { - throw new Error('Env RECRUIT_NOTION_PAGE_ID is not set.'); + if ( + !check(env, [ + 'BLOG_NOTION_API_KEY', + 'BLOG_NOTION_DB_ID', + 'RECRUIT_NOTION_API_KEY', + 'RECRUIT_NOTION_PAGE_ID', + 'INTERNAL_API_KEY', + ]) + ) { + throw new Error('Some env values are not properly set.'); } return fetchRequestHandler({ @@ -57,9 +64,26 @@ export default { checkApiKey(apiKey) { return apiKey.trim() === env.INTERNAL_API_KEY; }, + blog: { + notion: createNotionClient(env.BLOG_NOTION_API_KEY), + databaseId: env.BLOG_NOTION_DB_ID, + }, recruitNotionClient: createNotionClient(env.RECRUIT_NOTION_API_KEY), kv: env.MAKERS_PAGE_KV, }), }); }, }; + +function check( + obj: T, + keys: readonly K[], +): obj is T & Required<{ [key in K]: T[K] }> { + for (const key of keys) { + if (!(key in obj)) { + console.log(`Env value ${String(key)} is not properly set.`); + return false; + } + } + return true; +} diff --git a/apps/web/package.json b/apps/web/package.json index 50cf3b3..b2c4b76 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -11,20 +11,22 @@ "typecheck": "tsc --noEmit" }, "dependencies": { - "@trpc/client": "^10.33.1", - "@trpc/server": "^10.33.1", + "@trpc/client": "^10.35.0", + "@trpc/server": "^10.35.0", "axios": "^1.4.0", - "clsx": "^1.2.1", + "clsx": "^2.0.0", + "date-fns": "^2.30.0", "next": "^13.4.8", "react": "^18.2.0", "react-dom": "^18.2.0", "react-syntax-highlighter": "^15.5.0", "ssr-gateway": "*", - "superjson": "^1.12.4" + "superjson": "^1.13.1" }, "devDependencies": { - "@cloudflare/next-on-pages": "^1.2.0", + "@cloudflare/next-on-pages": "^1.3.1", "@cloudflare/workers-types": "^4.20230628.0", + "@types/date-fns": "^2.6.0", "@types/node": "^17.0.12", "@types/react": "^18.2.14", "@types/react-dom": "^18.2.6", @@ -37,6 +39,6 @@ "tsconfig": "*", "typescript": "^5.1.6", "vercel": "^30.0.0", - "wrangler": "^3.1.1" + "wrangler": "^3.3.0" } } diff --git a/apps/web/src/app/blog/article/[id]/page.tsx b/apps/web/src/app/blog/article/[id]/page.tsx new file mode 100644 index 0000000..e2635eb --- /dev/null +++ b/apps/web/src/app/blog/article/[id]/page.tsx @@ -0,0 +1,19 @@ +import { FC } from 'react'; + +import ArticlePage from '@/components/blog/ArticleDetail'; + +interface BlogArticlePageProps { + params: { id: string }; +} + +export const runtime = 'edge'; + +const BlogArticlePage: FC = ({ params }) => { + return ( + <> + + + ); +}; + +export default BlogArticlePage; diff --git a/apps/web/src/app/blog/category/[category]/page.tsx b/apps/web/src/app/blog/category/[category]/page.tsx new file mode 100644 index 0000000..def453a --- /dev/null +++ b/apps/web/src/app/blog/category/[category]/page.tsx @@ -0,0 +1,19 @@ +import { FC } from 'react'; + +import ArticleList from '@/components/blog/ArticleList'; + +interface BlogCategoryPageProps { + params: { category: string }; +} + +export const runtime = 'edge'; + +const BlogCategoryPage: FC = ({ params }) => { + return ( + <> + + + ); +}; + +export default BlogCategoryPage; diff --git a/apps/web/src/app/blog/page.tsx b/apps/web/src/app/blog/page.tsx new file mode 100644 index 0000000..bb45e75 --- /dev/null +++ b/apps/web/src/app/blog/page.tsx @@ -0,0 +1,17 @@ +import { FC } from 'react'; + +import ArticleList from '@/components/blog/ArticleList'; + +interface BlogPageProps {} + +export const runtime = 'edge'; + +const BlogPage: FC = async ({}) => { + return ( +
+ +
+ ); +}; + +export default BlogPage; diff --git a/apps/web/src/components/blog/ArticleDetail.tsx b/apps/web/src/components/blog/ArticleDetail.tsx new file mode 100644 index 0000000..d65e864 --- /dev/null +++ b/apps/web/src/components/blog/ArticleDetail.tsx @@ -0,0 +1,65 @@ +import Link from 'next/link'; + +import { gateway } from '@/gateway'; + +import { BlockRenderer } from '../notion/renderer'; + +interface ArticlePageProps { + id: string; +} + +async function ArticlePage({ id }: ArticlePageProps) { + const article = await gateway.blog.article.query({ id }); + + return ( +
+
+ + + 블로그 홈 가기 + + {article.thumbnail && ( +
+ Thumbnail Image +
+ )} +
+ {article.category && ( + <> + + + {article.category} + + + + )} +
+

+ {article.title} +

+
+ {/* {article.publishedAt && format(article.publishedAt, 'yyyy.MM.dd')} */} + {article.publishedAt.toString()} +
+
+
Subpage not allowed
} /> +
+
+
+ ); +} + +export default ArticlePage; + +function BackIcon(props: React.SVGProps) { + return ( + + + + ); +} diff --git a/apps/web/src/components/blog/ArticleList.tsx b/apps/web/src/components/blog/ArticleList.tsx new file mode 100644 index 0000000..20e0c91 --- /dev/null +++ b/apps/web/src/components/blog/ArticleList.tsx @@ -0,0 +1,93 @@ +import clsx from 'clsx'; +import Link from 'next/link'; +import { FC, ReactNode } from 'react'; + +import { gateway } from '@/gateway'; + +interface ArticleListProps { + category?: string; +} + +const ArticleList: FC = async ({ category: currentCategory }) => { + const { articles, categories } = await gateway.blog.list.query({ + category: currentCategory ? decodeURIComponent(currentCategory) : undefined, + }); + + return ( +
+
+
+ + 전체 + + {categories.map((category) => ( + + {category} + + ))} +
+
+ {articles.map((article) => ( + +
+
+ {/* {article.category} | {article.publishedAt && format(article.publishedAt, 'yyyy.MM.dd')} */} + {article.publishedAt.toString()} +
+

+ {article.title} +

+
+ {article.editors.length === 1 && ( + <> + {article.editors[0].name} + + {article.editors[0].role} + + )} + {article.editors.length >= 2 && ( + {article.editors.map((editor) => editor.name).join(', ')} + )} +
+
+ {article.thumbnail && ( +
+ Thumbnail +
+ )} + + ))} +
+
+
+ ); +}; + +export default ArticleList; + +interface ChipProps { + children: ReactNode; + active?: boolean; +} + +const Chip: FC = ({ children, active = false }) => { + return ( +
+ {children} +
+ ); +}; diff --git a/apps/web/tailwind.config.js b/apps/web/tailwind.config.js index a5a0b69..faafe6e 100644 --- a/apps/web/tailwind.config.js +++ b/apps/web/tailwind.config.js @@ -2,6 +2,32 @@ module.exports = { content: ['./src/**/*.{js,ts,jsx,tsx,mdx}'], theme: { + fontSize: { + h0: [ + '70px', + { + lineHeight: '140%', + letterSpacing: '-0.7px', + fontWeight: '700', + }, + ], + h1: [ + '50px', + { + lineHeight: '100%', + letterSpacing: '-0.5px', + fontWeight: 700, + }, + ], + h2: [ + '30px', + { + lineHeight: '170%', + letterSpacing: '-0.3px', + fontWeight: 600, + }, + ], + }, extend: {}, }, plugins: [], diff --git a/yarn.lock b/yarn.lock index 5571a9e..67efb0b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1370,7 +1370,7 @@ __metadata: languageName: node linkType: hard -"@babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.20.7, @babel/runtime@npm:^7.3.1, @babel/runtime@npm:^7.8.4": +"@babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.20.7, @babel/runtime@npm:^7.21.0, @babel/runtime@npm:^7.3.1, @babel/runtime@npm:^7.8.4": version: 7.22.6 resolution: "@babel/runtime@npm:7.22.6" dependencies: @@ -1428,9 +1428,9 @@ __metadata: languageName: node linkType: hard -"@cloudflare/next-on-pages@npm:^1.2.0": - version: 1.2.0 - resolution: "@cloudflare/next-on-pages@npm:1.2.0" +"@cloudflare/next-on-pages@npm:^1.3.1": + version: 1.3.1 + resolution: "@cloudflare/next-on-pages@npm:1.3.1" dependencies: acorn: ^8.8.0 ast-types: ^0.14.2 @@ -1448,41 +1448,41 @@ __metadata: wrangler: ^3.0.0 bin: next-on-pages: bin/index.js - checksum: 19719ccdd51218c631838ed09d8d156cfdc689b6a8a27256850d0d59d4fbd987d4f4f5d60e29a46c98004fe54ec252861ac693db9e801d2cc25c3f776fb1229b + checksum: f710464438cbd397fcaa3c7c55a7557c99e08cf996122ec12ee201b183e6334581dee3c8acfac2152b59745b43f70f91a7d6a274a10bff560c4c110140b51b6e languageName: node linkType: hard -"@cloudflare/workerd-darwin-64@npm:1.20230628.0": - version: 1.20230628.0 - resolution: "@cloudflare/workerd-darwin-64@npm:1.20230628.0" +"@cloudflare/workerd-darwin-64@npm:1.20230717.0": + version: 1.20230717.0 + resolution: "@cloudflare/workerd-darwin-64@npm:1.20230717.0" conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"@cloudflare/workerd-darwin-arm64@npm:1.20230628.0": - version: 1.20230628.0 - resolution: "@cloudflare/workerd-darwin-arm64@npm:1.20230628.0" +"@cloudflare/workerd-darwin-arm64@npm:1.20230717.0": + version: 1.20230717.0 + resolution: "@cloudflare/workerd-darwin-arm64@npm:1.20230717.0" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"@cloudflare/workerd-linux-64@npm:1.20230628.0": - version: 1.20230628.0 - resolution: "@cloudflare/workerd-linux-64@npm:1.20230628.0" +"@cloudflare/workerd-linux-64@npm:1.20230717.0": + version: 1.20230717.0 + resolution: "@cloudflare/workerd-linux-64@npm:1.20230717.0" conditions: os=linux & cpu=x64 languageName: node linkType: hard -"@cloudflare/workerd-linux-arm64@npm:1.20230628.0": - version: 1.20230628.0 - resolution: "@cloudflare/workerd-linux-arm64@npm:1.20230628.0" +"@cloudflare/workerd-linux-arm64@npm:1.20230717.0": + version: 1.20230717.0 + resolution: "@cloudflare/workerd-linux-arm64@npm:1.20230717.0" conditions: os=linux & cpu=arm64 languageName: node linkType: hard -"@cloudflare/workerd-windows-64@npm:1.20230628.0": - version: 1.20230628.0 - resolution: "@cloudflare/workerd-windows-64@npm:1.20230628.0" +"@cloudflare/workerd-windows-64@npm:1.20230717.0": + version: 1.20230717.0 + resolution: "@cloudflare/workerd-windows-64@npm:1.20230717.0" conditions: os=win32 & cpu=x64 languageName: node linkType: hard @@ -2603,19 +2603,19 @@ __metadata: languageName: node linkType: hard -"@trpc/client@npm:^10.33.1": - version: 10.33.1 - resolution: "@trpc/client@npm:10.33.1" +"@trpc/client@npm:^10.35.0": + version: 10.35.0 + resolution: "@trpc/client@npm:10.35.0" peerDependencies: - "@trpc/server": 10.33.1 - checksum: 051b53b33c29b3e10b25afc64d2a4d72caec6fee4d3e498775fc3ddc15e5d2a754cdf9cdfd797e2b803a6491caceddd2e3e9c30a4a4203b4a3d69c5ac3c6de31 + "@trpc/server": 10.35.0 + checksum: 907784dc40e11ead63f7bc6ad387614b2f320c6efb58080eb890bcf60807c8371f416dc765e25d47c215520858efd4d7e051d6f67c4d99e0ee76264261a197c1 languageName: node linkType: hard -"@trpc/server@npm:^10.33.1": - version: 10.33.1 - resolution: "@trpc/server@npm:10.33.1" - checksum: 3c63c25cc9a0dbdae846730444b937abcbf0872d18d12d244ebf636fdd9123474fe7f623aaec9e015327382012bffc57d459658cd21bf216f075b2132908ff6c +"@trpc/server@npm:^10.35.0": + version: 10.35.0 + resolution: "@trpc/server@npm:10.35.0" + checksum: 4184f467193d04d25a747b34f6a29f2b10cb1e6c97463b584dfb20850170293578d3a239b3ef6adc0426d1254f21cc15e66d48fc1024efb8dad889ab80cffdd4 languageName: node linkType: hard @@ -2700,6 +2700,15 @@ __metadata: languageName: node linkType: hard +"@types/date-fns@npm:^2.6.0": + version: 2.6.0 + resolution: "@types/date-fns@npm:2.6.0" + dependencies: + date-fns: "*" + checksum: 6839db1d12d6586cd572e8aa77efc2ea88fd6566b7902641cf3efe2e22518aff067df2ba433539a275a3ab1169612b54d007ddcc2800d846be4f7e629cd7bfb3 + languageName: node + linkType: hard + "@types/debug@npm:^4.0.0": version: 4.1.8 resolution: "@types/debug@npm:4.1.8" @@ -4367,10 +4376,10 @@ __metadata: languageName: node linkType: hard -"clsx@npm:^1.2.1": - version: 1.2.1 - resolution: "clsx@npm:1.2.1" - checksum: 30befca8019b2eb7dbad38cff6266cf543091dae2825c856a62a8ccf2c3ab9c2907c4d12b288b73101196767f66812365400a227581484a05f968b0307cfaf12 +"clsx@npm:^2.0.0": + version: 2.0.0 + resolution: "clsx@npm:2.0.0" + checksum: a2cfb2351b254611acf92faa0daf15220f4cd648bdf96ce369d729813b85336993871a4bf6978ddea2b81b5a130478339c20d9d0b5c6fc287e5147f0c059276e languageName: node linkType: hard @@ -4635,6 +4644,15 @@ __metadata: languageName: node linkType: hard +"date-fns@npm:*, date-fns@npm:^2.30.0": + version: 2.30.0 + resolution: "date-fns@npm:2.30.0" + dependencies: + "@babel/runtime": ^7.21.0 + checksum: f7be01523282e9bb06c0cd2693d34f245247a29098527d4420628966a2d9aad154bd0e90a6b1cf66d37adcb769cd108cf8a7bd49d76db0fb119af5cdd13644f4 + languageName: node + linkType: hard + "deasync@npm:^0.1.0": version: 0.1.28 resolution: "deasync@npm:0.1.28" @@ -9286,9 +9304,9 @@ __metadata: languageName: node linkType: hard -"miniflare@npm:^3.0.1": - version: 3.0.2 - resolution: "miniflare@npm:3.0.2" +"miniflare@npm:3.20230717.0": + version: 3.20230717.0 + resolution: "miniflare@npm:3.20230717.0" dependencies: acorn: ^8.8.0 acorn-walk: ^8.2.0 @@ -9298,14 +9316,15 @@ __metadata: glob-to-regexp: ^0.4.1 http-cache-semantics: ^4.1.0 kleur: ^4.1.5 + set-cookie-parser: ^2.6.0 source-map-support: 0.5.21 stoppable: ^1.1.0 undici: ^5.13.0 - workerd: ^1.20230512.0 + workerd: 1.20230717.0 ws: ^8.11.0 youch: ^3.2.2 zod: ^3.20.6 - checksum: 9a210b17dc06db6972c206040878bd35bedd6fe921cfcd9b882b09d33ddf1e789447ff689adb18733d87131db2da97acd05d7c5bb9d035503df84d7c5413315a + checksum: d37c9171bbc97a5723ef58cf35fad9649e70e19192411861648534f44a4bbe88695bb8d9d445f5b14609a7f140c7cd8486c4abf446886e2335f67ed03c8b1e3f languageName: node linkType: hard @@ -11540,7 +11559,7 @@ __metadata: languageName: node linkType: hard -"set-cookie-parser@npm:^2.4.8": +"set-cookie-parser@npm:^2.4.8, set-cookie-parser@npm:^2.6.0": version: 2.6.0 resolution: "set-cookie-parser@npm:2.6.0" checksum: bf11ebc594c53d84588f1b4c04f1b8ce14e0498b1c011b3d76b5c6d5aac481bbc3f7c5260ec4ce99bdc1d9aed19f9fc315e73166a36ca74d0f12349a73f6bdc9 @@ -11795,12 +11814,12 @@ __metadata: dependencies: "@cloudflare/workers-types": ^4.20230628.0 "@notionhq/client": ^2.2.6 - "@trpc/server": ^10.33.1 + "@trpc/server": ^10.35.0 eslint: ^8.44.0 eslint-config-custom: "*" - superjson: ^1.12.4 + superjson: ^1.13.1 typescript: ^5.1.6 - wrangler: ^3.1.1 + wrangler: ^3.3.0 zod: ^3.21.4 languageName: unknown linkType: soft @@ -12077,12 +12096,12 @@ __metadata: languageName: node linkType: hard -"superjson@npm:^1.12.4": - version: 1.12.4 - resolution: "superjson@npm:1.12.4" +"superjson@npm:^1.13.1": + version: 1.13.1 + resolution: "superjson@npm:1.13.1" dependencies: copy-anything: ^3.0.2 - checksum: fcf37714f734cd51af018b78fd7f630952a0682fc3cb060d00b824e5d0cd49f0206b8e23829469c99972fd7fcd6e468b9492abb1c4e258ea5ec7f45c345a56f2 + checksum: 9c8c664a924ce097250112428805ccc8b500018b31a91042e953d955108b8481c156005d836b413940c9fa5f124a3195f55f3a518fe76510a254a59f9151a204 languageName: node linkType: hard @@ -13114,17 +13133,19 @@ turbo@latest: version: 0.0.0-use.local resolution: "web@workspace:apps/web" dependencies: - "@cloudflare/next-on-pages": ^1.2.0 + "@cloudflare/next-on-pages": ^1.3.1 "@cloudflare/workers-types": ^4.20230628.0 - "@trpc/client": ^10.33.1 - "@trpc/server": ^10.33.1 + "@trpc/client": ^10.35.0 + "@trpc/server": ^10.35.0 + "@types/date-fns": ^2.6.0 "@types/node": ^17.0.12 "@types/react": ^18.2.14 "@types/react-dom": ^18.2.6 "@types/react-syntax-highlighter": ^15.5.7 autoprefixer: ^10.4.14 axios: ^1.4.0 - clsx: ^1.2.1 + clsx: ^2.0.0 + date-fns: ^2.30.0 eslint-config-custom: "*" next: ^13.4.8 postcss: ^8.4.24 @@ -13133,12 +13154,12 @@ turbo@latest: react-syntax-highlighter: ^15.5.0 server-only: ^0.0.1 ssr-gateway: "*" - superjson: ^1.12.4 + superjson: ^1.13.1 tailwindcss: ^3.3.2 tsconfig: "*" typescript: ^5.1.6 vercel: ^30.0.0 - wrangler: ^3.1.1 + wrangler: ^3.3.0 languageName: unknown linkType: soft @@ -13220,15 +13241,15 @@ turbo@latest: languageName: node linkType: hard -"workerd@npm:^1.20230512.0": - version: 1.20230628.0 - resolution: "workerd@npm:1.20230628.0" +"workerd@npm:1.20230717.0": + version: 1.20230717.0 + resolution: "workerd@npm:1.20230717.0" dependencies: - "@cloudflare/workerd-darwin-64": 1.20230628.0 - "@cloudflare/workerd-darwin-arm64": 1.20230628.0 - "@cloudflare/workerd-linux-64": 1.20230628.0 - "@cloudflare/workerd-linux-arm64": 1.20230628.0 - "@cloudflare/workerd-windows-64": 1.20230628.0 + "@cloudflare/workerd-darwin-64": 1.20230717.0 + "@cloudflare/workerd-darwin-arm64": 1.20230717.0 + "@cloudflare/workerd-linux-64": 1.20230717.0 + "@cloudflare/workerd-linux-arm64": 1.20230717.0 + "@cloudflare/workerd-windows-64": 1.20230717.0 dependenciesMeta: "@cloudflare/workerd-darwin-64": optional: true @@ -13242,13 +13263,13 @@ turbo@latest: optional: true bin: workerd: bin/workerd - checksum: da9ddbed2f8b940c57ec21737027249575a05ad551a963885dbbb0ba2edb8c317af5f76b9d7c3e8d3d208e8ddaf2dfef86051bebfb14fcc96476b435c85e81a6 + checksum: fd96df04675c406b5c897fd6ce4a25ace1ea6fbb89aa49d45cf7ef632041a780890b01d8830b62c8afbfc9c2b0aebb654d7ab46e43d17aeeef00832546e34aaa languageName: node linkType: hard -"wrangler@npm:^3.1.1": - version: 3.1.1 - resolution: "wrangler@npm:3.1.1" +"wrangler@npm:^3.3.0": + version: 3.3.0 + resolution: "wrangler@npm:3.3.0" dependencies: "@cloudflare/kv-asset-handler": ^0.2.0 "@esbuild-plugins/node-globals-polyfill": ^0.1.1 @@ -13257,7 +13278,7 @@ turbo@latest: chokidar: ^3.5.3 esbuild: 0.16.3 fsevents: ~2.3.2 - miniflare: ^3.0.1 + miniflare: 3.20230717.0 nanoid: ^3.3.3 path-to-regexp: ^6.2.0 selfsigned: ^2.0.1 @@ -13269,7 +13290,7 @@ turbo@latest: bin: wrangler: bin/wrangler.js wrangler2: bin/wrangler.js - checksum: f93c2690653ff85eb45fda73ccf17ddf86d3a72e488b487ab83adf7884736df366e4f661325f7f2ec2c0a6587dd145bf1c72710f8a791c1412787d4ca29ca639 + checksum: 27b157ffab9913fe7fffc4609babb9041f7017a6e2d55ab785153808758557067af64240af14cec8e955dbf423812e20f770cea7f281f6de576b5311704bae45 languageName: node linkType: hard