diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index b5de9ef2..d80071b6 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -1,18 +1,18 @@ # Contributor Covenant Code of Conduct -## Our Pledge +## Pledge -We as members, contributors, and leaders pledge to make participation in our +Members, contributors, and leaders pledge to make participation in the community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. -We pledge to act and interact in ways that contribute to an open, welcoming, +The pledge is to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community. -## Our Standards +## Standards Examples of behavior that contributes to a positive environment for our community include: @@ -20,9 +20,9 @@ community include: - Demonstrating empathy and kindness toward other people - Being respectful of differing opinions, viewpoints, and experiences - Giving and gracefully accepting constructive feedback -- Accepting responsibility and apologizing to those affected by our mistakes, +- Accepting responsibility and apologizing to those affected by mistakes, and learning from the experience -- Focusing on what is best not just for us as individuals, but for the +- Focusing on what is best not just for individuals, but for the overall community Examples of unacceptable behavior include: @@ -38,7 +38,7 @@ Examples of unacceptable behavior include: ## Enforcement Responsibilities -Community leaders are responsible for clarifying and enforcing our standards of +Community leaders are responsible for clarifying and enforcing standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful. @@ -52,15 +52,14 @@ decisions when appropriate. This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. -Examples of representing our community include using an official e-mail address, +Examples of representing the community include using an official e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be -reported to the community leaders responsible for enforcement by contacting the maintainer team -. +reported to community leaders responsible for enforcement by contacting the maintainer. All complaints will be reviewed and investigated promptly and fairly. All community leaders are obligated to respect the privacy and security of the diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d6b4df07..744ebf77 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,19 +1,19 @@ # Developer's Guide -Hey there! We're thrilled that you'd like to contribute to this project. Your help is essential for keeping it great and we truly appreciate your time and effort. +Hey there! Contributing to this project is highly encouraged. Community help is essential for maintaining its quality, and every effort is appreciated. > [!IMPORTANT] > Before submitting your contribution, please take a moment and read through the following guidelines. ## 👨‍💻 Repository Setup -This project uses [Bun](https://bun.sh) as a runtime as well as a package manager. It's a modern, fast, and lightweight alternative to [Node.js](https://nodejs.org/en/) and [npm](https://www.npmjs.com/). To install Bun on POSIX systems (like Ubuntu or macOS), run the following command: +This project uses [Bun](https://bun.com) as a runtime as well as a package manager. It's a modern, fast, and lightweight alternative to [Node.js](https://nodejs.org/en/) and [npm](https://www.npmjs.com/). To install Bun on POSIX systems (like Ubuntu or macOS), run the following command: ```sh -curl -fsSL https://bun.sh/install | bash +curl -fsSL https://bun.com/install | bash ``` -Otherwise, visit the [Bun installation page](https://bun.sh/docs/installation) for installation options. +Otherwise, visit the [Bun installation page](https://bun.com/docs/installation) for installation options. ## 💡 Commands @@ -27,7 +27,7 @@ Build the project for production. The result is under `dist/`. ### `bun check` -We use [Biome](https://biomejs.dev/) for **both linting and formatting**. It is an ultra-fast, Rust based linter and formatter. +Biome is used for **both linting and formatting**. It is an ultra-fast, Rust based linter and formatter. It also lints JSON. You can also run `bun fix` to apply any safe fixes automatically. @@ -54,7 +54,7 @@ For typo fixes, it's recommended to batch multiple typo fixes into one pull requ ### Pull Request -If you don't know how to send a Pull Request, we recommend reading [the guide](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request). +If you don't know how to send a Pull Request, it is recommended to read [the guide](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request). If your PR fixes or resolves an existing issue, please add the following line in your PR description according to the following example: @@ -75,19 +75,21 @@ Replacing: This will let GitHub know the issues are linked, and automatically close them once the PR gets merged. Learn more at [the guide](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue#linking-a-pull-request-to-an-issue-using-a-keyword). -It's ok to have multiple commits in a single PR, you don't need to rebase or force push for your changes as we will use `Squash and Merge` to squash the commits into one commit when merging. +It's ok to have multiple commits in a single PR; changes do not need to be rebased or force pushed, as Squash and Merge will be used to combine the commits into one when merging. ## 📖 References ### Lint -We use [Biome](https://biomejs.dev/) for both linting and formatting with [a few custom rules](./biome.jsonc). It is an ultra-fast, Rust based linter and formatter. +Biome is used for both linting and formatting with [a few custom rules](./biome.jsonc). It is an ultra-fast, Rust based linter and formatter.
#### IDE Setup -We recommend using [VS Code](https://code.visualstudio.com/) along with the [Biome extension](https://marketplace.visualstudio.com/items?itemName=biomejs.biome). +[Visual Studio Code](https://code.visualstudio.com/) is recommended for the best experience along with: +- [Biome extension](https://marketplace.visualstudio.com/items?itemName=biomejs.biome) for linting and formatting. +- [ArkType extension](https://marketplace.visualstudio.com/items?itemName=arktypeio.arkdark) for syntax highlighting and type-safe regex support. With the settings on the right, you can have auto fix and formatting when you save the code you are editing. diff --git a/GOVERNANCE.md b/GOVERNANCE.md deleted file mode 100644 index 70ec0e3f..00000000 --- a/GOVERNANCE.md +++ /dev/null @@ -1,34 +0,0 @@ - - -# Governance Model - -To understand the scope of this document, please read: - -- [_What is governance?_ by Adobe](https://github.com/adobe/open-development-template/blob/master/Governance.md#meritocracy) -- [_Jenkins Governance Document_ for an inspiration](https://www.jenkins.io/project/governance/) - -## Who we are - -We are a group of open-source developers who develop, use, promote the Elysia RealWorld example app, software around it, and other related activities for our mutual benefit. - -## Core Philosophy - -- **Lower barrier to entry** - - Everyone is welcome to contribute, regardless of their background, experience, or identity. We value all contributions, and we are committed to providing a friendly, safe, and welcoming environment for all. Everyone gets a voice, and everyone is listened to. - -- **Seeking consensus** - - We seek consensus among contributors, and we are open to new ideas and approaches. When consensus cannot be reached after giving the stage to all parties, we will use a majority-wins voting process. We will strive to resolve conflicts in a constructive manner. - -- **Meritocracy** - - We value contributions based on their technical merit, and we welcome new contributors based on their demonstrated ability to contribute. Valuable contributers will be granted access to private channels and will be able to participate in the decision-making process. - -- **Transparency** - - While the decision-making process is not always public, the results of the decision-making process must be public. Decisions will be made after thoughtful consideration of the community's input through [Discord](https://discord.gg/8UcP9QB5AV) and [GitHub Discussions](https://github.com/bedtime-coders/bedstack/discussions). - -- **Automation** - - We rely on automation to make our processes more efficient and to reduce the burden on contributors. We will automate as much as possible, and we will strive to make our automation tools accessible to all contributors. The goal is to abstract away the mundane tasks and let contributors focus on the fun stuff. diff --git a/README.md b/README.md index dc109b62..b0758d86 100644 --- a/README.md +++ b/README.md @@ -3,19 +3,16 @@ Logo for Bedstack RealWorld example

