diff --git a/.github/dependabot.yml b/.github/dependabot.yml
index 742679c..ac517fa 100644
--- a/.github/dependabot.yml
+++ b/.github/dependabot.yml
@@ -24,4 +24,4 @@ updates:
commit-message:
prefix: "fix"
prefix-development: "chore"
-
+
\ No newline at end of file
diff --git a/.github/workflows/nodejs.yml b/.github/workflows/nodejs.yml
index 6663a0b..dcf4f4d 100644
--- a/.github/workflows/nodejs.yml
+++ b/.github/workflows/nodejs.yml
@@ -9,11 +9,8 @@ jobs:
strategy:
matrix:
node-version: [12, 14]
- os: [macos-latest, windows-latest, ubuntu-latest]
+ os: [windows-latest, ubuntu-latest]
steps:
- - name: Disable autocrlf
- run: git config --global core.autocrlf false
- if: matrix.os == 'windows-latest'
- uses: actions/checkout@v2
- name: setup Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
@@ -31,6 +28,7 @@ jobs:
${{ runner.os }}-yarn-
- run: yarn --frozen-lockfile
- run: yarn lint
+ if: matrix.os != 'windows-latest'
- run: yarn typecheck
- run: yarn test --coverage
- run: npx codecov
diff --git a/.gitignore b/.gitignore
index f06235c..e060b02 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,4 @@
node_modules
dist
+benchmark
+coverage
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c3e9d10..34df7e7 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,64 @@
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
+## [0.12.0](https://github.com/frouriojs/frourio/compare/v0.11.0...v0.12.0) (2020-09-11)
+
+
+### ⚠ BREAKING CHANGES
+
+* override createController
+
+### Features
+
+* add benchmark ([ef5d373](https://github.com/frouriojs/frourio/commit/ef5d373ffcdde0d1452dcb4223e3de52ac8ad754))
+* add createHooks ([da05725](https://github.com/frouriojs/frourio/commit/da05725ee95d0b34c0f8488bbdeb6377cb63f171))
+* add noPublic ([8e2490b](https://github.com/frouriojs/frourio/commit/8e2490b4dd87779d8417cb01add7ffa31df71af2))
+* add noTypedParams ([ed36c1e](https://github.com/frouriojs/frourio/commit/ed36c1e9c0616ae9ba63113a837637cd0e13cfb5))
+* add noTypeorm ([a432380](https://github.com/frouriojs/frourio/commit/a432380e9340eb689ed6367cce8327b80c4b64e0))
+* build for benchmark ([79034f3](https://github.com/frouriojs/frourio/commit/79034f3eaf59a8f8bcc2509add95069e6028e38a))
+* combine index.ts into $app.ts ([2293cd9](https://github.com/frouriojs/frourio/commit/2293cd92d5ff6b3bcedcd9e9ad067cf009ff2e2e))
+* delete cors and helmet ([d17e07d](https://github.com/frouriojs/frourio/commit/d17e07d986226b3f7257e015fb92813aa25b8bc4))
+* generate controllers array ([babecd9](https://github.com/frouriojs/frourio/commit/babecd95eaf7f354f1dd0b6bc9c9029426896f80))
+* ignore empty dirs ([61320be](https://github.com/frouriojs/frourio/commit/61320beaf5f20fab718f23a160cc3381de6eafdb))
+* integrate fastify and express ([8203dbb](https://github.com/frouriojs/frourio/commit/8203dbbca94f12a9a71a8fbd137e0667b3afdb39))
+* override createController ([ac6e61b](https://github.com/frouriojs/frourio/commit/ac6e61b19d48485dfb81cac5ac90b9808e620aa0))
+* parse with tsc ([1880be1](https://github.com/frouriojs/frourio/commit/1880be1fe3b8355e49acf47ebb35aeb4bdd19f18))
+* remove dir option ([25f5844](https://github.com/frouriojs/frourio/commit/25f5844697895b15a98facf98432070d0d0a124b))
+* remove fastify ([6d59a3d](https://github.com/frouriojs/frourio/commit/6d59a3d60875f6ccddcc3688a506baf10cc297fc))
+* remove multer when not in use ([3456679](https://github.com/frouriojs/frourio/commit/34566790bce5ae2501752eec9b988aa7b9623351))
+* remove public dir ([b334555](https://github.com/frouriojs/frourio/commit/b33455528e293d565d9acae319ecd12284130078))
+* remove typeorm from deps ([76ff7ec](https://github.com/frouriojs/frourio/commit/76ff7ecb674dca0565b7c7305e14a2c94e9bc82f))
+* remove typeorm from deps ([5f04bd8](https://github.com/frouriojs/frourio/commit/5f04bd8d2b6817887ece972f60244b722aecf26f))
+* rename createController to defineController ([cb6889b](https://github.com/frouriojs/frourio/commit/cb6889b7eac208e0baa4ad7d5d361ffcd0182db5))
+* rename createHooks to defineHooks ([17a04aa](https://github.com/frouriojs/frourio/commit/17a04aa4cee1c7f090287757999f3999132a75d6))
+* rename types to validators ([ad0adac](https://github.com/frouriojs/frourio/commit/ad0adac86dd639531018ed923f2d42301242c2fc))
+* suport fastify ([efce77e](https://github.com/frouriojs/frourio/commit/efce77e251fe7aeb5f1e3980cb26ebd8518feeec))
+* support crlf ([e8829d8](https://github.com/frouriojs/frourio/commit/e8829d8c728bf6ebec6f2978ef2166b327c670f6))
+* support number type query params ([e859624](https://github.com/frouriojs/frourio/commit/e85962430b0fabc4df4075172942d7975f8a64ab))
+* support paths from tsconfig ([c5c9eac](https://github.com/frouriojs/frourio/commit/c5c9eac62b6e8bbbac7e4815a872a07f59c01da2))
+* support velona ([521edec](https://github.com/frouriojs/frourio/commit/521edec2f0c7c221e02a205cb6f6411782c215be))
+* update aspida@0.20.2 ([0018225](https://github.com/frouriojs/frourio/commit/00182254793792a4960f0e45950d8605d1c8e04c))
+
+
+### Bug Fixes
+
+* delete $arrayTypeKeysName ([f21b612](https://github.com/frouriojs/frourio/commit/f21b612bd744fc5f05401a8de690325fa907014f))
+* parse JSON ([6d06947](https://github.com/frouriojs/frourio/commit/6d06947b541e334d4d553e0e8649d9acea1922c7))
+
+
+### Refactors
+
+* add createValidateHandler ([997bc0f](https://github.com/frouriojs/frourio/commit/997bc0f68d44dce42c9a5350f86984fe1b8ed808))
+* apply router to app ([d99f5ed](https://github.com/frouriojs/frourio/commit/d99f5ed0f247aab3e3ad978a2ca736bca02572ba))
+* optimize controllers ([0641d38](https://github.com/frouriojs/frourio/commit/0641d38e45bb45568f787a1e4cb0c540c434dd8e))
+* optimize deps ([83b8d4e](https://github.com/frouriojs/frourio/commit/83b8d4e591148a6ee870c9d133b97eee6c31d217))
+
+
+### Documentation
+
+* fix broken twitter image link ([9e8ea10](https://github.com/frouriojs/frourio/commit/9e8ea10be447b78d3845b1b6f2c8be08d0da1cfa))
+* update README ([a69a219](https://github.com/frouriojs/frourio/commit/a69a2190e98a811549cc1dcf19939d99f06731b2))
+
## [0.11.0](https://github.com/frouriojs/frourio/compare/v0.10.4...v0.11.0) (2020-07-19)
diff --git a/README.md b/README.md
index 598c447..968310b 100644
--- a/README.md
+++ b/README.md
@@ -1,20 +1,30 @@
-# frourio
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
-> Perfectly type-checkable REST framework for TypeScript.
+
+Frourio is a perfectly type-checkable REST framework for TypeScript.
+
+
+
## Why frourio ?
@@ -49,7 +59,7 @@ The front is checked by the type to see if it is making an API request as define
-## Usage
+## Install
Make sure you have [npx](https://www.npmjs.com/package/npx) installed (`npx` is shipped by default since [npm](https://www.npmjs.com/get-npm) `5.2.0`)
@@ -74,25 +84,6 @@ yarn create frourio-app
Frourio requires TypeScript 3.9 or higher.
If the TypeScript version of VSCode is low, an error is displayed during development.
-## Type definition of API endpoints
-
-aspida: `/apis` --> frourio: `/server/api`
-
-[See: Create an endpoint type definition file | aspida](https://github.com/aspidajs/aspida#create-an-endpoint-type-definition-file)
-
-#### Warning !
-examples.
-
-> GET: /api/test
-
-x- /server/api/test.ts
-o- /server/api/test/index.ts
-
-> GET: /api/test/{testId}
-
-x- /server/api/test/_testId.ts
-o- /server/api/test/_testId/index.ts
-
## Support
@@ -101,4 +92,4 @@ o- /server/api/test/_testId/index.ts
## License
-frourio is licensed under a [MIT License](https://github.com/frouriojs/frourio/blob/master/packages/frourio/LICENSE).
+Frourio is licensed under a [MIT License](https://github.com/frouriojs/frourio/blob/master/packages/frourio/LICENSE).
diff --git a/commitlint.config.js b/commitlint.config.js
deleted file mode 100644
index a4f4369..0000000
--- a/commitlint.config.js
+++ /dev/null
@@ -1,3 +0,0 @@
-module.exports = {
- extends: ['@commitlint/config-conventional']
-}
diff --git a/docs/assets/images/ogp.png b/docs/assets/images/ogp.png
new file mode 100644
index 0000000..ae34963
Binary files /dev/null and b/docs/assets/images/ogp.png differ
diff --git a/jest.config.js b/jest.config.js
index 146e539..a07c06a 100644
--- a/jest.config.js
+++ b/jest.config.js
@@ -1,7 +1,10 @@
+const { pathsToModuleNameMapper } = require('ts-jest/utils')
+const { compilerOptions } = require('./tsconfig')
+
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
globals: { Blob: {} },
- testPathIgnorePatterns: ['apis'],
- coveragePathIgnorePatterns: ['apis', 'dist']
+ moduleNameMapper: pathsToModuleNameMapper(compilerOptions.paths, { prefix: '/' }),
+ coveragePathIgnorePatterns: ['\\$api.ts', 'dist']
}
diff --git a/package.json b/package.json
index 94d5285..6c98cdd 100644
--- a/package.json
+++ b/package.json
@@ -4,9 +4,10 @@
"packages/*"
],
"scripts": {
- "dev": "npm run build && cd packages/frourio && cd server && aspida --build && node ../bin/index.js --build",
- "build": "npm run rimraf -- frourio && cd packages/frourio && tsc --project tsconfig.json",
- "rimraf": "node -e \"require('fs').rmdirSync('packages/' + process.argv[1] + '/dist', { recursive: true })\"",
+ "dev": "npm run build && cd packages/frourio/servers && aspida && node build.js",
+ "build": "npm run rimraf -- packages/frourio/dist && tsc -p packages/frourio",
+ "build:benchmark": "npm run rimraf -- benchmark && tsc -p tsconfig.benchmark.json",
+ "rimraf": "node -e \"require('fs').rmdirSync(process.argv[1], { recursive: true })\"",
"release": "standard-version --skip.tag",
"release:major": "npm run release -- --release-as major",
"release:minor": "npm run release -- --release-as minor",
@@ -14,7 +15,7 @@
"lint": "eslint --ext .js,.ts --ignore-path .gitignore .",
"lint:fix": "npm run lint -- --fix",
"test": "jest",
- "typecheck": "npm run build && tsc --noEmit"
+ "typecheck": "tsc --noEmit"
},
"eslintConfig": {
"env": {
@@ -64,27 +65,30 @@
]
},
"devDependencies": {
- "@aspida/axios": "^0.9.4",
- "@commitlint/cli": "^9.1.1",
- "@commitlint/config-conventional": "^9.1.1",
- "@types/jest": "^26.0.4",
- "@types/minimist": "^1.2.0",
- "@typescript-eslint/eslint-plugin": "^3.6.1",
- "@typescript-eslint/parser": "^3.6.1",
- "axios": "^0.19.2",
- "eslint": "^7.4.0",
+ "@aspida/axios": "^0.11.3",
+ "@types/express": "^4.17.7",
+ "@types/jest": "^26.0.13",
+ "@types/multer": "^1.4.4",
+ "@typescript-eslint/eslint-plugin": "^4.1.0",
+ "@typescript-eslint/parser": "^4.1.0",
+ "axios": "^0.20.0",
+ "class-validator": "^0.12.2",
+ "eslint": "^7.8.1",
"eslint-config-prettier": "^6.11.0",
"eslint-config-standard": "^14.1.1",
"eslint-plugin-import": "^2.22.0",
- "eslint-plugin-jest": "^23.18.0",
+ "eslint-plugin-jest": "^24.0.0",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-prettier": "^3.1.4",
"eslint-plugin-promise": "^4.2.1",
"eslint-plugin-standard": "^4.0.1",
+ "express": "^4.17.1",
"form-data": "^3.0.0",
- "jest": "^26.1.0",
- "prettier": "2.0.5",
- "standard-version": "^8.0.2",
- "ts-jest": "^26.1.3"
+ "jest": "^26.4.2",
+ "multer": "^1.4.2",
+ "prettier": "^2.1.1",
+ "standard-version": "^9.0.0",
+ "ts-jest": "^26.3.0",
+ "typescript": "^4.0.2"
}
}
diff --git a/packages/frourio/__test__/build.spec.ts b/packages/frourio/__test__/build.spec.ts
index f3a3a8a..88caeeb 100644
--- a/packages/frourio/__test__/build.spec.ts
+++ b/packages/frourio/__test__/build.spec.ts
@@ -1,20 +1,37 @@
import fs from 'fs'
+import { version } from '../package.json'
+import { run } from '../src/cli'
import build from '../src/buildServerFile'
import aspidaBuild from 'aspida/dist/buildTemplate'
const basePath = 'packages/frourio'
+test('version command', () => {
+ const spyLog = jest.spyOn(console, 'log')
+ const args = ['--version']
+
+ run(args)
+ expect(console.log).toHaveBeenCalledWith(`v${version}`)
+
+ spyLog.mockRestore()
+})
+
test('build', () => {
- const inputDir = `${basePath}/server`
+ const inputDir = `${basePath}/servers`
- const result = build(inputDir)
- expect(result.text.replace(inputDir, 'server')).toBe(fs.readFileSync(result.filePath, 'utf8'))
+ fs.readdirSync(inputDir, { withFileTypes: true })
+ .filter(d => d.isDirectory())
+ .map(d => `${inputDir}/${d.name}`)
+ .forEach(input => {
+ const result = build(input)
+ expect(result.text).toBe(fs.readFileSync(result.filePath, 'utf8').replace(/\r/g, ''))
- const [target] = aspidaBuild({
- input: `${inputDir}/api`,
- baseURL: '',
- trailingSlash: false,
- outputEachDir: false
- })
- expect(target.text).toBe(fs.readFileSync(target.filePath, 'utf8'))
+ const [target] = aspidaBuild({
+ input: `${input}/api`,
+ baseURL: '',
+ trailingSlash: false,
+ outputEachDir: false
+ })
+ expect(target.text).toBe(fs.readFileSync(target.filePath, 'utf8').replace(/\r/g, ''))
+ })
})
diff --git a/packages/frourio/__test__/index.spec.ts b/packages/frourio/__test__/index.spec.ts
index 3bc1c69..1f7b160 100644
--- a/packages/frourio/__test__/index.spec.ts
+++ b/packages/frourio/__test__/index.spec.ts
@@ -1,28 +1,31 @@
+/* eslint-disable jest/no-done-callback */
import { Server } from 'http'
import fs from 'fs'
+import express from 'express'
import FormData from 'form-data'
import axios from 'axios'
-import { $arrayTypeKeysName } from 'aspida'
import aspida from '@aspida/axios'
-import api from '../server/api/$api'
-import { run } from '../server/$app'
+import api from '../servers/all/api/$api'
+import frourio from '../servers/all/$app'
const port = 11111
const baseURL = `http://localhost:${port}`
const client = api(aspida(undefined, { baseURL }))
let server: Server
-beforeEach(async () => {
- server = (await run({ port })).server
+beforeEach(cb => {
+ server = frourio(express()).listen(port, cb)
})
afterEach(fn => {
- fs.rmdirSync('packages/frourio/server/.upload', { recursive: true })
+ fs.rmdirSync('packages/frourio/servers/all/.upload', { recursive: true })
server.close(fn)
})
test('GET: 200', async () => {
- const res = await client.$get({ query: { id: '1', disable: 'false' } })
+ const res = await client.$get({
+ query: { requiredNum: 1, requiredNumArr: [1, 2], id: '1', disable: 'false' }
+ })
expect(res?.id).toBe(1)
})
@@ -40,17 +43,27 @@ test('GET: params.userId', async () => {
test('GET: 400', async () => {
await Promise.all([
- expect(client.get({ query: { id: '1', disable: 'no boolean' } })).rejects.toHaveProperty(
- 'response.status',
- 400
- ),
- expect(client.get({ query: { id: 'no number', disable: 'true' } })).rejects.toHaveProperty(
- 'response.status',
- 400
- )
+ expect(
+ client.get({ query: { requiredNum: 0, requiredNumArr: [], id: '1', disable: 'no boolean' } })
+ ).rejects.toHaveProperty('response.status', 400),
+ expect(
+ client.get({
+ query: { requiredNum: 1, requiredNumArr: [1, 2], id: 'no number', disable: 'true' }
+ })
+ ).rejects.toHaveProperty('response.status', 400)
])
})
+test('GET: 500', async () => {
+ await expect(client.$500.get()).rejects.toHaveProperty('response.status', 500)
+})
+
+test('PUT: JSON', async () => {
+ const id = 'abcd'
+ const res = await client.texts.sample.$put({ body: { id } })
+ expect(res?.id).toBe(id)
+})
+
test('POST: formdata', async () => {
const port = '3000'
const fileName = 'tsconfig.json'
@@ -59,7 +72,7 @@ test('POST: formdata', async () => {
form.append('file', fs.createReadStream(fileName))
const res = await axios.post(baseURL, form, {
headers: form.getHeaders(),
- params: { id: 1, disable: true }
+ params: { requiredNum: 0, requiredNumArr: [], id: 1, disable: true }
})
expect(res.data.port).toBe(port)
expect(res.data.fileName).toBe(fileName)
@@ -69,18 +82,19 @@ test('POST: multi file upload', async () => {
const fileName = 'tsconfig.json'
const form = new FormData()
const fileST = fs.createReadStream(fileName)
+ form.append('optionalArr', 'sample')
form.append('name', 'sample')
form.append('vals', 'dammy')
form.append('icon', fileST)
form.append('files', fileST)
form.append('files', fileST)
- form.append($arrayTypeKeysName, ['empty', 'vals', 'files'].join(','))
const res = await axios.post(`${baseURL}/multiForm`, form, {
headers: form.getHeaders()
})
+
expect(res.data).toEqual({
- [$arrayTypeKeysName]: undefined,
- empty: 0,
+ requiredArr: 0,
+ optionalArr: 1,
name: -1,
icon: -1,
vals: 1,
@@ -95,7 +109,6 @@ test('POST: 400', async () => {
form.append('name', 'sample')
form.append('vals', 'dammy')
form.append('icon', fileST)
- form.append($arrayTypeKeysName, ['empty', 'vals', 'files'].join(','))
await expect(
axios.post(`${baseURL}/multiForm`, form, {
@@ -103,8 +116,3 @@ test('POST: 400', async () => {
})
).rejects.toHaveProperty('response.status', 400)
})
-
-test('GET: static', async () => {
- const res = await axios.get(`http://localhost:${port}/sample.json`)
- expect(res.data.sample).toBe(true)
-})
diff --git a/packages/frourio/__test__/unit.spec.ts b/packages/frourio/__test__/unit.spec.ts
new file mode 100644
index 0000000..026e708
--- /dev/null
+++ b/packages/frourio/__test__/unit.spec.ts
@@ -0,0 +1,42 @@
+import fs from 'fs'
+import createDefaultFilesIfNotExists from '../src/createDefaultFilesIfNotExists'
+
+test('createDefaultFilesIfNotExists', () => {
+ const dir = 'tmp'
+ fs.mkdirSync(dir)
+ createDefaultFilesIfNotExists(dir)
+
+ expect(fs.readFileSync(`${dir}/index.ts`, 'utf8')).toBe(`export type Methods = {
+ get: {
+ resBody: string
+ }
+}
+`)
+
+ expect(fs.readFileSync(`${dir}/controller.ts`, 'utf8'))
+ .toBe(`import { defineController } from './$relay'
+
+export default defineController(() => ({
+ get: () => ({ status: 200, body: 'Hello' })
+}))
+`)
+
+ expect(fs.existsSync(`${dir}/hooks.ts`)).toBeFalsy()
+
+ fs.writeFileSync(`${dir}/hooks.ts`, '', 'utf8')
+ createDefaultFilesIfNotExists(dir)
+
+ expect(fs.readFileSync(`${dir}/hooks.ts`, 'utf8')).toBe(
+ `import { defineHooks } from './$relay'
+
+export default defineHooks(() => ({
+ onRequest: (req, res, next) => {
+ console.log('Directory level onRequest hook:', req.path)
+ next()
+ }
+}))
+`
+ )
+
+ fs.rmdirSync(dir, { recursive: true })
+})
diff --git a/packages/frourio/package.json b/packages/frourio/package.json
index 0bbce91..55804bc 100644
--- a/packages/frourio/package.json
+++ b/packages/frourio/package.json
@@ -1,12 +1,12 @@
{
"name": "frourio",
- "version": "0.11.0",
+ "version": "0.12.0",
"description": "Perfectly type-checkable REST framework for TypeScript",
"author": "Solufa ",
"license": "MIT",
"main": "dist/index.js",
"bin": "bin/index.js",
- "homepage": "https://github.com/frouriojs/frourio/tree/master/packages/frourio#readme",
+ "homepage": "https://github.com/frouriojs/frourio#readme",
"repository": {
"type": "git",
"url": "git+https://github.com/frouriojs/frourio.git"
@@ -23,19 +23,7 @@
"server"
],
"dependencies": {
- "@types/cors": "^2.8.6",
- "@types/express": "^4.17.7",
- "@types/express-serve-static-core": "^4.17.8",
- "@types/helmet": "^0.0.47",
- "@types/multer": "^1.4.3",
- "aspida": "^0.19.4",
- "class-validator": "^0.12.2",
- "cors": "^2.8.5",
- "express": "^4.17.1",
- "helmet": "^3.23.3",
- "multer": "^1.4.2",
- "reflect-metadata": "^0.1.13",
- "typeorm": "^0.2.25",
- "typescript": "^3.9.7"
+ "aspida": "^0.21.3",
+ "velona": "^0.5.1"
}
}
diff --git a/packages/frourio/server/$app.ts b/packages/frourio/server/$app.ts
deleted file mode 100644
index a44386b..0000000
--- a/packages/frourio/server/$app.ts
+++ /dev/null
@@ -1,137 +0,0 @@
-/* eslint-disable */
-import 'reflect-metadata'
-import { Server } from 'http'
-import path from 'path'
-import express, { Express } from 'express'
-import multer from 'multer'
-import helmet from 'helmet'
-import cors from 'cors'
-import { createConnection, Connection } from 'typeorm'
-import { createRouter, Config } from 'frourio'
-import { Task as Entity0 } from './entity/Task'
-import { TaskSubscriber as Subscriber0 } from './subscriber/TaskSubscriber'
-import * as Types from './types'
-import controller0, { middleware as ctrlMiddleware0 } from './api/@controller'
-import controller1 from './api/multiForm/@controller'
-import controller2 from './api/texts/@controller'
-import controller3 from './api/texts/sample/@controller'
-import controller4, { middleware as ctrlMiddleware1 } from './api/users/@controller'
-import controller5 from './api/users/_userId@number/@controller'
-import middleware0 from './api/@middleware'
-import middleware1 from './api/users/@middleware'
-
-export const controllers = {
- name: '/',
- validator: {
- get: {
- query: { required: false, Class: Types.ValidQuery }
- },
- post: {
- query: { required: true, Class: Types.ValidQuery },
- body: { required: true, Class: Types.ValidBody }
- }
- },
- uploader: ['post'],
- controller: controller0,
- ctrlMiddleware: ctrlMiddleware0,
- middleware: middleware0,
- children: {
- names: [
- {
- name: '/multiForm',
- validator: {
- post: {
- body: { required: true, Class: Types.ValidMultiForm }
- }
- },
- uploader: ['post'],
- controller: controller1
- },
- {
- name: '/texts',
- controller: controller2,
- children: {
- names: [
- {
- name: '/sample',
- controller: controller3
- }
- ]
- }
- },
- {
- name: '/users',
- validator: {
- post: {
- body: { required: true, Class: Types.ValidUserInfo }
- }
- },
- controller: controller4,
- ctrlMiddleware: ctrlMiddleware1,
- middleware: middleware1,
- children: {
- value: {
- name: '/_userId@number',
- controller: controller5
- }
- }
- }
- ]
- }
-}
-
-export const entities = [Entity0]
-export const migrations = []
-export const subscribers = [Subscriber0]
-export const run = async (config: Config) => {
- const app = express()
- const router = createRouter(
- controllers,
- multer(
- config.multer ?? { dest: path.join(__dirname, '.upload'), limits: { fileSize: 1024 ** 3 } }
- ).any()
- )
-
- if (config.helmet) app.use(helmet(config.helmet === true ? {} : config.helmet))
- if (config.cors) app.use(cors(config.cors === true ? {} : config.cors))
-
- app.use((req, res, next) => {
- express.json()(req, res, err => {
- if (err) return res.sendStatus(400)
-
- next()
- })
- })
-
- const staticMiddleware = express.static(path.join(__dirname, 'public'))
- if (config.basePath && config.basePath !== '/') {
- const staticPath = config.basePath.startsWith('/') ? config.basePath : `/${config.basePath}`
- app.use(staticPath, router)
- app.use(staticPath, staticMiddleware)
- } else {
- app.use(router)
- app.use(staticMiddleware)
- }
-
- let connection: Connection
-
- if (config.typeorm) {
- connection = await createConnection({
- entities,
- migrations,
- subscribers,
- ...config.typeorm
- })
- }
-
- return new Promise<{
- app: Express
- server: Server
- connection?: Connection
- }>(resolve => {
- const server = app.listen(config.port, () => {
- console.log(`Frourio is running on http://localhost:${config.port}`)
- resolve({ app, server, connection })
- })
- })
-}
diff --git a/packages/frourio/server/api/$relay.ts b/packages/frourio/server/api/$relay.ts
deleted file mode 100644
index 0a7eeff..0000000
--- a/packages/frourio/server/api/$relay.ts
+++ /dev/null
@@ -1,17 +0,0 @@
-/* eslint-disable */
-import { RequestHandler } from 'express'
-import { ServerMethods } from 'frourio'
-import { Methods } from './'
-
-type ControllerMethods = ServerMethods
-
-export const createMiddleware = <
- T extends RequestHandler | [] | [RequestHandler, ...RequestHandler[]]
->(handler: T): T => handler
-
-export const createController = (methods: ControllerMethods) => methods
-
-export const createInjectableController = (
- cb: (deps: T) => ControllerMethods,
- deps: T
-) => ({ ...cb(deps), inject: (d: T) => cb(d) })
diff --git a/packages/frourio/server/api/@controller.ts b/packages/frourio/server/api/@controller.ts
deleted file mode 100644
index 58fee96..0000000
--- a/packages/frourio/server/api/@controller.ts
+++ /dev/null
@@ -1,17 +0,0 @@
-import { createController, createMiddleware } from './$relay'
-
-export const middleware = createMiddleware((req, res, next) => {
- console.log('Controller level middleware:', req.path)
- next()
-})
-
-export default createController({
- get: async v => {
- return await { status: 200, body: { id: +(v.query?.id || 0) } }
- },
- post: v => ({
- // @ts-expect-error
- status: 200,
- body: { id: +v.query.id, port: v.body.port, fileName: v.body.file.originalname }
- })
-})
diff --git a/packages/frourio/server/api/@middleware.ts b/packages/frourio/server/api/@middleware.ts
deleted file mode 100644
index 7357476..0000000
--- a/packages/frourio/server/api/@middleware.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-import { createMiddleware } from './$relay'
-
-export default createMiddleware((req, res, next) => {
- console.log('Directory level middleware:', req.path)
- next()
-})
diff --git a/packages/frourio/server/api/multiForm/$relay.ts b/packages/frourio/server/api/multiForm/$relay.ts
deleted file mode 100644
index 0a7eeff..0000000
--- a/packages/frourio/server/api/multiForm/$relay.ts
+++ /dev/null
@@ -1,17 +0,0 @@
-/* eslint-disable */
-import { RequestHandler } from 'express'
-import { ServerMethods } from 'frourio'
-import { Methods } from './'
-
-type ControllerMethods = ServerMethods
-
-export const createMiddleware = <
- T extends RequestHandler | [] | [RequestHandler, ...RequestHandler[]]
->(handler: T): T => handler
-
-export const createController = (methods: ControllerMethods) => methods
-
-export const createInjectableController = (
- cb: (deps: T) => ControllerMethods,
- deps: T
-) => ({ ...cb(deps), inject: (d: T) => cb(d) })
diff --git a/packages/frourio/server/api/texts/$relay.ts b/packages/frourio/server/api/texts/$relay.ts
deleted file mode 100644
index 0a7eeff..0000000
--- a/packages/frourio/server/api/texts/$relay.ts
+++ /dev/null
@@ -1,17 +0,0 @@
-/* eslint-disable */
-import { RequestHandler } from 'express'
-import { ServerMethods } from 'frourio'
-import { Methods } from './'
-
-type ControllerMethods = ServerMethods
-
-export const createMiddleware = <
- T extends RequestHandler | [] | [RequestHandler, ...RequestHandler[]]
->(handler: T): T => handler
-
-export const createController = (methods: ControllerMethods) => methods
-
-export const createInjectableController = (
- cb: (deps: T) => ControllerMethods,
- deps: T
-) => ({ ...cb(deps), inject: (d: T) => cb(d) })
diff --git a/packages/frourio/server/api/texts/@controller.ts b/packages/frourio/server/api/texts/@controller.ts
deleted file mode 100644
index 27dc37e..0000000
--- a/packages/frourio/server/api/texts/@controller.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-import { createController } from './$relay'
-
-// @ts-expect-error
-export default createController({
- get: ({ query }) => ({ status: 200, body: query.val })
-})
diff --git a/packages/frourio/server/api/texts/sample/$relay.ts b/packages/frourio/server/api/texts/sample/$relay.ts
deleted file mode 100644
index 0a7eeff..0000000
--- a/packages/frourio/server/api/texts/sample/$relay.ts
+++ /dev/null
@@ -1,17 +0,0 @@
-/* eslint-disable */
-import { RequestHandler } from 'express'
-import { ServerMethods } from 'frourio'
-import { Methods } from './'
-
-type ControllerMethods = ServerMethods
-
-export const createMiddleware = <
- T extends RequestHandler | [] | [RequestHandler, ...RequestHandler[]]
->(handler: T): T => handler
-
-export const createController = (methods: ControllerMethods) => methods
-
-export const createInjectableController = (
- cb: (deps: T) => ControllerMethods,
- deps: T
-) => ({ ...cb(deps), inject: (d: T) => cb(d) })
diff --git a/packages/frourio/server/api/users/$relay.ts b/packages/frourio/server/api/users/$relay.ts
deleted file mode 100644
index ea0d2de..0000000
--- a/packages/frourio/server/api/users/$relay.ts
+++ /dev/null
@@ -1,20 +0,0 @@
-/* eslint-disable */
-import { RequestHandler } from 'express'
-import { ServerMethods } from 'frourio'
-import { User } from './@middleware'
-import { Methods } from './'
-
-type ControllerMethods = ServerMethods
-
-export const createMiddleware = <
- T extends RequestHandler | [] | [RequestHandler, ...RequestHandler[]]
->(handler: T): T => handler
-
-export const createController = (methods: ControllerMethods) => methods
-
-export const createInjectableController = (
- cb: (deps: T) => ControllerMethods,
- deps: T
-) => ({ ...cb(deps), inject: (d: T) => cb(d) })
diff --git a/packages/frourio/server/api/users/@controller.ts b/packages/frourio/server/api/users/@controller.ts
deleted file mode 100644
index 80380ae..0000000
--- a/packages/frourio/server/api/users/@controller.ts
+++ /dev/null
@@ -1,15 +0,0 @@
-import { createController, createMiddleware } from './$relay'
-
-const middleware = createMiddleware([
- (req, res, next) => {
- console.log('Controller level middleware:', req.path)
- next()
- }
-])
-
-export { middleware }
-
-export default createController({
- get: async () => ({ status: 200, body: [{ id: 1, name: 'aa' }] }),
- post: () => ({ status: 204 })
-})
diff --git a/packages/frourio/server/api/users/@middleware.ts b/packages/frourio/server/api/users/@middleware.ts
deleted file mode 100644
index 5986a66..0000000
--- a/packages/frourio/server/api/users/@middleware.ts
+++ /dev/null
@@ -1,13 +0,0 @@
-import { createMiddleware } from './$relay'
-
-export type User = {
- id: number
- name: string
- role: 'admin' | 'user'
-}
-
-export default createMiddleware((req, res, next) => {
- console.log('Added user')
- ;(req as any).user = { id: 1, name: 'user name', role: 'admin' }
- next()
-})
diff --git a/packages/frourio/server/api/users/_userId@number/$relay.ts b/packages/frourio/server/api/users/_userId@number/$relay.ts
deleted file mode 100644
index 72d7a57..0000000
--- a/packages/frourio/server/api/users/_userId@number/$relay.ts
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-import { RequestHandler } from 'express'
-import { ServerMethods } from 'frourio'
-import { User } from './../@middleware'
-import { Methods } from './'
-
-type ControllerMethods = ServerMethods
-
-export const createMiddleware = <
- T extends RequestHandler | [] | [RequestHandler, ...RequestHandler[]]
->(handler: T): T => handler
-
-export const createController = (methods: ControllerMethods) => methods
-
-export const createInjectableController = (
- cb: (deps: T) => ControllerMethods,
- deps: T
-) => ({ ...cb(deps), inject: (d: T) => cb(d) })
diff --git a/packages/frourio/server/api/users/_userId@number/@controller.ts b/packages/frourio/server/api/users/_userId@number/@controller.ts
deleted file mode 100644
index 6472b95..0000000
--- a/packages/frourio/server/api/users/_userId@number/@controller.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-import { createController } from './$relay'
-
-export default createController({
- get: ({ params }) => ({ status: 200, body: { id: params.userId, name: 'bbb' } })
-})
diff --git a/packages/frourio/server/api/users/_userId@number/index.ts b/packages/frourio/server/api/users/_userId@number/index.ts
deleted file mode 100644
index e686e44..0000000
--- a/packages/frourio/server/api/users/_userId@number/index.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-import { ValidUserInfo } from '../../../types'
-
-export type Methods = {
- get: {
- resBody: ValidUserInfo
- }
-}
diff --git a/packages/frourio/server/api/users/index.ts b/packages/frourio/server/api/users/index.ts
deleted file mode 100644
index d54407a..0000000
--- a/packages/frourio/server/api/users/index.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-import { ValidUserInfo } from '../../types'
-
-export type Methods = {
- get: {
- resBody: ValidUserInfo[]
- }
-
- post: {
- reqBody: ValidUserInfo
- }
-}
diff --git a/packages/frourio/server/entity/Task.ts b/packages/frourio/server/entity/Task.ts
deleted file mode 100644
index d165155..0000000
--- a/packages/frourio/server/entity/Task.ts
+++ /dev/null
@@ -1,13 +0,0 @@
-import { Entity, PrimaryColumn, Column } from 'typeorm'
-
-@Entity()
-export class Task {
- @PrimaryColumn()
- id: number
-
- @Column({ length: 100 })
- label: string
-
- @Column({ default: false })
- done: boolean
-}
diff --git a/packages/frourio/server/index.ts b/packages/frourio/server/index.ts
deleted file mode 100644
index 12da900..0000000
--- a/packages/frourio/server/index.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-import { run } from './$app'
-
-run({
- port: 10000,
- basePath: '/api',
- cors: true
-})
diff --git a/packages/frourio/server/public/sample.json b/packages/frourio/server/public/sample.json
deleted file mode 100644
index 09c61a0..0000000
--- a/packages/frourio/server/public/sample.json
+++ /dev/null
@@ -1 +0,0 @@
-{ "sample": true }
diff --git a/packages/frourio/server/subscriber/TaskSubscriber.ts b/packages/frourio/server/subscriber/TaskSubscriber.ts
deleted file mode 100644
index c03285b..0000000
--- a/packages/frourio/server/subscriber/TaskSubscriber.ts
+++ /dev/null
@@ -1,13 +0,0 @@
-import { EntitySubscriberInterface, EventSubscriber, InsertEvent } from 'typeorm'
-import { Task } from '../entity/Task'
-
-@EventSubscriber()
-export class TaskSubscriber implements EntitySubscriberInterface {
- listenTo() {
- return Task
- }
-
- afterInsert(event: InsertEvent) {
- console.log(event)
- }
-}
diff --git a/packages/frourio/servers/all/$app.ts b/packages/frourio/servers/all/$app.ts
new file mode 100644
index 0000000..95ff0bd
--- /dev/null
+++ b/packages/frourio/servers/all/$app.ts
@@ -0,0 +1,347 @@
+/* eslint-disable */
+import path from 'path'
+import {
+ LowerHttpMethod,
+ AspidaMethods,
+ HttpMethod,
+ HttpStatusOk,
+ AspidaMethodParams
+} from 'aspida'
+import { Deps } from 'velona'
+import express, { Express, RequestHandler, Request } from 'express'
+import multer, { Options } from 'multer'
+import { validateOrReject } from 'class-validator'
+
+type Hooks = {
+ onRequest?: RequestHandler | RequestHandler[]
+ preParsing?: RequestHandler | RequestHandler[]
+ preValidation?: RequestHandler | RequestHandler[]
+ preHandler?: RequestHandler | RequestHandler[]
+ onSend?: RequestHandler | RequestHandler[]
+}
+
+export function defineHooks(hooks: () => T): T
+export function defineHooks>(deps: U, cb: (deps: Deps) => T): T & { inject: (d: Deps) => T }
+export function defineHooks>(hooks: () => T | U, cb?: (deps: Deps) => T) {
+ return typeof hooks === 'function' ? hooks() : { ...cb!(hooks), inject: (d: Deps) => cb!(d) }
+}
+
+import * as Validators from './validators'
+import controller0, { hooks as ctrlHooks0 } from './api/controller'
+import controller1 from './api/500/controller'
+import controller2 from './api/empty/noEmpty/controller'
+import controller3 from './api/multiForm/controller'
+import controller4 from './api/texts/controller'
+import controller5 from './api/texts/sample/controller'
+import controller6, { hooks as ctrlHooks1 } from './api/users/controller'
+import controller7 from './api/users/_userId@number/controller'
+import hooks0 from './api/hooks'
+import hooks1 from './api/users/hooks'
+
+export type FrourioOptions = {
+ basePath?: string
+ multer?: Options
+}
+
+export type MulterFile = Express.Multer.File
+
+type HttpStatusNoOk =
+ | 301
+ | 302
+ | 400
+ | 401
+ | 402
+ | 403
+ | 404
+ | 405
+ | 406
+ | 409
+ | 500
+ | 501
+ | 502
+ | 503
+ | 504
+ | 505
+
+type PartiallyPartial = Omit & Partial>
+
+type BaseResponse = {
+ status: V extends number ? V : HttpStatusOk
+ body: T
+ headers: U
+}
+
+type ServerResponse =
+ | (K['resBody'] extends {} | null
+ ? K['resHeaders'] extends {}
+ ? BaseResponse
+ : PartiallyPartial<
+ BaseResponse<
+ K['resBody'],
+ K['resHeaders'] extends {} | undefined ? K['resHeaders'] : undefined,
+ K['status']
+ >,
+ 'headers'
+ >
+ : K['resHeaders'] extends {}
+ ? PartiallyPartial<
+ BaseResponse<
+ K['resBody'] extends {} | null | undefined ? K['resBody'] : undefined,
+ K['resHeaders'],
+ K['status']
+ >,
+ 'body'
+ >
+ : PartiallyPartial<
+ BaseResponse<
+ K['resBody'] extends {} | null | undefined ? K['resBody'] : undefined,
+ K['resHeaders'] extends {} | undefined ? K['resHeaders'] : undefined,
+ K['status']
+ >,
+ 'body' | 'headers'
+ >)
+ | PartiallyPartial, 'body' | 'headers'>
+
+type ServerValues = {
+ params?: Record
+ user?: any
+}
+
+type BlobToFile = T['reqFormat'] extends FormData
+ ? {
+ [P in keyof T['reqBody']]: Required[P] extends Blob
+ ? MulterFile
+ : Required[P] extends Blob[]
+ ? MulterFile[]
+ : T['reqBody'][P]
+ }
+ : T['reqBody']
+
+type RequestParams = {
+ path: string
+ method: HttpMethod
+ query: T['query']
+ body: BlobToFile
+ headers: T['reqHeaders']
+}
+
+export type ServerMethods = {
+ [K in keyof T]: (
+ req: RequestParams & U
+ ) => ServerResponse | Promise>
+}
+
+const parseNumberTypeQueryParams = (numberTypeParams: [string, boolean, boolean][]): RequestHandler => ({ query }, res, next) => {
+ for (const [key, isOptional, isArray] of numberTypeParams) {
+ const param = query[key]
+
+ if (isArray) {
+ if (!isOptional && param === undefined) {
+ query[key] = []
+ } else if (!isOptional || param !== undefined) {
+ if (!Array.isArray(param)) {
+ res.sendStatus(400)
+ return
+ }
+
+ const vals = (param as string[]).map(Number)
+
+ if (vals.some(isNaN)) {
+ res.sendStatus(400)
+ return
+ }
+
+ query[key] = vals as any
+ }
+ } else if (!isOptional || param !== undefined) {
+ const val = Number(param)
+
+ if (isNaN(val)) {
+ res.sendStatus(400)
+ return
+ }
+
+ query[key] = val as any
+ }
+ }
+
+ next()
+}
+
+const parseJSONBoby: RequestHandler = (req, res, next) => {
+ express.json()(req, res, err => {
+ if (err) return res.sendStatus(400)
+
+ next()
+ })
+}
+
+const createTypedParamsHandler = (numberTypeParams: string[]): RequestHandler => (
+ req,
+ res,
+ next
+) => {
+ const typedParams: Record = { ...req.params }
+
+ for (const key of numberTypeParams) {
+ const val = Number(typedParams[key])
+ if (isNaN(val)) {
+ res.sendStatus(400)
+ return
+ }
+
+ typedParams[key] = val
+ }
+
+ ;(req as any).typedParams = typedParams
+ next()
+}
+
+const createValidateHandler = (validators: (req: Request) => (Promise | null)[]): RequestHandler =>
+ (req, res, next) => Promise.all(validators(req)).then(() => next()).catch(() => res.sendStatus(400))
+
+const methodsToHandler = (
+ methodCallback: ServerMethods[LowerHttpMethod]
+): RequestHandler => async (req, res) => {
+ try {
+ const result = methodCallback({
+ query: req.query,
+ path: req.path,
+ method: req.method as HttpMethod,
+ body: req.body,
+ headers: req.headers,
+ params: (req as any).typedParams,
+ user: (req as any).user
+ })
+
+ const { status, body, headers } = result instanceof Promise ? await result : result
+
+ for (const key in headers) {
+ res.setHeader(key, headers[key])
+ }
+
+ res.status(status).send(body)
+ } catch (e) {
+ res.sendStatus(500)
+ }
+}
+
+const formatMulterData = (arrayTypeKeys: [string, boolean][]): RequestHandler => ({ body, files }, _res, next) => {
+ for (const [key] of arrayTypeKeys) {
+ if (body[key] === undefined) body[key] = []
+ else if (!Array.isArray(body[key])) {
+ body[key] = [body[key]]
+ }
+ }
+
+ for (const file of files as MulterFile[]) {
+ if (Array.isArray(body[file.fieldname])) {
+ body[file.fieldname].push(file)
+ } else {
+ body[file.fieldname] = file
+ }
+ }
+
+ for (const [key, isOptional] of arrayTypeKeys) {
+ if (!body[key].length && isOptional) delete body[key]
+ }
+
+ next()
+}
+
+export default (app: Express, options: FrourioOptions = {}) => {
+ const basePath = options.basePath ?? ''
+ const uploader = multer(
+ options.multer ?? { dest: path.join(__dirname, '.upload'), limits: { fileSize: 1024 ** 3 } }
+ ).any()
+
+ app.get(`${basePath}/`, [
+ ...hooks0.onRequest,
+ ctrlHooks0.onRequest,
+ parseNumberTypeQueryParams([['requiredNum', false, false], ['optionalNum', true, false], ['optionalNumArr', true, true], ['emptyNum', true, false], ['requiredNumArr', false, true]]),
+ createValidateHandler(req => [
+ Object.keys(req.query).length ? validateOrReject(Object.assign(new Validators.Query(), req.query)) : null
+ ]),
+ methodsToHandler(controller0.get)
+ ])
+
+ app.post(`${basePath}/`, [
+ ...hooks0.onRequest,
+ ctrlHooks0.onRequest,
+ hooks0.preParsing,
+ parseNumberTypeQueryParams([['requiredNum', false, false], ['optionalNum', true, false], ['optionalNumArr', true, true], ['emptyNum', true, false], ['requiredNumArr', false, true]]),
+ uploader,
+ formatMulterData([]),
+ createValidateHandler(req => [
+ validateOrReject(Object.assign(new Validators.Query(), req.query)),
+ validateOrReject(Object.assign(new Validators.Body(), req.body))
+ ]),
+ methodsToHandler(controller0.post)
+ ])
+
+ app.get(`${basePath}/500`, [
+ ...hooks0.onRequest,
+ methodsToHandler(controller1.get)
+ ])
+
+ app.get(`${basePath}/empty/noEmpty`, [
+ ...hooks0.onRequest,
+ methodsToHandler(controller2.get)
+ ])
+
+ app.post(`${basePath}/multiForm`, [
+ ...hooks0.onRequest,
+ hooks0.preParsing,
+ uploader,
+ formatMulterData([['requiredArr', false], ['optionalArr', true], ['empty', true], ['vals', false], ['files', false]]),
+ createValidateHandler(req => [
+ validateOrReject(Object.assign(new Validators.MultiForm(), req.body))
+ ]),
+ methodsToHandler(controller3.post)
+ ])
+
+ app.get(`${basePath}/texts`, [
+ ...hooks0.onRequest,
+ methodsToHandler(controller4.get)
+ ])
+
+ app.put(`${basePath}/texts`, [
+ ...hooks0.onRequest,
+ methodsToHandler(controller4.put)
+ ])
+
+ app.put(`${basePath}/texts/sample`, [
+ ...hooks0.onRequest,
+ hooks0.preParsing,
+ parseJSONBoby,
+ methodsToHandler(controller5.put)
+ ])
+
+ app.get(`${basePath}/users`, [
+ ...hooks0.onRequest,
+ hooks1.onRequest,
+ ...ctrlHooks1.preHandler,
+ methodsToHandler(controller6.get)
+ ])
+
+ app.post(`${basePath}/users`, [
+ ...hooks0.onRequest,
+ hooks1.onRequest,
+ hooks0.preParsing,
+ parseJSONBoby,
+ createValidateHandler(req => [
+ validateOrReject(Object.assign(new Validators.UserInfo(), req.body))
+ ]),
+ ...ctrlHooks1.preHandler,
+ methodsToHandler(controller6.post)
+ ])
+
+ app.get(`${basePath}/users/:userId`, [
+ ...hooks0.onRequest,
+ hooks1.onRequest,
+ createTypedParamsHandler(['userId']),
+ methodsToHandler(controller7.get)
+ ])
+
+ return app
+}
diff --git a/packages/frourio/servers/all/api/$api.ts b/packages/frourio/servers/all/api/$api.ts
new file mode 100644
index 0000000..b3d59b5
--- /dev/null
+++ b/packages/frourio/servers/all/api/$api.ts
@@ -0,0 +1,103 @@
+/* eslint-disable */
+import { AspidaClient, BasicHeaders, dataToURLString } from 'aspida'
+import { Methods as Methods0 } from '.'
+import { Methods as Methods1 } from './500'
+import { Methods as Methods2 } from './empty/noEmpty'
+import { Methods as Methods3 } from './multiForm'
+import { Methods as Methods4 } from './texts'
+import { Methods as Methods5 } from './texts/sample'
+import { Methods as Methods6 } from './users'
+import { Methods as Methods7 } from './users/_userId@number'
+
+const api = ({ baseURL, fetch }: AspidaClient) => {
+ const prefix = (baseURL === undefined ? '' : baseURL).replace(/\/$/, '')
+ const PATH0 = '/500'
+ const PATH1 = '/empty/noEmpty'
+ const PATH2 = '/multiForm'
+ const PATH3 = '/texts'
+ const PATH4 = '/texts/sample'
+ const PATH5 = '/users'
+ const GET = 'GET'
+ const POST = 'POST'
+ const PUT = 'PUT'
+
+ return {
+ $500: {
+ get: (option?: { config?: T }) =>
+ fetch(prefix, PATH0, GET, option).text(),
+ $get: (option?: { config?: T }) =>
+ fetch(prefix, PATH0, GET, option).text().then(r => r.body),
+ $path: () => `${prefix}${PATH0}`
+ },
+ empty: {
+ noEmpty: {
+ get: (option?: { config?: T }) =>
+ fetch(prefix, PATH1, GET, option).text(),
+ $get: (option?: { config?: T }) =>
+ fetch(prefix, PATH1, GET, option).text().then(r => r.body),
+ $path: () => `${prefix}${PATH1}`
+ }
+ },
+ multiForm: {
+ post: (option: { body: Methods3['post']['reqBody'], config?: T }) =>
+ fetch(prefix, PATH2, POST, option, 'FormData').json(),
+ $post: (option: { body: Methods3['post']['reqBody'], config?: T }) =>
+ fetch(prefix, PATH2, POST, option, 'FormData').json().then(r => r.body),
+ $path: () => `${prefix}${PATH2}`
+ },
+ texts: {
+ sample: {
+ put: (option: { body: Methods5['put']['reqBody'], config?: T }) =>
+ fetch(prefix, PATH4, PUT, option).json(),
+ $put: (option: { body: Methods5['put']['reqBody'], config?: T }) =>
+ fetch(prefix, PATH4, PUT, option).json().then(r => r.body),
+ $path: () => `${prefix}${PATH4}`
+ },
+ get: (option: { query: Methods4['get']['query'], config?: T }) =>
+ fetch(prefix, PATH3, GET, option).text(),
+ $get: (option: { query: Methods4['get']['query'], config?: T }) =>
+ fetch(prefix, PATH3, GET, option).text().then(r => r.body),
+ put: (option?: { config?: T }) =>
+ fetch(prefix, PATH3, PUT, option).send(),
+ $put: (option?: { config?: T }) =>
+ fetch(prefix, PATH3, PUT, option).send().then(r => r.body),
+ $path: (option?: { method?: 'get'; query: Methods4['get']['query'] }) =>
+ `${prefix}${PATH3}${option && option.query ? `?${dataToURLString(option.query)}` : ''}`
+ },
+ users: {
+ _userId: (val0: number) => {
+ const prefix0 = `${PATH5}/${val0}`
+
+ return {
+ get: (option?: { config?: T }) =>
+ fetch(prefix, prefix0, GET, option).json(),
+ $get: (option?: { config?: T }) =>
+ fetch(prefix, prefix0, GET, option).json().then(r => r.body),
+ $path: () => `${prefix}${prefix0}`
+ }
+ },
+ get: (option?: { config?: T }) =>
+ fetch(prefix, PATH5, GET, option).json(),
+ $get: (option?: { config?: T }) =>
+ fetch(prefix, PATH5, GET, option).json().then(r => r.body),
+ post: (option: { body: Methods6['post']['reqBody'], config?: T }) =>
+ fetch(prefix, PATH5, POST, option).send(),
+ $post: (option: { body: Methods6['post']['reqBody'], config?: T }) =>
+ fetch(prefix, PATH5, POST, option).send().then(r => r.body),
+ $path: () => `${prefix}${PATH5}`
+ },
+ get: (option?: { query?: Methods0['get']['query'], config?: T }) =>
+ fetch(prefix, '', GET, option).json(),
+ $get: (option?: { query?: Methods0['get']['query'], config?: T }) =>
+ fetch(prefix, '', GET, option).json().then(r => r.body),
+ post: (option: { body: Methods0['post']['reqBody'], query: Methods0['post']['query'], config?: T }) =>
+ fetch(prefix, '', POST, option, 'FormData').json(),
+ $post: (option: { body: Methods0['post']['reqBody'], query: Methods0['post']['query'], config?: T }) =>
+ fetch(prefix, '', POST, option, 'FormData').json().then(r => r.body),
+ $path: (option?: { method?: 'get'; query: Methods0['get']['query'] } | { method: 'post'; query: Methods0['post']['query'] }) =>
+ `${prefix}${''}${option && option.query ? `?${dataToURLString(option.query)}` : ''}`
+ }
+}
+
+export type ApiInstance = ReturnType
+export default api
diff --git a/packages/frourio/servers/all/api/$relay.ts b/packages/frourio/servers/all/api/$relay.ts
new file mode 100644
index 0000000..f064967
--- /dev/null
+++ b/packages/frourio/servers/all/api/$relay.ts
@@ -0,0 +1,14 @@
+/* eslint-disable */
+import { Deps } from 'velona'
+import { ServerMethods, defineHooks } from '../$app'
+import { Methods } from './'
+
+type ControllerMethods = ServerMethods
+
+export { defineHooks }
+
+export function defineController(methods: () => ControllerMethods): ControllerMethods
+export function defineController>(deps: T, cb: (deps: Deps) => ControllerMethods): ControllerMethods & { inject: (d: Deps) => ControllerMethods }
+export function defineController>(methods: () => ControllerMethods | T, cb?: (deps: Deps) => ControllerMethods) {
+ return typeof methods === 'function' ? methods() : { ...cb!(methods), inject: (d: Deps) => cb!(d) }
+}
diff --git a/packages/frourio/servers/all/api/500/$relay.ts b/packages/frourio/servers/all/api/500/$relay.ts
new file mode 100644
index 0000000..8b7c89b
--- /dev/null
+++ b/packages/frourio/servers/all/api/500/$relay.ts
@@ -0,0 +1,14 @@
+/* eslint-disable */
+import { Deps } from 'velona'
+import { ServerMethods, defineHooks } from '../../$app'
+import { Methods } from './'
+
+type ControllerMethods = ServerMethods
+
+export { defineHooks }
+
+export function defineController(methods: () => ControllerMethods): ControllerMethods
+export function defineController>(deps: T, cb: (deps: Deps) => ControllerMethods): ControllerMethods & { inject: (d: Deps) => ControllerMethods }
+export function defineController>(methods: () => ControllerMethods | T, cb?: (deps: Deps) => ControllerMethods) {
+ return typeof methods === 'function' ? methods() : { ...cb!(methods), inject: (d: Deps) => cb!(d) }
+}
diff --git a/packages/frourio/servers/all/api/500/controller.ts b/packages/frourio/servers/all/api/500/controller.ts
new file mode 100644
index 0000000..a583290
--- /dev/null
+++ b/packages/frourio/servers/all/api/500/controller.ts
@@ -0,0 +1,7 @@
+import { defineController } from './$relay'
+
+export default defineController(() => ({
+ get: () => {
+ throw new Error('500 error test')
+ }
+}))
diff --git a/packages/frourio/servers/all/api/500/index.ts b/packages/frourio/servers/all/api/500/index.ts
new file mode 100644
index 0000000..0556ee1
--- /dev/null
+++ b/packages/frourio/servers/all/api/500/index.ts
@@ -0,0 +1,5 @@
+export type Methods = {
+ get: {
+ resBody: string
+ }
+}
diff --git a/packages/frourio/servers/all/api/controller.ts b/packages/frourio/servers/all/api/controller.ts
new file mode 100644
index 0000000..869a03f
--- /dev/null
+++ b/packages/frourio/servers/all/api/controller.ts
@@ -0,0 +1,22 @@
+import { defineController, defineHooks } from '~/$relay'
+import { depend } from 'velona'
+
+const hooks = defineHooks({ print: (...args: string[]) => console.log(...args) }, ({ print }) => ({
+ onRequest: depend({}, (deps, req, res, next) => {
+ print('Controller level onRequest hook:', req.path)
+ next()
+ })
+}))
+
+export default defineController(() => ({
+ get: async v => {
+ return await { status: 200, body: { id: +(v.query?.id || 0) } }
+ },
+ post: v => ({
+ // @ts-expect-error
+ status: 200,
+ body: { id: +v.query.id, port: v.body.port, fileName: v.body.file.originalname }
+ })
+}))
+
+export { hooks }
diff --git a/packages/frourio/servers/all/api/empty/$relay.ts b/packages/frourio/servers/all/api/empty/$relay.ts
new file mode 100644
index 0000000..8b7c89b
--- /dev/null
+++ b/packages/frourio/servers/all/api/empty/$relay.ts
@@ -0,0 +1,14 @@
+/* eslint-disable */
+import { Deps } from 'velona'
+import { ServerMethods, defineHooks } from '../../$app'
+import { Methods } from './'
+
+type ControllerMethods = ServerMethods
+
+export { defineHooks }
+
+export function defineController(methods: () => ControllerMethods): ControllerMethods
+export function defineController>(deps: T, cb: (deps: Deps) => ControllerMethods): ControllerMethods & { inject: (d: Deps) => ControllerMethods }
+export function defineController>(methods: () => ControllerMethods | T, cb?: (deps: Deps) => ControllerMethods) {
+ return typeof methods === 'function' ? methods() : { ...cb!(methods), inject: (d: Deps) => cb!(d) }
+}
diff --git a/packages/frourio/servers/all/api/empty/controller.ts b/packages/frourio/servers/all/api/empty/controller.ts
new file mode 100644
index 0000000..edbfd03
--- /dev/null
+++ b/packages/frourio/servers/all/api/empty/controller.ts
@@ -0,0 +1,3 @@
+import { defineController } from './$relay'
+
+export default defineController(() => ({}))
diff --git a/packages/frourio/servers/all/api/empty/index.ts b/packages/frourio/servers/all/api/empty/index.ts
new file mode 100644
index 0000000..4dcad57
--- /dev/null
+++ b/packages/frourio/servers/all/api/empty/index.ts
@@ -0,0 +1 @@
+export type Methods = {}
diff --git a/packages/frourio/servers/all/api/empty/noEmpty/$relay.ts b/packages/frourio/servers/all/api/empty/noEmpty/$relay.ts
new file mode 100644
index 0000000..26ca223
--- /dev/null
+++ b/packages/frourio/servers/all/api/empty/noEmpty/$relay.ts
@@ -0,0 +1,14 @@
+/* eslint-disable */
+import { Deps } from 'velona'
+import { ServerMethods, defineHooks } from '../../../$app'
+import { Methods } from './'
+
+type ControllerMethods = ServerMethods
+
+export { defineHooks }
+
+export function defineController(methods: () => ControllerMethods): ControllerMethods
+export function defineController>(deps: T, cb: (deps: Deps) => ControllerMethods): ControllerMethods & { inject: (d: Deps) => ControllerMethods }
+export function defineController>(methods: () => ControllerMethods | T, cb?: (deps: Deps) => ControllerMethods) {
+ return typeof methods === 'function' ? methods() : { ...cb!(methods), inject: (d: Deps) => cb!(d) }
+}
diff --git a/packages/frourio/servers/all/api/empty/noEmpty/controller.ts b/packages/frourio/servers/all/api/empty/noEmpty/controller.ts
new file mode 100644
index 0000000..eb73909
--- /dev/null
+++ b/packages/frourio/servers/all/api/empty/noEmpty/controller.ts
@@ -0,0 +1,5 @@
+import { defineController } from './$relay'
+
+export default defineController(() => ({
+ get: () => ({ status: 200, body: 'sample' })
+}))
diff --git a/packages/frourio/servers/all/api/empty/noEmpty/index.ts b/packages/frourio/servers/all/api/empty/noEmpty/index.ts
new file mode 100644
index 0000000..0556ee1
--- /dev/null
+++ b/packages/frourio/servers/all/api/empty/noEmpty/index.ts
@@ -0,0 +1,5 @@
+export type Methods = {
+ get: {
+ resBody: string
+ }
+}
diff --git a/packages/frourio/servers/all/api/hooks.ts b/packages/frourio/servers/all/api/hooks.ts
new file mode 100644
index 0000000..cc590f1
--- /dev/null
+++ b/packages/frourio/servers/all/api/hooks.ts
@@ -0,0 +1,14 @@
+import { defineHooks } from './$relay'
+
+export default defineHooks(() => ({
+ onRequest: [
+ (req, res, next) => {
+ console.log('Directory level middleware:', req.path)
+ next()
+ }
+ ],
+ preParsing: (req, res, next) => {
+ console.log('Directory level middleware:', req.path)
+ next()
+ }
+}))
diff --git a/packages/frourio/server/api/index.ts b/packages/frourio/servers/all/api/index.ts
similarity index 65%
rename from packages/frourio/server/api/index.ts
rename to packages/frourio/servers/all/api/index.ts
index db0549a..a9d6441 100644
--- a/packages/frourio/server/api/index.ts
+++ b/packages/frourio/servers/all/api/index.ts
@@ -1,16 +1,16 @@
-import { ValidQuery, ValidBody } from '../types'
+import { Query, Body } from 'validators'
export type Methods = {
get: {
- query?: ValidQuery
+ query?: Query
status: 200
resBody?: { id: number }
}
post: {
- query: ValidQuery
+ query: Query
reqFormat: FormData
- reqBody: ValidBody
+ reqBody: Body
status: 201
resBody: {
id: number
diff --git a/packages/frourio/servers/all/api/multiForm/$relay.ts b/packages/frourio/servers/all/api/multiForm/$relay.ts
new file mode 100644
index 0000000..8b7c89b
--- /dev/null
+++ b/packages/frourio/servers/all/api/multiForm/$relay.ts
@@ -0,0 +1,14 @@
+/* eslint-disable */
+import { Deps } from 'velona'
+import { ServerMethods, defineHooks } from '../../$app'
+import { Methods } from './'
+
+type ControllerMethods = ServerMethods
+
+export { defineHooks }
+
+export function defineController(methods: () => ControllerMethods): ControllerMethods
+export function defineController>(deps: T, cb: (deps: Deps) => ControllerMethods): ControllerMethods & { inject: (d: Deps) => ControllerMethods }
+export function defineController>(methods: () => ControllerMethods | T, cb?: (deps: Deps) => ControllerMethods) {
+ return typeof methods === 'function' ? methods() : { ...cb!(methods), inject: (d: Deps) => cb!(d) }
+}
diff --git a/packages/frourio/server/api/multiForm/@controller.ts b/packages/frourio/servers/all/api/multiForm/controller.ts
similarity index 73%
rename from packages/frourio/server/api/multiForm/@controller.ts
rename to packages/frourio/servers/all/api/multiForm/controller.ts
index 8c46a3f..dd166bc 100644
--- a/packages/frourio/server/api/multiForm/@controller.ts
+++ b/packages/frourio/servers/all/api/multiForm/controller.ts
@@ -1,7 +1,7 @@
-import { createController } from './$relay'
+import { defineController } from './$relay'
import { Methods } from './'
-export default createController({
+export default defineController(() => ({
post: ({ body }) => ({
status: 201,
body: Object.entries(body).reduce(
@@ -9,4 +9,4 @@ export default createController({
{} as Methods['post']['resBody']
)
})
-})
+}))
diff --git a/packages/frourio/server/api/multiForm/index.ts b/packages/frourio/servers/all/api/multiForm/index.ts
similarity index 71%
rename from packages/frourio/server/api/multiForm/index.ts
rename to packages/frourio/servers/all/api/multiForm/index.ts
index 2e8e234..f307b9c 100644
--- a/packages/frourio/server/api/multiForm/index.ts
+++ b/packages/frourio/servers/all/api/multiForm/index.ts
@@ -1,9 +1,9 @@
-import { ValidMultiForm } from '../../types'
+import { MultiForm } from 'validators'
export type Methods = {
post: {
reqFormat: FormData
- reqBody: ValidMultiForm
+ reqBody: MultiForm
resBody: {
empty: number
name: number
diff --git a/packages/frourio/servers/all/api/texts/$relay.ts b/packages/frourio/servers/all/api/texts/$relay.ts
new file mode 100644
index 0000000..8b7c89b
--- /dev/null
+++ b/packages/frourio/servers/all/api/texts/$relay.ts
@@ -0,0 +1,14 @@
+/* eslint-disable */
+import { Deps } from 'velona'
+import { ServerMethods, defineHooks } from '../../$app'
+import { Methods } from './'
+
+type ControllerMethods = ServerMethods
+
+export { defineHooks }
+
+export function defineController(methods: () => ControllerMethods): ControllerMethods
+export function defineController>(deps: T, cb: (deps: Deps) => ControllerMethods): ControllerMethods & { inject: (d: Deps) => ControllerMethods }
+export function defineController>(methods: () => ControllerMethods | T, cb?: (deps: Deps) => ControllerMethods) {
+ return typeof methods === 'function' ? methods() : { ...cb!(methods), inject: (d: Deps) => cb!(d) }
+}
diff --git a/packages/frourio/servers/all/api/texts/controller.ts b/packages/frourio/servers/all/api/texts/controller.ts
new file mode 100644
index 0000000..edca134
--- /dev/null
+++ b/packages/frourio/servers/all/api/texts/controller.ts
@@ -0,0 +1,6 @@
+import { defineController } from './$relay'
+
+// @ts-expect-error
+export default defineController(() => ({
+ get: ({ query }) => ({ status: 200, body: query.val })
+}))
diff --git a/packages/frourio/server/api/texts/index.ts b/packages/frourio/servers/all/api/texts/index.ts
similarity index 100%
rename from packages/frourio/server/api/texts/index.ts
rename to packages/frourio/servers/all/api/texts/index.ts
diff --git a/packages/frourio/servers/all/api/texts/sample/$relay.ts b/packages/frourio/servers/all/api/texts/sample/$relay.ts
new file mode 100644
index 0000000..26ca223
--- /dev/null
+++ b/packages/frourio/servers/all/api/texts/sample/$relay.ts
@@ -0,0 +1,14 @@
+/* eslint-disable */
+import { Deps } from 'velona'
+import { ServerMethods, defineHooks } from '../../../$app'
+import { Methods } from './'
+
+type ControllerMethods = ServerMethods
+
+export { defineHooks }
+
+export function defineController(methods: () => ControllerMethods): ControllerMethods
+export function defineController>(deps: T, cb: (deps: Deps) => ControllerMethods): ControllerMethods & { inject: (d: Deps) => ControllerMethods }
+export function defineController>(methods: () => ControllerMethods | T, cb?: (deps: Deps) => ControllerMethods) {
+ return typeof methods === 'function' ? methods() : { ...cb!(methods), inject: (d: Deps) => cb!(d) }
+}
diff --git a/packages/frourio/server/api/texts/sample/@controller.ts b/packages/frourio/servers/all/api/texts/sample/controller.ts
similarity index 58%
rename from packages/frourio/server/api/texts/sample/@controller.ts
rename to packages/frourio/servers/all/api/texts/sample/controller.ts
index 69a4b45..8aa3058 100644
--- a/packages/frourio/server/api/texts/sample/@controller.ts
+++ b/packages/frourio/servers/all/api/texts/sample/controller.ts
@@ -1,7 +1,7 @@
-import { createController } from './$relay'
+import { defineController } from './$relay'
-export default createController({
+export default defineController(() => ({
// @ts-expect-error
get: ({ query }) => ({ status: 200, body: query.val }),
put: ({ body }) => ({ status: 200, body })
-})
+}))
diff --git a/packages/frourio/server/api/texts/sample/index.ts b/packages/frourio/servers/all/api/texts/sample/index.ts
similarity index 100%
rename from packages/frourio/server/api/texts/sample/index.ts
rename to packages/frourio/servers/all/api/texts/sample/index.ts
diff --git a/packages/frourio/servers/all/api/users/$relay.ts b/packages/frourio/servers/all/api/users/$relay.ts
new file mode 100644
index 0000000..2f2292e
--- /dev/null
+++ b/packages/frourio/servers/all/api/users/$relay.ts
@@ -0,0 +1,17 @@
+/* eslint-disable */
+import { Deps } from 'velona'
+import { ServerMethods, defineHooks } from '../../$app'
+import { User } from './hooks'
+import { Methods } from './'
+
+type ControllerMethods = ServerMethods
+
+export { defineHooks }
+
+export function defineController(methods: () => ControllerMethods): ControllerMethods
+export function defineController>(deps: T, cb: (deps: Deps) => ControllerMethods): ControllerMethods & { inject: (d: Deps) => ControllerMethods }
+export function defineController>(methods: () => ControllerMethods | T, cb?: (deps: Deps) => ControllerMethods) {
+ return typeof methods === 'function' ? methods() : { ...cb!(methods), inject: (d: Deps) => cb!(d) }
+}
diff --git a/packages/frourio/servers/all/api/users/_userId@number/$relay.ts b/packages/frourio/servers/all/api/users/_userId@number/$relay.ts
new file mode 100644
index 0000000..b3fd1d5
--- /dev/null
+++ b/packages/frourio/servers/all/api/users/_userId@number/$relay.ts
@@ -0,0 +1,20 @@
+/* eslint-disable */
+import { Deps } from 'velona'
+import { ServerMethods, defineHooks } from '../../../$app'
+import { User } from './../hooks'
+import { Methods } from './'
+
+type ControllerMethods = ServerMethods
+
+export { defineHooks }
+
+export function defineController(methods: () => ControllerMethods): ControllerMethods
+export function defineController>(deps: T, cb: (deps: Deps) => ControllerMethods): ControllerMethods & { inject: (d: Deps) => ControllerMethods }
+export function defineController>(methods: () => ControllerMethods | T, cb?: (deps: Deps) => ControllerMethods) {
+ return typeof methods === 'function' ? methods() : { ...cb!(methods), inject: (d: Deps) => cb!(d) }
+}
diff --git a/packages/frourio/servers/all/api/users/_userId@number/controller.ts b/packages/frourio/servers/all/api/users/_userId@number/controller.ts
new file mode 100644
index 0000000..047e709
--- /dev/null
+++ b/packages/frourio/servers/all/api/users/_userId@number/controller.ts
@@ -0,0 +1,5 @@
+import { defineController } from './$relay'
+
+export default defineController(() => ({
+ get: ({ params }) => ({ status: 200, body: { id: params.userId, name: 'bbb' } })
+}))
diff --git a/packages/frourio/servers/all/api/users/_userId@number/index.ts b/packages/frourio/servers/all/api/users/_userId@number/index.ts
new file mode 100644
index 0000000..6cebe6d
--- /dev/null
+++ b/packages/frourio/servers/all/api/users/_userId@number/index.ts
@@ -0,0 +1,7 @@
+import { UserInfo } from 'validators'
+
+export type Methods = {
+ get: {
+ resBody: UserInfo
+ }
+}
diff --git a/packages/frourio/servers/all/api/users/controller.ts b/packages/frourio/servers/all/api/users/controller.ts
new file mode 100644
index 0000000..7c9ed22
--- /dev/null
+++ b/packages/frourio/servers/all/api/users/controller.ts
@@ -0,0 +1,17 @@
+import { defineController, defineHooks } from './$relay'
+
+const hooks = defineHooks(() => ({
+ preHandler: [
+ (req, res, next) => {
+ console.log('Controller level preHandler hook:', req.path)
+ next()
+ }
+ ]
+}))
+
+export { hooks }
+
+export default defineController(() => ({
+ get: async () => ({ status: 200, body: [{ id: 1, name: 'aa' }] }),
+ post: () => ({ status: 204 })
+}))
diff --git a/packages/frourio/servers/all/api/users/hooks.ts b/packages/frourio/servers/all/api/users/hooks.ts
new file mode 100644
index 0000000..036c246
--- /dev/null
+++ b/packages/frourio/servers/all/api/users/hooks.ts
@@ -0,0 +1,15 @@
+import { defineHooks } from './$relay'
+
+export type User = {
+ id: number
+ name: string
+ role: 'admin' | 'user'
+}
+
+export default defineHooks(() => ({
+ onRequest: (req, res, next) => {
+ console.log('Added user')
+ ;(req as any).user = { id: 1, name: 'user name', role: 'admin' }
+ next()
+ }
+}))
diff --git a/packages/frourio/servers/all/api/users/index.ts b/packages/frourio/servers/all/api/users/index.ts
new file mode 100644
index 0000000..bc9fdab
--- /dev/null
+++ b/packages/frourio/servers/all/api/users/index.ts
@@ -0,0 +1,11 @@
+import { UserInfo } from 'validators'
+
+export type Methods = {
+ get: {
+ resBody: UserInfo[]
+ }
+
+ post: {
+ reqBody: UserInfo
+ }
+}
diff --git a/packages/frourio/servers/all/tsconfig.json b/packages/frourio/servers/all/tsconfig.json
new file mode 100644
index 0000000..991c48a
--- /dev/null
+++ b/packages/frourio/servers/all/tsconfig.json
@@ -0,0 +1,10 @@
+{
+ "extends": "../../../../tsconfig.json",
+ "compilerOptions": {
+ "baseUrl": ".",
+ "paths": {
+ "~/*": ["api/*"],
+ "validators": ["validators"]
+ }
+ }
+}
diff --git a/packages/frourio/servers/all/validators/index.ts b/packages/frourio/servers/all/validators/index.ts
new file mode 100644
index 0000000..5966e93
--- /dev/null
+++ b/packages/frourio/servers/all/validators/index.ts
@@ -0,0 +1,65 @@
+import {
+ IsNumberString,
+ IsBooleanString,
+ IsPort,
+ IsInt,
+ MaxLength,
+ IsString,
+ Allow,
+ IsOptional,
+ ArrayNotEmpty
+} from 'class-validator'
+
+export class Query {
+ requiredNum: number
+ optionalNum?: number
+ optionalNumArr?: Array
+
+ @IsOptional()
+ emptyNum?: number
+
+ @IsInt({ each: true })
+ requiredNumArr: number[]
+
+ @IsNumberString()
+ id: string
+
+ @IsBooleanString()
+ disable: string
+}
+
+export class Body {
+ @IsPort()
+ port: string
+
+ file: File
+}
+
+export class UserInfo {
+ @IsInt()
+ id: number
+
+ @MaxLength(20)
+ name: string
+}
+
+export class MultiForm {
+ requiredArr: string[]
+ optionalArr?: string[]
+
+ @IsOptional()
+ @IsInt({ each: true })
+ empty?: number[]
+
+ @IsString()
+ name: string
+
+ @Allow()
+ icon: Blob
+
+ @IsString({ each: true })
+ vals: string[]
+
+ @ArrayNotEmpty()
+ files: Blob[]
+}
diff --git a/packages/frourio/servers/aspida.config.js b/packages/frourio/servers/aspida.config.js
new file mode 100644
index 0000000..a2f2c33
--- /dev/null
+++ b/packages/frourio/servers/aspida.config.js
@@ -0,0 +1,6 @@
+const fs = require('fs')
+
+module.exports = fs
+ .readdirSync('.', { withFileTypes: true })
+ .filter(d => d.isDirectory())
+ .map(d => ({ input: `${d.name}/api` }))
diff --git a/packages/frourio/servers/build.js b/packages/frourio/servers/build.js
new file mode 100644
index 0000000..603a907
--- /dev/null
+++ b/packages/frourio/servers/build.js
@@ -0,0 +1,7 @@
+const fs = require('fs')
+const write = require('aspida/dist/writeRouteFile').default
+const build = require('../dist/buildServerFile').default
+
+fs.readdirSync(__dirname, { withFileTypes: true }).forEach(dir => {
+ if (dir.isDirectory()) write(build(`${__dirname}/${dir.name}`))
+})
diff --git a/packages/frourio/servers/frourio.ts b/packages/frourio/servers/frourio.ts
new file mode 100644
index 0000000..25b7b6c
--- /dev/null
+++ b/packages/frourio/servers/frourio.ts
@@ -0,0 +1,4 @@
+import express from 'express'
+import frourio from './frourio/$app'
+
+frourio(express()).listen(3000)
diff --git a/packages/frourio/servers/frourio/$app.ts b/packages/frourio/servers/frourio/$app.ts
new file mode 100644
index 0000000..315772d
--- /dev/null
+++ b/packages/frourio/servers/frourio/$app.ts
@@ -0,0 +1,140 @@
+/* eslint-disable */
+import {
+ LowerHttpMethod,
+ AspidaMethods,
+ HttpMethod,
+ HttpStatusOk,
+ AspidaMethodParams
+} from 'aspida'
+import { Deps } from 'velona'
+import { Express, RequestHandler } from 'express'
+
+type Hooks = {
+ onRequest?: RequestHandler | RequestHandler[]
+ preParsing?: RequestHandler | RequestHandler[]
+ preValidation?: RequestHandler | RequestHandler[]
+ preHandler?: RequestHandler | RequestHandler[]
+ onSend?: RequestHandler | RequestHandler[]
+}
+
+export function defineHooks(hooks: () => T): T
+export function defineHooks>(deps: U, cb: (deps: Deps) => T): T & { inject: (d: Deps) => T }
+export function defineHooks>(hooks: () => T | U, cb?: (deps: Deps) => T) {
+ return typeof hooks === 'function' ? hooks() : { ...cb!(hooks), inject: (d: Deps) => cb!(d) }
+}
+
+import controller0 from './api/controller'
+
+export type FrourioOptions = {
+ basePath?: string
+}
+
+type HttpStatusNoOk =
+ | 301
+ | 302
+ | 400
+ | 401
+ | 402
+ | 403
+ | 404
+ | 405
+ | 406
+ | 409
+ | 500
+ | 501
+ | 502
+ | 503
+ | 504
+ | 505
+
+type PartiallyPartial = Omit & Partial>
+
+type BaseResponse = {
+ status: V extends number ? V : HttpStatusOk
+ body: T
+ headers: U
+}
+
+type ServerResponse =
+ | (K['resBody'] extends {} | null
+ ? K['resHeaders'] extends {}
+ ? BaseResponse
+ : PartiallyPartial<
+ BaseResponse<
+ K['resBody'],
+ K['resHeaders'] extends {} | undefined ? K['resHeaders'] : undefined,
+ K['status']
+ >,
+ 'headers'
+ >
+ : K['resHeaders'] extends {}
+ ? PartiallyPartial<
+ BaseResponse<
+ K['resBody'] extends {} | null | undefined ? K['resBody'] : undefined,
+ K['resHeaders'],
+ K['status']
+ >,
+ 'body'
+ >
+ : PartiallyPartial<
+ BaseResponse<
+ K['resBody'] extends {} | null | undefined ? K['resBody'] : undefined,
+ K['resHeaders'] extends {} | undefined ? K['resHeaders'] : undefined,
+ K['status']
+ >,
+ 'body' | 'headers'
+ >)
+ | PartiallyPartial, 'body' | 'headers'>
+
+type ServerValues = {
+ params?: Record
+ user?: any
+}
+
+type RequestParams = {
+ path: string
+ method: HttpMethod
+ query: T['query']
+ body: T['reqBody']
+ headers: T['reqHeaders']
+}
+
+export type ServerMethods = {
+ [K in keyof T]: (
+ req: RequestParams & U
+ ) => ServerResponse | Promise>
+}
+
+const methodsToHandler = (
+ methodCallback: ServerMethods[LowerHttpMethod]
+): RequestHandler => async (req, res) => {
+ try {
+ const result = methodCallback({
+ query: req.query,
+ path: req.path,
+ method: req.method as HttpMethod,
+ body: req.body,
+ headers: req.headers,
+ params: (req as any).typedParams,
+ user: (req as any).user
+ })
+
+ const { status, body, headers } = result instanceof Promise ? await result : result
+
+ for (const key in headers) {
+ res.setHeader(key, headers[key])
+ }
+
+ res.status(status).send(body)
+ } catch (e) {
+ res.sendStatus(500)
+ }
+}
+
+export default (app: Express, options: FrourioOptions = {}) => {
+ const basePath = options.basePath ?? ''
+
+ app.get(`${basePath}/`, methodsToHandler(controller0.get))
+
+ return app
+}
diff --git a/packages/frourio/servers/frourio/api/$api.ts b/packages/frourio/servers/frourio/api/$api.ts
new file mode 100644
index 0000000..bf69e8f
--- /dev/null
+++ b/packages/frourio/servers/frourio/api/$api.ts
@@ -0,0 +1,20 @@
+/* eslint-disable */
+import { AspidaClient } from 'aspida'
+import { Methods as Methods0 } from '.'
+
+const api = ({ baseURL, fetch }: AspidaClient) => {
+ const prefix = (baseURL === undefined ? '' : baseURL).replace(/\/$/, '')
+
+ const GET = 'GET'
+
+ return {
+ get: (option?: { config?: T }) =>
+ fetch(prefix, '', GET, option).text(),
+ $get: (option?: { config?: T }) =>
+ fetch(prefix, '', GET, option).text().then(r => r.body),
+ $path: () => `${prefix}${''}`
+ }
+}
+
+export type ApiInstance = ReturnType
+export default api
diff --git a/packages/frourio/servers/frourio/api/$relay.ts b/packages/frourio/servers/frourio/api/$relay.ts
new file mode 100644
index 0000000..f064967
--- /dev/null
+++ b/packages/frourio/servers/frourio/api/$relay.ts
@@ -0,0 +1,14 @@
+/* eslint-disable */
+import { Deps } from 'velona'
+import { ServerMethods, defineHooks } from '../$app'
+import { Methods } from './'
+
+type ControllerMethods = ServerMethods
+
+export { defineHooks }
+
+export function defineController(methods: () => ControllerMethods): ControllerMethods
+export function defineController>(deps: T, cb: (deps: Deps) => ControllerMethods): ControllerMethods & { inject: (d: Deps) => ControllerMethods }
+export function defineController>(methods: () => ControllerMethods | T, cb?: (deps: Deps) => ControllerMethods) {
+ return typeof methods === 'function' ? methods() : { ...cb!(methods), inject: (d: Deps) => cb!(d) }
+}
diff --git a/packages/frourio/servers/frourio/api/controller.ts b/packages/frourio/servers/frourio/api/controller.ts
new file mode 100644
index 0000000..9de8c56
--- /dev/null
+++ b/packages/frourio/servers/frourio/api/controller.ts
@@ -0,0 +1,5 @@
+import { defineController } from './$relay'
+
+export default defineController(() => ({
+ get: () => ({ status: 200, body: 'Hello world' })
+}))
diff --git a/packages/frourio/servers/frourio/api/index.ts b/packages/frourio/servers/frourio/api/index.ts
new file mode 100644
index 0000000..0556ee1
--- /dev/null
+++ b/packages/frourio/servers/frourio/api/index.ts
@@ -0,0 +1,5 @@
+export type Methods = {
+ get: {
+ resBody: string
+ }
+}
diff --git a/packages/frourio/servers/noMulter/$app.ts b/packages/frourio/servers/noMulter/$app.ts
new file mode 100644
index 0000000..4fbaf1b
--- /dev/null
+++ b/packages/frourio/servers/noMulter/$app.ts
@@ -0,0 +1,245 @@
+/* eslint-disable */
+import {
+ LowerHttpMethod,
+ AspidaMethods,
+ HttpMethod,
+ HttpStatusOk,
+ AspidaMethodParams
+} from 'aspida'
+import { Deps } from 'velona'
+import express, { Express, RequestHandler, Request } from 'express'
+import { validateOrReject } from 'class-validator'
+
+type Hooks = {
+ onRequest?: RequestHandler | RequestHandler[]
+ preParsing?: RequestHandler | RequestHandler[]
+ preValidation?: RequestHandler | RequestHandler[]
+ preHandler?: RequestHandler | RequestHandler[]
+ onSend?: RequestHandler | RequestHandler[]
+}
+
+export function defineHooks(hooks: () => T): T
+export function defineHooks>(deps: U, cb: (deps: Deps) => T): T & { inject: (d: Deps) => T }
+export function defineHooks>(hooks: () => T | U, cb?: (deps: Deps) => T) {
+ return typeof hooks === 'function' ? hooks() : { ...cb!(hooks), inject: (d: Deps) => cb!(d) }
+}
+
+import * as Validators from './validators'
+import controller0, { hooks as ctrlHooks0 } from './api/controller'
+import controller1 from './api/empty/noEmpty/controller'
+import controller2 from './api/texts/controller'
+import controller3 from './api/texts/sample/controller'
+import controller4, { hooks as ctrlHooks1 } from './api/users/controller'
+import controller5 from './api/users/_userId@number/controller'
+import hooks0 from './api/hooks'
+import hooks1 from './api/users/hooks'
+
+export type FrourioOptions = {
+ basePath?: string
+}
+
+type HttpStatusNoOk =
+ | 301
+ | 302
+ | 400
+ | 401
+ | 402
+ | 403
+ | 404
+ | 405
+ | 406
+ | 409
+ | 500
+ | 501
+ | 502
+ | 503
+ | 504
+ | 505
+
+type PartiallyPartial = Omit & Partial>
+
+type BaseResponse = {
+ status: V extends number ? V : HttpStatusOk
+ body: T
+ headers: U
+}
+
+type ServerResponse =
+ | (K['resBody'] extends {} | null
+ ? K['resHeaders'] extends {}
+ ? BaseResponse
+ : PartiallyPartial<
+ BaseResponse<
+ K['resBody'],
+ K['resHeaders'] extends {} | undefined ? K['resHeaders'] : undefined,
+ K['status']
+ >,
+ 'headers'
+ >
+ : K['resHeaders'] extends {}
+ ? PartiallyPartial<
+ BaseResponse<
+ K['resBody'] extends {} | null | undefined ? K['resBody'] : undefined,
+ K['resHeaders'],
+ K['status']
+ >,
+ 'body'
+ >
+ : PartiallyPartial<
+ BaseResponse<
+ K['resBody'] extends {} | null | undefined ? K['resBody'] : undefined,
+ K['resHeaders'] extends {} | undefined ? K['resHeaders'] : undefined,
+ K['status']
+ >,
+ 'body' | 'headers'
+ >)
+ | PartiallyPartial, 'body' | 'headers'>
+
+type ServerValues = {
+ params?: Record
+ user?: any
+}
+
+type RequestParams = {
+ path: string
+ method: HttpMethod
+ query: T['query']
+ body: T['reqBody']
+ headers: T['reqHeaders']
+}
+
+export type ServerMethods = {
+ [K in keyof T]: (
+ req: RequestParams & U
+ ) => ServerResponse | Promise>
+}
+
+const parseJSONBoby: RequestHandler = (req, res, next) => {
+ express.json()(req, res, err => {
+ if (err) return res.sendStatus(400)
+
+ next()
+ })
+}
+
+const createTypedParamsHandler = (numberTypeParams: string[]): RequestHandler => (
+ req,
+ res,
+ next
+) => {
+ const typedParams: Record = { ...req.params }
+
+ for (const key of numberTypeParams) {
+ const val = Number(typedParams[key])
+ if (isNaN(val)) {
+ res.sendStatus(400)
+ return
+ }
+
+ typedParams[key] = val
+ }
+
+ ;(req as any).typedParams = typedParams
+ next()
+}
+
+const createValidateHandler = (validators: (req: Request) => (Promise | null)[]): RequestHandler =>
+ (req, res, next) => Promise.all(validators(req)).then(() => next()).catch(() => res.sendStatus(400))
+
+const methodsToHandler = (
+ methodCallback: ServerMethods[LowerHttpMethod]
+): RequestHandler => async (req, res) => {
+ try {
+ const result = methodCallback({
+ query: req.query,
+ path: req.path,
+ method: req.method as HttpMethod,
+ body: req.body,
+ headers: req.headers,
+ params: (req as any).typedParams,
+ user: (req as any).user
+ })
+
+ const { status, body, headers } = result instanceof Promise ? await result : result
+
+ for (const key in headers) {
+ res.setHeader(key, headers[key])
+ }
+
+ res.status(status).send(body)
+ } catch (e) {
+ res.sendStatus(500)
+ }
+}
+
+export default (app: Express, options: FrourioOptions = {}) => {
+ const basePath = options.basePath ?? ''
+
+ app.get(`${basePath}/`, [
+ hooks0.onRequest,
+ ctrlHooks0.onRequest,
+ createValidateHandler(req => [
+ Object.keys(req.query).length ? validateOrReject(Object.assign(new Validators.Query(), req.query)) : null
+ ]),
+ methodsToHandler(controller0.get)
+ ])
+
+ app.post(`${basePath}/`, [
+ hooks0.onRequest,
+ ctrlHooks0.onRequest,
+ parseJSONBoby,
+ createValidateHandler(req => [
+ validateOrReject(Object.assign(new Validators.Query(), req.query)),
+ validateOrReject(Object.assign(new Validators.Body(), req.body))
+ ]),
+ methodsToHandler(controller0.post)
+ ])
+
+ app.get(`${basePath}/empty/noEmpty`, [
+ hooks0.onRequest,
+ methodsToHandler(controller1.get)
+ ])
+
+ app.get(`${basePath}/texts`, [
+ hooks0.onRequest,
+ methodsToHandler(controller2.get)
+ ])
+
+ app.put(`${basePath}/texts`, [
+ hooks0.onRequest,
+ methodsToHandler(controller2.put)
+ ])
+
+ app.put(`${basePath}/texts/sample`, [
+ hooks0.onRequest,
+ parseJSONBoby,
+ methodsToHandler(controller3.put)
+ ])
+
+ app.get(`${basePath}/users`, [
+ hooks0.onRequest,
+ hooks1.onRequest,
+ ...ctrlHooks1.preHandler,
+ methodsToHandler(controller4.get)
+ ])
+
+ app.post(`${basePath}/users`, [
+ hooks0.onRequest,
+ hooks1.onRequest,
+ parseJSONBoby,
+ createValidateHandler(req => [
+ validateOrReject(Object.assign(new Validators.UserInfo(), req.body))
+ ]),
+ ...ctrlHooks1.preHandler,
+ methodsToHandler(controller4.post)
+ ])
+
+ app.get(`${basePath}/users/:userId`, [
+ hooks0.onRequest,
+ hooks1.onRequest,
+ createTypedParamsHandler(['userId']),
+ methodsToHandler(controller5.get)
+ ])
+
+ return app
+}
diff --git a/packages/frourio/server/api/$api.ts b/packages/frourio/servers/noMulter/api/$api.ts
similarity index 68%
rename from packages/frourio/server/api/$api.ts
rename to packages/frourio/servers/noMulter/api/$api.ts
index 379c426..82ecc29 100644
--- a/packages/frourio/server/api/$api.ts
+++ b/packages/frourio/servers/noMulter/api/$api.ts
@@ -1,35 +1,39 @@
/* eslint-disable */
-import { AspidaClient, BasicHeaders } from 'aspida'
+import { AspidaClient, BasicHeaders, dataToURLString } from 'aspida'
import { Methods as Methods0 } from '.'
-import { Methods as Methods1 } from './multiForm'
+import { Methods as Methods1 } from './empty/noEmpty'
import { Methods as Methods2 } from './texts'
import { Methods as Methods3 } from './texts/sample'
import { Methods as Methods4 } from './users'
import { Methods as Methods5 } from './users/_userId@number'
-const GET = 'GET'
-const POST = 'POST'
-const PUT = 'PUT'
-const PATH0 = '/multiForm'
-const PATH1 = '/texts'
-const PATH2 = '/texts/sample'
-const PATH3 = '/users'
const api = ({ baseURL, fetch }: AspidaClient) => {
const prefix = (baseURL === undefined ? '' : baseURL).replace(/\/$/, '')
+ const PATH0 = '/empty/noEmpty'
+ const PATH1 = '/texts'
+ const PATH2 = '/texts/sample'
+ const PATH3 = '/users'
+ const GET = 'GET'
+ const POST = 'POST'
+ const PUT = 'PUT'
return {
- multiForm: {
- post: (option: { body: Methods1['post']['reqBody'], config?: T }) =>
- fetch(prefix, PATH0, POST, option, 'FormData').json(),
- $post: (option: { body: Methods1['post']['reqBody'], config?: T }) =>
- fetch(prefix, PATH0, POST, option, 'FormData').json().then(r => r.body)
+ empty: {
+ noEmpty: {
+ get: (option?: { config?: T }) =>
+ fetch(prefix, PATH0, GET, option).text(),
+ $get: (option?: { config?: T }) =>
+ fetch(prefix, PATH0, GET, option).text().then(r => r.body),
+ $path: () => `${prefix}${PATH0}`
+ }
},
texts: {
sample: {
put: (option: { body: Methods3['put']['reqBody'], config?: T }) =>
fetch(prefix, PATH2, PUT, option).json(),
$put: (option: { body: Methods3['put']['reqBody'], config?: T }) =>
- fetch(prefix, PATH2, PUT, option).json().then(r => r.body)
+ fetch(prefix, PATH2, PUT, option).json().then(r => r.body),
+ $path: () => `${prefix}${PATH2}`
},
get: (option: { query: Methods2['get']['query'], config?: T }) =>
fetch(prefix, PATH1, GET, option).text(),
@@ -38,7 +42,9 @@ const api = ({ baseURL, fetch }: AspidaClient) => {
put: (option?: { config?: T }) =>
fetch(prefix, PATH1, PUT, option).send(),
$put: (option?: { config?: T }) =>
- fetch(prefix, PATH1, PUT, option).send().then(r => r.body)
+ fetch(prefix, PATH1, PUT, option).send().then(r => r.body),
+ $path: (option?: { method?: 'get'; query: Methods2['get']['query'] }) =>
+ `${prefix}${PATH1}${option && option.query ? `?${dataToURLString(option.query)}` : ''}`
},
users: {
_userId: (val0: number) => {
@@ -48,7 +54,8 @@ const api = ({ baseURL, fetch }: AspidaClient) => {
get: (option?: { config?: T }) =>
fetch(prefix, prefix0, GET, option).json(),
$get: (option?: { config?: T }) =>
- fetch(prefix, prefix0, GET, option).json().then(r => r.body)
+ fetch(prefix, prefix0, GET, option).json().then(r => r.body),
+ $path: () => `${prefix}${prefix0}`
}
},
get: (option?: { config?: T }) =>
@@ -58,16 +65,19 @@ const api = ({ baseURL, fetch }: AspidaClient) => {
post: (option: { body: Methods4['post']['reqBody'], config?: T }) =>
fetch(prefix, PATH3, POST, option).send(),
$post: (option: { body: Methods4['post']['reqBody'], config?: T }) =>
- fetch(prefix, PATH3, POST, option).send().then(r => r.body)
+ fetch(prefix, PATH3, POST, option).send().then(r => r.body),
+ $path: () => `${prefix}${PATH3}`
},
get: (option?: { query?: Methods0['get']['query'], config?: T }) =>
fetch(prefix, '', GET, option).json(),
$get: (option?: { query?: Methods0['get']['query'], config?: T }) =>
fetch(prefix, '', GET, option).json().then(r => r.body),
post: (option: { body: Methods0['post']['reqBody'], query: Methods0['post']['query'], config?: T }) =>
- fetch(prefix, '', POST, option, 'FormData').json(),
+ fetch(prefix, '', POST, option).json(),
$post: (option: { body: Methods0['post']['reqBody'], query: Methods0['post']['query'], config?: T }) =>
- fetch(prefix, '', POST, option, 'FormData').json().then(r => r.body)
+ fetch(prefix, '', POST, option).json().then(r => r.body),
+ $path: (option?: { method?: 'get'; query: Methods0['get']['query'] } | { method: 'post'; query: Methods0['post']['query'] }) =>
+ `${prefix}${''}${option && option.query ? `?${dataToURLString(option.query)}` : ''}`
}
}
diff --git a/packages/frourio/servers/noMulter/api/$relay.ts b/packages/frourio/servers/noMulter/api/$relay.ts
new file mode 100644
index 0000000..f064967
--- /dev/null
+++ b/packages/frourio/servers/noMulter/api/$relay.ts
@@ -0,0 +1,14 @@
+/* eslint-disable */
+import { Deps } from 'velona'
+import { ServerMethods, defineHooks } from '../$app'
+import { Methods } from './'
+
+type ControllerMethods = ServerMethods
+
+export { defineHooks }
+
+export function defineController(methods: () => ControllerMethods): ControllerMethods
+export function defineController>(deps: T, cb: (deps: Deps) => ControllerMethods): ControllerMethods & { inject: (d: Deps) => ControllerMethods }
+export function defineController>(methods: () => ControllerMethods | T, cb?: (deps: Deps) => ControllerMethods) {
+ return typeof methods === 'function' ? methods() : { ...cb!(methods), inject: (d: Deps) => cb!(d) }
+}
diff --git a/packages/frourio/servers/noMulter/api/controller.ts b/packages/frourio/servers/noMulter/api/controller.ts
new file mode 100644
index 0000000..7bac341
--- /dev/null
+++ b/packages/frourio/servers/noMulter/api/controller.ts
@@ -0,0 +1,19 @@
+import { defineController, defineHooks } from './$relay'
+
+export const hooks = defineHooks(() => ({
+ onRequest: (req, res, next) => {
+ console.log('Controller level onRequest hook:', req.path)
+ next()
+ }
+}))
+
+export default defineController(() => ({
+ get: async v => {
+ return await { status: 200, body: { id: +(v.query?.id || 0) } }
+ },
+ post: v => ({
+ // @ts-expect-error
+ status: 200,
+ body: { id: +v.query.id, port: v.body.port }
+ })
+}))
diff --git a/packages/frourio/servers/noMulter/api/empty/$relay.ts b/packages/frourio/servers/noMulter/api/empty/$relay.ts
new file mode 100644
index 0000000..8b7c89b
--- /dev/null
+++ b/packages/frourio/servers/noMulter/api/empty/$relay.ts
@@ -0,0 +1,14 @@
+/* eslint-disable */
+import { Deps } from 'velona'
+import { ServerMethods, defineHooks } from '../../$app'
+import { Methods } from './'
+
+type ControllerMethods = ServerMethods
+
+export { defineHooks }
+
+export function defineController(methods: () => ControllerMethods): ControllerMethods
+export function defineController>(deps: T, cb: (deps: Deps) => ControllerMethods): ControllerMethods & { inject: (d: Deps) => ControllerMethods }
+export function defineController>(methods: () => ControllerMethods | T, cb?: (deps: Deps) => ControllerMethods) {
+ return typeof methods === 'function' ? methods() : { ...cb!(methods), inject: (d: Deps) => cb!(d) }
+}
diff --git a/packages/frourio/servers/noMulter/api/empty/controller.ts b/packages/frourio/servers/noMulter/api/empty/controller.ts
new file mode 100644
index 0000000..edbfd03
--- /dev/null
+++ b/packages/frourio/servers/noMulter/api/empty/controller.ts
@@ -0,0 +1,3 @@
+import { defineController } from './$relay'
+
+export default defineController(() => ({}))
diff --git a/packages/frourio/servers/noMulter/api/empty/index.ts b/packages/frourio/servers/noMulter/api/empty/index.ts
new file mode 100644
index 0000000..4dcad57
--- /dev/null
+++ b/packages/frourio/servers/noMulter/api/empty/index.ts
@@ -0,0 +1 @@
+export type Methods = {}
diff --git a/packages/frourio/servers/noMulter/api/empty/noEmpty/$relay.ts b/packages/frourio/servers/noMulter/api/empty/noEmpty/$relay.ts
new file mode 100644
index 0000000..26ca223
--- /dev/null
+++ b/packages/frourio/servers/noMulter/api/empty/noEmpty/$relay.ts
@@ -0,0 +1,14 @@
+/* eslint-disable */
+import { Deps } from 'velona'
+import { ServerMethods, defineHooks } from '../../../$app'
+import { Methods } from './'
+
+type ControllerMethods = ServerMethods
+
+export { defineHooks }
+
+export function defineController(methods: () => ControllerMethods): ControllerMethods
+export function defineController>(deps: T, cb: (deps: Deps) => ControllerMethods): ControllerMethods & { inject: (d: Deps) => ControllerMethods }
+export function defineController>(methods: () => ControllerMethods | T, cb?: (deps: Deps) => ControllerMethods) {
+ return typeof methods === 'function' ? methods() : { ...cb!(methods), inject: (d: Deps) => cb!(d) }
+}
diff --git a/packages/frourio/servers/noMulter/api/empty/noEmpty/controller.ts b/packages/frourio/servers/noMulter/api/empty/noEmpty/controller.ts
new file mode 100644
index 0000000..eb73909
--- /dev/null
+++ b/packages/frourio/servers/noMulter/api/empty/noEmpty/controller.ts
@@ -0,0 +1,5 @@
+import { defineController } from './$relay'
+
+export default defineController(() => ({
+ get: () => ({ status: 200, body: 'sample' })
+}))
diff --git a/packages/frourio/servers/noMulter/api/empty/noEmpty/index.ts b/packages/frourio/servers/noMulter/api/empty/noEmpty/index.ts
new file mode 100644
index 0000000..0556ee1
--- /dev/null
+++ b/packages/frourio/servers/noMulter/api/empty/noEmpty/index.ts
@@ -0,0 +1,5 @@
+export type Methods = {
+ get: {
+ resBody: string
+ }
+}
diff --git a/packages/frourio/servers/noMulter/api/hooks.ts b/packages/frourio/servers/noMulter/api/hooks.ts
new file mode 100644
index 0000000..c0c0ad6
--- /dev/null
+++ b/packages/frourio/servers/noMulter/api/hooks.ts
@@ -0,0 +1,8 @@
+import { defineHooks } from './$relay'
+
+export default defineHooks(() => ({
+ onRequest: (req, res, next) => {
+ console.log('Directory level middleware:', req.path)
+ next()
+ }
+}))
diff --git a/packages/frourio/servers/noMulter/api/index.ts b/packages/frourio/servers/noMulter/api/index.ts
new file mode 100644
index 0000000..7909803
--- /dev/null
+++ b/packages/frourio/servers/noMulter/api/index.ts
@@ -0,0 +1,19 @@
+import { Query, Body } from '../validators'
+
+export type Methods = {
+ get: {
+ query?: Query
+ status: 200
+ resBody?: { id: number }
+ }
+
+ post: {
+ query: Query
+ reqBody: Body
+ status: 201
+ resBody: {
+ id: number
+ port: string
+ }
+ }
+}
diff --git a/packages/frourio/servers/noMulter/api/texts/$relay.ts b/packages/frourio/servers/noMulter/api/texts/$relay.ts
new file mode 100644
index 0000000..8b7c89b
--- /dev/null
+++ b/packages/frourio/servers/noMulter/api/texts/$relay.ts
@@ -0,0 +1,14 @@
+/* eslint-disable */
+import { Deps } from 'velona'
+import { ServerMethods, defineHooks } from '../../$app'
+import { Methods } from './'
+
+type ControllerMethods = ServerMethods
+
+export { defineHooks }
+
+export function defineController(methods: () => ControllerMethods): ControllerMethods
+export function defineController>(deps: T, cb: (deps: Deps) => ControllerMethods): ControllerMethods & { inject: (d: Deps) => ControllerMethods }
+export function defineController>(methods: () => ControllerMethods | T, cb?: (deps: Deps) => ControllerMethods) {
+ return typeof methods === 'function' ? methods() : { ...cb!(methods), inject: (d: Deps) => cb!(d) }
+}
diff --git a/packages/frourio/servers/noMulter/api/texts/controller.ts b/packages/frourio/servers/noMulter/api/texts/controller.ts
new file mode 100644
index 0000000..edca134
--- /dev/null
+++ b/packages/frourio/servers/noMulter/api/texts/controller.ts
@@ -0,0 +1,6 @@
+import { defineController } from './$relay'
+
+// @ts-expect-error
+export default defineController(() => ({
+ get: ({ query }) => ({ status: 200, body: query.val })
+}))
diff --git a/packages/frourio/servers/noMulter/api/texts/index.ts b/packages/frourio/servers/noMulter/api/texts/index.ts
new file mode 100644
index 0000000..5ff9a66
--- /dev/null
+++ b/packages/frourio/servers/noMulter/api/texts/index.ts
@@ -0,0 +1,10 @@
+export type Methods = {
+ get: {
+ query: {
+ val: string
+ }
+ resBody: string
+ }
+
+ put: {}
+}
diff --git a/packages/frourio/servers/noMulter/api/texts/sample/$relay.ts b/packages/frourio/servers/noMulter/api/texts/sample/$relay.ts
new file mode 100644
index 0000000..26ca223
--- /dev/null
+++ b/packages/frourio/servers/noMulter/api/texts/sample/$relay.ts
@@ -0,0 +1,14 @@
+/* eslint-disable */
+import { Deps } from 'velona'
+import { ServerMethods, defineHooks } from '../../../$app'
+import { Methods } from './'
+
+type ControllerMethods = ServerMethods
+
+export { defineHooks }
+
+export function defineController(methods: () => ControllerMethods): ControllerMethods
+export function defineController>(deps: T, cb: (deps: Deps) => ControllerMethods): ControllerMethods & { inject: (d: Deps) => ControllerMethods }
+export function defineController