diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000..1521c8b --- /dev/null +++ b/.eslintignore @@ -0,0 +1 @@ +dist diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000..8d82e9b --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,17 @@ +const path = require('path'); + +module.exports = { + root: true, + extends: ['standard-with-typescript', 'prettier'], + parserOptions: { + project: [ + path.resolve(__dirname, './packages/*/tsconfig.json'), + path.resolve(__dirname, './apps/*/tsconfig.json') + ] + }, + rules: { + '@typescript-eslint/strict-boolean-expressions': 'off', + '@typescript-eslint/explicit-function-return-type': 'off', + '@typescript-eslint/restrict-template-expressions': 'off' + } +}; diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..2b74247 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,33 @@ +name: "CI" + +on: + push: + branches: [main] + pull_request: + +jobs: + build: + name: "Run CI pipeline" + runs-on: ubuntu-latest + strategy: + matrix: + node: [14, 16, 18] + steps: + - uses: actions/checkout@v3 + - uses: pnpm/action-setup@v2.2.4 + with: + version: 7.5.1 + - uses: actions/setup-node@v3 + with: + node-version: ${{ matrix.node }} + cache: "pnpm" + - run: pnpm i + - run: pnpm build + - run: pnpm type-check + - run: pnpm lint + - run: pnpm test + + # - name: Coverage + # uses: codecov/codecov-action@v3 + # with: + # fail_ci_if_error: true diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b512c09 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +node_modules \ No newline at end of file diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..b01cb82 --- /dev/null +++ b/.npmrc @@ -0,0 +1,2 @@ +auto-install-peers=true +ignore-workspace-root-check=true diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..3afeedf --- /dev/null +++ b/.prettierignore @@ -0,0 +1,3 @@ +dist +.next +coverage diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..92fae2c --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,8 @@ +All notable changes to this project will be documented in this file. +We follow the [Semantic Versioning 2.0.0](http://semver.org/) format. + +## 1.0.0 - 2022-12-03 + +### Added + +- Added initial version of Next REST Framework. diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..81f416e --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,132 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +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, caste, color, religion, or sexual +identity and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +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, + and learning from the experience +- Focusing on what is best not just for us as individuals, but for the overall + community + +Examples of unacceptable behavior include: + +- The use of sexualized language or imagery, and sexual attention or advances of + any kind +- Trolling, insulting or derogatory comments, and personal or political attacks +- Public or private harassment +- Publishing others' private information, such as a physical or email address, + without their explicit permission +- Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our 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. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +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, +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 at +blomqvist_markus@hotmail.com. +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series of +actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or permanent +ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within the +community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.1, available at +[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. + +Community Impact Guidelines were inspired by +[Mozilla's code of conduct enforcement ladder][mozilla coc]. + +For answers to common questions about this code of conduct, see the FAQ at +[https://www.contributor-covenant.org/faq][faq]. Translations are available at +[https://www.contributor-covenant.org/translations][translations]. + +[homepage]: https://www.contributor-covenant.org +[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html +[mozilla coc]: https://github.com/mozilla/diversity +[faq]: https://www.contributor-covenant.org/faq +[translations]: https://www.contributor-covenant.org/translations diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..71b2a28 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,63 @@ +# Development + +Clone the repository: + +``` +git clone git@github.com:blomqma/next-rest-framework.git +``` + +Install dependencies: + +``` +pnpm i +``` + +Build the `next-rest-framework` package: + +``` +pnpm run build +``` + +Run the development Next.js project: + +``` +pnpm run dev +``` + +To test your changes in the `next-rest-framework` package against the dev project, simply rebuild the package with `pnpm run build` and the changes will be synced automatically. + +Run unit tests: + +``` +pnpm run test +``` + +Run unit tests in watch mode: + +``` +pnpm run test:watch +``` + +Run formatting: + +``` +pnpm run format +``` + +Run linting: + +``` +pnpm run lint +``` + +# Documentation + +All documentation is in [README.md](https://github.com/blomqma/next-rest-framework/blob/main/README.md), please update that when needed. + +# Pull requests + +All pull requests are welcome and done against the `main` branch, please write tests for all changes, write documentation when needed and use descriptive commit messages. No need to update the changelog. + +# License + +By contributing, you agree to license your contribution under the [LICENSE](https://github.com/blomqma/next-rest-framework/blob/main/LICENCE). diff --git a/LICENCE b/LICENCE new file mode 120000 index 0000000..5dd47ba --- /dev/null +++ b/LICENCE @@ -0,0 +1 @@ +packages/next-rest-framework/LICENCE \ No newline at end of file diff --git a/README.md b/README.md new file mode 120000 index 0000000..a2e0fdb --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +packages/next-rest-framework/README.md \ No newline at end of file diff --git a/apps/dev/.eslintrc.js b/apps/dev/.eslintrc.js new file mode 100644 index 0000000..df72793 --- /dev/null +++ b/apps/dev/.eslintrc.js @@ -0,0 +1,5 @@ +module.exports = { + rules: { + '@typescript-eslint/triple-slash-reference': 'off' + } +} diff --git a/apps/dev/.gitignore b/apps/dev/.gitignore new file mode 100644 index 0000000..c87c9b3 --- /dev/null +++ b/apps/dev/.gitignore @@ -0,0 +1,36 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* +.pnpm-debug.log* + +# local env files +.env*.local + +# vercel +.vercel + +# typescript +*.tsbuildinfo +next-env.d.ts diff --git a/apps/dev/next-rest-framework/client.ts b/apps/dev/next-rest-framework/client.ts new file mode 100644 index 0000000..e321a5e --- /dev/null +++ b/apps/dev/next-rest-framework/client.ts @@ -0,0 +1,5 @@ +import { NextRestFramework } from 'next-rest-framework'; + +export const { defineCatchAllHandler, defineEndpoints } = NextRestFramework({ + middleware: () => ({ foo: 'foo', bar: 'bar', baz: 'baz' }) +}); diff --git a/apps/dev/next.config.js b/apps/dev/next.config.js new file mode 100644 index 0000000..3d3bc99 --- /dev/null +++ b/apps/dev/next.config.js @@ -0,0 +1,7 @@ +/** @type {import('next').NextConfig} */ +const nextConfig = { + reactStrictMode: true, + swcMinify: true, +}; + +module.exports = nextConfig; diff --git a/apps/dev/package.json b/apps/dev/package.json new file mode 100644 index 0000000..afc296c --- /dev/null +++ b/apps/dev/package.json @@ -0,0 +1,21 @@ +{ + "name": "dev", + "private": true, + "scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start", + "lint": "next lint", + "type-check": "tsc --noEmit" + }, + "dependencies": { + "next-rest-framework": "workspace:*" + }, + "devDependencies": { + "@types/node": "18.11.9", + "@types/react": "18.0.25", + "@types/react-dom": "18.0.8", + "react": "18.2.0", + "react-dom": "18.2.0" + } +} diff --git a/apps/dev/pages/api/[[...next-rest-framework]].ts b/apps/dev/pages/api/[[...next-rest-framework]].ts new file mode 100644 index 0000000..4980bb9 --- /dev/null +++ b/apps/dev/pages/api/[[...next-rest-framework]].ts @@ -0,0 +1,18 @@ +import { defineCatchAllHandler } from 'next-rest-framework/client'; +import * as z from 'zod'; + +export default defineCatchAllHandler({ + GET: { + responses: [ + { + status: 200, + contentType: 'text/plain', + schema: z.string() + } + ], + handler: ({ res }) => { + res.setHeader('Content-Type', 'text/plain'); + res.status(200).send('Hello, world!'); + } + } +}); diff --git a/apps/dev/pages/api/demo.ts b/apps/dev/pages/api/demo.ts new file mode 100644 index 0000000..6443f32 --- /dev/null +++ b/apps/dev/pages/api/demo.ts @@ -0,0 +1,31 @@ +import z from 'zod'; +import { defineEndpoints } from 'next-rest-framework/client'; + +export default defineEndpoints({ + POST: { + requestBody: { + contentType: 'application/json', + schema: z.object({ + bar: z.string() + }) + }, + responses: [ + { + status: 200, + contentType: 'text/html', + schema: z.object({ + bar: z.string() + }) + } + ], + handler: async ({ + res, + req: { + body: { bar } + } + }) => { + res.setHeader('content-type', 'text/html'); + res.status(200).json({ bar }); + } + } +}); diff --git a/apps/dev/pages/api/openapi.json.ts b/apps/dev/pages/api/openapi.json.ts new file mode 100644 index 0000000..178fbde --- /dev/null +++ b/apps/dev/pages/api/openapi.json.ts @@ -0,0 +1,19 @@ +import { defineEndpoints } from 'next-rest-framework/client'; +import * as z from 'zod'; + +export default defineEndpoints({ + GET: { + responses: [ + { + status: 200, + contentType: 'application/json', + schema: z.object({ + foo: z.string() + }) + } + ], + handler: ({ res }) => { + res.status(200).json({ foo: 'bar' }); + } + } +}); diff --git a/apps/dev/pages/api/todos.ts b/apps/dev/pages/api/todos.ts new file mode 100644 index 0000000..c42ae98 --- /dev/null +++ b/apps/dev/pages/api/todos.ts @@ -0,0 +1,108 @@ +import sampleData from 'sample-data.json'; +import * as z from 'zod'; +import * as yup from 'yup'; +import { defineEndpoints } from 'next-rest-framework/client'; + +export default defineEndpoints({ + middleware: ({ params: { foo, bar, baz } }) => ({ + foo: bar, + bar: baz, + baz: foo, + asd: 'asd' + }), + GET: { + middleware: () => ({ + qux: 'qux' + }), + responses: [ + { + status: 200, + contentType: 'text/html', + schema: z.object({ + data: z.array( + z.object({ + userId: z.number(), + id: z.number(), + title: z.string(), + completed: z.boolean() + }) + ) + }) + } + ], + handler: async ({ res, params: { foo, bar, baz, qux } }) => { + console.log({ foo, bar, baz, qux }); + res.setHeader('content-type', 'text/html'); + res.status(200).json({ data: sampleData }); + } + }, + POST: { + tags: ['Todo'], + description: 'Create a new todo', + externalDocs: { + description: 'Find out more about Swagger', + url: 'http://swagger.io' + }, + operationId: 'createTodo', + parameters: [ + { + name: 'todo', + in: 'body', + description: 'The todo to create', + required: true, + deprecated: false, + allowEmptyValue: false + } + ], + deprecated: false, + security: [ + { + foo: ['bar'] + } + ], + servers: [ + { + url: 'http://localhost:3000', + description: 'Development server', + variables: {} + } + ], + requestBody: { + description: 'The todo to create', + contentType: 'application/json', + schema: yup.object({ + foo: yup.string(), + bar: yup.number() + }), + example: { + foo: 'Buy milk', + bar: 1 + }, + examples: {}, + encoding: {}, + required: true + }, + responses: [ + { + status: 201, + contentType: 'application/json', + schema: z.object({ + data: z.array( + z.object({ + userId: z.number(), + id: z.number(), + title: z.string(), + completed: z.boolean() + }) + ) + }) + } + ], + middleware: () => ({ + qux: 'qux' + }), + handler: async ({ res }) => { + res.status(201).json({ data: sampleData }); + } + } +}); diff --git a/apps/dev/sample-data.json b/apps/dev/sample-data.json new file mode 100644 index 0000000..a718b9e --- /dev/null +++ b/apps/dev/sample-data.json @@ -0,0 +1,1202 @@ +[ + { + "userId": 1, + "id": 1, + "title": "delectus aut autem", + "completed": false + }, + { + "userId": 1, + "id": 2, + "title": "quis ut nam facilis et officia qui", + "completed": false + }, + { + "userId": 1, + "id": 3, + "title": "fugiat veniam minus", + "completed": false + }, + { + "userId": 1, + "id": 4, + "title": "et porro tempora", + "completed": true + }, + { + "userId": 1, + "id": 5, + "title": "laboriosam mollitia et enim quasi adipisci quia provident illum", + "completed": false + }, + { + "userId": 1, + "id": 6, + "title": "qui ullam ratione quibusdam voluptatem quia omnis", + "completed": false + }, + { + "userId": 1, + "id": 7, + "title": "illo expedita consequatur quia in", + "completed": false + }, + { + "userId": 1, + "id": 8, + "title": "quo adipisci enim quam ut ab", + "completed": true + }, + { + "userId": 1, + "id": 9, + "title": "molestiae perspiciatis ipsa", + "completed": false + }, + { + "userId": 1, + "id": 10, + "title": "illo est ratione doloremque quia maiores aut", + "completed": true + }, + { + "userId": 1, + "id": 11, + "title": "vero rerum temporibus dolor", + "completed": true + }, + { + "userId": 1, + "id": 12, + "title": "ipsa repellendus fugit nisi", + "completed": true + }, + { + "userId": 1, + "id": 13, + "title": "et doloremque nulla", + "completed": false + }, + { + "userId": 1, + "id": 14, + "title": "repellendus sunt dolores architecto voluptatum", + "completed": true + }, + { + "userId": 1, + "id": 15, + "title": "ab voluptatum amet voluptas", + "completed": true + }, + { + "userId": 1, + "id": 16, + "title": "accusamus eos facilis sint et aut voluptatem", + "completed": true + }, + { + "userId": 1, + "id": 17, + "title": "quo laboriosam deleniti aut qui", + "completed": true + }, + { + "userId": 1, + "id": 18, + "title": "dolorum est consequatur ea mollitia in culpa", + "completed": false + }, + { + "userId": 1, + "id": 19, + "title": "molestiae ipsa aut voluptatibus pariatur dolor nihil", + "completed": true + }, + { + "userId": 1, + "id": 20, + "title": "ullam nobis libero sapiente ad optio sint", + "completed": true + }, + { + "userId": 2, + "id": 21, + "title": "suscipit repellat esse quibusdam voluptatem incidunt", + "completed": false + }, + { + "userId": 2, + "id": 22, + "title": "distinctio vitae autem nihil ut molestias quo", + "completed": true + }, + { + "userId": 2, + "id": 23, + "title": "et itaque necessitatibus maxime molestiae qui quas velit", + "completed": false + }, + { + "userId": 2, + "id": 24, + "title": "adipisci non ad dicta qui amet quaerat doloribus ea", + "completed": false + }, + { + "userId": 2, + "id": 25, + "title": "voluptas quo tenetur perspiciatis explicabo natus", + "completed": true + }, + { + "userId": 2, + "id": 26, + "title": "aliquam aut quasi", + "completed": true + }, + { + "userId": 2, + "id": 27, + "title": "veritatis pariatur delectus", + "completed": true + }, + { + "userId": 2, + "id": 28, + "title": "nesciunt totam sit blanditiis sit", + "completed": false + }, + { + "userId": 2, + "id": 29, + "title": "laborum aut in quam", + "completed": false + }, + { + "userId": 2, + "id": 30, + "title": "nemo perspiciatis repellat ut dolor libero commodi blanditiis omnis", + "completed": true + }, + { + "userId": 2, + "id": 31, + "title": "repudiandae totam in est sint facere fuga", + "completed": false + }, + { + "userId": 2, + "id": 32, + "title": "earum doloribus ea doloremque quis", + "completed": false + }, + { + "userId": 2, + "id": 33, + "title": "sint sit aut vero", + "completed": false + }, + { + "userId": 2, + "id": 34, + "title": "porro aut necessitatibus eaque distinctio", + "completed": false + }, + { + "userId": 2, + "id": 35, + "title": "repellendus veritatis molestias dicta incidunt", + "completed": true + }, + { + "userId": 2, + "id": 36, + "title": "excepturi deleniti adipisci voluptatem et neque optio illum ad", + "completed": true + }, + { + "userId": 2, + "id": 37, + "title": "sunt cum tempora", + "completed": false + }, + { + "userId": 2, + "id": 38, + "title": "totam quia non", + "completed": false + }, + { + "userId": 2, + "id": 39, + "title": "doloremque quibusdam asperiores libero corrupti illum qui omnis", + "completed": false + }, + { + "userId": 2, + "id": 40, + "title": "totam atque quo nesciunt", + "completed": true + }, + { + "userId": 3, + "id": 41, + "title": "aliquid amet impedit consequatur aspernatur placeat eaque fugiat suscipit", + "completed": false + }, + { + "userId": 3, + "id": 42, + "title": "rerum perferendis error quia ut eveniet", + "completed": false + }, + { + "userId": 3, + "id": 43, + "title": "tempore ut sint quis recusandae", + "completed": true + }, + { + "userId": 3, + "id": 44, + "title": "cum debitis quis accusamus doloremque ipsa natus sapiente omnis", + "completed": true + }, + { + "userId": 3, + "id": 45, + "title": "velit soluta adipisci molestias reiciendis harum", + "completed": false + }, + { + "userId": 3, + "id": 46, + "title": "vel voluptatem repellat nihil placeat corporis", + "completed": false + }, + { + "userId": 3, + "id": 47, + "title": "nam qui rerum fugiat accusamus", + "completed": false + }, + { + "userId": 3, + "id": 48, + "title": "sit reprehenderit omnis quia", + "completed": false + }, + { + "userId": 3, + "id": 49, + "title": "ut necessitatibus aut maiores debitis officia blanditiis velit et", + "completed": false + }, + { + "userId": 3, + "id": 50, + "title": "cupiditate necessitatibus ullam aut quis dolor voluptate", + "completed": true + }, + { + "userId": 3, + "id": 51, + "title": "distinctio exercitationem ab doloribus", + "completed": false + }, + { + "userId": 3, + "id": 52, + "title": "nesciunt dolorum quis recusandae ad pariatur ratione", + "completed": false + }, + { + "userId": 3, + "id": 53, + "title": "qui labore est occaecati recusandae aliquid quam", + "completed": false + }, + { + "userId": 3, + "id": 54, + "title": "quis et est ut voluptate quam dolor", + "completed": true + }, + { + "userId": 3, + "id": 55, + "title": "voluptatum omnis minima qui occaecati provident nulla voluptatem ratione", + "completed": true + }, + { + "userId": 3, + "id": 56, + "title": "deleniti ea temporibus enim", + "completed": true + }, + { + "userId": 3, + "id": 57, + "title": "pariatur et magnam ea doloribus similique voluptatem rerum quia", + "completed": false + }, + { + "userId": 3, + "id": 58, + "title": "est dicta totam qui explicabo doloribus qui dignissimos", + "completed": false + }, + { + "userId": 3, + "id": 59, + "title": "perspiciatis velit id laborum placeat iusto et aliquam odio", + "completed": false + }, + { + "userId": 3, + "id": 60, + "title": "et sequi qui architecto ut adipisci", + "completed": true + }, + { + "userId": 4, + "id": 61, + "title": "odit optio omnis qui sunt", + "completed": true + }, + { + "userId": 4, + "id": 62, + "title": "et placeat et tempore aspernatur sint numquam", + "completed": false + }, + { + "userId": 4, + "id": 63, + "title": "doloremque aut dolores quidem fuga qui nulla", + "completed": true + }, + { + "userId": 4, + "id": 64, + "title": "voluptas consequatur qui ut quia magnam nemo esse", + "completed": false + }, + { + "userId": 4, + "id": 65, + "title": "fugiat pariatur ratione ut asperiores necessitatibus magni", + "completed": false + }, + { + "userId": 4, + "id": 66, + "title": "rerum eum molestias autem voluptatum sit optio", + "completed": false + }, + { + "userId": 4, + "id": 67, + "title": "quia voluptatibus voluptatem quos similique maiores repellat", + "completed": false + }, + { + "userId": 4, + "id": 68, + "title": "aut id perspiciatis voluptatem iusto", + "completed": false + }, + { + "userId": 4, + "id": 69, + "title": "doloribus sint dolorum ab adipisci itaque dignissimos aliquam suscipit", + "completed": false + }, + { + "userId": 4, + "id": 70, + "title": "ut sequi accusantium et mollitia delectus sunt", + "completed": false + }, + { + "userId": 4, + "id": 71, + "title": "aut velit saepe ullam", + "completed": false + }, + { + "userId": 4, + "id": 72, + "title": "praesentium facilis facere quis harum voluptatibus voluptatem eum", + "completed": false + }, + { + "userId": 4, + "id": 73, + "title": "sint amet quia totam corporis qui exercitationem commodi", + "completed": true + }, + { + "userId": 4, + "id": 74, + "title": "expedita tempore nobis eveniet laborum maiores", + "completed": false + }, + { + "userId": 4, + "id": 75, + "title": "occaecati adipisci est possimus totam", + "completed": false + }, + { + "userId": 4, + "id": 76, + "title": "sequi dolorem sed", + "completed": true + }, + { + "userId": 4, + "id": 77, + "title": "maiores aut nesciunt delectus exercitationem vel assumenda eligendi at", + "completed": false + }, + { + "userId": 4, + "id": 78, + "title": "reiciendis est magnam amet nemo iste recusandae impedit quaerat", + "completed": false + }, + { + "userId": 4, + "id": 79, + "title": "eum ipsa maxime ut", + "completed": true + }, + { + "userId": 4, + "id": 80, + "title": "tempore molestias dolores rerum sequi voluptates ipsum consequatur", + "completed": true + }, + { + "userId": 5, + "id": 81, + "title": "suscipit qui totam", + "completed": true + }, + { + "userId": 5, + "id": 82, + "title": "voluptates eum voluptas et dicta", + "completed": false + }, + { + "userId": 5, + "id": 83, + "title": "quidem at rerum quis ex aut sit quam", + "completed": true + }, + { + "userId": 5, + "id": 84, + "title": "sunt veritatis ut voluptate", + "completed": false + }, + { + "userId": 5, + "id": 85, + "title": "et quia ad iste a", + "completed": true + }, + { + "userId": 5, + "id": 86, + "title": "incidunt ut saepe autem", + "completed": true + }, + { + "userId": 5, + "id": 87, + "title": "laudantium quae eligendi consequatur quia et vero autem", + "completed": true + }, + { + "userId": 5, + "id": 88, + "title": "vitae aut excepturi laboriosam sint aliquam et et accusantium", + "completed": false + }, + { + "userId": 5, + "id": 89, + "title": "sequi ut omnis et", + "completed": true + }, + { + "userId": 5, + "id": 90, + "title": "molestiae nisi accusantium tenetur dolorem et", + "completed": true + }, + { + "userId": 5, + "id": 91, + "title": "nulla quis consequatur saepe qui id expedita", + "completed": true + }, + { + "userId": 5, + "id": 92, + "title": "in omnis laboriosam", + "completed": true + }, + { + "userId": 5, + "id": 93, + "title": "odio iure consequatur molestiae quibusdam necessitatibus quia sint", + "completed": true + }, + { + "userId": 5, + "id": 94, + "title": "facilis modi saepe mollitia", + "completed": false + }, + { + "userId": 5, + "id": 95, + "title": "vel nihil et molestiae iusto assumenda nemo quo ut", + "completed": true + }, + { + "userId": 5, + "id": 96, + "title": "nobis suscipit ducimus enim asperiores voluptas", + "completed": false + }, + { + "userId": 5, + "id": 97, + "title": "dolorum laboriosam eos qui iure aliquam", + "completed": false + }, + { + "userId": 5, + "id": 98, + "title": "debitis accusantium ut quo facilis nihil quis sapiente necessitatibus", + "completed": true + }, + { + "userId": 5, + "id": 99, + "title": "neque voluptates ratione", + "completed": false + }, + { + "userId": 5, + "id": 100, + "title": "excepturi a et neque qui expedita vel voluptate", + "completed": false + }, + { + "userId": 6, + "id": 101, + "title": "explicabo enim cumque porro aperiam occaecati minima", + "completed": false + }, + { + "userId": 6, + "id": 102, + "title": "sed ab consequatur", + "completed": false + }, + { + "userId": 6, + "id": 103, + "title": "non sunt delectus illo nulla tenetur enim omnis", + "completed": false + }, + { + "userId": 6, + "id": 104, + "title": "excepturi non laudantium quo", + "completed": false + }, + { + "userId": 6, + "id": 105, + "title": "totam quia dolorem et illum repellat voluptas optio", + "completed": true + }, + { + "userId": 6, + "id": 106, + "title": "ad illo quis voluptatem temporibus", + "completed": true + }, + { + "userId": 6, + "id": 107, + "title": "praesentium facilis omnis laudantium fugit ad iusto nihil nesciunt", + "completed": false + }, + { + "userId": 6, + "id": 108, + "title": "a eos eaque nihil et exercitationem incidunt delectus", + "completed": true + }, + { + "userId": 6, + "id": 109, + "title": "autem temporibus harum quisquam in culpa", + "completed": true + }, + { + "userId": 6, + "id": 110, + "title": "aut aut ea corporis", + "completed": true + }, + { + "userId": 6, + "id": 111, + "title": "magni accusantium labore et id quis provident", + "completed": false + }, + { + "userId": 6, + "id": 112, + "title": "consectetur impedit quisquam qui deserunt non rerum consequuntur eius", + "completed": false + }, + { + "userId": 6, + "id": 113, + "title": "quia atque aliquam sunt impedit voluptatum rerum assumenda nisi", + "completed": false + }, + { + "userId": 6, + "id": 114, + "title": "cupiditate quos possimus corporis quisquam exercitationem beatae", + "completed": false + }, + { + "userId": 6, + "id": 115, + "title": "sed et ea eum", + "completed": false + }, + { + "userId": 6, + "id": 116, + "title": "ipsa dolores vel facilis ut", + "completed": true + }, + { + "userId": 6, + "id": 117, + "title": "sequi quae est et qui qui eveniet asperiores", + "completed": false + }, + { + "userId": 6, + "id": 118, + "title": "quia modi consequatur vero fugiat", + "completed": false + }, + { + "userId": 6, + "id": 119, + "title": "corporis ducimus ea perspiciatis iste", + "completed": false + }, + { + "userId": 6, + "id": 120, + "title": "dolorem laboriosam vel voluptas et aliquam quasi", + "completed": false + }, + { + "userId": 7, + "id": 121, + "title": "inventore aut nihil minima laudantium hic qui omnis", + "completed": true + }, + { + "userId": 7, + "id": 122, + "title": "provident aut nobis culpa", + "completed": true + }, + { + "userId": 7, + "id": 123, + "title": "esse et quis iste est earum aut impedit", + "completed": false + }, + { + "userId": 7, + "id": 124, + "title": "qui consectetur id", + "completed": false + }, + { + "userId": 7, + "id": 125, + "title": "aut quasi autem iste tempore illum possimus", + "completed": false + }, + { + "userId": 7, + "id": 126, + "title": "ut asperiores perspiciatis veniam ipsum rerum saepe", + "completed": true + }, + { + "userId": 7, + "id": 127, + "title": "voluptatem libero consectetur rerum ut", + "completed": true + }, + { + "userId": 7, + "id": 128, + "title": "eius omnis est qui voluptatem autem", + "completed": false + }, + { + "userId": 7, + "id": 129, + "title": "rerum culpa quis harum", + "completed": false + }, + { + "userId": 7, + "id": 130, + "title": "nulla aliquid eveniet harum laborum libero alias ut unde", + "completed": true + }, + { + "userId": 7, + "id": 131, + "title": "qui ea incidunt quis", + "completed": false + }, + { + "userId": 7, + "id": 132, + "title": "qui molestiae voluptatibus velit iure harum quisquam", + "completed": true + }, + { + "userId": 7, + "id": 133, + "title": "et labore eos enim rerum consequatur sunt", + "completed": true + }, + { + "userId": 7, + "id": 134, + "title": "molestiae doloribus et laborum quod ea", + "completed": false + }, + { + "userId": 7, + "id": 135, + "title": "facere ipsa nam eum voluptates reiciendis vero qui", + "completed": false + }, + { + "userId": 7, + "id": 136, + "title": "asperiores illo tempora fuga sed ut quasi adipisci", + "completed": false + }, + { + "userId": 7, + "id": 137, + "title": "qui sit non", + "completed": false + }, + { + "userId": 7, + "id": 138, + "title": "placeat minima consequatur rem qui ut", + "completed": true + }, + { + "userId": 7, + "id": 139, + "title": "consequatur doloribus id possimus voluptas a voluptatem", + "completed": false + }, + { + "userId": 7, + "id": 140, + "title": "aut consectetur in blanditiis deserunt quia sed laboriosam", + "completed": true + }, + { + "userId": 8, + "id": 141, + "title": "explicabo consectetur debitis voluptates quas quae culpa rerum non", + "completed": true + }, + { + "userId": 8, + "id": 142, + "title": "maiores accusantium architecto necessitatibus reiciendis ea aut", + "completed": true + }, + { + "userId": 8, + "id": 143, + "title": "eum non recusandae cupiditate animi", + "completed": false + }, + { + "userId": 8, + "id": 144, + "title": "ut eum exercitationem sint", + "completed": false + }, + { + "userId": 8, + "id": 145, + "title": "beatae qui ullam incidunt voluptatem non nisi aliquam", + "completed": false + }, + { + "userId": 8, + "id": 146, + "title": "molestiae suscipit ratione nihil odio libero impedit vero totam", + "completed": true + }, + { + "userId": 8, + "id": 147, + "title": "eum itaque quod reprehenderit et facilis dolor autem ut", + "completed": true + }, + { + "userId": 8, + "id": 148, + "title": "esse quas et quo quasi exercitationem", + "completed": false + }, + { + "userId": 8, + "id": 149, + "title": "animi voluptas quod perferendis est", + "completed": false + }, + { + "userId": 8, + "id": 150, + "title": "eos amet tempore laudantium fugit a", + "completed": false + }, + { + "userId": 8, + "id": 151, + "title": "accusamus adipisci dicta qui quo ea explicabo sed vero", + "completed": true + }, + { + "userId": 8, + "id": 152, + "title": "odit eligendi recusandae doloremque cumque non", + "completed": false + }, + { + "userId": 8, + "id": 153, + "title": "ea aperiam consequatur qui repellat eos", + "completed": false + }, + { + "userId": 8, + "id": 154, + "title": "rerum non ex sapiente", + "completed": true + }, + { + "userId": 8, + "id": 155, + "title": "voluptatem nobis consequatur et assumenda magnam", + "completed": true + }, + { + "userId": 8, + "id": 156, + "title": "nam quia quia nulla repellat assumenda quibusdam sit nobis", + "completed": true + }, + { + "userId": 8, + "id": 157, + "title": "dolorem veniam quisquam deserunt repellendus", + "completed": true + }, + { + "userId": 8, + "id": 158, + "title": "debitis vitae delectus et harum accusamus aut deleniti a", + "completed": true + }, + { + "userId": 8, + "id": 159, + "title": "debitis adipisci quibusdam aliquam sed dolore ea praesentium nobis", + "completed": true + }, + { + "userId": 8, + "id": 160, + "title": "et praesentium aliquam est", + "completed": false + }, + { + "userId": 9, + "id": 161, + "title": "ex hic consequuntur earum omnis alias ut occaecati culpa", + "completed": true + }, + { + "userId": 9, + "id": 162, + "title": "omnis laboriosam molestias animi sunt dolore", + "completed": true + }, + { + "userId": 9, + "id": 163, + "title": "natus corrupti maxime laudantium et voluptatem laboriosam odit", + "completed": false + }, + { + "userId": 9, + "id": 164, + "title": "reprehenderit quos aut aut consequatur est sed", + "completed": false + }, + { + "userId": 9, + "id": 165, + "title": "fugiat perferendis sed aut quidem", + "completed": false + }, + { + "userId": 9, + "id": 166, + "title": "quos quo possimus suscipit minima ut", + "completed": false + }, + { + "userId": 9, + "id": 167, + "title": "et quis minus quo a asperiores molestiae", + "completed": false + }, + { + "userId": 9, + "id": 168, + "title": "recusandae quia qui sunt libero", + "completed": false + }, + { + "userId": 9, + "id": 169, + "title": "ea odio perferendis officiis", + "completed": true + }, + { + "userId": 9, + "id": 170, + "title": "quisquam aliquam quia doloribus aut", + "completed": false + }, + { + "userId": 9, + "id": 171, + "title": "fugiat aut voluptatibus corrupti deleniti velit iste odio", + "completed": true + }, + { + "userId": 9, + "id": 172, + "title": "et provident amet rerum consectetur et voluptatum", + "completed": false + }, + { + "userId": 9, + "id": 173, + "title": "harum ad aperiam quis", + "completed": false + }, + { + "userId": 9, + "id": 174, + "title": "similique aut quo", + "completed": false + }, + { + "userId": 9, + "id": 175, + "title": "laudantium eius officia perferendis provident perspiciatis asperiores", + "completed": true + }, + { + "userId": 9, + "id": 176, + "title": "magni soluta corrupti ut maiores rem quidem", + "completed": false + }, + { + "userId": 9, + "id": 177, + "title": "et placeat temporibus voluptas est tempora quos quibusdam", + "completed": false + }, + { + "userId": 9, + "id": 178, + "title": "nesciunt itaque commodi tempore", + "completed": true + }, + { + "userId": 9, + "id": 179, + "title": "omnis consequuntur cupiditate impedit itaque ipsam quo", + "completed": true + }, + { + "userId": 9, + "id": 180, + "title": "debitis nisi et dolorem repellat et", + "completed": true + }, + { + "userId": 10, + "id": 181, + "title": "ut cupiditate sequi aliquam fuga maiores", + "completed": false + }, + { + "userId": 10, + "id": 182, + "title": "inventore saepe cumque et aut illum enim", + "completed": true + }, + { + "userId": 10, + "id": 183, + "title": "omnis nulla eum aliquam distinctio", + "completed": true + }, + { + "userId": 10, + "id": 184, + "title": "molestias modi perferendis perspiciatis", + "completed": false + }, + { + "userId": 10, + "id": 185, + "title": "voluptates dignissimos sed doloribus animi quaerat aut", + "completed": false + }, + { + "userId": 10, + "id": 186, + "title": "explicabo odio est et", + "completed": false + }, + { + "userId": 10, + "id": 187, + "title": "consequuntur animi possimus", + "completed": false + }, + { + "userId": 10, + "id": 188, + "title": "vel non beatae est", + "completed": true + }, + { + "userId": 10, + "id": 189, + "title": "culpa eius et voluptatem et", + "completed": true + }, + { + "userId": 10, + "id": 190, + "title": "accusamus sint iusto et voluptatem exercitationem", + "completed": true + }, + { + "userId": 10, + "id": 191, + "title": "temporibus atque distinctio omnis eius impedit tempore molestias pariatur", + "completed": true + }, + { + "userId": 10, + "id": 192, + "title": "ut quas possimus exercitationem sint voluptates", + "completed": false + }, + { + "userId": 10, + "id": 193, + "title": "rerum debitis voluptatem qui eveniet tempora distinctio a", + "completed": true + }, + { + "userId": 10, + "id": 194, + "title": "sed ut vero sit molestiae", + "completed": false + }, + { + "userId": 10, + "id": 195, + "title": "rerum ex veniam mollitia voluptatibus pariatur", + "completed": true + }, + { + "userId": 10, + "id": 196, + "title": "consequuntur aut ut fugit similique", + "completed": true + }, + { + "userId": 10, + "id": 197, + "title": "dignissimos quo nobis earum saepe", + "completed": true + }, + { + "userId": 10, + "id": 198, + "title": "quis eius est sint explicabo", + "completed": true + }, + { + "userId": 10, + "id": 199, + "title": "numquam repellendus a magnam", + "completed": true + }, + { + "userId": 10, + "id": 200, + "title": "ipsam aperiam voluptates qui", + "completed": false + } +] diff --git a/apps/dev/tsconfig.json b/apps/dev/tsconfig.json new file mode 100644 index 0000000..cb6b3f7 --- /dev/null +++ b/apps/dev/tsconfig.json @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "target": "es5", + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, + "skipLibCheck": true, + "strict": true, + "forceConsistentCasingInFileNames": true, + "noEmit": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "node", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "preserve", + "incremental": true, + "baseUrl": "./", + "paths": { + "*": ["./*"] + } + }, + "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"], + "exclude": ["node_modules"] +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..eda61c4 --- /dev/null +++ b/package.json @@ -0,0 +1,32 @@ +{ + "name": "root", + "private": true, + "scripts": { + "dev": "pnpm --filter dev run dev", + "build": "pnpm --filter next-rest-framework run build", + "test": "pnpm --filter next-rest-framework run test", + "test:watch": "pnpm --filter next-rest-framework run test:watch", + "type-check": "pnpm run -r type-check", + "format": "prettier --write '**/*.{ts,json}' && eslint --fix --max-warnings=0 --ext=.ts .", + "lint": "prettier --check '**/*.{ts,json}' && eslint --max-warnings=0 --ext=.ts ." + }, + "devDependencies": { + "@typescript-eslint/eslint-plugin": "5.0.0", + "eslint": "8.0.1", + "eslint-config-prettier": "8.5.0", + "eslint-config-standard-with-typescript": "23.0.0", + "eslint-plugin-import": "2.25.2", + "eslint-plugin-n": "15.0.0", + "eslint-plugin-promise": "6.0.0", + "next": "13.0.2", + "prettier": "2.7.1", + "typescript": "4.9.3", + "zod": "3.19.1", + "yup": "0.32.11" + }, + "prettier": { + "semi": true, + "singleQuote": true, + "trailingComma": "none" + } +} diff --git a/packages/next-rest-framework/.eslintrc.js b/packages/next-rest-framework/.eslintrc.js new file mode 100644 index 0000000..12547b2 --- /dev/null +++ b/packages/next-rest-framework/.eslintrc.js @@ -0,0 +1,5 @@ +module.exports = { + rules: { + 'no-var': 'off' + } +} diff --git a/packages/next-rest-framework/.gitignore b/packages/next-rest-framework/.gitignore new file mode 100644 index 0000000..e86ef84 --- /dev/null +++ b/packages/next-rest-framework/.gitignore @@ -0,0 +1,2 @@ +coverage +dist \ No newline at end of file diff --git a/packages/next-rest-framework/LICENCE b/packages/next-rest-framework/LICENCE new file mode 100644 index 0000000..b8586bd --- /dev/null +++ b/packages/next-rest-framework/LICENCE @@ -0,0 +1,15 @@ +ISC License + +Copyright (c) 2022-, Markus Blomqvist + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/packages/next-rest-framework/README.md b/packages/next-rest-framework/README.md new file mode 100644 index 0000000..2c120d7 --- /dev/null +++ b/packages/next-rest-framework/README.md @@ -0,0 +1,365 @@ +
+
+
+
Type-safe, self-documenting REST APIs for Next.js
+>16&255,u[c++]=t>>8&255,u[c++]=255&t;2===s&&(t=n[e.charCodeAt(r)]<<2|n[e.charCodeAt(r+1)]>>4,u[c++]=255&t);1===s&&(t=n[e.charCodeAt(r)]<<10|n[e.charCodeAt(r+1)]<<4|n[e.charCodeAt(r+2)]>>2,u[c++]=t>>8&255,u[c++]=255&t);return u},t.fromByteArray=function(e){for(var t,n=e.length,o=n%3,a=[],i=16383,s=0,l=n-o;s0)throw new Error("Invalid string. Length must be a multiple of 4");var r=e.indexOf("=");return-1===r&&(r=t),[r,r===t?0:4-r%4]}function u(e,t,n){for(var o,a,i=[],s=t;ss&&(r=s-l),a=r;a>=0;a--){let r=!0;for(let n=0;n>>=0,isFinite(r)?(r>>>=0,void 0===n&&(n="utf8")):(n=r,r=void 0)}const o=this.length-t;if((void 0===r||r>o)&&(r=o),e.length>0&&(r<0||t<0)||t>this.length)throw new RangeError("Attempt to write outside buffer bounds");n||(n="utf8");let a=!1;for(;;)switch(n){case"hex":return w(this,e,t,r);case"utf8":case"utf-8":return E(this,e,t,r);case"ascii":case"latin1":case"binary":return x(this,e,t,r);case"base64":return _(this,e,t,r);case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return S(this,e,t,r);default:if(a)throw new TypeError("Unknown encoding: "+n);n=(""+n).toLowerCase(),a=!0}},l.prototype.toJSON=function(){return{type:"Buffer",data:Array.prototype.slice.call(this._arr||this,0)}};const C=4096;function O(e,t,r){let n="";r=Math.min(e.length,r);for(let o=t;o