Bedstack

-[![Tests Status](https://github.com/bedtime-coders/bedstack/actions/workflows/tests.yml/badge.svg?event=push&branch=main&)](https://github.com/bedtime-coders/bedstack/actions/workflows/tests.yml?query=branch%3Amain+event%3Apush) [![Discord](https://img.shields.io/discord/1164270344115335320?label=Chat&color=5865f4&logo=discord&labelColor=121214)](https://discord.gg/8UcP9QB5AV) [![License](https://custom-icon-badges.demolab.com/github/license/bedtime-coders/bedstack?label=License&color=blue&logo=law&labelColor=0d1117)](https://github.com/bedtime-coders/bedstack/blob/main/LICENSE) [![Bun](https://img.shields.io/badge/Bun-14151a?logo=bun&logoColor=fbf0df)](https://bun.sh/) [![ElysiaJS](https://custom-icon-badges.demolab.com/badge/ElysiaJS-0f172b.svg?logo=elysia)](https://elysiajs.com/) [![Drizzle](https://img.shields.io/badge/Drizzle-C5F74F?logo=drizzle&logoColor=000)](https://drizzle.team/) [![Biome](https://img.shields.io/badge/Biome-24272f?logo=biome&logoColor=f6f6f9)](https://biomejs.dev/) [![Scalar](https://img.shields.io/badge/Scalar-080808?logo=scalar&logoColor=e7e7e7)](https://scalar.com/) [![Star](https://custom-icon-badges.demolab.com/github/stars/bedtime-coders/bedstack?logo=star&logoColor=373737&label=Star)](https://github.com/bedtime-coders/bedstack/stargazers/) +[![Tests Status](https://github.com/bedtime-coders/bedstack/actions/workflows/tests.yml/badge.svg?event=push&branch=main&)](https://github.com/bedtime-coders/bedstack/actions/workflows/tests.yml?query=branch%3Amain+event%3Apush) [![Discord](https://img.shields.io/discord/1164270344115335320?label=Chat&color=5865f4&logo=discord&labelColor=121214)](https://discord.gg/8UcP9QB5AV) [![License](https://custom-icon-badges.demolab.com/github/license/bedtime-coders/bedstack?label=License&color=blue&logo=law&labelColor=0d1117)](https://github.com/bedtime-coders/bedstack/blob/main/LICENSE) [![Bun](https://img.shields.io/badge/Bun-14151a?logo=bun&logoColor=fbf0df)](https://bun.com/) [![ElysiaJS](https://custom-icon-badges.demolab.com/badge/ElysiaJS-0f172b.svg?logo=elysia)](https://elysiajs.com/) [![Drizzle](https://img.shields.io/badge/Drizzle-C5F74F?logo=drizzle&logoColor=000)](https://drizzle.team/) [![ArkType](https://custom-icon-badges.demolab.com/badge/ArkType-0d1526?logo=arktype2&logoColor=e9eef9)](https://arktype.io/) [![Biome](https://img.shields.io/badge/Biome-24272f?logo=biome&logoColor=f6f6f9)](https://biomejs.dev/) [![Scalar](https://img.shields.io/badge/Scalar-080808?logo=scalar&logoColor=e7e7e7)](https://scalar.com/) [![Star](https://custom-icon-badges.demolab.com/github/stars/bedtime-coders/bedstack?logo=star&logoColor=373737&label=Star)](https://github.com/bedtime-coders/bedstack/stargazers/) -[Bun](https://bun.sh/) + [ElysiaJS](https://elysiajs.com/) + [Drizzle](https://orm.drizzle.team/) = the stack you don't want to sleep on +[Bun](https://bun.com/) + [ElysiaJS](https://elysiajs.com/) + [Drizzle](https://orm.drizzle.team/) = the stack you don't want to sleep on [bedstack.js.org](https://bedstack.js.org) > [!TIP] -> ⚡ **New!** Our Prisma-based alternative to Bedstack was just approved on CodebaseShow. Check it out: [Bepstack](https://github.com/bedtime-coders/bepstack) - -> [!TIP] -> ⚡ **New!** We've added support for [Drizzle v1 Beta](https://orm.drizzle.team/roadmap) in _Bedstack (Stripped)_. Check it out: [`drizzle-v1` branch](https://github.com/bedtime-coders/bedstack-stripped/tree/drizzle-v1) +> ⚡ Bedstack supports [Drizzle v1 Beta](https://orm.drizzle.team/roadmap)! Check it out: [`drizzle-v1` branch](https://github.com/bedtime-coders/bedstack-stripped/tree/drizzle-v1)

diff --git a/SUPPORT.md b/SUPPORT.md index 4e63fc6a..4bd65b6b 100644 --- a/SUPPORT.md +++ b/SUPPORT.md @@ -4,16 +4,16 @@ This article explains where to get help with Bedstack. Please read through the following guidelines. > [!WARNING] -> Before participating in our community, please read our +> Before participating in the community, please read the > [code of conduct](CODE_OF_CONDUCT.md). > By interacting with this repository, organization, or community you agree to > abide by its terms. ## Asking quality questions -Questions can go to our [Discord server](https://discord.gg/8UcP9QB5AV) or [GitHub discussions](https://github.com/bedtime-coders/bedstack/discussions). +Questions can go to the [Discord server](https://discord.gg/8UcP9QB5AV) or [GitHub discussions](https://github.com/bedtime-coders/bedstack/discussions). -Help us help you! +Clear questions are easier to answer. Spend time framing questions and add links and resources. Spending the extra time up front can help save everyone time in the long run. Here are some tips: @@ -29,7 +29,7 @@ Here are some tips: * Provide sample code, such as a [CodeSandbox](https://codesandbox.io/) or video, if possible * Screenshots can help, but if there’s important text such as code or error messages in them, please also provide those as text -* The more time you put into asking your question, the better we can help you +* The more time put into asking a question, the better the community can provide assistance ## Contributions diff --git a/apps/conduit/README.md b/apps/conduit/README.md index 058defc9..d1b4e9b5 100644 --- a/apps/conduit/README.md +++ b/apps/conduit/README.md @@ -3,7 +3,7 @@ Logo for Bedstack RealWorld example

Conduit - Bedstack real world example

-[![Tests Status](https://github.com/bedtime-coders/bedstack/actions/workflows/tests.yml/badge.svg?event=push&branch=main&)](https://github.com/bedtime-coders/bedstack/actions/workflows/tests.yml?query=branch%3Amain+event%3Apush) [![Discord](https://img.shields.io/discord/1164270344115335320?label=Chat&color=5865f4&logo=discord&labelColor=121214)](https://discord.gg/8UcP9QB5AV) [![License](https://custom-icon-badges.demolab.com/github/license/bedtime-coders/bedstack?label=License&color=blue&logo=law&labelColor=0d1117)](https://github.com/bedtime-coders/bedstack/blob/main/LICENSE) [![Bun](https://img.shields.io/badge/Bun-14151a?logo=bun&logoColor=fbf0df)](https://bun.sh/) [![ElysiaJS](https://custom-icon-badges.demolab.com/badge/ElysiaJS-0f172b.svg?logo=elysia)](https://elysiajs.com/) [![Drizzle](https://img.shields.io/badge/Drizzle-C5F74F?logo=drizzle&logoColor=000)](https://orm.drizzle.team/) [![Biome](https://img.shields.io/badge/Biome-24272f?logo=biome&logoColor=f6f6f9)](https://biomejs.dev/) [![Scalar](https://img.shields.io/badge/Scalar-080808?logo=scalar&logoColor=e7e7e7)](https://scalar.com/) [![Star](https://custom-icon-badges.demolab.com/github/stars/bedtime-coders/bedstack?logo=star&logoColor=373737&label=Star)](https://github.com/bedtime-coders/bedstack/stargazers/) +[![Tests Status](https://github.com/bedtime-coders/bedstack/actions/workflows/tests.yml/badge.svg?event=push&branch=main&)](https://github.com/bedtime-coders/bedstack/actions/workflows/tests.yml?query=branch%3Amain+event%3Apush) [![Discord](https://img.shields.io/discord/1164270344115335320?label=Chat&color=5865f4&logo=discord&labelColor=121214)](https://discord.gg/8UcP9QB5AV) [![License](https://custom-icon-badges.demolab.com/github/license/bedtime-coders/bedstack?label=License&color=blue&logo=law&labelColor=0d1117)](https://github.com/bedtime-coders/bedstack/blob/main/LICENSE) [![Bun](https://img.shields.io/badge/Bun-14151a?logo=bun&logoColor=fbf0df)](https://bun.com/) [![ElysiaJS](https://custom-icon-badges.demolab.com/badge/ElysiaJS-0f172b.svg?logo=elysia)](https://elysiajs.com/) [![Drizzle](https://img.shields.io/badge/Drizzle-C5F74F?logo=drizzle&logoColor=000)](https://orm.drizzle.team/) [![Biome](https://img.shields.io/badge/Biome-24272f?logo=biome&logoColor=f6f6f9)](https://biomejs.dev/) [![Scalar](https://img.shields.io/badge/Scalar-080808?logo=scalar&logoColor=e7e7e7)](https://scalar.com/) [![Star](https://custom-icon-badges.demolab.com/github/stars/bedtime-coders/bedstack?logo=star&logoColor=373737&label=Star)](https://github.com/bedtime-coders/bedstack/stargazers/) [RealWorld](https://realworld-docs.netlify.app/) example app for [Bedstack](https://bedstack.js.org/) diff --git a/apps/conduit/package.json b/apps/conduit/package.json index 2f9308b3..ef00aa65 100644 --- a/apps/conduit/package.json +++ b/apps/conduit/package.json @@ -36,8 +36,9 @@ "@elysiajs/bearer": "^1.4.2", "@elysiajs/jwt": "^1.4.0", "@elysiajs/swagger": "^1.3.1", - "@sinclair/typebox": "0.34.47", "@yolk-oss/elysia-env": "^3.0.0", + "arkregex": "0.0.5", + "arktype": "^2.1.29", "chalk": "^5.6.2", "drizzle-orm": "^0.45.1", "elysia": "^1.4.21", @@ -54,8 +55,5 @@ "drizzle-seed": "^0.3.1", "pg": "^8.16.3", "typescript": "catalog:" - }, - "overrides": { - "@sinclair/typebox": "0.34.34" } } diff --git a/apps/conduit/src/app.module.ts b/apps/conduit/src/app.module.ts index 03df3026..deb0a61e 100644 --- a/apps/conduit/src/app.module.ts +++ b/apps/conduit/src/app.module.ts @@ -28,7 +28,7 @@ export const setupApp = () => { set.status = error.status; return pick(error, ['errors']); } - // Elysia validation errors (TypeBox based) + // Elysia validation errors (ArkType based) if (error instanceof ValidationError) { return formatValidationError(error); } diff --git a/apps/conduit/src/articles/articles.controller.ts b/apps/conduit/src/articles/articles.controller.ts index 785f335d..f024cb72 100644 --- a/apps/conduit/src/articles/articles.controller.ts +++ b/apps/conduit/src/articles/articles.controller.ts @@ -1,4 +1,5 @@ -import { Elysia, t } from 'elysia'; +import { type } from 'arktype'; +import { Elysia } from 'elysia'; import { StatusCodes } from 'http-status-codes'; import { setupArticles } from '@/articles/articles.module'; import { DEFAULT_LIMIT, DEFAULT_OFFSET } from '@/shared/constants'; @@ -173,9 +174,7 @@ export const articlesController = new Elysia().use(setupArticles).group( { beforeHandle: app.store.authService.requireLogin, response: { - [StatusCodes.NO_CONTENT]: t.Void({ - description: 'No content', - }), + [StatusCodes.NO_CONTENT]: type('undefined'), }, detail: { summary: 'Delete Article', diff --git a/apps/conduit/src/articles/dto/article-feed-query.dto.ts b/apps/conduit/src/articles/dto/article-feed-query.dto.ts index 0f23491c..0a3d7ea6 100644 --- a/apps/conduit/src/articles/dto/article-feed-query.dto.ts +++ b/apps/conduit/src/articles/dto/article-feed-query.dto.ts @@ -1,4 +1,4 @@ -import { t } from 'elysia'; +import { type } from 'arktype'; import { DEFAULT_LIMIT, DEFAULT_OFFSET, @@ -13,22 +13,9 @@ import { * - limit: number of items per request (default: DEFAULT_LIMIT, min: MIN_LIMIT, max: MAX_LIMIT) * - offset: number of items to skip (default: DEFAULT_OFFSET, min: MIN_OFFSET) */ -export const ArticleFeedQueryDto = t.Object({ - limit: t.Optional( - t.Integer({ - minimum: MIN_LIMIT, - maximum: MAX_LIMIT, - default: DEFAULT_LIMIT, - description: `Number of items per request (between ${MIN_LIMIT} and ${MAX_LIMIT}, defaults to ${DEFAULT_LIMIT})`, - }), - ), - offset: t.Optional( - t.Integer({ - minimum: MIN_OFFSET, - default: DEFAULT_OFFSET, - description: `Number of items to skip (at least ${MIN_OFFSET}, defaults to ${DEFAULT_OFFSET})`, - }), - ), +export const ArticleFeedQueryDto = type({ + limit: `${MIN_LIMIT} <= number.integer <= ${MAX_LIMIT} = ${DEFAULT_LIMIT}`, + offset: `number.integer >= ${MIN_OFFSET} = ${DEFAULT_OFFSET}`, }); -export type ArticleFeedQueryDto = typeof ArticleFeedQueryDto.static; +export type ArticleFeedQueryDto = typeof ArticleFeedQueryDto.infer; diff --git a/apps/conduit/src/articles/dto/article-response.dto.ts b/apps/conduit/src/articles/dto/article-response.dto.ts index 43badf80..ab24e3d5 100644 --- a/apps/conduit/src/articles/dto/article-response.dto.ts +++ b/apps/conduit/src/articles/dto/article-response.dto.ts @@ -1,22 +1,23 @@ -import { t } from 'elysia'; +import { type } from 'arktype'; -export const ArticleResponseDto = t.Object({ - article: t.Object({ - slug: t.String(), - title: t.String(), - description: t.String(), - body: t.String(), - tagList: t.Array(t.String()), - createdAt: t.String(), - updatedAt: t.String(), - favorited: t.Boolean(), - favoritesCount: t.Number(), - author: t.Object({ - username: t.String(), - bio: t.Union([t.Null(), t.String()]), - image: t.Union([t.Null(), t.String()]), - following: t.Boolean(), - }), - }), +export const ArticleResponseDto = type({ + article: { + slug: 'string', + title: 'string', + description: 'string', + body: 'string', + tagList: 'string[]', + createdAt: 'string', + updatedAt: 'string', + favorited: 'boolean', + favoritesCount: 'number', + author: { + username: 'string', + 'bio?': 'string | null', + 'image?': 'string | null', + following: 'boolean', + }, + }, }); -export type ArticleResponseDto = typeof ArticleResponseDto.static; + +export type ArticleResponseDto = typeof ArticleResponseDto.infer; diff --git a/apps/conduit/src/articles/dto/articles-response.dto.ts b/apps/conduit/src/articles/dto/articles-response.dto.ts index 695ae4b4..586a293e 100644 --- a/apps/conduit/src/articles/dto/articles-response.dto.ts +++ b/apps/conduit/src/articles/dto/articles-response.dto.ts @@ -1,8 +1,9 @@ -import { t } from 'elysia'; +import { type } from 'arktype'; import { ArticleResponseDto } from './article-response.dto'; -export const ArticlesResponseDto = t.Object({ - articles: t.Array(t.Omit(ArticleResponseDto.properties.article, ['body'])), - articlesCount: t.Number(), +export const ArticlesResponseDto = type({ + articles: ArticleResponseDto.get('article').omit('body').array(), + articlesCount: 'number', }); -export type ArticlesResponseDto = typeof ArticlesResponseDto.static; + +export type ArticlesResponseDto = typeof ArticlesResponseDto.infer; diff --git a/apps/conduit/src/articles/dto/create-article.dto.ts b/apps/conduit/src/articles/dto/create-article.dto.ts index def2dffe..80617815 100644 --- a/apps/conduit/src/articles/dto/create-article.dto.ts +++ b/apps/conduit/src/articles/dto/create-article.dto.ts @@ -1,11 +1,12 @@ -import { t } from 'elysia'; +import { type } from 'arktype'; -export const CreateArticleDto = t.Object({ - article: t.Object({ - title: t.String({ minLength: 1 }), - description: t.String({ minLength: 1 }), - body: t.String({ minLength: 1 }), - tagList: t.Optional(t.Array(t.String({ minLength: 1 }))), - }), +export const CreateArticleDto = type({ + article: { + title: 'string > 0', + description: 'string > 0', + body: 'string > 0', + 'tagList?': 'string[]', + }, }); -export type CreateArticleDto = typeof CreateArticleDto.static; + +export type CreateArticleDto = typeof CreateArticleDto.infer; diff --git a/apps/conduit/src/articles/dto/list-articles-query.dto.ts b/apps/conduit/src/articles/dto/list-articles-query.dto.ts index 42adf51e..74477174 100644 --- a/apps/conduit/src/articles/dto/list-articles-query.dto.ts +++ b/apps/conduit/src/articles/dto/list-articles-query.dto.ts @@ -1,12 +1,9 @@ -import { t } from 'elysia'; import { ArticleFeedQueryDto } from './article-feed-query.dto'; -export const ListArticlesQueryDto = t.Composite([ - ArticleFeedQueryDto, - t.Object({ - tag: t.Optional(t.String({ minLength: 1 })), - author: t.Optional(t.String({ minLength: 1 })), - favorited: t.Optional(t.String({ minLength: 1 })), - }), -]); -export type ListArticlesQueryDto = typeof ListArticlesQueryDto.static; +export const ListArticlesQueryDto = ArticleFeedQueryDto.merge({ + 'tag?': 'string > 0', + 'author?': 'string > 0', + 'favorited?': 'string > 0', +}); + +export type ListArticlesQueryDto = typeof ListArticlesQueryDto.infer; diff --git a/apps/conduit/src/articles/dto/update-article.dto.ts b/apps/conduit/src/articles/dto/update-article.dto.ts index d10a6552..4b8e754a 100644 --- a/apps/conduit/src/articles/dto/update-article.dto.ts +++ b/apps/conduit/src/articles/dto/update-article.dto.ts @@ -1,7 +1,8 @@ -import { t } from 'elysia'; +import { type } from 'arktype'; import { CreateArticleDto } from './create-article.dto'; -export const UpdateArticleDto = t.Object({ - article: t.Partial(CreateArticleDto.properties.article), +export const UpdateArticleDto = type({ + article: CreateArticleDto.get('article').partial(), }); -export type UpdateArticleDto = typeof UpdateArticleDto.static; + +export type UpdateArticleDto = typeof UpdateArticleDto.infer; diff --git a/apps/conduit/src/comments/comments.controller.ts b/apps/conduit/src/comments/comments.controller.ts index 63d2cc55..77eb9a1f 100644 --- a/apps/conduit/src/comments/comments.controller.ts +++ b/apps/conduit/src/comments/comments.controller.ts @@ -1,4 +1,5 @@ -import { Elysia, t } from 'elysia'; +import { type } from 'arktype'; +import { Elysia } from 'elysia'; import { StatusCodes } from 'http-status-codes'; import { setupComments } from './comments.module'; import { @@ -32,8 +33,8 @@ export const commentsController = new Elysia().use(setupComments).group( body: CreateCommentDto, response: { [StatusCodes.CREATED]: CommentResponseDto, - [StatusCodes.UNAUTHORIZED]: t.Void({ - description: 'Authentication required', + [StatusCodes.UNAUTHORIZED]: type({ + errors: 'Record', }), }, detail: { @@ -79,14 +80,12 @@ export const commentsController = new Elysia().use(setupComments).group( }, { beforeHandle: app.store.authService.requireLogin, - params: t.Object({ - slug: t.String(), - id: t.Numeric(), + params: type({ + slug: 'string', + id: 'string.numeric.parse', }), response: { - [StatusCodes.NO_CONTENT]: t.Void({ - description: 'No content', - }), + [StatusCodes.NO_CONTENT]: type('undefined'), }, detail: { summary: 'Delete Comment', diff --git a/apps/conduit/src/comments/dto/comment-response.dto.ts b/apps/conduit/src/comments/dto/comment-response.dto.ts index 8cf8f58a..373f449b 100644 --- a/apps/conduit/src/comments/dto/comment-response.dto.ts +++ b/apps/conduit/src/comments/dto/comment-response.dto.ts @@ -1,18 +1,18 @@ -import { t } from 'elysia'; +import { type } from 'arktype'; -export const CommentResponseDto = t.Object({ - comment: t.Object({ - id: t.Number(), - body: t.String(), - createdAt: t.String(), - updatedAt: t.String(), - author: t.Object({ - username: t.String(), - bio: t.Union([t.Null(), t.String()]), - image: t.Union([t.Null(), t.String()]), - following: t.Boolean(), - }), - }), +export const CommentResponseDto = type({ + comment: { + id: 'number', + body: 'string', + createdAt: 'string', + updatedAt: 'string', + author: { + username: 'string', + 'bio?': 'string | null', + 'image?': 'string | null', + following: 'boolean', + }, + }, }); -export type CommentResponseDto = typeof CommentResponseDto.static; +export type CommentResponseDto = typeof CommentResponseDto.infer; diff --git a/apps/conduit/src/comments/dto/comments-response.dto.ts b/apps/conduit/src/comments/dto/comments-response.dto.ts index e4b1d16b..09e241be 100644 --- a/apps/conduit/src/comments/dto/comments-response.dto.ts +++ b/apps/conduit/src/comments/dto/comments-response.dto.ts @@ -1,7 +1,8 @@ -import { t } from 'elysia'; +import { type } from 'arktype'; import { CommentResponseDto } from './comment-response.dto'; -export const CommentsResponseDto = t.Object({ - comments: t.Array(CommentResponseDto.properties.comment), +export const CommentsResponseDto = type({ + comments: CommentResponseDto.get('comment').array(), }); -export type CommentsResponseDto = typeof CommentsResponseDto.static; + +export type CommentsResponseDto = typeof CommentsResponseDto.infer; diff --git a/apps/conduit/src/comments/dto/create-comment.dto.ts b/apps/conduit/src/comments/dto/create-comment.dto.ts index 4c5015a0..b0502c50 100644 --- a/apps/conduit/src/comments/dto/create-comment.dto.ts +++ b/apps/conduit/src/comments/dto/create-comment.dto.ts @@ -1,8 +1,9 @@ -import { t } from 'elysia'; +import { type } from 'arktype'; -export const CreateCommentDto = t.Object({ - comment: t.Object({ - body: t.String({ minLength: 1 }), - }), +export const CreateCommentDto = type({ + comment: { + body: 'string > 0', + }, }); -export type CreateCommentDto = typeof CreateCommentDto.static; + +export type CreateCommentDto = typeof CreateCommentDto.infer; diff --git a/apps/conduit/src/profiles/dto/profile-response.dto.ts b/apps/conduit/src/profiles/dto/profile-response.dto.ts index bd58a95e..08c02063 100644 --- a/apps/conduit/src/profiles/dto/profile-response.dto.ts +++ b/apps/conduit/src/profiles/dto/profile-response.dto.ts @@ -1,12 +1,12 @@ -import { t } from 'elysia'; +import { type } from 'arktype'; -export const profileResponseSchema = t.Object({ - profile: t.Object({ - username: t.String(), - bio: t.Union([t.String(), t.Null()]), - image: t.Union([t.String(), t.Null()]), - following: t.Boolean(), - }), +export const profileResponseSchema = type({ + profile: { + username: 'string', + 'bio?': 'string | null', + 'image?': 'string | null', + following: 'boolean', + }, }); -export type ProfileResponseDto = typeof profileResponseSchema.static; +export type ProfileResponseDto = typeof profileResponseSchema.infer; diff --git a/apps/conduit/src/shared/errors/errors.utils.ts b/apps/conduit/src/shared/errors/errors.utils.ts index 120a01fb..02453f96 100644 --- a/apps/conduit/src/shared/errors/errors.utils.ts +++ b/apps/conduit/src/shared/errors/errors.utils.ts @@ -39,20 +39,47 @@ export function formatValidationError(error: ValidationError) { const result: Record = {}; for (const err of error.all) { - const path = 'path' in err ? parsePath(err.path) : 'general'; - let message = - 'schema' in err - ? (err.schema.description ?? err.summary ?? 'Invalid value') - : (err.summary ?? 'Invalid value'); + let path = 'general'; + if ('path' in err) { + if (Array.isArray(err.path)) { + path = err.path.join('.'); + } else if (typeof err.path === 'string') { + path = parsePath(err.path); + } + } + + if (!path || path === '/') { + path = 'general'; + } + + let message: string; + if ('message' in err && typeof err.message === 'string') { + message = err.message; + } else if ( + 'schema' in err && + err.schema && + typeof err.schema === 'object' && + 'description' in err.schema && + typeof err.schema.description === 'string' + ) { + message = err.schema.description; + } else if ('summary' in err && typeof err.summary === 'string') { + message = err.summary; + } else { + message = 'Invalid value'; + } // 🧼 Remove redundant prefix: "Property 'user.image' should be ..." message = message.replace(/^Property '.*?' should /i, 'should '); - if (!result[path]) { - result[path] = []; + if (path) { + let messages = result[path]; + if (!messages) { + messages = []; + result[path] = messages; + } + messages.push(message); } - - result[path].push(message); } // Remove duplicates in each path’s messages diff --git a/apps/conduit/src/tags/dto/tags-response.dto.ts b/apps/conduit/src/tags/dto/tags-response.dto.ts index 5d7a5f52..ab5d1275 100644 --- a/apps/conduit/src/tags/dto/tags-response.dto.ts +++ b/apps/conduit/src/tags/dto/tags-response.dto.ts @@ -1,7 +1,7 @@ -import { t } from 'elysia'; +import { type } from 'arktype'; -export const TagsResponseDto = t.Object({ - tags: t.Array(t.String()), +export const TagsResponseDto = type({ + tags: 'string[]', }); -export type TagsResponseDto = typeof TagsResponseDto.static; +export type TagsResponseDto = typeof TagsResponseDto.infer; diff --git a/apps/conduit/src/users/dto/create-user.dto.ts b/apps/conduit/src/users/dto/create-user.dto.ts index 62df8516..3c434187 100644 --- a/apps/conduit/src/users/dto/create-user.dto.ts +++ b/apps/conduit/src/users/dto/create-user.dto.ts @@ -1,30 +1,24 @@ -import { t } from 'elysia'; +import { regex } from 'arkregex'; +import { type } from 'arktype'; -export const CreateUserDto = t.Object({ - user: t.Object({ - email: t.String({ - format: 'email', - minLength: 3, - maxLength: 255, - description: 'must be a valid email address', - }), - password: t.String({ - minLength: 8, - maxLength: 100, - pattern: '^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)[a-zA-Z\\d@$!%*?&]{8,}$', - description: - 'must be at least 8 characters and contain uppercase, lowercase, and numbers', - }), - username: t.String({ - minLength: 3, - maxLength: 50, - pattern: '^[a-zA-Z0-9_-]+$', - description: - 'must be 3-50 characters and contain only letters, numbers, underscores, and hyphens', - }), - bio: t.Optional(t.String({ maxLength: 1000 })), - image: t.Optional(t.String({ format: 'uri' })), - }), +export const CreateUserDto = type({ + user: { + email: 'string.email', + password: type( + regex('^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)[a-zA-Z\\d@$!%*?&]{8,}$'), + ) + .and('8 <= string <= 100') + .describe( + 'at least 8 characters and contain uppercase, lowercase, and numbers', + ), + username: type(regex('^[a-zA-Z0-9_-]+$')) + .and('3 <= string <= 50') + .describe( + '3-50 characters and contain only letters, numbers, underscores, and hyphens', + ), + 'bio?': 'string <= 1000', + 'image?': 'string.url', + }, }); -export type CreateUserDto = typeof CreateUserDto.static; +export type CreateUserDto = typeof CreateUserDto.infer; diff --git a/apps/conduit/src/users/dto/login-user.dto.ts b/apps/conduit/src/users/dto/login-user.dto.ts index 422c309e..01bb63e1 100644 --- a/apps/conduit/src/users/dto/login-user.dto.ts +++ b/apps/conduit/src/users/dto/login-user.dto.ts @@ -1,15 +1,10 @@ -import { t } from 'elysia'; +import { type } from 'arktype'; -export const LoginUserDto = t.Object({ - user: t.Object({ - email: t.String({ - format: 'email', - minLength: 3, - maxLength: 255, - description: 'must be a valid email address', - }), - password: t.String(), - }), +export const LoginUserDto = type({ + user: { + email: 'string.email', + password: 'string', + }, }); -export type LoginUserDto = typeof LoginUserDto.static; +export type LoginUserDto = typeof LoginUserDto.infer; diff --git a/apps/conduit/src/users/dto/update-user.dto.ts b/apps/conduit/src/users/dto/update-user.dto.ts index 43db2eab..1e56d3b1 100644 --- a/apps/conduit/src/users/dto/update-user.dto.ts +++ b/apps/conduit/src/users/dto/update-user.dto.ts @@ -1,8 +1,8 @@ -import { t } from 'elysia'; +import { type } from 'arktype'; import { CreateUserDto } from './create-user.dto'; -export const UpdateUserDto = t.Object({ - user: t.Partial(CreateUserDto.properties.user), +export const UpdateUserDto = type({ + user: CreateUserDto.get('user').partial(), }); -export type UpdateUserDto = typeof UpdateUserDto.static; +export type UpdateUserDto = typeof UpdateUserDto.infer; diff --git a/apps/conduit/src/users/dto/user-response.dto.ts b/apps/conduit/src/users/dto/user-response.dto.ts index 64a577af..7a07e082 100644 --- a/apps/conduit/src/users/dto/user-response.dto.ts +++ b/apps/conduit/src/users/dto/user-response.dto.ts @@ -1,13 +1,13 @@ -import { t } from 'elysia'; +import { type } from 'arktype'; -export const UserResponseDto = t.Object({ - user: t.Object({ - email: t.String(), - token: t.String(), - username: t.String(), - bio: t.Union([t.String(), t.Null()]), - image: t.Union([t.String(), t.Null()]), - }), +export const UserResponseDto = type({ + user: { + email: 'string', + token: 'string', + username: 'string', + bio: 'string | null', + image: 'string | null', + }, }); -export type UserResponseDto = typeof UserResponseDto.static; +export type UserResponseDto = typeof UserResponseDto.infer; diff --git a/apps/www/getting-started.md b/apps/www/getting-started.md index d2eb69bb..8aedd7d1 100644 --- a/apps/www/getting-started.md +++ b/apps/www/getting-started.md @@ -1,6 +1,6 @@ # Getting Started -Getting started with this RealWorld project is as easy as installing a few prerequisites, running a few commands, and opening your favorite text editor. We'd love to have you [contribute](https://github.com/bedtime-coders/bedstack/blob/main/CONTRIBUTING.md) to this project. If you have any questions, please reach out on [Discord](https://discord.gg/8UcP9QB5AV) or [GitHub Discussions]( +Getting started with this RealWorld project is as easy as installing a few prerequisites, running a few commands, and opening your favorite text editor. [Contributions](https://github.com/bedtime-coders/bedstack/blob/main/CONTRIBUTING.md) are welcome. If you have any questions, please reach out on [Discord](https://discord.gg/8UcP9QB5AV) or [GitHub Discussions]( https://github.com/bedtime-coders/bedstack/discussions )! @@ -8,10 +8,10 @@ Getting started with this RealWorld project is as easy as installing a few prere ### Prerequisites -- [Bun](https://bun.sh/) version 1.0.6 or higher. +- [Bun](https://bun.com/) version 1.0.6 or higher. - Terminal for accessing Bun via its command-line interface (CLI). - Text Editor with TypeScript support. - - We recommend [Visual Studio Code](https://code.visualstudio.com/); other IDEs have been reported to cause issues with ElysiaJS's type inference system. + - [Visual Studio Code](https://code.visualstudio.com/) is recommended for the best experience; other IDEs have been reported to cause issues with ElysiaJS's type inference system. ### Setup @@ -43,7 +43,7 @@ $ bun dev ## What's Next? -* Once you're ready to contribute, check out our [contributing guide](https://github.com/bedtime-coders/bedstack/blob/main/CONTRIBUTING.md). We're excited to have you! -* If you're looking for a place to start, check out our [good first issues](https://github.com/bedtime-coders/bedstack/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22). -* If you need support, check out our [support page](https://github.com/bedtime-coders/bedstack/blob/main/SUPPORT.md). -* If you have any questions or just want to chat, join our [Discord server](https://discord.gg/8UcP9QB5AV) or [GitHub Discussions](https://github.com/bedtime-coders/bedstack/discussions). +* To contribute, see the [contributing guide](https://github.com/bedtime-coders/bedstack/blob/main/CONTRIBUTING.md). +* For a place to start, see the [good first issues](https://github.com/bedtime-coders/bedstack/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22). +* For support, visit the [support page](https://github.com/bedtime-coders/bedstack/blob/main/SUPPORT.md). +* To ask questions or chat, join the [Discord server](https://discord.gg/8UcP9QB5AV) or [GitHub Discussions](https://github.com/bedtime-coders/bedstack/discussions). diff --git a/apps/www/index.md b/apps/www/index.md index f3ccdf6d..813da3a7 100644 --- a/apps/www/index.md +++ b/apps/www/index.md @@ -23,7 +23,7 @@ hero: features: - title: Bun details: All-in-one JavaScript runtime & toolkit designed for speed, complete with a bundler, test runner, and Node.js-compatible package manager. - link: https://bun.sh/ + link: https://bun.com/ icon: src: /bun-press-kit/logo-centered.svg diff --git a/apps/www/what-is-bedstack.md b/apps/www/what-is-bedstack.md index ca178426..ffee7a1f 100644 --- a/apps/www/what-is-bedstack.md +++ b/apps/www/what-is-bedstack.md @@ -1,35 +1,46 @@ # What is Bedstack? -Bedstack is a bleeding-edge tech stack for building backend applications with TypeScript. It's an acronym for [**B**un](https://bun.sh/) + [**E**lysiaJS](https://elysiajs.com/) + [**D**rizzle](https://orm.drizzle.team/). +Bedstack is a bleeding-edge tech stack for building backend applications with TypeScript. The name is an acronym for [**B**un](https://bun.com/) + [**E**lysiaJS](https://elysiajs.com/) + [**D**rizzle](https://orm.drizzle.team/) + [**A**rkType](https://arktype.io/). -Its core philosophy is simplicity on the surface with immense power underneath; it will make you [fall into the pit of success](https://blog.codinghorror.com/falling-into-the-pit-of-success/). It is typesafe by default, and flexible enough to handle complex use cases as demonstrated by this [RealWorld](https://github.com/gothinkster/realworld) example project. +Bedstack’s philosophy is simple on the surface, powerful underneath. It is designed to guide developers into the pit of success by default. Strong type safety is not optional, and the stack remains flexible enough to support complex, real production use cases, as demonstrated by its [RealWorld](https://github.com/gothinkster/realworld) example project. -This project is an implementation of the [RealWorld backend spec](https://realworld-docs.netlify.app/specifications/backend/introduction/). By design, it is completely interchangeable with any of the other [backend implementations](https://codebase.show/projects/realworld?category=backend&language=typescript) in the RealWorld family, and can be used with any of the RealWorld [frontend implementations](https://codebase.show/projects/realworld?category=frontend). +This [repository](https://github.com/bedtime-coders/bedstack) implements the RealWorld backend specification. By design, it is fully interchangeable with other RealWorld backend implementations and works with any RealWorld-compatible frontend. ## Core technologies -We carefully chose some of the latest and greatest technologies from the bleeding edge that share our core philosophy. They are: +The selected technologies share Bedstack's core philosophy. They are: -- **[Bun](https://bun.sh/)** - All-in-one JavaScript runtime & toolkit designed for speed, complete with a bundler, test runner, and Node.js-compatible package manager. +- **[Bun](https://bun.com/)** - All-in-one JavaScript runtime & toolkit designed for speed, complete with a bundler, test runner, and Node.js-compatible package manager. - **[ElysiaJS](https://elysiajs.com/)** - TypeScript framework supercharged by Bun with End-to-End Type Safety, unified type system and outstanding developer experience. -- **[Drizzle](https://orm.drizzle.team/)** - Lightweight, performant, typesafe, non lactose, gluten-free, flexible, serverless-ready, and headless TypeScript ORM with both relational and SQL-like query APIs. +- **[Drizzle](https://orm.drizzle.team/)** - Lightweight, performant, typesafe, non-lactose, gluten-free, flexible, serverless-ready, and headless TypeScript ORM with both relational and SQL-like query APIs. + +While not part of the flashy acronym, these tools are also core to Bedstack’s philosophy: + +- **[ArkType](https://arktype.io/)** - A runtime validator that can 1:1 match TypeScript types. It aligns strongest with Bedstack's philosophy of being typesafe by default with minimal ceremony. + +- **[Biome](https://biomejs.dev/)** - A toolchain for web projects, aiming to provide a fast, reliable, and user-friendly experience for formatting, linting, and other code quality tasks. + ## Why RealWorld? -The RealWorld project is fantastic because it's suitable for developers across the entire spectrum of experience. +The RealWorld project provides a rare combination of clarity and depth. + +For **beginners**, it offers a concrete API and dozens of implementations across languages and frameworks. This makes it easy to learn new tools, compare approaches, and validate understanding against real examples. + +For **experienced developers**, RealWorld is a practical benchmark. It is a structured way to evaluate new technologies, stress-test architectural ideas, and contribute high-quality reference implementations back to the community. -**Beginners** will find the dozens of implementations in different languages and frameworks to be a great way to learn new technologies, and to compare their work to the best practices of similar tools. Not sure how to implement a feature? Just look at how [other implementations](https://codebase.show/projects/realworld?category=backend&language=typescript) have done it! They all implement the exact same API, so you can compare them side-by-side. +## Why Bun, ElysiaJS, Drizzle, or any of the core technologies? -**Experts** will find satisfaction in contributing best practice examples to the community, and in helping beginners learn the ropes. They'll also find that the RealWorld spec is a great way to test out new technologies, and to compare them to other tools in the same space. A new tool came out that is all the rage? Just follow the well-defined spec and see how it holds up in the RealWorld! +When [Bun](https://bun.com/) was announced, it promised a simpler and more unified approach to modern TypeScript development. It was tested under real conditions. -## Why Bun, ElysiaJS, and Drizzle? +The question was simple: -When [Bun](https://bun.sh/) was announced, the community expectedly touted it as a [Node.js killer](https://levelup.gitconnected.com/is-bun-js-the-node-js-killer-ffeb0f89196a). It promised to eliminate many of the hassles that come with TypeScript development, and provide an ecosystem that is unified, performant, and easy to use. +> What better way to evaluate a new runtime than to build a full RealWorld backend with it? -We wanted to put it to the test. We thought: +ElysiaJS, Drizzle, and ArkType were selected because they are designed with Bun and TypeScript-first development in mind, and because they reinforce the same core values. -> What better way to test a new tool than to implement a RealWorld app with it? +Bedstack is unapologetically opinionated. A tech stack is not a menu of options, it is a curated path to correct decisions. ArkType offers excellent type inference and makes the correct choice the easiest choice. -We chose other tools on the bleeding edge that have been specifically designed with Bun in mind, like [ElysiaJS](https://elysiajs.com/) and [Drizzle](https://orm.drizzle.team/). We wanted to see how they would work together, and how they compare to other implementations in the RealWorld family. +That said, Bedstack relies on the [Standard Schema](https://github.com/standard-schema/standard-schema) interface. Advanced users can substitute another compliant validator if needed, but ArkType remains the recommended and supported path. diff --git a/bun.lock b/bun.lock index c87e50e4..c02431c9 100644 --- a/bun.lock +++ b/bun.lock @@ -19,8 +19,9 @@ "@elysiajs/bearer": "^1.4.2", "@elysiajs/jwt": "^1.4.0", "@elysiajs/swagger": "^1.3.1", - "@sinclair/typebox": "0.34.47", "@yolk-oss/elysia-env": "^3.0.0", + "arkregex": "0.0.5", + "arktype": "^2.1.29", "chalk": "^5.6.2", "drizzle-orm": "^0.45.1", "elysia": "^1.4.21", @@ -96,6 +97,10 @@ "@algolia/requester-node-http": ["@algolia/requester-node-http@5.46.3", "", { "dependencies": { "@algolia/client-common": "5.46.3" } }, "sha512-4No9iTjr1GZ0JWsFbQJj9aZBnmKyY1sTxOoEud9+SGe3U6iAulF0A0lI4cWi/F/Gcfg8V3jkaddcqSQKDnE45w=="], + "@ark/schema": ["@ark/schema@0.56.0", "", { "dependencies": { "@ark/util": "0.56.0" } }, "sha512-ECg3hox/6Z/nLajxXqNhgPtNdHWC9zNsDyskwO28WinoFEnWow4IsERNz9AnXRhTZJnYIlAJ4uGn3nlLk65vZA=="], + + "@ark/util": ["@ark/util@0.56.0", "", {}, "sha512-BghfRC8b9pNs3vBoDJhcta0/c1J1rsoS1+HgVUreMFPdhz/CRAKReAu57YEllNaSy98rWAdY1gE+gFup7OXpgA=="], + "@babel/helper-string-parser": ["@babel/helper-string-parser@7.27.1", "", {}, "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA=="], "@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.28.5", "", {}, "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q=="], @@ -366,6 +371,10 @@ "argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], + "arkregex": ["arkregex@0.0.5", "", { "dependencies": { "@ark/util": "0.56.0" } }, "sha512-ncYjBdLlh5/QnVsAA8De16Tc9EqmYM7y/WU9j+236KcyYNUXogpz3sC4ATIZYzzLxwI+0sEOaQLEmLmRleaEXw=="], + + "arktype": ["arktype@2.1.29", "", { "dependencies": { "@ark/schema": "0.56.0", "@ark/util": "0.56.0", "arkregex": "0.0.5" } }, "sha512-jyfKk4xIOzvYNayqnD8ZJQqOwcrTOUbIU4293yrzAjA3O1dWh61j71ArMQ6tS/u4pD7vabSPe7nG3RCyoXW6RQ=="], + "async": ["async@3.2.6", "", {}, "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA=="], "birpc": ["birpc@2.9.0", "", {}, "sha512-KrayHS5pBi69Xi9JmvoqrIgYGDkD6mcSe/i6YKi3w5kekCLzrX4+nawcXqrj2tIp50Kw/mT/s3p+GVK0A0sKxw=="], diff --git a/package.json b/package.json index 03f7a6c1..3de496a0 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "bedstack", "title": "Bedstack", - "description": "The stack you don't want to sleep on", + "description": "Bun + ElysiaJS + Drizzle = the stack you don't want to sleep on", "type": "module", "private": true, "scripts": {