Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@ Hey there! We're thrilled that you'd like to contribute to this project. Your he

## 👨‍💻 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

Expand Down Expand Up @@ -87,7 +87,9 @@ We use [Biome](https://biomejs.dev/) for both linting and formatting with [a few

#### IDE Setup

We recommend using [VS Code](https://code.visualstudio.com/) along with the [Biome extension](https://marketplace.visualstudio.com/items?itemName=biomejs.biome).
We recommend using [VS Code](https://code.visualstudio.com/) 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.

Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
<img src="apps/www/public/logo-mini.png" alt="Logo for Bedstack RealWorld example" width=200>
<h1>Bedstack</h1>

[![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)

Expand Down
2 changes: 1 addition & 1 deletion apps/conduit/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<img src="../www/public/logo-mini.png" alt="Logo for Bedstack RealWorld example" width=200>
<h1>Conduit - Bedstack real world example</h1>

[![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/)
</div>
Expand Down
6 changes: 2 additions & 4 deletions apps/conduit/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -54,8 +55,5 @@
"drizzle-seed": "^0.3.1",
"pg": "^8.16.3",
"typescript": "catalog:"
},
"overrides": {
"@sinclair/typebox": "0.34.34"
}
}
2 changes: 1 addition & 1 deletion apps/conduit/src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
7 changes: 3 additions & 4 deletions apps/conduit/src/articles/articles.controller.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand Down Expand Up @@ -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',
Expand Down
23 changes: 5 additions & 18 deletions apps/conduit/src/articles/dto/article-feed-query.dto.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { t } from 'elysia';
import { type } from 'arktype';
import {
DEFAULT_LIMIT,
DEFAULT_OFFSET,
Expand All @@ -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;
41 changes: 21 additions & 20 deletions apps/conduit/src/articles/dto/article-response.dto.ts
Original file line number Diff line number Diff line change
@@ -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;
11 changes: 6 additions & 5 deletions apps/conduit/src/articles/dto/articles-response.dto.ts
Original file line number Diff line number Diff line change
@@ -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;
19 changes: 10 additions & 9 deletions apps/conduit/src/articles/dto/create-article.dto.ts
Original file line number Diff line number Diff line change
@@ -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;
17 changes: 7 additions & 10 deletions apps/conduit/src/articles/dto/list-articles-query.dto.ts
Original file line number Diff line number Diff line change
@@ -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;
9 changes: 5 additions & 4 deletions apps/conduit/src/articles/dto/update-article.dto.ts
Original file line number Diff line number Diff line change
@@ -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;
19 changes: 8 additions & 11 deletions apps/conduit/src/comments/comments.controller.ts
Original file line number Diff line number Diff line change
@@ -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 {
Expand Down Expand Up @@ -32,9 +33,7 @@ export const commentsController = new Elysia().use(setupComments).group(
body: CreateCommentDto,
response: {
[StatusCodes.CREATED]: CommentResponseDto,
[StatusCodes.UNAUTHORIZED]: t.Void({
description: 'Authentication required',
}),
[StatusCodes.UNAUTHORIZED]: type('undefined'),
},
detail: {
summary: 'Add Comments to an Article',
Expand Down Expand Up @@ -72,21 +71,19 @@ export const commentsController = new Elysia().use(setupComments).group(
async ({ params, store, request, set }) => {
await store.commentsService.deleteComment(
params.slug,
params.id,
Number(params.id),
await store.authService.getUserIdFromHeader(request.headers),
);
set.status = StatusCodes.NO_CONTENT;
},
{
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',
Expand Down
30 changes: 15 additions & 15 deletions apps/conduit/src/comments/dto/comment-response.dto.ts
Original file line number Diff line number Diff line change
@@ -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;
9 changes: 5 additions & 4 deletions apps/conduit/src/comments/dto/comments-response.dto.ts
Original file line number Diff line number Diff line change
@@ -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;
13 changes: 7 additions & 6 deletions apps/conduit/src/comments/dto/create-comment.dto.ts
Original file line number Diff line number Diff line change
@@ -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;
18 changes: 9 additions & 9 deletions apps/conduit/src/profiles/dto/profile-response.dto.ts
Original file line number Diff line number Diff line change
@@ -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;
Loading