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 @@
Bedstack-[](https://github.com/bedtime-coders/bedstack/actions/workflows/tests.yml?query=branch%3Amain+event%3Apush) [](https://discord.gg/8UcP9QB5AV) [](https://github.com/bedtime-coders/bedstack/blob/main/LICENSE) [](https://bun.sh/) [](https://elysiajs.com/) [](https://drizzle.team/) [](https://biomejs.dev/) [](https://scalar.com/) [](https://github.com/bedtime-coders/bedstack/stargazers/) +[](https://github.com/bedtime-coders/bedstack/actions/workflows/tests.yml?query=branch%3Amain+event%3Apush) [](https://discord.gg/8UcP9QB5AV) [](https://github.com/bedtime-coders/bedstack/blob/main/LICENSE) [](https://bun.com/) [](https://elysiajs.com/) [](https://drizzle.team/) [](https://arktype.io/) [](https://biomejs.dev/) [](https://scalar.com/) [](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 @@
Conduit - Bedstack real world example-[](https://github.com/bedtime-coders/bedstack/actions/workflows/tests.yml?query=branch%3Amain+event%3Apush) [](https://discord.gg/8UcP9QB5AV) [](https://github.com/bedtime-coders/bedstack/blob/main/LICENSE) [](https://bun.sh/) [](https://elysiajs.com/) [](https://orm.drizzle.team/) [](https://biomejs.dev/) [](https://scalar.com/) [](https://github.com/bedtime-coders/bedstack/stargazers/) +[](https://github.com/bedtime-coders/bedstack/actions/workflows/tests.yml?query=branch%3Amain+event%3Apush) [](https://discord.gg/8UcP9QB5AV) [](https://github.com/bedtime-coders/bedstack/blob/main/LICENSE) [](https://bun.com/) [](https://elysiajs.com/) [](https://orm.drizzle.team/) [](https://biomejs.dev/) [](https://scalar.com/) [](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 |