├── .eslintrc.js ├── .gitignore ├── .prettierignore ├── .prettierrc.json ├── .vscode ├── extensions.json └── settings.json ├── LICENSE ├── README.md ├── all.md ├── examples ├── bridge-mongodb-boilerplate │ ├── .env.example │ ├── .eslintrc.js │ ├── .gitignore │ ├── .prettierignore │ ├── .prettierrc.json │ ├── JSONType.json │ ├── bridge.config.json │ ├── nodemon.json │ ├── openapi.json │ ├── package.json │ ├── pnpm-lock.yaml │ ├── sdk │ │ ├── bridgeFetchMethod.ts │ │ ├── hey.ts │ │ └── index.ts │ ├── src │ │ ├── config │ │ │ ├── api.ts │ │ │ └── index.ts │ │ ├── errorHandler.ts │ │ ├── index.ts │ │ ├── models │ │ │ ├── index.ts │ │ │ └── user.ts │ │ └── routes.ts │ └── tsconfig.json ├── comparizon │ ├── .gitignore │ ├── express.ts │ ├── package.json │ └── tsconfig.json ├── minimal-express │ ├── .gitignore │ ├── .prettierignore │ ├── .prettierrc.json │ ├── JSONType.json │ ├── bridge.config.json │ ├── index.ts │ ├── openapi.json │ ├── package.json │ ├── sdk │ │ ├── bridgeFetchMethod.ts │ │ ├── hello.ts │ │ └── index.ts │ └── tsconfig.json └── minimal-http │ ├── .gitignore │ ├── JSONType.json │ ├── bridge.config.json │ ├── index.ts │ ├── openapi.json │ ├── package.json │ ├── pnpm-lock.yaml │ ├── sdk │ ├── bridgeFetchMethod.ts │ ├── hello.ts │ └── index.ts │ └── tsconfig.json ├── nodemon.json ├── package-lock.json ├── packages ├── bridge-compile │ ├── .gitignore │ ├── .prettierignore │ ├── .prettierrc.json │ ├── README.md │ ├── bin │ │ ├── index.ts │ │ ├── openapi.ts │ │ ├── parser │ │ │ ├── extract-zip.ts │ │ │ ├── find-source-file.ts │ │ │ ├── remove-added-code.ts │ │ │ ├── transform-json-type.ts │ │ │ └── ts-parser │ │ │ │ ├── create-json-type.ts │ │ │ │ ├── index.ts │ │ │ │ ├── recursive-checker.ts │ │ │ │ └── utils.ts │ │ ├── sdk-compilers │ │ │ ├── index.ts │ │ │ ├── typescript │ │ │ │ ├── compilers │ │ │ │ │ ├── fetch-file-compiler.ts │ │ │ │ │ ├── handler-file-compiler.ts │ │ │ │ │ └── index-import-file-compiler.ts │ │ │ │ ├── index.ts │ │ │ │ ├── json-type-to-ts-interface.ts │ │ │ │ └── write-handlers-file.ts │ │ │ └── utils.ts │ │ ├── types.ts │ │ └── utils │ │ │ ├── fsAsync.ts │ │ │ ├── index.ts │ │ │ ├── methods.ts │ │ │ ├── run-command.ts │ │ │ ├── sleep.ts │ │ │ └── statusCode.ts │ ├── package.json │ └── tsconfig.json ├── bridge-react-query │ ├── .gitignore │ ├── README.md │ ├── package-lock.json │ ├── package.json │ ├── source │ │ └── index.ts │ └── tsconfig.json ├── bridge-studio │ ├── .gitignore │ ├── README.md │ ├── bin │ │ └── index.ts │ ├── package.json │ ├── pnpm-lock.yaml │ ├── sdk │ │ ├── bridgeFetchMethod.ts │ │ ├── confirmation │ │ │ └── getCodeByEmail.ts │ │ ├── deploy │ │ │ ├── getLast.ts │ │ │ └── typescript-sdk │ │ │ │ └── get.ts │ │ ├── github │ │ │ ├── getMyConnectedAccounts.ts │ │ │ ├── getReposWithTS.ts │ │ │ └── subscribeRepoToBridge.ts │ │ ├── hello.ts │ │ ├── index.ts │ │ ├── project │ │ │ ├── compileWithCommandLine.ts │ │ │ ├── createProjectFromCLI.ts │ │ │ ├── get.ts │ │ │ ├── getFromCLI.ts │ │ │ ├── getMine.ts │ │ │ ├── reCompile.ts │ │ │ └── update.ts │ │ └── user │ │ │ ├── deleteMyAccount.ts │ │ │ ├── getMe.ts │ │ │ ├── isEmailInUse.ts │ │ │ ├── logout.ts │ │ │ ├── refreshToken.ts │ │ │ ├── signinWithPassword.ts │ │ │ ├── updateForgottenPassword.ts │ │ │ └── updateMyPassword.ts │ └── tsconfig.json ├── bridge │ ├── .gitignore │ ├── README.md │ ├── package-lock.json │ ├── package.json │ ├── source │ │ ├── bridge.ts │ │ ├── core │ │ │ ├── handler.ts │ │ │ ├── handlers │ │ │ │ ├── bridge.ts │ │ │ │ ├── data-validator.ts │ │ │ │ ├── file-validator.ts │ │ │ │ ├── index.ts │ │ │ │ ├── middleware.ts │ │ │ │ └── resolver.ts │ │ │ ├── index.ts │ │ │ └── types.ts │ │ ├── error │ │ │ ├── index.ts │ │ │ ├── listener.ts │ │ │ └── status.ts │ │ ├── index.ts │ │ ├── routes │ │ │ ├── convert.ts │ │ │ ├── index.ts │ │ │ ├── method.ts │ │ │ └── types.ts │ │ ├── server │ │ │ ├── adapters │ │ │ │ ├── express.ts │ │ │ │ ├── index.ts │ │ │ │ ├── node-http.ts │ │ │ │ └── serverless.ts │ │ │ ├── http-transormers │ │ │ │ ├── body-json.ts │ │ │ │ ├── formidable-async.ts │ │ │ │ ├── index.ts │ │ │ │ └── query-json.ts │ │ │ └── index.ts │ │ └── utilities │ │ │ ├── apply.ts │ │ │ ├── dirExists.ts │ │ │ ├── formidable.ts │ │ │ ├── index.ts │ │ │ └── types.ts │ ├── test.ts │ └── tsconfig.json ├── create-bridge-app │ ├── .gitignore │ ├── README.md │ ├── bin │ │ ├── index.ts │ │ └── renameSync.ts │ ├── package.json │ └── tsconfig.json └── fetch-bridge-sdk │ ├── .gitignore │ ├── README.md │ ├── bin │ └── index.ts │ ├── package.json │ ├── pnpm-lock.yaml │ └── tsconfig.json └── www ├── .gitignore ├── README.md ├── babel.config.js ├── blog ├── authors.yml ├── how-to-create-and-deploy-a-bridge-app-to-vercel.mdx ├── trpc.mdx └── use-bridge-with-express.mdx ├── docs ├── bridge-client │ ├── dart.md │ ├── dart │ │ └── coming-soon.md │ ├── kotlin.md │ ├── kotlin │ │ └── coming-soon.md │ ├── python.md │ ├── python │ │ └── coming-soon.md │ ├── swift.md │ ├── swift │ │ └── coming-soon.md │ └── typescript │ │ ├── fetch.md │ │ ├── how-to-use.md │ │ └── react-query.md ├── bridge-studio.md ├── bridge │ ├── adapters │ │ ├── express.md │ │ ├── fastify.md │ │ └── http.md │ ├── automatic_documentation.md │ ├── client_code.md │ ├── data_validation.md │ ├── errors.md │ ├── examples │ │ ├── advanced.md │ │ ├── basic.md │ │ └── medium.md │ ├── files.md │ ├── handler.md │ ├── launch_server │ │ ├── express copy 3.md │ │ ├── express.md │ │ ├── fastify.md │ │ └── node-http.md │ ├── middlewares.md │ ├── routes.md │ ├── server.md │ ├── server_calls.md │ └── socket.md ├── coming-soon.md ├── compilation.md ├── documentation.md ├── examples │ └── example.md ├── introduction.md ├── quickstart.md └── typescript-sdk.md ├── docusaurus.config.js ├── docusaurus.preferredTheme.js ├── global.d.ts ├── min-light-with-diff.json ├── package-lock.json ├── package.json ├── sidebars.js ├── src ├── analytics │ └── mixpanel.ts ├── components │ ├── Code.tsx │ ├── HomepageFeatures │ │ ├── index.tsx │ │ └── styles.module.css │ ├── Layout.tsx │ ├── NewCode.tsx │ └── Newsletter.tsx ├── css │ └── custom.css ├── pages │ ├── code.tsx │ ├── index.module.css │ ├── index.tsx │ ├── markdown-page.md │ ├── ok.tsx │ ├── studio.tsx │ ├── test.tsx │ └── test2.tsx └── theme │ └── Layout │ └── index.js ├── static ├── favicon.png ├── img │ ├── bg-lines.png │ ├── bg.png │ ├── dashboard.png │ ├── doc.png │ ├── favicon.ico │ ├── globe-bottom.svg │ ├── globe.png │ ├── globe.svg │ ├── header.svg │ ├── logo_b.svg │ ├── logo_b_round.svg │ ├── logo_w.svg │ └── logo_w_round.svg ├── studio │ ├── all-languages.svg │ ├── auto-completion.svg │ ├── bug-icon.svg │ ├── customcard.svg │ ├── doc.svg │ ├── documentation-logo.svg │ ├── dots.svg │ ├── error-handling.svg │ ├── fetch-code.svg │ ├── fetch-danger.svg │ ├── fetch.svg │ ├── github-integration.svg │ ├── input-autocompletion.svg │ ├── lights.svg │ ├── main.svg │ ├── postman-img.svg │ ├── receive.svg │ ├── sdk-icon.svg │ ├── send.svg │ ├── speed.svg │ ├── studio-header.png │ ├── studio-header.svg │ ├── team.svg │ └── tests │ │ ├── bg-main.png │ │ ├── bg-main.svg │ │ └── bg-main2.svg └── twitter.png ├── tailwind.config.js ├── test.ts └── tsconfig.json /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | parser: '@typescript-eslint/parser', 3 | parserOptions: { 4 | project: 'tsconfig.json', 5 | sourceType: 'module', 6 | }, 7 | plugins: ['@typescript-eslint/eslint-plugin'], 8 | extends: ['plugin:@typescript-eslint/recommended', 'plugin:prettier/recommended'], 9 | root: true, 10 | env: { 11 | node: true, 12 | jest: true, 13 | }, 14 | ignorePatterns: ['.eslintrc.js', 'generated.ts', 'fetchSDK.js'], 15 | rules: { 16 | '@typescript-eslint/interface-name-prefix': 'off', 17 | '@typescript-eslint/explicit-function-return-type': 'off', 18 | '@typescript-eslint/explicit-module-boundary-types': 'off', 19 | '@typescript-eslint/no-explicit-any': 'off', 20 | }, 21 | }; 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /dist 2 | /node_modules 3 | 4 | # Logs 5 | logs 6 | *.log 7 | npm-debug.log* 8 | pnpm-debug.log* 9 | yarn-debug.log* 10 | yarn-error.log* 11 | lerna-debug.log* 12 | 13 | # OS 14 | .DS_Store 15 | 16 | # Tests 17 | /coverage 18 | /.nyc_output 19 | 20 | # IDEs and editors 21 | /.idea 22 | .project 23 | .classpath 24 | .c9/ 25 | *.launch 26 | .settings/ 27 | *.sublime-workspace 28 | 29 | # IDE - VSCode 30 | .vscode/* 31 | !.vscode/settings.json 32 | !.vscode/tasks.json 33 | !.vscode/launch.json 34 | !.vscode/extensions.json 35 | .vercel 36 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | coverage 3 | dist 4 | build 5 | public -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "printWidth": 100, 4 | "trailingComma": "all", 5 | "endOfLine": "auto" 6 | } 7 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "yzhang.markdown-all-in-one", 4 | "esbenp.prettier-vscode", 5 | "dbaeumer.vscode-eslint", 6 | "bungcip.better-toml" 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "typescript.tsdk": "node_modules/typescript/lib", 3 | "editor.formatOnSave": true, 4 | // Auto-fix issues with ESLint when you save code changes 5 | "editor.codeActionsOnSave": { 6 | "source.fixAll.eslint": "explicit" 7 | }, 8 | "workbench.colorCustomizations": { 9 | "titleBar.activeBackground": "#291b25", 10 | "titleBar.inactiveBackground": "#2a242a" 11 | } 12 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Bridge 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /all.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bridge-codes/bridge/094896601a59d956929eb571e8fe6f5835e3970a/all.md -------------------------------------------------------------------------------- /examples/bridge-mongodb-boilerplate/.env.example: -------------------------------------------------------------------------------- 1 | # ENV EXAMPLE 2 | 3 | ENV=example 4 | 5 | # Project name 6 | PROJECT_NAME=Bridge MongoDB Boilerplate 7 | 8 | # Port number 9 | PORT=8080 10 | 11 | # Mongo DB 12 | MONGODB_URL=mongodb+srv://user:example@user.mongodb.net/example 13 | 14 | # ACCESS TOKEN 15 | JWT_EXPIRATION_TIME=8m 16 | ACCESS_TOKEN_KEY=lygfdYGYnkdgfkuygNOUBYFGiytufd 17 | REFRESH_TOKEN_KEY=KUYgbdjhfbkdsypmioq678sd -------------------------------------------------------------------------------- /examples/bridge-mongodb-boilerplate/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | parser: '@typescript-eslint/parser', 3 | parserOptions: { 4 | project: 'tsconfig.json', 5 | sourceType: 'module', 6 | }, 7 | plugins: ['@typescript-eslint/eslint-plugin'], 8 | extends: ['plugin:@typescript-eslint/recommended', 'plugin:prettier/recommended'], 9 | root: true, 10 | env: { 11 | node: true, 12 | jest: true, 13 | }, 14 | ignorePatterns: ['.eslintrc.js', 'generated.ts', 'fetchSDK.js'], 15 | rules: { 16 | '@typescript-eslint/interface-name-prefix': 'off', 17 | '@typescript-eslint/explicit-function-return-type': 'off', 18 | '@typescript-eslint/explicit-module-boundary-types': 'off', 19 | '@typescript-eslint/no-explicit-any': 'off', 20 | }, 21 | }; 22 | -------------------------------------------------------------------------------- /examples/bridge-mongodb-boilerplate/.gitignore: -------------------------------------------------------------------------------- 1 | /dist 2 | /node_modules 3 | 4 | # Logs 5 | logs 6 | *.log 7 | npm-debug.log* 8 | pnpm-debug.log* 9 | yarn-debug.log* 10 | yarn-error.log* 11 | lerna-debug.log* 12 | 13 | # OS 14 | .DS_Store 15 | 16 | # Tests 17 | /coverage 18 | /.nyc_output 19 | 20 | # IDEs and editors 21 | /.idea 22 | .project 23 | .classpath 24 | .c9/ 25 | *.launch 26 | .settings/ 27 | *.sublime-workspace 28 | 29 | # IDE - VSCode 30 | .vscode/* 31 | !.vscode/settings.json 32 | !.vscode/tasks.json 33 | !.vscode/launch.json 34 | !.vscode/extensions.json 35 | .vercel 36 | -------------------------------------------------------------------------------- /examples/bridge-mongodb-boilerplate/.prettierignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | coverage 3 | dist 4 | build 5 | public -------------------------------------------------------------------------------- /examples/bridge-mongodb-boilerplate/.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "printWidth": 125, 4 | "trailingComma": "es5", 5 | "endOfLine": "auto" 6 | } 7 | -------------------------------------------------------------------------------- /examples/bridge-mongodb-boilerplate/JSONType.json: -------------------------------------------------------------------------------- 1 | { 2 | "object": { 3 | "hey": { 4 | "object": { 5 | "returnBridgeType": { 6 | "data": { "optional": false, "primitive": "string" }, 7 | "error": { "union": [] } 8 | } 9 | } 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /examples/bridge-mongodb-boilerplate/bridge.config.json: -------------------------------------------------------------------------------- 1 | { "serverUrl": "http://localhost:8080" } 2 | -------------------------------------------------------------------------------- /examples/bridge-mongodb-boilerplate/nodemon.json: -------------------------------------------------------------------------------- 1 | { 2 | "restartable": "rs", 3 | "ignore": [".git", "dist/", "coverage/"], 4 | "watch": ["src"], 5 | "execMap": { 6 | "ts": "node -r ts-node/register" 7 | }, 8 | "env": { 9 | "NODE_ENV": "test" 10 | }, 11 | "ext": "js,json,ts" 12 | } 13 | -------------------------------------------------------------------------------- /examples/bridge-mongodb-boilerplate/openapi.json: -------------------------------------------------------------------------------- 1 | { 2 | "openapi": "3.0.0", 3 | "info": { "title": "bridge-mongodb-boilerplate", "version": "Bridge: 1.0.0" }, 4 | "paths": { 5 | "/hey": { 6 | "post": { 7 | "parameters": [], 8 | "responses": { 9 | "200": { 10 | "description": "Success", 11 | "content": { 12 | "application/json": { "schema": { "type": "string" } } 13 | } 14 | }, 15 | "400": { 16 | "description": "BAD_REQUEST", 17 | "content": { 18 | "application/json": { 19 | "schema": { 20 | "type": "object", 21 | "required": ["name", "status", "data"], 22 | "properties": { 23 | "name": { "enum": ["Axios Error"], "type": "string" }, 24 | "status": { "enum": [400], "type": "number" }, 25 | "data": {} 26 | } 27 | } 28 | } 29 | } 30 | }, 31 | "500": { 32 | "description": "INTERNAL_SERVER_ERROR", 33 | "content": { 34 | "application/json": { 35 | "schema": { 36 | "type": "object", 37 | "required": ["name", "status"], 38 | "properties": { 39 | "name": { 40 | "enum": ["Internal Server Error"], 41 | "type": "string" 42 | }, 43 | "status": { "enum": [500], "type": "number" } 44 | } 45 | } 46 | } 47 | } 48 | } 49 | } 50 | } 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /examples/bridge-mongodb-boilerplate/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bridge-mongodb-boilerplate", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "build": "tsc", 8 | "start": "node ./dist/src/index.js", 9 | "dev": "clear && nodemon --config nodemon.json ./src/index.ts" 10 | }, 11 | "keywords": [], 12 | "author": "Bridge Team ", 13 | "license": "MIT", 14 | "dependencies": { 15 | "bridge": "^2.0.46", 16 | "bridge-mongo": "^1.0.13", 17 | "cors": "^2.8.5", 18 | "dotenv": "^16.0.3", 19 | "express": "^4.18.2", 20 | "helmet": "^6.0.1", 21 | "morgan": "^1.10.0" 22 | }, 23 | "devDependencies": { 24 | "@types/cors": "^2.8.13", 25 | "@types/express": "^4.17.17", 26 | "@types/morgan": "^1.9.4", 27 | "@types/node": "^18.14.0", 28 | "nodemon": "^2.0.20", 29 | "ts-node": "^10.9.1", 30 | "typescript": "^4.9.5" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /examples/bridge-mongodb-boilerplate/sdk/bridgeFetchMethod.ts: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | import FormData from 'form-data'; 3 | 4 | export const serverUrl = 'http://localhost:8080'; 5 | 6 | interface FETCH { 7 | method: 'GET' | 'POST' | 'PATCH' | 'DELETE' | 'PUT'; 8 | path: string; 9 | body?: Record; 10 | query?: Record; 11 | headers?: Record; 12 | file?: Record; 13 | } 14 | 15 | const getQueryUrl = (query: FETCH['query']): string => { 16 | if (!query || !Object.values(query).some((val) => val)) return ''; 17 | 18 | return Object.entries(query) 19 | .filter(([_, value]) => value !== undefined) 20 | .map(([key, value]) => `${key}=${value}`) 21 | .join('&'); 22 | }; 23 | 24 | export default async ({ path, method, body, query, headers, file }: FETCH) => { 25 | let completeUrl = serverUrl + path; 26 | if (query && Object.keys(query).length > 0) 27 | completeUrl += '?' + getQueryUrl(query); 28 | 29 | const config: any = { url: completeUrl, method }; 30 | 31 | if (headers) config.headers = headers; 32 | if (body) config.data = body; 33 | else if (file) { 34 | const formData = new FormData(); 35 | Object.entries(file).forEach(([name, f]) => formData.append(name, f)); 36 | config.data = formData; 37 | config.headers = { 38 | ...config.headers, 39 | 'Content-Type': 'multipart/form-data', 40 | }; 41 | } 42 | 43 | return axios(config) 44 | .then((res) => res.data) 45 | .catch((err) => { 46 | if ( 47 | err.response && 48 | err.response.data && 49 | 'error' in err.response.data && 50 | 'status' in err.response.data.error 51 | ) 52 | return err.response.data; 53 | 54 | return { 55 | error: { 56 | name: 'Axios Error', 57 | status: 400, 58 | data: err, 59 | }, 60 | }; 61 | }); 62 | }; 63 | -------------------------------------------------------------------------------- /examples/bridge-mongodb-boilerplate/sdk/hey.ts: -------------------------------------------------------------------------------- 1 | import Fetch from './bridgeFetchMethod'; 2 | 3 | export default async (): Promise< 4 | | { data: string; error: undefined } 5 | | { 6 | data: undefined; 7 | error: 8 | | { name: 'Axios Error'; status: 400; data: any } 9 | | { name: 'Internal Server Error'; status: 500 }; 10 | } 11 | > => { 12 | const res = await Fetch({ method: 'POST', path: '/hey' }); 13 | 14 | if (res.error && typeof res.error.status === 'number') 15 | return { data: undefined, error: res.error }; 16 | else return { data: res, error: undefined }; 17 | }; 18 | -------------------------------------------------------------------------------- /examples/bridge-mongodb-boilerplate/sdk/index.ts: -------------------------------------------------------------------------------- 1 | import hey from './/hey'; 2 | 3 | export const API = { hey: hey }; 4 | -------------------------------------------------------------------------------- /examples/bridge-mongodb-boilerplate/src/config/api.ts: -------------------------------------------------------------------------------- 1 | import dotenv from 'dotenv'; 2 | import path from 'path'; 3 | 4 | if (!process.env.MONGODB_URI) dotenv.config({ path: path.join('.env.example') }); 5 | 6 | export const api = { 7 | env: process.env.ENV, 8 | projectName: process.env.PROJECT_NAME, 9 | port: parseInt(process.env.PORT || '') || 8080, 10 | jwt: { 11 | expirationTime: process.env.JWT_EXPIRATION_TIME || '8m', 12 | accessTokenKey: process.env.ACCESS_TOKEN_KEY || '', 13 | refreshTokenKey: process.env.REFRESH_TOKEN_KEY || '', 14 | }, 15 | mongodb: { 16 | url: process.env.MONGODB_URI, 17 | }, 18 | }; 19 | -------------------------------------------------------------------------------- /examples/bridge-mongodb-boilerplate/src/config/index.ts: -------------------------------------------------------------------------------- 1 | export * from './api'; 2 | -------------------------------------------------------------------------------- /examples/bridge-mongodb-boilerplate/src/errorHandler.ts: -------------------------------------------------------------------------------- 1 | import { onError } from 'bridge'; 2 | 3 | export default onError(({ error, path }) => { 4 | console.log(path, error); 5 | if (error.name === 'Internal server error') { 6 | console.error(path, error); 7 | // Send to bug reporting 8 | } else { 9 | // Normal http errors 10 | } 11 | }); 12 | -------------------------------------------------------------------------------- /examples/bridge-mongodb-boilerplate/src/index.ts: -------------------------------------------------------------------------------- 1 | import { initBridge } from 'bridge'; 2 | import express from 'express'; 3 | import morgan from 'morgan'; 4 | import helmet from 'helmet'; 5 | import cors from 'cors'; 6 | import { api } from './config'; 7 | import routes from './routes'; 8 | import errorHandler from './errorHandler'; 9 | 10 | const app = express(); 11 | 12 | app.enable('trust proxy'); 13 | app.use(express.static('public')); 14 | app.use(cors()); 15 | app.use(morgan('dev')); 16 | app.use(helmet()); 17 | 18 | app.get('/', (req, res) => res.send(`Welcome on ${api.projectName}, mode: ${api.env}`)); 19 | 20 | const bridge = initBridge({ routes, errorHandler }); 21 | 22 | app.use('', bridge.expressMiddleware()); 23 | 24 | app.listen(api.port, () => { 25 | console.log(`Listening on port ${api.port}`); 26 | }); 27 | -------------------------------------------------------------------------------- /examples/bridge-mongodb-boilerplate/src/models/index.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bridge-codes/bridge/094896601a59d956929eb571e8fe6f5835e3970a/examples/bridge-mongodb-boilerplate/src/models/index.ts -------------------------------------------------------------------------------- /examples/bridge-mongodb-boilerplate/src/models/user.ts: -------------------------------------------------------------------------------- 1 | import { Schema } from 'bridge-mongo'; 2 | 3 | const userSchema = new Schema( 4 | { 5 | username: { type: String, required: true, unique: true }, 6 | age: Number, 7 | password: String, 8 | }, 9 | { timestamps: true } 10 | ); 11 | -------------------------------------------------------------------------------- /examples/bridge-mongodb-boilerplate/src/routes.ts: -------------------------------------------------------------------------------- 1 | import { handler } from 'bridge'; 2 | 3 | export default { 4 | hey: handler({ resolve: () => 'hey' }), 5 | }; 6 | -------------------------------------------------------------------------------- /examples/comparizon/.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /dist 3 | package-lock.json 4 | pnpm-lock.yaml -------------------------------------------------------------------------------- /examples/comparizon/express.ts: -------------------------------------------------------------------------------- 1 | import express from 'express'; 2 | 3 | const port = 8080; 4 | 5 | const app = express(); 6 | 7 | app.post('/user', (req, res) => { 8 | const user = req.body; 9 | 10 | if (!user) { 11 | return res.status(400).json({ error: 'No user provided' }); 12 | } 13 | 14 | return res.status(201).json(user); 15 | }); 16 | 17 | app.listen(port, () => { 18 | console.log(`Listening on port ${port}`); 19 | }); 20 | -------------------------------------------------------------------------------- /examples/comparizon/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "comparizon", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "node ./dist/index.js", 8 | "build": "tsc", 9 | "dev": "ts-node ./index.ts" 10 | }, 11 | "author": "", 12 | "license": "ISC", 13 | "dependencies": { 14 | "bridge": "^2.0.66", 15 | "express": "^4.18.2" 16 | }, 17 | "devDependencies": { 18 | "@types/express": "^4.17.17", 19 | "@types/node": "^20.3.1", 20 | "ts-node": "^10.9.1", 21 | "typescript": "^5.1.3" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /examples/minimal-express/.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /dist 3 | package-lock.json 4 | pnpm-lock.yaml -------------------------------------------------------------------------------- /examples/minimal-express/.prettierignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | coverage 3 | dist 4 | build 5 | public -------------------------------------------------------------------------------- /examples/minimal-express/.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "printWidth": 100, 4 | "trailingComma": "all", 5 | "endOfLine": "auto" 6 | } 7 | -------------------------------------------------------------------------------- /examples/minimal-express/JSONType.json: -------------------------------------------------------------------------------- 1 | { 2 | "object": { 3 | "hello": { 4 | "object": { 5 | "returnBridgeType": { 6 | "data": { 7 | "optional": false, 8 | "primitive": "string", 9 | "value": "hello" 10 | }, 11 | "error": { "union": [] } 12 | } 13 | } 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /examples/minimal-express/bridge.config.json: -------------------------------------------------------------------------------- 1 | { "serverUrl": "http://localhost:8080" } 2 | -------------------------------------------------------------------------------- /examples/minimal-express/index.ts: -------------------------------------------------------------------------------- 1 | import { handler, initBridge } from 'bridge'; 2 | import express from 'express'; 3 | 4 | const port = 8080; 5 | const routes = { hello: handler({ resolve: () => 'hello' as const }) }; 6 | 7 | const app = express(); 8 | 9 | app.use('', initBridge({ routes }).expressMiddleware()); 10 | 11 | app.listen(port, () => { 12 | console.log(`Listening on port ${port}`); 13 | }); 14 | -------------------------------------------------------------------------------- /examples/minimal-express/openapi.json: -------------------------------------------------------------------------------- 1 | { 2 | "openapi": "3.0.0", 3 | "info": { "title": "minimal-express", "version": "Bridge: 1.0.0" }, 4 | "paths": { 5 | "/hello": { 6 | "post": { 7 | "parameters": [], 8 | "responses": { 9 | "200": { 10 | "description": "Success", 11 | "content": { 12 | "application/json": { 13 | "schema": { "enum": ["hello"], "type": "string" } 14 | } 15 | } 16 | }, 17 | "400": { 18 | "description": "BAD_REQUEST", 19 | "content": { 20 | "application/json": { 21 | "schema": { 22 | "type": "object", 23 | "required": ["name", "status", "data"], 24 | "properties": { 25 | "name": { "enum": ["Axios Error"], "type": "string" }, 26 | "status": { "enum": [400], "type": "number" }, 27 | "data": {} 28 | } 29 | } 30 | } 31 | } 32 | }, 33 | "500": { 34 | "description": "INTERNAL_SERVER_ERROR", 35 | "content": { 36 | "application/json": { 37 | "schema": { 38 | "type": "object", 39 | "required": ["name", "status"], 40 | "properties": { 41 | "name": { 42 | "enum": ["Internal Server Error"], 43 | "type": "string" 44 | }, 45 | "status": { "enum": [500], "type": "number" } 46 | } 47 | } 48 | } 49 | } 50 | } 51 | } 52 | } 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /examples/minimal-express/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "minimal-express", 3 | "version": "1.0.0", 4 | "scripts": { 5 | "start": "node ./dist/index.js", 6 | "build": "tsc", 7 | "dev": "ts-node ./index.ts" 8 | }, 9 | "dependencies": { 10 | "axios": "^1.4.0", 11 | "bridge": "^2.0.45", 12 | "express": "^4.18.2", 13 | "form-data": "^4.0.0" 14 | }, 15 | "devDependencies": { 16 | "@types/express": "^4.17.15", 17 | "@types/node": "^18.11.16", 18 | "ts-node": "^10.9.1", 19 | "typescript": "^4.9.4" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /examples/minimal-express/sdk/bridgeFetchMethod.ts: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | import FormData from 'form-data'; 3 | 4 | export const serverUrl = 'http://localhost:8080'; 5 | 6 | interface FETCH { 7 | method: 'GET' | 'POST' | 'PATCH' | 'DELETE' | 'PUT'; 8 | path: string; 9 | body?: Record; 10 | query?: Record; 11 | headers?: Record; 12 | file?: Record; 13 | } 14 | 15 | const getQueryUrl = (query: FETCH['query']): string => { 16 | if (!query || !Object.values(query).some((val) => val)) return ''; 17 | 18 | return Object.entries(query) 19 | .filter(([_, value]) => value !== undefined) 20 | .map(([key, value]) => `${key}=${value}`) 21 | .join('&'); 22 | }; 23 | 24 | export default async ({ path, method, body, query, headers, file }: FETCH) => { 25 | let completeUrl = serverUrl + path; 26 | if (query && Object.keys(query).length > 0) 27 | completeUrl += '?' + getQueryUrl(query); 28 | 29 | const config: any = { url: completeUrl, method }; 30 | 31 | if (headers) config.headers = headers; 32 | if (body) config.data = body; 33 | else if (file) { 34 | const formData = new FormData(); 35 | Object.entries(file).forEach(([name, f]) => formData.append(name, f)); 36 | config.data = formData; 37 | config.headers = { 38 | ...config.headers, 39 | 'Content-Type': 'multipart/form-data', 40 | }; 41 | } 42 | 43 | return axios(config) 44 | .then((res) => res.data) 45 | .catch((err) => { 46 | if ( 47 | err.response && 48 | err.response.data && 49 | 'error' in err.response.data && 50 | 'status' in err.response.data.error 51 | ) 52 | return err.response.data; 53 | 54 | return { 55 | error: { 56 | name: 'Axios Error', 57 | status: 400, 58 | data: err, 59 | }, 60 | }; 61 | }); 62 | }; 63 | -------------------------------------------------------------------------------- /examples/minimal-express/sdk/hello.ts: -------------------------------------------------------------------------------- 1 | import Fetch from './bridgeFetchMethod'; 2 | 3 | export default async (): Promise< 4 | | { data: 'hello'; error: undefined } 5 | | { 6 | data: undefined; 7 | error: 8 | | { name: 'Axios Error'; status: 400; data: any } 9 | | { name: 'Internal Server Error'; status: 500 }; 10 | } 11 | > => { 12 | const res = await Fetch({ method: 'POST', path: '/hello' }); 13 | 14 | if (res.error && typeof res.error.status === 'number') 15 | return { data: undefined, error: res.error }; 16 | else return { data: res, error: undefined }; 17 | }; 18 | -------------------------------------------------------------------------------- /examples/minimal-express/sdk/index.ts: -------------------------------------------------------------------------------- 1 | import hello from './/hello'; 2 | 3 | export const API = { hello: hello }; 4 | -------------------------------------------------------------------------------- /examples/minimal-http/.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /dist 3 | package-lock.json 4 | pnpm-lock.yaml -------------------------------------------------------------------------------- /examples/minimal-http/JSONType.json: -------------------------------------------------------------------------------- 1 | { 2 | "object": { 3 | "hello": { 4 | "object": { 5 | "returnBridgeType": { 6 | "data": { "optional": false, "primitive": "string" }, 7 | "error": { "union": [] } 8 | } 9 | } 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /examples/minimal-http/bridge.config.json: -------------------------------------------------------------------------------- 1 | { "serverUrl": "http://localhost:8080" } 2 | -------------------------------------------------------------------------------- /examples/minimal-http/index.ts: -------------------------------------------------------------------------------- 1 | import { handler, initBridge } from 'bridge'; 2 | 3 | const port = 8080; 4 | const routes = { hello: handler({ resolve: () => 'hello' }) }; 5 | 6 | const httpServer = initBridge({ routes }).HTTPServer(); 7 | 8 | httpServer.listen(port, () => { 9 | console.log(`Listening on port ${port}`); 10 | }); 11 | -------------------------------------------------------------------------------- /examples/minimal-http/openapi.json: -------------------------------------------------------------------------------- 1 | { 2 | "openapi": "3.0.0", 3 | "info": { "title": "minimal-http", "version": "Bridge: 1.0.0" }, 4 | "paths": { 5 | "/hello": { 6 | "post": { 7 | "parameters": [], 8 | "responses": { 9 | "200": { 10 | "description": "Success", 11 | "content": { 12 | "application/json": { "schema": { "type": "string" } } 13 | } 14 | }, 15 | "400": { 16 | "description": "BAD_REQUEST", 17 | "content": { 18 | "application/json": { 19 | "schema": { 20 | "type": "object", 21 | "required": ["name", "status", "data"], 22 | "properties": { 23 | "name": { "enum": ["Axios Error"], "type": "string" }, 24 | "status": { "enum": [400], "type": "number" }, 25 | "data": {} 26 | } 27 | } 28 | } 29 | } 30 | }, 31 | "500": { 32 | "description": "INTERNAL_SERVER_ERROR", 33 | "content": { 34 | "application/json": { 35 | "schema": { 36 | "type": "object", 37 | "required": ["name", "status"], 38 | "properties": { 39 | "name": { 40 | "enum": ["Internal Server Error"], 41 | "type": "string" 42 | }, 43 | "status": { "enum": [500], "type": "number" } 44 | } 45 | } 46 | } 47 | } 48 | } 49 | } 50 | } 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /examples/minimal-http/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "minimal-http", 3 | "version": "1.0.0", 4 | "scripts": { 5 | "start": "node ./dist/index.js", 6 | "build": "tsc", 7 | "dev": "ts-node ./index.ts" 8 | }, 9 | "dependencies": { 10 | "bridge": "^2.0.45" 11 | }, 12 | "devDependencies": { 13 | "@types/node": "^18.11.16", 14 | "ts-node": "^10.9.1", 15 | "typescript": "^4.9.4" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /examples/minimal-http/sdk/bridgeFetchMethod.ts: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | import FormData from 'form-data'; 3 | 4 | export const serverUrl = 'http://localhost:8080'; 5 | 6 | interface FETCH { 7 | method: 'GET' | 'POST' | 'PATCH' | 'DELETE' | 'PUT'; 8 | path: string; 9 | body?: Record; 10 | query?: Record; 11 | headers?: Record; 12 | file?: Record; 13 | } 14 | 15 | const getQueryUrl = (query: FETCH['query']): string => { 16 | if (!query || !Object.values(query).some((val) => val)) return ''; 17 | 18 | return Object.entries(query) 19 | .filter(([_, value]) => value !== undefined) 20 | .map(([key, value]) => `${key}=${value}`) 21 | .join('&'); 22 | }; 23 | 24 | export default async ({ path, method, body, query, headers, file }: FETCH) => { 25 | let completeUrl = serverUrl + path; 26 | if (query && Object.keys(query).length > 0) 27 | completeUrl += '?' + getQueryUrl(query); 28 | 29 | const config: any = { url: completeUrl, method }; 30 | 31 | if (headers) config.headers = headers; 32 | if (body) config.data = body; 33 | else if (file) { 34 | const formData = new FormData(); 35 | Object.entries(file).forEach(([name, f]) => formData.append(name, f)); 36 | config.data = formData; 37 | config.headers = { 38 | ...config.headers, 39 | 'Content-Type': 'multipart/form-data', 40 | }; 41 | } 42 | 43 | return axios(config) 44 | .then((res) => res.data) 45 | .catch((err) => { 46 | if ( 47 | err.response && 48 | err.response.data && 49 | 'error' in err.response.data && 50 | 'status' in err.response.data.error 51 | ) 52 | return err.response.data; 53 | 54 | return { 55 | error: { 56 | name: 'Axios Error', 57 | status: 400, 58 | data: err, 59 | }, 60 | }; 61 | }); 62 | }; 63 | -------------------------------------------------------------------------------- /examples/minimal-http/sdk/hello.ts: -------------------------------------------------------------------------------- 1 | import Fetch from './bridgeFetchMethod'; 2 | 3 | export default async (): Promise< 4 | | { data: string; error: undefined } 5 | | { 6 | data: undefined; 7 | error: 8 | | { name: 'Axios Error'; status: 400; data: any } 9 | | { name: 'Internal Server Error'; status: 500 }; 10 | } 11 | > => { 12 | const res = await Fetch({ method: 'POST', path: '/hello' }); 13 | 14 | if (res.error && typeof res.error.status === 'number') 15 | return { data: undefined, error: res.error }; 16 | else return { data: res, error: undefined }; 17 | }; 18 | -------------------------------------------------------------------------------- /examples/minimal-http/sdk/index.ts: -------------------------------------------------------------------------------- 1 | import hello from './/hello'; 2 | 3 | export const API = { hello: hello }; 4 | -------------------------------------------------------------------------------- /nodemon.json: -------------------------------------------------------------------------------- 1 | { 2 | "restartable": "rs", 3 | "ignore": [".git", "dist/", "coverage/"], 4 | "watch": ["Lib", "Example"], 5 | "execMap": { 6 | "ts": "node -r ts-node/register" 7 | }, 8 | "env": { 9 | "NODE_ENV": "development" 10 | }, 11 | "ext": "js,json,ts" 12 | } 13 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bridge-codes", 3 | "lockfileVersion": 3, 4 | "requires": true, 5 | "packages": {} 6 | } 7 | -------------------------------------------------------------------------------- /packages/bridge-compile/.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /dist 3 | package-lock.json 4 | pnpm-lock.yaml -------------------------------------------------------------------------------- /packages/bridge-compile/.prettierignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | coverage 3 | dist 4 | build 5 | public -------------------------------------------------------------------------------- /packages/bridge-compile/.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | "singleQuote": true, 4 | "printWidth": 120, 5 | "trailingComma": "es5", 6 | "endOfLine": "auto" 7 | } 8 | -------------------------------------------------------------------------------- /packages/bridge-compile/README.md: -------------------------------------------------------------------------------- 1 | # Bridge Studio 2 | 3 | Compile a completely typed sdk of your Bridge project with one command. -------------------------------------------------------------------------------- /packages/bridge-compile/bin/parser/extract-zip.ts: -------------------------------------------------------------------------------- 1 | import AdmZip from 'adm-zip'; 2 | import * as os from 'os'; 3 | 4 | export const extractZipFromBufferAndGetPath = (zipBuffer: Buffer): string => { 5 | const zipFolder = new AdmZip(zipBuffer); 6 | 7 | const folderName = zipFolder.getEntries()[0].entryName; 8 | 9 | console.log(folderName); 10 | 11 | const tmpDir = os.tmpdir(); 12 | 13 | zipFolder.extractAllTo(tmpDir, true); 14 | 15 | return `${tmpDir}/${folderName}`; 16 | }; 17 | -------------------------------------------------------------------------------- /packages/bridge-compile/bin/parser/find-source-file.ts: -------------------------------------------------------------------------------- 1 | import fs from 'fs'; 2 | import path from 'path'; 3 | 4 | /** 5 | * 6 | */ 7 | export const findSourceFilePath = async (dir: string): Promise<{ sourceFilePath: string; addedCode: string }> => { 8 | let sourceFilePath: string = ''; 9 | let addedCode: string = ''; 10 | 11 | for (const file of fs.readdirSync(dir)) { 12 | const absolute = path.join(dir, file); 13 | 14 | if (fs.statSync(absolute).isDirectory()) { 15 | const { sourceFilePath: sourceFilePathInDir, addedCode: addedCodeInDir } = await findSourceFilePath(absolute); 16 | if (sourceFilePathInDir) return { sourceFilePath: sourceFilePathInDir, addedCode: addedCodeInDir }; 17 | } else if (absolute.substring(absolute.length - 3) === '.ts') { 18 | const file = fs.readFileSync(absolute, 'utf8'); 19 | const matches = file.match(/initBridge\((.*?)\)/); 20 | 21 | if (matches && matches?.length > 1 && matches[1].includes('routes')) { 22 | // console.log(matches[1]); 23 | 24 | addedCode = `\nconst bridgeGeneratedVariable = initBridge(${matches[1]});\ntype BridgeGenereatedType = typeof bridgeGeneratedVariable['bridgeType'];\n`; 25 | 26 | await fs.promises.appendFile(absolute, addedCode, 'utf-8'); 27 | 28 | sourceFilePath = absolute; 29 | return { sourceFilePath, addedCode }; 30 | } 31 | } 32 | } 33 | 34 | return { sourceFilePath, addedCode }; 35 | }; 36 | -------------------------------------------------------------------------------- /packages/bridge-compile/bin/parser/remove-added-code.ts: -------------------------------------------------------------------------------- 1 | import fs from 'fs'; 2 | 3 | export async function removeAddedCode(absolutePath: string, addedCode: string) { 4 | try { 5 | const data = fs.readFileSync(absolutePath, 'utf-8'); 6 | const newData = data.replace(addedCode, ''); 7 | 8 | fs.writeFileSync(absolutePath, newData, 'utf-8'); 9 | } catch (error) { 10 | console.error(`Error: ${error}`); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /packages/bridge-compile/bin/parser/ts-parser/index.ts: -------------------------------------------------------------------------------- 1 | import ts from 'typescript'; 2 | import { JSONTypeI } from '../../types'; 3 | import { createJSONType } from './create-json-type'; 4 | import { cleanJSONType } from '../transform-json-type'; 5 | 6 | /** 7 | * 8 | * WARNING: 9 | * This is not at all the best way to extract the type of the bridge project 10 | * If all the parameters of the initBridge function are not declared outside any closure, 11 | * the code wont work 12 | * 13 | * TO-DO 14 | * - [ ] Find with ts AST where is the initBridge call and extract the type from there 15 | */ 16 | export default (sourceFilePath: string): JSONTypeI => { 17 | try { 18 | const program: ts.Program = ts.createProgram([sourceFilePath], { 19 | // allowJs: true, 20 | target: ts.ScriptTarget.ESNext, 21 | module: ts.ModuleKind.CommonJS, 22 | declaration: true, 23 | declarationMap: true, 24 | sourceMap: true, 25 | strict: true, 26 | noErrorTruncation: true, 27 | forceConsistentCasingInFileNames: true, 28 | skipLibCheck: true, 29 | emitDecoratorMetadata: true, 30 | experimentalDecorators: true, 31 | esModuleInterop: true, 32 | allowSyntheticDefaultImports: true, 33 | resolveJsonModule: true, 34 | moduleResolution: 2, 35 | }); 36 | 37 | const checker: ts.TypeChecker = program.getTypeChecker(); 38 | 39 | const sourceFile = program.getSourceFile(sourceFilePath); 40 | if (!sourceFile) throw new Error('Program failed to laod sourceFile'); 41 | 42 | let BridgeGenereatedTypeNode: ts.Node = null as any; 43 | 44 | ts.forEachChild(sourceFile, (node) => { 45 | if (ts.isTypeAliasDeclaration(node) && node.name.escapedText === 'BridgeGenereatedType') 46 | BridgeGenereatedTypeNode = node; 47 | }); 48 | 49 | if (!BridgeGenereatedTypeNode) throw new Error('TS Node not found'); 50 | 51 | const JSONType = cleanJSONType( 52 | createJSONType(checker, checker.getTypeAtLocation(BridgeGenereatedTypeNode), BridgeGenereatedTypeNode) 53 | ); 54 | 55 | return JSONType; 56 | } catch (err) { 57 | console.error(err); 58 | return { primitive: 'unrecognized', value: `Error in program load` }; 59 | } 60 | }; 61 | -------------------------------------------------------------------------------- /packages/bridge-compile/bin/parser/ts-parser/recursive-checker.ts: -------------------------------------------------------------------------------- 1 | import ts from 'typescript'; 2 | import { utilityTypes } from '../../types'; 3 | import { isArrayWithPos } from './utils'; 4 | 5 | export const isTypeDepthGreaterThan = ( 6 | checker: ts.TypeChecker, 7 | type: ts.Type, 8 | node: ts.Node, 9 | maxDepth: number, 10 | depth = 0 11 | ): boolean => { 12 | try { 13 | if (depth > maxDepth) return true; 14 | 15 | if (checker.typeToString(type) === 'ObjectId') return false; 16 | 17 | if (type.isUnionOrIntersection()) 18 | for (const t of type.types) { 19 | if (isTypeDepthGreaterThan(checker, t, node, maxDepth, depth + 1)) return true; 20 | } 21 | 22 | if ( 23 | !utilityTypes.includes((type.aliasSymbol?.name || '') as any) && 24 | (type.isClassOrInterface() || ['__type', '__object'].includes(type.symbol?.name as any)) 25 | ) 26 | for (const property of type.getProperties()) { 27 | const valueType = checker.getTypeOfSymbolAtLocation(property, node); 28 | if (isTypeDepthGreaterThan(checker, valueType, node, maxDepth, depth + 1)) return true; 29 | } 30 | 31 | if ( 32 | type.symbol?.name === 'Array' && 33 | isTypeDepthGreaterThan(checker, checker.getTypeArguments(type as any)?.[0], node, maxDepth, depth + 1) 34 | ) 35 | return true; 36 | 37 | if (isArrayWithPos(type) && type.symbol?.name !== 'Array' && checker.typeToString(type) !== 'string') 38 | for (const t of (type as any).resolvedTypeArguments || []) 39 | if (isTypeDepthGreaterThan(checker, t, node, maxDepth, depth + 1)) return true; 40 | 41 | return false; 42 | } catch (err) { 43 | console.error(err); 44 | return true; 45 | } 46 | }; 47 | -------------------------------------------------------------------------------- /packages/bridge-compile/bin/parser/ts-parser/utils.ts: -------------------------------------------------------------------------------- 1 | import { Type } from 'typescript'; 2 | 3 | export function isArrayWithPos(type: Type) { 4 | return type 5 | .getProperties() 6 | .map((p) => p.escapedName) 7 | .some((p) => String(p).startsWith('__@iterator')); 8 | } 9 | -------------------------------------------------------------------------------- /packages/bridge-compile/bin/sdk-compilers/index.ts: -------------------------------------------------------------------------------- 1 | // REFACTO WITH CLASS AND INHERITANCE 2 | import compileTypescriptSDK from './typescript'; 3 | 4 | export { compileTypescriptSDK }; 5 | -------------------------------------------------------------------------------- /packages/bridge-compile/bin/sdk-compilers/typescript/compilers/fetch-file-compiler.ts: -------------------------------------------------------------------------------- 1 | import prettier from 'prettier'; 2 | 3 | export default (serverUrl: string) => 4 | prettier.format( 5 | `import axios from 'axios'; 6 | import FormData from 'form-data'; 7 | 8 | export const serverUrl = '${serverUrl.replace(/\/$/, '')}'; 9 | 10 | interface FETCH { 11 | method: 'GET' | 'POST' | 'PATCH' | 'DELETE' | 'PUT'; 12 | path: string; 13 | body?: Record; 14 | query?: Record; 15 | headers?: Record; 16 | file?: Record; 17 | } 18 | 19 | const getQueryUrl = (query: FETCH["query"]): string => { 20 | if (!query || !Object.values(query).some((val) => val)) return ""; 21 | 22 | return Object.entries(query) 23 | .filter(([_, value]) => value !== undefined) 24 | .map(([key, value]) => \`\${key}=\${value}\`) 25 | .join("&"); 26 | }; 27 | 28 | export default async ({ path, method, body, query, headers, file }: FETCH) => { 29 | let completeUrl = serverUrl + path; 30 | if (query && Object.keys(query).length > 0) completeUrl += '?' + getQueryUrl(query) 31 | 32 | const config: any = { url: completeUrl, method }; 33 | 34 | if (headers) config.headers = headers; 35 | if (body) config.data = body; 36 | else if (file) { 37 | const formData = new FormData(); 38 | Object.entries(file).forEach(([name, f]) => formData.append(name, f)); 39 | config.data = formData; 40 | config.headers = { ...config.headers, 'Content-Type': 'multipart/form-data' }; 41 | } 42 | 43 | return axios(config) 44 | .then((res) => res.data) 45 | .catch((err) => { 46 | if (err.response && err.response.data && 'error' in err.response.data && 'status' in err.response.data.error) 47 | return err.response.data; 48 | 49 | return { 50 | error: { 51 | name: 'Axios Error', 52 | status: 400, 53 | data: err, 54 | }, 55 | }; 56 | }); 57 | };`, 58 | { parser: 'typescript', singleQuote: true } 59 | ); 60 | -------------------------------------------------------------------------------- /packages/bridge-compile/bin/sdk-compilers/typescript/compilers/handler-file-compiler.ts: -------------------------------------------------------------------------------- 1 | import { JSONTypeToTSInterface } from '../json-type-to-ts-interface'; 2 | import prettier from 'prettier'; 3 | import { JSONHandler, formatReturnTypeJSON } from '../../utils'; 4 | 5 | const axiosError = { 6 | object: { 7 | name: { 8 | optional: false, 9 | primitive: 'string', 10 | value: 'Axios Error', 11 | }, 12 | status: { 13 | optional: false, 14 | primitive: 'number', 15 | value: 400, 16 | }, 17 | data: { 18 | optional: false, 19 | primitive: 'any', 20 | }, 21 | }, 22 | } as const; 23 | 24 | export const getHandlerTSFileContent = ( 25 | handler: JSONHandler, 26 | pathArray: string[], 27 | depth: number, 28 | method: 'GET' | 'POST' | 'PATCH' | 'PUT' | 'DELETE' 29 | ): string => { 30 | const bodyInterface = handler.object.body === undefined ? undefined : JSONTypeToTSInterface(handler.object.body); 31 | const queryInterface = handler.object.query === undefined ? undefined : JSONTypeToTSInterface(handler.object.query); 32 | const headersInterface = 33 | handler.object.headers === undefined ? undefined : JSONTypeToTSInterface(handler.object.headers); 34 | const filesInterface = 35 | handler.object.files === undefined 36 | ? undefined 37 | : 'record' in handler.object.files 38 | ? `Record` 39 | : `{${Object.keys(handler.object.files.object) 40 | .map((key) => `${key}: File | Buffer`) 41 | .join(';')}}`; 42 | 43 | const returnInterface = JSONTypeToTSInterface(formatReturnTypeJSON(handler, [axiosError])); 44 | 45 | const hasParameters = bodyInterface || queryInterface || headersInterface || filesInterface; 46 | 47 | let importFetch = './'; 48 | if (depth > 0) importFetch = '../'.repeat(depth); 49 | importFetch += 'bridgeFetchMethod'; 50 | 51 | return prettier.format( 52 | `import Fetch from '${importFetch}';\n\nexport default async (${ 53 | hasParameters 54 | ? `data: {${bodyInterface ? `body: ${bodyInterface},` : ``}${ 55 | queryInterface ? `query: ${queryInterface},` : `` 56 | }${headersInterface ? `headers: ${headersInterface},` : ``}${ 57 | filesInterface ? `file: ${filesInterface}` : '' 58 | }}` 59 | : '' 60 | }): Promise<${returnInterface}> => { 61 | const res = await Fetch({ ${hasParameters ? `...data,` : ''} method: "${method}", path: '${pathArray 62 | .map((p) => `/${p}`) 63 | .join('')}' }); 64 | 65 | if (res.error && typeof res.error.status === 'number') return { data: undefined, error: res.error }; 66 | else return { data: res, error: undefined }; 67 | }`, 68 | { parser: 'typescript', singleQuote: true } 69 | ); 70 | }; 71 | -------------------------------------------------------------------------------- /packages/bridge-compile/bin/sdk-compilers/typescript/compilers/index-import-file-compiler.ts: -------------------------------------------------------------------------------- 1 | import { BridgeRoutesTree } from '../../utils'; 2 | 3 | export const getIndexTSFileContent = ( 4 | root: BridgeRoutesTree, 5 | path: string[] = [] 6 | ): { importsString: string; exportAPIStringObject: string } => { 7 | let exportAPIStringObject = '{'; 8 | let importsString = ''; 9 | 10 | for (const [key, value] of Object.entries(root)) { 11 | if (value === 'handler') { 12 | importsString += `import ${path.join('').replaceAll('-', '')}${key} from "./${path.join('/')}/${key}";\n`; 13 | exportAPIStringObject += `${key}: ${path.join('').replaceAll('-', '')}${key},`; 14 | } else { 15 | exportAPIStringObject += `${key.replaceAll('-', '')}:`; 16 | const res = getIndexTSFileContent(value, [...path, key]); 17 | importsString += res.importsString; 18 | exportAPIStringObject += res.exportAPIStringObject; 19 | } 20 | } 21 | 22 | exportAPIStringObject += '},'; 23 | 24 | return { importsString, exportAPIStringObject }; 25 | }; 26 | -------------------------------------------------------------------------------- /packages/bridge-compile/bin/sdk-compilers/typescript/index.ts: -------------------------------------------------------------------------------- 1 | import { JSONTypeI } from '../../types'; 2 | import { mkdirAsync, writeFileAsync } from '../../utils'; 3 | import { writeHandlersFilesAndFolders } from './write-handlers-file'; 4 | import getFetchFileContent from './compilers/fetch-file-compiler'; 5 | import { getIndexTSFileContent } from './compilers/index-import-file-compiler'; 6 | import prettier from 'prettier'; 7 | import AdmZip from 'adm-zip'; 8 | import fs from 'fs'; 9 | import os from 'os'; 10 | 11 | export default async (JSONType: JSONTypeI, serverUrl: string): Promise => { 12 | const sdkTempPath = `${os.tmpdir()}/ts-sdk-${new Date().getTime()}`; 13 | 14 | await mkdirAsync(sdkTempPath); 15 | 16 | const [routesTree] = await Promise.all([ 17 | writeHandlersFilesAndFolders(JSONType, sdkTempPath, {}), 18 | writeFileAsync(`${sdkTempPath}/bridgeFetchMethod.ts`, getFetchFileContent(serverUrl)), 19 | ]); 20 | 21 | const { importsString, exportAPIStringObject } = getIndexTSFileContent(routesTree); 22 | 23 | await writeFileAsync( 24 | `${sdkTempPath}/index.ts`, 25 | prettier.format(`${importsString}\nexport const API = ${exportAPIStringObject.slice(0, -1)}`, { 26 | parser: 'typescript', 27 | singleQuote: true, 28 | }) 29 | ); 30 | 31 | const zip = new AdmZip(); 32 | zip.addLocalFolder(sdkTempPath); 33 | 34 | const buffer = await zip.toBufferPromise(); 35 | 36 | fs.rm(sdkTempPath, { recursive: true }, () => {}); 37 | 38 | return buffer; 39 | }; 40 | -------------------------------------------------------------------------------- /packages/bridge-compile/bin/sdk-compilers/typescript/json-type-to-ts-interface.ts: -------------------------------------------------------------------------------- 1 | import { JSONTypeI } from '../../types'; 2 | 3 | export const JSONTypeToTSInterface = (JSONType: JSONTypeI): string => { 4 | // HAVE TO FIX 5 | if (Object.values(JSONType).length === 0) return '{}'; 6 | if ('primitive' in JSONType) 7 | return JSONType.value && JSONType.primitive !== 'nested' 8 | ? JSONType.primitive === 'string' 9 | ? `"${JSONType.value.toString()}"` 10 | : JSONType.value.toString() 11 | : ['unrecognized', 'nested'].includes(JSONType.primitive) 12 | ? 'any' 13 | : JSONType.primitive; 14 | 15 | if ('array' in JSONType) return `Array<${JSONTypeToTSInterface(JSONType.array)}>`; 16 | 17 | if ('record' in JSONType) 18 | return `Record<${JSONTypeToTSInterface(JSONType.record.key)}, ${JSONTypeToTSInterface(JSONType.record.value)}>`; 19 | 20 | if ('union' in JSONType && JSONType.union.length > 0) 21 | return '(' + JSONType.union.map((elem) => JSONTypeToTSInterface(elem)).join(' | ') + ')'; 22 | 23 | if ('intersection' in JSONType && JSONType.intersection.length > 0) 24 | return '(' + JSONType.intersection.map((elem) => JSONTypeToTSInterface(elem)).join(' & ') + ')'; 25 | 26 | if ('arrayWithPos' in JSONType) 27 | return '[' + JSONType.arrayWithPos.map((elem) => JSONTypeToTSInterface(elem)).join(', ') + ']'; 28 | else if ('object' in JSONType) 29 | return ( 30 | '{' + 31 | Object.entries(JSONType.object) 32 | .map(([key, value]) => `"${key}"${value.optional ? '?' : ''}: ${JSONTypeToTSInterface(value)}`) 33 | .join(';') + 34 | '}' 35 | ); 36 | 37 | return 'any'; 38 | }; 39 | -------------------------------------------------------------------------------- /packages/bridge-compile/bin/sdk-compilers/typescript/write-handlers-file.ts: -------------------------------------------------------------------------------- 1 | import { writeFileAsync, mkdirAsync } from '../../utils'; 2 | import { JSONTypeI } from '../../types'; 3 | import { BridgeRoutesTree, isHandler } from '../utils'; 4 | import { getHandlerTSFileContent } from './compilers/handler-file-compiler'; 5 | 6 | const handlerNameToMethod: Record = { 7 | GET_BRIDGE_METHOD: 'GET', 8 | POST_BRIDGE_METHOD: 'POST', 9 | PATCH_BRIDGE_METHOD: 'PATCH', 10 | PUT_BRIDGE_METHOD: 'PUT', 11 | DELETE_BRIDGE_METHOD: 'DELETE', 12 | }; 13 | 14 | const handlerNameToFunctionName: Record = { 15 | GET_BRIDGE_METHOD: 'get', 16 | POST_BRIDGE_METHOD: 'post', 17 | PATCH_BRIDGE_METHOD: 'patch', 18 | PUT_BRIDGE_METHOD: 'put', 19 | DELETE_BRIDGE_METHOD: 'delete', 20 | }; 21 | 22 | export const writeHandlersFilesAndFolders = async ( 23 | JSONType: JSONTypeI, 24 | filePath: string, 25 | routesTree: BridgeRoutesTree, 26 | pathArray: string[] = [] 27 | ): Promise => { 28 | if (!('object' in JSONType)) return routesTree; 29 | 30 | const promises: Promise[] = []; 31 | 32 | for (const [key, value] of Object.entries(JSONType.object)) { 33 | if (!('object' in value)) return routesTree; 34 | 35 | if (isHandler(value)) { 36 | const method = handlerNameToMethod[key] || 'POST'; 37 | const newKey = handlerNameToFunctionName[key] || key; 38 | const newPathArray = newKey !== key ? [...pathArray] : [...pathArray, newKey]; 39 | 40 | promises.push( 41 | writeFileAsync( 42 | `${filePath}/${newKey}.ts`, 43 | getHandlerTSFileContent(value, newPathArray, pathArray.length, method) 44 | ) 45 | ); 46 | routesTree[newKey] = 'handler'; 47 | } else { 48 | promises.push( 49 | mkdirAsync(`${filePath}/${key}`).then(async () => { 50 | routesTree[key] = await writeHandlersFilesAndFolders(value, `${filePath}/${key}`, {}, [...pathArray, key]); 51 | }) 52 | ); 53 | } 54 | } 55 | 56 | await Promise.all(promises); 57 | 58 | return routesTree; 59 | }; 60 | -------------------------------------------------------------------------------- /packages/bridge-compile/bin/sdk-compilers/utils.ts: -------------------------------------------------------------------------------- 1 | import { JSONTypeI, JSONTypeObject } from '../types'; 2 | 3 | export interface JSONHandler { 4 | object: { 5 | body?: { object: JSONTypeObject; optional: boolean }; 6 | query?: { object: JSONTypeObject; optional: boolean }; 7 | headers?: { object: JSONTypeObject; optional: boolean }; 8 | files?: ({ record: { key: JSONTypeI; value: JSONTypeI } } | { object: JSONTypeObject }) & { optional: boolean }; 9 | returnBridgeType: { data: JSONTypeI; error: { union: JSONTypeI[] } }; 10 | }; 11 | } 12 | 13 | export type BridgeRoutesTree = { [key: string]: 'handler' | BridgeRoutesTree }; 14 | 15 | export const isHandler = (data: any): data is JSONHandler => { 16 | return 'object' in data && 'returnBridgeType' in data.object && 'data' in data.object.returnBridgeType; 17 | }; 18 | 19 | const params = ['body', 'query', 'headers', 'files'] as const; 20 | 21 | function capitalizeFirstLetter(text: string) { 22 | return text.charAt(0).toUpperCase() + text.slice(1); 23 | } 24 | 25 | const internalSeverError = { 26 | object: { 27 | name: { 28 | optional: false, 29 | primitive: 'string', 30 | value: 'Internal Server Error', 31 | }, 32 | status: { 33 | optional: false, 34 | primitive: 'number', 35 | value: 500, 36 | }, 37 | }, 38 | } as const; 39 | 40 | /** 41 | * TO-DO 42 | * - [ ] missingFiles: string[] 43 | */ 44 | const getParamError = (param: (typeof params)[number]) => 45 | ({ 46 | object: { 47 | name: { 48 | optional: false, 49 | primitive: 'string', 50 | value: `${capitalizeFirstLetter(param)} schema validation error`, 51 | }, 52 | status: { 53 | optional: false, 54 | primitive: 'number', 55 | value: 400, 56 | }, 57 | data: { 58 | optional: false, 59 | primitive: 'any', 60 | }, 61 | }, 62 | } as const); 63 | 64 | export const formatReturnTypeJSON = (jsonHandler: JSONHandler, additionalErrors: JSONTypeI[] = []): JSONTypeI => { 65 | const errors = jsonHandler.object.returnBridgeType.error.union; 66 | const data = jsonHandler.object.returnBridgeType.data; 67 | 68 | // adding errors from specific client library 69 | errors.push(...additionalErrors); 70 | 71 | // adding internal server error 72 | errors.push(internalSeverError); 73 | 74 | // adding errors from parameters 75 | for (const param of params) if (jsonHandler.object[param]) errors.push(getParamError(param)); 76 | 77 | return { 78 | union: [ 79 | { object: { data: { optional: false, ...data }, error: { optional: false, primitive: 'undefined' } } }, 80 | { object: { data: { optional: false, primitive: 'undefined' }, error: { optional: false, union: errors } } }, 81 | ], 82 | }; 83 | }; 84 | -------------------------------------------------------------------------------- /packages/bridge-compile/bin/utils/fsAsync.ts: -------------------------------------------------------------------------------- 1 | import fs from 'fs'; 2 | 3 | export const writeFileAsync = (filePath: string, content: string) => { 4 | return new Promise(function (resolve, reject) { 5 | fs.writeFile(filePath, content, function (err) { 6 | if (err) reject(err); 7 | else resolve({ success: true }); 8 | }); 9 | }); 10 | }; 11 | 12 | export const mkdirAsync = (folderPath: string) => { 13 | return new Promise(function (resolve, reject) { 14 | fs.mkdir(folderPath, function (err) { 15 | if (err) reject(err); 16 | else resolve({ success: true }); 17 | }); 18 | }); 19 | }; 20 | -------------------------------------------------------------------------------- /packages/bridge-compile/bin/utils/index.ts: -------------------------------------------------------------------------------- 1 | export * from './fsAsync'; 2 | export * from './sleep'; 3 | export * from './run-command'; 4 | export * from './statusCode'; 5 | export * from './methods'; 6 | -------------------------------------------------------------------------------- /packages/bridge-compile/bin/utils/methods.ts: -------------------------------------------------------------------------------- 1 | export const methodConvertor = { 2 | GET_BRIDGE_METHOD: 'get', 3 | POST_BRIDGE_METHOD: 'post', 4 | PATCH_BRIDGE_METHOD: 'patch', 5 | PUT_BRIDGE_METHOD: 'put', 6 | DELETE_BRIDGE_METHOD: 'delete', 7 | } as const; 8 | -------------------------------------------------------------------------------- /packages/bridge-compile/bin/utils/run-command.ts: -------------------------------------------------------------------------------- 1 | import { exec } from 'child_process'; 2 | 3 | export async function runAsyncCommand(command: string): Promise<{ stdout: string; stderr: string }> { 4 | return new Promise((resolve, reject) => { 5 | exec(command, (error, stdout, stderr) => { 6 | if (error) { 7 | reject(error); 8 | } else { 9 | resolve({ stdout, stderr }); 10 | } 11 | }); 12 | }); 13 | } 14 | -------------------------------------------------------------------------------- /packages/bridge-compile/bin/utils/sleep.ts: -------------------------------------------------------------------------------- 1 | export const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); 2 | // 3 | -------------------------------------------------------------------------------- /packages/bridge-compile/bin/utils/statusCode.ts: -------------------------------------------------------------------------------- 1 | export const descriptionOfStatusCode: any = { 2 | '400': 'BAD_REQUEST', 3 | '401': 'UNAUTHORIZED', 4 | '402': 'PAYMENT_REQUIRED', 5 | '403': 'FORBIDDEN', 6 | '404': 'NOT_FOUND', 7 | '405': 'METHOD_NOT_ALLOWED', 8 | '406': 'NOT_ACCEPTABLE', 9 | '407': 'PROXY_AUTHENTICATION_REQUIRED', 10 | '408': 'REQUEST_TIMEOUT', 11 | '409': 'CONFLICT', 12 | '410': 'GONE', 13 | '411': 'LENGTH_REQUIRED', 14 | '412': 'PRECONDITION_FAILED', 15 | '413': 'PAYLOAD_TOO_LARGE', 16 | '414': 'URI_TOO_LONG', 17 | '415': 'UNSUPORTED_MEDIA_TYPE', 18 | '416': 'RANGE_NOT_SATISFIABLE', 19 | '417': 'EXPECTATION_FAILED', 20 | '418': 'I_AM_A_TEAPOT', 21 | '419': 'MISDIRECTED_REQUEST', 22 | '422': 'UNPROCESSABLE_ENTITY', 23 | '423': 'LOCKED', 24 | '424': 'FAILED_DEPENDENCY', 25 | '425': 'TOO_EARLY', 26 | '426': 'UPGRADE_REQUIRED', 27 | '428': 'PRECONDITION_REQUIRED', 28 | '429': 'TOO_MANY_REQUEST', 29 | '431': 'REQUEST_HEADER_FIELDS_TOO_LARGE', 30 | '451': 'UNAVAILABLE_FOR_LEGAL_REASONS', 31 | '500': 'INTERNAL_SERVER_ERROR', 32 | '501': 'NOT_IMPLEMENTED', 33 | '502': 'BAD_GATEWAY', 34 | '503': 'SERVICE_UNAVAILABLE', 35 | '504': 'GATEWAY_TIMEOUT', 36 | '505': 'HTTP_VERSION_NOT_SUPPORTED', 37 | '506': 'VARIANT_ALSO_NEGOCIATES', 38 | '507': 'INSUFFICIENT_STORAGE', 39 | '508': 'LOOP_DETECTED', 40 | '510': 'NOT_EXTENTED', 41 | '511': 'NETWORK_AUTHENTICATION_REQUIRED', 42 | }; 43 | -------------------------------------------------------------------------------- /packages/bridge-compile/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bridge-compile", 3 | "version": "1.0.30", 4 | "description": "Compile the sdk of your Bridge project with one command locally", 5 | "author": "Bridge Team ", 6 | "bin": { 7 | "bridge-compile": "./dist/index.js" 8 | }, 9 | "files": [ 10 | "/dist" 11 | ], 12 | "scripts": { 13 | "build": "tsc", 14 | "publish-minor": "tsc && npm version patch && npm publish" 15 | }, 16 | "keywords": [ 17 | "sdk", 18 | "compilation", 19 | "bridge", 20 | "typescript" 21 | ], 22 | "license": "MIT", 23 | "bugs": "https://github.com/bridge-codes/bridge/issues", 24 | "homepage": "https://bridge.codes", 25 | "repository": { 26 | "type": "git", 27 | "url": "git+https://github.com/bridge-codes/bridge.git", 28 | "directory": "packages/bridge-compile" 29 | }, 30 | "dependencies": { 31 | "adm-zip": "^0.5.10", 32 | "axios": "^1.2.5", 33 | "bridge": "^2.0.39", 34 | "chalk": "^5.2.0", 35 | "cli-spinners": "^2.7.0", 36 | "colors": "^1.4.0", 37 | "express": "^4.18.2", 38 | "form-data": "^4.0.0", 39 | "minimist": "^1.2.8", 40 | "ora": "^3.4.0", 41 | "prettier": "^2.8.3", 42 | "prompts": "^2.4.2", 43 | "typescript": "^4.9.4", 44 | "zod": "^3.20.2" 45 | }, 46 | "devDependencies": { 47 | "@types/adm-zip": "^0.5.0", 48 | "@types/express": "^4.17.16", 49 | "@types/ora": "^3.2.0", 50 | "@types/prettier": "^2.7.2", 51 | "@types/prompts": "^2.4.2" 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /packages/bridge-react-query/.gitignore: -------------------------------------------------------------------------------- 1 | /dist 2 | /node_modules 3 | pnpm-lock.yaml 4 | 5 | # Logs 6 | logs 7 | *.log 8 | npm-debug.log* 9 | pnpm-debug.log* 10 | yarn-debug.log* 11 | yarn-error.log* 12 | lerna-debug.log* 13 | 14 | # OS 15 | .DS_Store 16 | 17 | # Tests 18 | /coverage 19 | /.nyc_output 20 | 21 | # IDEs and editors 22 | /.idea 23 | .project 24 | .classpath 25 | .c9/ 26 | *.launch 27 | .settings/ 28 | *.sublime-workspace 29 | 30 | # IDE - VSCode 31 | .vscode/* 32 | !.vscode/settings.json 33 | !.vscode/tasks.json 34 | !.vscode/launch.json 35 | !.vscode/extensions.json 36 | 37 | sdk -------------------------------------------------------------------------------- /packages/bridge-react-query/README.md: -------------------------------------------------------------------------------- 1 | # Bridge React Query 2 | 3 | Bridge between react-query and bridge-sdk 4 | -------------------------------------------------------------------------------- /packages/bridge-react-query/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bridge-react-query", 3 | "version": "1.0.8", 4 | "description": "Bridge between react-query and bridge-sdk", 5 | "author": "Bridge Team ", 6 | "license": "MIT", 7 | "main": "dist/source/index.js", 8 | "types": "dist/source/index.d.ts", 9 | "bugs": "https://github.com/bridge-codes/bridge/issues", 10 | "homepage": "https://bridge.codes", 11 | "files": [ 12 | "/dist" 13 | ], 14 | "scripts": { 15 | "build": "tsc", 16 | "publish-minor": "tsc && npm version patch && npm publish" 17 | }, 18 | "repository": { 19 | "type": "git", 20 | "url": "git+https://github.com/bridge-codes/bridge.git", 21 | "directory": "packages/bridge-react-query" 22 | }, 23 | "keywords": [ 24 | "bridge", 25 | "sdk", 26 | "react-query", 27 | "typescript" 28 | ], 29 | "dependencies": { 30 | "@tanstack/react-query": "^4.23.0" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /packages/bridge-react-query/source/index.ts: -------------------------------------------------------------------------------- 1 | import { 2 | UseMutationOptions, 3 | useQuery, 4 | useMutation, 5 | UseQueryOptions, 6 | QueryKey, 7 | QueryFunction, 8 | MutationFunction, 9 | } from '@tanstack/react-query'; 10 | 11 | const useBridgeQuery = ( 12 | queryKey: TQueryKey, 13 | queryFn: QueryFunction< 14 | | { data: Exclude; error: undefined } 15 | | { data: undefined; error: Exclude }, 16 | TQueryKey 17 | >, 18 | options?: Omit< 19 | UseQueryOptions< 20 | | { data: Exclude; error: undefined } 21 | | { data: undefined; error: Exclude }, 22 | Exclude, 23 | Exclude, 24 | TQueryKey 25 | >, 26 | 'queryKey' | 'queryFn' | 'initialData' 27 | > & { 28 | initialData?: () => undefined; 29 | }, 30 | ) => 31 | useQuery( 32 | queryKey, 33 | async (context) => { 34 | const { data, error } = await queryFn(context); 35 | if (error) throw error; 36 | return data as any; 37 | }, 38 | options, 39 | ); 40 | 41 | const useBridgeMutation = ( 42 | mutationFn: MutationFunction< 43 | { data: TData; error: undefined } | { data: undefined; error: TError }, 44 | TVariables 45 | >, 46 | options?: Omit< 47 | UseMutationOptions, Exclude, TVariables, TContext>, 48 | 'mutationFn' 49 | >, 50 | ) => 51 | useMutation(async (input: TVariables) => { 52 | const { data, error } = await mutationFn(input); 53 | if (error) throw error; 54 | return data as Exclude; 55 | }, options); 56 | 57 | export * from '@tanstack/react-query'; 58 | export { useBridgeQuery, useBridgeMutation }; 59 | 60 | type rs = { error: undefined; data: { yo: true } } | { error: { name: 'sdf' }; data: undefined }; 61 | 62 | const req = async (): Promise => ({} as any); 63 | 64 | useBridgeQuery(['d'], () => req(), { 65 | onError: (error) => {}, 66 | }); 67 | 68 | useBridgeMutation(req, { 69 | onError: (error) => {}, 70 | }); 71 | -------------------------------------------------------------------------------- /packages/bridge-studio/.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /dist 3 | package-lock.json 4 | pnpm-lock.yaml -------------------------------------------------------------------------------- /packages/bridge-studio/README.md: -------------------------------------------------------------------------------- 1 | # Bridge Studio 2 | 3 | Compile a completely typed sdk of your Bridge project with one command. -------------------------------------------------------------------------------- /packages/bridge-studio/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bridge-studio", 3 | "version": "1.0.9", 4 | "description": "Compile the sdk of your Bridge project with one command", 5 | "author": "Bridge Team ", 6 | "bin": { 7 | "bridge-studio": "./dist/bin/index.js" 8 | }, 9 | "files": [ 10 | "/dist" 11 | ], 12 | "scripts": { 13 | "build": "tsc", 14 | "publish-minor": "tsc && npm version patch && npm publish" 15 | }, 16 | "keywords": [ 17 | "sdk", 18 | "compilation", 19 | "bridge", 20 | "typescript" 21 | ], 22 | "license": "MIT", 23 | "bugs": "https://github.com/bridge-codes/bridge/issues", 24 | "homepage": "https://bridge.codes", 25 | "repository": { 26 | "type": "git", 27 | "url": "git+https://github.com/bridge-codes/bridge.git", 28 | "directory": "packages/fetch-bridge-sdk" 29 | }, 30 | "dependencies": { 31 | "adm-zip": "^0.5.10", 32 | "axios": "^1.2.5", 33 | "bridge": "^2.0.39", 34 | "chalk": "^5.2.0", 35 | "cli-spinners": "^2.7.0", 36 | "colors": "^1.4.0", 37 | "express": "^4.18.2", 38 | "form-data": "^4.0.0", 39 | "ora": "^3.4.0", 40 | "prettier": "^2.8.3", 41 | "prompts": "^2.4.2", 42 | "zod": "^3.20.2" 43 | }, 44 | "devDependencies": { 45 | "@types/prettier": "^2.7.2", 46 | "@types/adm-zip": "^0.5.0", 47 | "@types/express": "^4.17.16", 48 | "@types/ora": "^3.2.0", 49 | "@types/prompts": "^2.4.2" 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /packages/bridge-studio/sdk/bridgeFetchMethod.ts: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | import FormData from 'form-data'; 3 | 4 | export const serverUrl = 'https://api-prod.bridge.codes'; 5 | 6 | interface FETCH { 7 | method: 'GET' | 'POST' | 'PATCH' | 'DELETE' | 'PUT'; 8 | path: string; 9 | body?: Record; 10 | query?: Record; 11 | headers?: Record; 12 | file?: Record; 13 | } 14 | 15 | const getQueryUrl = (query: FETCH['query']): string => { 16 | if (!query || !Object.values(query).some((val) => val)) return ''; 17 | 18 | return Object.entries(query) 19 | .filter(([_, value]) => value) 20 | .map(([key, value]) => `${key}=${value}`) 21 | .join('&'); 22 | }; 23 | 24 | export default async ({ path, method, body, query, headers, file }: FETCH) => { 25 | let completeUrl = serverUrl + path; 26 | if (query && Object.keys(query).length > 0) completeUrl += '?' + getQueryUrl(query); 27 | 28 | const config: any = { url: completeUrl, method }; 29 | 30 | if (headers) config.headers = headers; 31 | if (body) config.data = body; 32 | else if (file) { 33 | const formData = new FormData(); 34 | Object.entries(file).forEach(([name, f]) => formData.append(name, f)); 35 | config.data = formData; 36 | config.headers = { 37 | ...config.headers, 38 | 'Content-Type': 'multipart/form-data', 39 | }; 40 | } 41 | 42 | return axios(config) 43 | .then((res) => res.data) 44 | .catch((err) => { 45 | console.error(err); 46 | if ( 47 | err.response && 48 | err.response.data && 49 | err.response.data.error && 50 | err.response.data.error.status 51 | ) 52 | return err.response.data; 53 | 54 | return { 55 | error: { 56 | name: 'Axios Error', 57 | status: 400, 58 | data: err, 59 | }, 60 | }; 61 | }); 62 | }; 63 | -------------------------------------------------------------------------------- /packages/bridge-studio/sdk/confirmation/getCodeByEmail.ts: -------------------------------------------------------------------------------- 1 | import Fetch from '../bridgeFetchMethod'; 2 | 3 | export default async (data: { 4 | query: { email: string }; 5 | }): Promise< 6 | | { data: 'Code sent by email'; error: null } 7 | | { 8 | data: null; 9 | error: 10 | | { name: 'Query schema validation error'; status: 422; data: any } 11 | | { name: 'Axios Error'; status: 400; data: any } 12 | | { name: 'Internal Server Error'; status: 500 }; 13 | } 14 | > => { 15 | const res = await Fetch({ 16 | ...data, 17 | method: 'POST', 18 | path: '/confirmation/getCodeByEmail', 19 | }); 20 | 21 | if (res.error && typeof res.error.status === 'number') 22 | return { data: null, error: res.error }; 23 | else return { data: res, error: null }; 24 | }; 25 | -------------------------------------------------------------------------------- /packages/bridge-studio/sdk/deploy/getLast.ts: -------------------------------------------------------------------------------- 1 | import Fetch from '../bridgeFetchMethod'; 2 | 3 | export default async (data: { 4 | query: { project: string; owner: string }; 5 | }): Promise< 6 | | { 7 | data: { 8 | project: { createdAt: Date; name: string } & { _id: string }; 9 | createdAt: Date; 10 | owner: { createdAt: Date; name: string } & { _id: string }; 11 | _id: string; 12 | }; 13 | error: null; 14 | } 15 | | { 16 | data: null; 17 | error: 18 | | { name: 'Document not found'; data?: any; status: 404 } 19 | | { name: 'Query schema validation error'; status: 422; data: any } 20 | | { name: 'Axios Error'; status: 400; data: any } 21 | | { name: 'Internal Server Error'; status: 500 }; 22 | } 23 | > => { 24 | const res = await Fetch({ ...data, method: 'POST', path: '/deploy/getLast' }); 25 | 26 | if (res.error && typeof res.error.status === 'number') 27 | return { data: null, error: res.error }; 28 | else return { data: res, error: null }; 29 | }; 30 | -------------------------------------------------------------------------------- /packages/bridge-studio/sdk/deploy/typescript-sdk/get.ts: -------------------------------------------------------------------------------- 1 | import Fetch from '../../bridgeFetchMethod'; 2 | 3 | export default async (data: { 4 | query: { project: string; owner: string }; 5 | }): Promise< 6 | | { data: string; error: null } 7 | | { 8 | data: null; 9 | error: 10 | | { name: 'Document not found'; data?: any; status: 404 } 11 | | { name: 'Query schema validation error'; status: 422; data: any } 12 | | { name: 'Axios Error'; status: 400; data: any } 13 | | { name: 'Internal Server Error'; status: 500 }; 14 | } 15 | > => { 16 | const res = await Fetch({ 17 | ...data, 18 | method: 'GET', 19 | path: '/deploy/typescript-sdk', 20 | }); 21 | 22 | if (res.error && typeof res.error.status === 'number') 23 | return { data: null, error: res.error }; 24 | else return { data: res, error: null }; 25 | }; 26 | -------------------------------------------------------------------------------- /packages/bridge-studio/sdk/github/getMyConnectedAccounts.ts: -------------------------------------------------------------------------------- 1 | import Fetch from '../bridgeFetchMethod'; 2 | 3 | export default async (data: { 4 | headers: { token: string }; 5 | }): Promise< 6 | | { 7 | data: { 8 | githubAccounts: Array<{ 9 | id: string; 10 | name: string; 11 | avatar: string; 12 | createdAt: string; 13 | }>; 14 | }; 15 | error: null; 16 | } 17 | | { 18 | data: null; 19 | error: 20 | | { name: 'Wrong permission'; data?: any; status: 401 } 21 | | { name: 'Expired token'; data?: any; status: 401 } 22 | | { name: 'Invalid token'; data?: any; status: 401 } 23 | | { status: 404; name: 'Document not found' } 24 | | { name: 'Headers schema validation error'; status: 422; data: any } 25 | | { name: 'Headers schema validation error'; status: 422; data: any } 26 | | { name: 'Axios Error'; status: 400; data: any } 27 | | { name: 'Internal Server Error'; status: 500 }; 28 | } 29 | > => { 30 | const res = await Fetch({ 31 | ...data, 32 | method: 'POST', 33 | path: '/github/getMyConnectedAccounts', 34 | }); 35 | 36 | if (res.error && typeof res.error.status === 'number') 37 | return { data: null, error: res.error }; 38 | else return { data: res, error: null }; 39 | }; 40 | -------------------------------------------------------------------------------- /packages/bridge-studio/sdk/github/getReposWithTS.ts: -------------------------------------------------------------------------------- 1 | import Fetch from '../bridgeFetchMethod'; 2 | 3 | export default async (data: { 4 | body: { repoName?: string; perPage?: number; connectedAccountName: string }; 5 | headers: { token: string }; 6 | }): Promise< 7 | | { 8 | data: Array<{ 9 | id: number; 10 | name: string; 11 | full_name: string; 12 | visibility: string; 13 | owner: string; 14 | createdAt: string; 15 | updatedAt: string; 16 | }>; 17 | error: null; 18 | } 19 | | { 20 | data: null; 21 | error: 22 | | { name: 'Wrong permission'; data?: any; status: 401 } 23 | | { name: 'Expired token'; data?: any; status: 401 } 24 | | { name: 'Invalid token'; data?: any; status: 401 } 25 | | { status: 404; name: 'Document not found' } 26 | | { name: 'Headers schema validation error'; status: 422; data: any } 27 | | { name: 'Body schema validation error'; status: 422; data: any } 28 | | { name: 'Headers schema validation error'; status: 422; data: any } 29 | | { name: 'Axios Error'; status: 400; data: any } 30 | | { name: 'Internal Server Error'; status: 500 }; 31 | } 32 | > => { 33 | const res = await Fetch({ 34 | ...data, 35 | method: 'POST', 36 | path: '/github/getReposWithTS', 37 | }); 38 | 39 | if (res.error && typeof res.error.status === 'number') 40 | return { data: null, error: res.error }; 41 | else return { data: res, error: null }; 42 | }; 43 | -------------------------------------------------------------------------------- /packages/bridge-studio/sdk/github/subscribeRepoToBridge.ts: -------------------------------------------------------------------------------- 1 | import Fetch from '../bridgeFetchMethod'; 2 | 3 | export default async (data: { 4 | body: { 5 | serverUrl: string; 6 | githubRepo: { 7 | id: number; 8 | name: string; 9 | owner: string; 10 | full_name: string; 11 | visibility: 'private' | 'public'; 12 | createdAt: Date; 13 | }; 14 | projectName: string; 15 | }; 16 | headers: { token: string }; 17 | }): Promise< 18 | | { 19 | data: { 20 | owner: string; 21 | name: string; 22 | provider: 'github'; 23 | subscribedLanguages: Array<'typescript'>; 24 | serverUrl: string; 25 | pendingCompilation: true; 26 | githubRepo: { 27 | id: number; 28 | name: string; 29 | owner: string; 30 | full_name: string; 31 | visibility: 'private' | 'public'; 32 | createdAt: Date; 33 | }; 34 | lastCompilationSuccess: true; 35 | } & { _id: string; createdAt: Date; updatedAt: Date }; 36 | error: null; 37 | } 38 | | { 39 | data: null; 40 | error: 41 | | { name: 'Wrong permission'; data?: any; status: 401 } 42 | | { name: 'Expired token'; data?: any; status: 401 } 43 | | { name: 'Invalid token'; data?: any; status: 401 } 44 | | { status: 404; name: 'Document not found' } 45 | | { name: 'Headers schema validation error'; status: 422; data: any } 46 | | { name: 'Already exists'; data?: any; status: 406 } 47 | | { name: 'User not connected with github'; data?: any; status: 401 } 48 | | { 49 | name: 'Undefined in result of github.getRepoZip'; 50 | data?: any; 51 | status: 500; 52 | } 53 | | { status: 409; name: 'Already exists'; data?: any } 54 | | { name: 'Body schema validation error'; status: 422; data: any } 55 | | { name: 'Headers schema validation error'; status: 422; data: any } 56 | | { name: 'Axios Error'; status: 400; data: any } 57 | | { name: 'Internal Server Error'; status: 500 }; 58 | } 59 | > => { 60 | const res = await Fetch({ 61 | ...data, 62 | method: 'POST', 63 | path: '/github/subscribeRepoToBridge', 64 | }); 65 | 66 | if (res.error && typeof res.error.status === 'number') 67 | return { data: null, error: res.error }; 68 | else return { data: res, error: null }; 69 | }; 70 | -------------------------------------------------------------------------------- /packages/bridge-studio/sdk/hello.ts: -------------------------------------------------------------------------------- 1 | import Fetch from './bridgeFetchMethod'; 2 | 3 | export default async (): Promise< 4 | | { data: string; error: null } 5 | | { 6 | data: null; 7 | error: 8 | | { name: 'Axios Error'; status: 400; data: any } 9 | | { name: 'Internal Server Error'; status: 500 }; 10 | } 11 | > => { 12 | const res = await Fetch({ method: 'POST', path: '/hello' }); 13 | 14 | if (res.error && typeof res.error.status === 'number') 15 | return { data: null, error: res.error }; 16 | else return { data: res, error: null }; 17 | }; 18 | -------------------------------------------------------------------------------- /packages/bridge-studio/sdk/index.ts: -------------------------------------------------------------------------------- 1 | import hello from './/hello'; 2 | import usergetMe from './user/getMe'; 3 | import userisEmailInUse from './user/isEmailInUse'; 4 | import usersigninWithPassword from './user/signinWithPassword'; 5 | import userupdateMyPassword from './user/updateMyPassword'; 6 | import userupdateForgottenPassword from './user/updateForgottenPassword'; 7 | import userrefreshToken from './user/refreshToken'; 8 | import userlogout from './user/logout'; 9 | import userdeleteMyAccount from './user/deleteMyAccount'; 10 | import confirmationgetCodeByEmail from './confirmation/getCodeByEmail'; 11 | import githubgetMyConnectedAccounts from './github/getMyConnectedAccounts'; 12 | import githubgetReposWithTS from './github/getReposWithTS'; 13 | import githubsubscribeRepoToBridge from './github/subscribeRepoToBridge'; 14 | import deploygetLast from './deploy/getLast'; 15 | import deploytypescriptsdkget from './deploy/typescript-sdk/get'; 16 | import projectget from './project/get'; 17 | import projectgetMine from './project/getMine'; 18 | import projectupdate from './project/update'; 19 | import projectreCompile from './project/reCompile'; 20 | import projectgetFromCLI from './project/getFromCLI'; 21 | import projectcreateProjectFromCLI from './project/createProjectFromCLI'; 22 | import projectcompileWithCommandLine from './project/compileWithCommandLine'; 23 | 24 | export const API = { 25 | hello: hello, 26 | user: { 27 | getMe: usergetMe, 28 | isEmailInUse: userisEmailInUse, 29 | signinWithPassword: usersigninWithPassword, 30 | updateMyPassword: userupdateMyPassword, 31 | updateForgottenPassword: userupdateForgottenPassword, 32 | refreshToken: userrefreshToken, 33 | logout: userlogout, 34 | deleteMyAccount: userdeleteMyAccount, 35 | }, 36 | confirmation: { getCodeByEmail: confirmationgetCodeByEmail }, 37 | github: { 38 | getMyConnectedAccounts: githubgetMyConnectedAccounts, 39 | getReposWithTS: githubgetReposWithTS, 40 | subscribeRepoToBridge: githubsubscribeRepoToBridge, 41 | }, 42 | deploy: { 43 | getLast: deploygetLast, 44 | typescriptsdk: { get: deploytypescriptsdkget }, 45 | }, 46 | project: { 47 | get: projectget, 48 | getMine: projectgetMine, 49 | update: projectupdate, 50 | reCompile: projectreCompile, 51 | getFromCLI: projectgetFromCLI, 52 | createProjectFromCLI: projectcreateProjectFromCLI, 53 | compileWithCommandLine: projectcompileWithCommandLine, 54 | }, 55 | }; 56 | -------------------------------------------------------------------------------- /packages/bridge-studio/sdk/project/compileWithCommandLine.ts: -------------------------------------------------------------------------------- 1 | import Fetch from '../bridgeFetchMethod'; 2 | 3 | export default async (data: { 4 | query: { serverUrl: string; projectName: string; projectId: string }; 5 | headers: { token: string }; 6 | file: { projectZipBuffer: File | Buffer }; 7 | }): Promise< 8 | | { 9 | data: { 10 | project: { 11 | createdAt: Date; 12 | name: string; 13 | owner: string; 14 | serverUrl: string; 15 | provider: 'github' | 'gitlab' | 'cli'; 16 | subscribedLanguages: Array<'python' | 'javascript' | 'typescript'>; 17 | lastCompilationSuccess: false | true; 18 | pendingCompilation: false | true; 19 | githubRepo?: { 20 | id: number; 21 | name: string; 22 | owner: string; 23 | full_name: string; 24 | visibility: 'private' | 'public'; 25 | createdAt: Date; 26 | }; 27 | } & { _id: string }; 28 | user: { username: string } & { _id: string }; 29 | }; 30 | error: null; 31 | } 32 | | { 33 | data: null; 34 | error: 35 | | { name: 'Wrong permission'; data?: any; status: 401 } 36 | | { name: 'Invalid token'; data?: any; status: 401 } 37 | | { status: 404; name: 'Document not found' } 38 | | { name: 'Headers schema validation error'; status: 422; data: any } 39 | | { name: 'Headers schema validation error'; status: 422; data: any } 40 | | { name: 'Query schema validation error'; status: 422; data: any } 41 | | { name: 'Axios Error'; status: 400; data: any } 42 | | { name: 'Internal Server Error'; status: 500 }; 43 | } 44 | > => { 45 | const res = await Fetch({ 46 | ...data, 47 | method: 'POST', 48 | path: '/project/compileWithCommandLine', 49 | }); 50 | 51 | if (res.error && typeof res.error.status === 'number') 52 | return { data: null, error: res.error }; 53 | else return { data: res, error: null }; 54 | }; 55 | -------------------------------------------------------------------------------- /packages/bridge-studio/sdk/project/createProjectFromCLI.ts: -------------------------------------------------------------------------------- 1 | import Fetch from '../bridgeFetchMethod'; 2 | 3 | export default async (data: { 4 | query: { serverUrl: string; projectName: string }; 5 | headers: { token: string }; 6 | }): Promise< 7 | | { 8 | data: { 9 | owner: string; 10 | name: string; 11 | serverUrl: string; 12 | provider: 'cli'; 13 | subscribedLanguages: Array<'typescript'>; 14 | pendingCompilation: false; 15 | lastCompilationSuccess: true; 16 | } & { _id: string; createdAt: Date; updatedAt: Date }; 17 | error: null; 18 | } 19 | | { 20 | data: null; 21 | error: 22 | | { name: 'Wrong permission'; data?: any; status: 401 } 23 | | { name: 'Invalid token'; data?: any; status: 401 } 24 | | { status: 409; name: 'Already exists'; data?: any } 25 | | { name: 'Headers schema validation error'; status: 422; data: any } 26 | | { name: 'Headers schema validation error'; status: 422; data: any } 27 | | { name: 'Query schema validation error'; status: 422; data: any } 28 | | { name: 'Axios Error'; status: 400; data: any } 29 | | { name: 'Internal Server Error'; status: 500 }; 30 | } 31 | > => { 32 | const res = await Fetch({ 33 | ...data, 34 | method: 'POST', 35 | path: '/project/createProjectFromCLI', 36 | }); 37 | 38 | if (res.error && typeof res.error.status === 'number') 39 | return { data: null, error: res.error }; 40 | else return { data: res, error: null }; 41 | }; 42 | -------------------------------------------------------------------------------- /packages/bridge-studio/sdk/project/get.ts: -------------------------------------------------------------------------------- 1 | import Fetch from '../bridgeFetchMethod'; 2 | 3 | export default async (data: { 4 | query: { _id: string }; 5 | headers: { token: string }; 6 | }): Promise< 7 | | { 8 | data: { 9 | createdAt: Date; 10 | name: string; 11 | owner: string; 12 | serverUrl: string; 13 | provider: 'github' | 'gitlab' | 'cli'; 14 | subscribedLanguages: Array<'python' | 'javascript' | 'typescript'>; 15 | lastCompilationSuccess: false | true; 16 | pendingCompilation: false | true; 17 | githubRepo?: { 18 | id: number; 19 | name: string; 20 | owner: string; 21 | full_name: string; 22 | visibility: 'private' | 'public'; 23 | createdAt: Date; 24 | }; 25 | } & { _id: string }; 26 | error: null; 27 | } 28 | | { 29 | data: null; 30 | error: 31 | | { name: 'Wrong permission'; data?: any; status: 401 } 32 | | { name: 'Expired token'; data?: any; status: 401 } 33 | | { name: 'Invalid token'; data?: any; status: 401 } 34 | | { status: 404; name: 'Document not found' } 35 | | { name: 'Headers schema validation error'; status: 422; data: any } 36 | | { name: 'Headers schema validation error'; status: 422; data: any } 37 | | { name: 'Query schema validation error'; status: 422; data: any } 38 | | { name: 'Axios Error'; status: 400; data: any } 39 | | { name: 'Internal Server Error'; status: 500 }; 40 | } 41 | > => { 42 | const res = await Fetch({ ...data, method: 'POST', path: '/project/get' }); 43 | 44 | if (res.error && typeof res.error.status === 'number') 45 | return { data: null, error: res.error }; 46 | else return { data: res, error: null }; 47 | }; 48 | -------------------------------------------------------------------------------- /packages/bridge-studio/sdk/project/getFromCLI.ts: -------------------------------------------------------------------------------- 1 | import Fetch from '../bridgeFetchMethod'; 2 | 3 | export default async (data: { 4 | query: { _id: string }; 5 | headers: { token: string }; 6 | }): Promise< 7 | | { 8 | data: { 9 | createdAt: Date; 10 | name: string; 11 | owner: string; 12 | serverUrl: string; 13 | provider: 'github' | 'gitlab' | 'cli'; 14 | subscribedLanguages: Array<'python' | 'javascript' | 'typescript'>; 15 | lastCompilationSuccess: false | true; 16 | pendingCompilation: false | true; 17 | githubRepo?: { 18 | id: number; 19 | name: string; 20 | owner: string; 21 | full_name: string; 22 | visibility: 'private' | 'public'; 23 | createdAt: Date; 24 | }; 25 | } & { _id: string }; 26 | error: null; 27 | } 28 | | { 29 | data: null; 30 | error: 31 | | { name: 'Wrong permission'; data?: any; status: 401 } 32 | | { name: 'Invalid token'; data?: any; status: 401 } 33 | | { status: 404; name: 'Document not found' } 34 | | { name: 'Headers schema validation error'; status: 422; data: any } 35 | | { name: 'Headers schema validation error'; status: 422; data: any } 36 | | { name: 'Query schema validation error'; status: 422; data: any } 37 | | { name: 'Axios Error'; status: 400; data: any } 38 | | { name: 'Internal Server Error'; status: 500 }; 39 | } 40 | > => { 41 | const res = await Fetch({ 42 | ...data, 43 | method: 'POST', 44 | path: '/project/getFromCLI', 45 | }); 46 | 47 | if (res.error && typeof res.error.status === 'number') 48 | return { data: null, error: res.error }; 49 | else return { data: res, error: null }; 50 | }; 51 | -------------------------------------------------------------------------------- /packages/bridge-studio/sdk/project/getMine.ts: -------------------------------------------------------------------------------- 1 | import Fetch from '../bridgeFetchMethod'; 2 | 3 | export default async (data: { 4 | query: { limit?: string; skip?: string }; 5 | headers: { token: string }; 6 | }): Promise< 7 | | { 8 | data: { 9 | data: Array< 10 | { 11 | createdAt: Date; 12 | name: string; 13 | owner: string; 14 | serverUrl: string; 15 | provider: 'github' | 'gitlab' | 'cli'; 16 | subscribedLanguages: Array<'python' | 'javascript' | 'typescript'>; 17 | lastCompilationSuccess: false | true; 18 | pendingCompilation: false | true; 19 | githubRepo?: { 20 | id: number; 21 | name: string; 22 | owner: string; 23 | full_name: string; 24 | visibility: 'private' | 'public'; 25 | createdAt: Date; 26 | }; 27 | } & { _id: string } 28 | >; 29 | total: number; 30 | skip: number; 31 | limit: number; 32 | }; 33 | error: null; 34 | } 35 | | { 36 | data: null; 37 | error: 38 | | { name: 'Wrong permission'; data?: any; status: 401 } 39 | | { name: 'Expired token'; data?: any; status: 401 } 40 | | { name: 'Invalid token'; data?: any; status: 401 } 41 | | { name: 'Headers schema validation error'; status: 422; data: any } 42 | | { name: 'Query schema validation error'; status: 422; data: any } 43 | | { name: 'Headers schema validation error'; status: 422; data: any } 44 | | { name: 'Query schema validation error'; status: 422; data: any } 45 | | { name: 'Axios Error'; status: 400; data: any } 46 | | { name: 'Internal Server Error'; status: 500 }; 47 | } 48 | > => { 49 | const res = await Fetch({ 50 | ...data, 51 | method: 'POST', 52 | path: '/project/getMine', 53 | }); 54 | 55 | if (res.error && typeof res.error.status === 'number') 56 | return { data: null, error: res.error }; 57 | else return { data: res, error: null }; 58 | }; 59 | -------------------------------------------------------------------------------- /packages/bridge-studio/sdk/project/reCompile.ts: -------------------------------------------------------------------------------- 1 | import Fetch from '../bridgeFetchMethod'; 2 | 3 | export default async (data: { 4 | query: { projectId: string }; 5 | headers: { token: string }; 6 | }): Promise< 7 | | { 8 | data: { 9 | createdAt: Date; 10 | name: string; 11 | owner: string; 12 | serverUrl: string; 13 | provider: 'github' | 'gitlab' | 'cli'; 14 | subscribedLanguages: Array<'python' | 'javascript' | 'typescript'>; 15 | lastCompilationSuccess: false | true; 16 | pendingCompilation: false | true; 17 | githubRepo?: { 18 | id: number; 19 | name: string; 20 | owner: string; 21 | full_name: string; 22 | visibility: 'private' | 'public'; 23 | createdAt: Date; 24 | }; 25 | } & { _id: string }; 26 | error: null; 27 | } 28 | | { 29 | data: null; 30 | error: 31 | | { name: 'Wrong permission'; data?: any; status: 401 } 32 | | { name: 'Expired token'; data?: any; status: 401 } 33 | | { name: 'Invalid token'; data?: any; status: 401 } 34 | | { status: 404; name: 'Document not found' } 35 | | { name: 'Headers schema validation error'; status: 422; data: any } 36 | | { name: 'User not connected with github'; data?: any; status: 401 } 37 | | { 38 | name: 'Undefined in result of github.getRepoZip'; 39 | data?: any; 40 | status: 500; 41 | } 42 | | { 43 | name: 'Project not connected to github or gitlab'; 44 | data?: any; 45 | status: 401; 46 | } 47 | | { name: 'Headers schema validation error'; status: 422; data: any } 48 | | { name: 'Query schema validation error'; status: 422; data: any } 49 | | { name: 'Axios Error'; status: 400; data: any } 50 | | { name: 'Internal Server Error'; status: 500 }; 51 | } 52 | > => { 53 | const res = await Fetch({ 54 | ...data, 55 | method: 'POST', 56 | path: '/project/reCompile', 57 | }); 58 | 59 | if (res.error && typeof res.error.status === 'number') 60 | return { data: null, error: res.error }; 61 | else return { data: res, error: null }; 62 | }; 63 | -------------------------------------------------------------------------------- /packages/bridge-studio/sdk/project/update.ts: -------------------------------------------------------------------------------- 1 | import Fetch from '../bridgeFetchMethod'; 2 | 3 | export default async (data: { 4 | body: { name: string; serverUrl: string }; 5 | query: { _id: string }; 6 | headers: { token: string }; 7 | }): Promise< 8 | | { 9 | data: { 10 | createdAt: Date; 11 | name: string; 12 | owner: string; 13 | serverUrl: string; 14 | provider: 'github' | 'gitlab' | 'cli'; 15 | subscribedLanguages: Array<'python' | 'javascript' | 'typescript'>; 16 | lastCompilationSuccess: false | true; 17 | pendingCompilation: false | true; 18 | githubRepo?: { 19 | id: number; 20 | name: string; 21 | owner: string; 22 | full_name: string; 23 | visibility: 'private' | 'public'; 24 | createdAt: Date; 25 | }; 26 | } & { _id: string }; 27 | error: null; 28 | } 29 | | { 30 | data: null; 31 | error: 32 | | { name: 'Wrong permission'; data?: any; status: 401 } 33 | | { name: 'Expired token'; data?: any; status: 401 } 34 | | { name: 'Invalid token'; data?: any; status: 401 } 35 | | { name: 'Headers schema validation error'; status: 422; data: any } 36 | | { status: 404; name: 'Document not found' } 37 | | { name: 'Body schema validation error'; status: 422; data: any } 38 | | { name: 'Headers schema validation error'; status: 422; data: any } 39 | | { name: 'Query schema validation error'; status: 422; data: any } 40 | | { name: 'Axios Error'; status: 400; data: any } 41 | | { name: 'Internal Server Error'; status: 500 }; 42 | } 43 | > => { 44 | const res = await Fetch({ ...data, method: 'POST', path: '/project/update' }); 45 | 46 | if (res.error && typeof res.error.status === 'number') 47 | return { data: null, error: res.error }; 48 | else return { data: res, error: null }; 49 | }; 50 | -------------------------------------------------------------------------------- /packages/bridge-studio/sdk/user/deleteMyAccount.ts: -------------------------------------------------------------------------------- 1 | import Fetch from '../bridgeFetchMethod'; 2 | 3 | export default async (data: { 4 | body: { password: string }; 5 | headers: { refreshtoken: string }; 6 | }): Promise< 7 | | { data: any; error: null } 8 | | { 9 | data: null; 10 | error: 11 | | { name: 'Axios Error'; status: 400; data: any } 12 | | { name: 'Internal Server Error'; status: 500 }; 13 | } 14 | > => { 15 | const res = await Fetch({ 16 | ...data, 17 | method: 'POST', 18 | path: '/user/deleteMyAccount', 19 | }); 20 | 21 | if (res.error && typeof res.error.status === 'number') 22 | return { data: null, error: res.error }; 23 | else return { data: res, error: null }; 24 | }; 25 | -------------------------------------------------------------------------------- /packages/bridge-studio/sdk/user/getMe.ts: -------------------------------------------------------------------------------- 1 | import Fetch from '../bridgeFetchMethod'; 2 | 3 | export default async (data: { 4 | headers: { token: string }; 5 | }): Promise< 6 | | { 7 | data: { 8 | github?: { 9 | id: string; 10 | accessToken: string; 11 | refreshToken: string; 12 | lastUpdate: Date; 13 | username: string; 14 | profileUrl: string; 15 | accountCreation: Date; 16 | public_repos_nbr: number; 17 | followers: number; 18 | }; 19 | createdAt: Date; 20 | lastConnexionDate: Date; 21 | name: string; 22 | username: string; 23 | email: string; 24 | language: 'english' | 'french'; 25 | twitter_username?: string; 26 | avatar?: string; 27 | } & { _id: string }; 28 | error: null; 29 | } 30 | | { 31 | data: null; 32 | error: 33 | | { name: 'Wrong permission'; data?: any; status: 401 } 34 | | { name: 'Expired token'; data?: any; status: 401 } 35 | | { name: 'Invalid token'; data?: any; status: 401 } 36 | | { status: 404; name: 'Document not found' } 37 | | { name: 'Headers schema validation error'; status: 422; data: any } 38 | | { name: 'Headers schema validation error'; status: 422; data: any } 39 | | { name: 'Axios Error'; status: 400; data: any } 40 | | { name: 'Internal Server Error'; status: 500 }; 41 | } 42 | > => { 43 | const res = await Fetch({ ...data, method: 'POST', path: '/user/getMe' }); 44 | 45 | if (res.error && typeof res.error.status === 'number') 46 | return { data: null, error: res.error }; 47 | else return { data: res, error: null }; 48 | }; 49 | -------------------------------------------------------------------------------- /packages/bridge-studio/sdk/user/isEmailInUse.ts: -------------------------------------------------------------------------------- 1 | import Fetch from '../bridgeFetchMethod'; 2 | 3 | export default async (data: { 4 | query: { email: string }; 5 | }): Promise< 6 | | { data: { exists: false | true }; error: null } 7 | | { 8 | data: null; 9 | error: 10 | | { name: 'Query schema validation error'; status: 422; data: any } 11 | | { name: 'Axios Error'; status: 400; data: any } 12 | | { name: 'Internal Server Error'; status: 500 }; 13 | } 14 | > => { 15 | const res = await Fetch({ 16 | ...data, 17 | method: 'POST', 18 | path: '/user/isEmailInUse', 19 | }); 20 | 21 | if (res.error && typeof res.error.status === 'number') 22 | return { data: null, error: res.error }; 23 | else return { data: res, error: null }; 24 | }; 25 | -------------------------------------------------------------------------------- /packages/bridge-studio/sdk/user/logout.ts: -------------------------------------------------------------------------------- 1 | import Fetch from '../bridgeFetchMethod'; 2 | 3 | export default async (data: { 4 | body: { all: false | true }; 5 | headers: { refreshtoken: string }; 6 | }): Promise< 7 | | { data: any; error: null } 8 | | { 9 | data: null; 10 | error: 11 | | { name: 'Axios Error'; status: 400; data: any } 12 | | { name: 'Internal Server Error'; status: 500 }; 13 | } 14 | > => { 15 | const res = await Fetch({ ...data, method: 'POST', path: '/user/logout' }); 16 | 17 | if (res.error && typeof res.error.status === 'number') 18 | return { data: null, error: res.error }; 19 | else return { data: res, error: null }; 20 | }; 21 | -------------------------------------------------------------------------------- /packages/bridge-studio/sdk/user/refreshToken.ts: -------------------------------------------------------------------------------- 1 | import Fetch from '../bridgeFetchMethod'; 2 | 3 | export default async (data: { 4 | headers: { refreshtoken: string }; 5 | }): Promise< 6 | | { data: string; error: null } 7 | | { 8 | data: null; 9 | error: 10 | | { name: 'Wrong permission'; data?: any; status: 401 } 11 | | { name: 'Invalid token'; data?: any; status: 401 } 12 | | { name: 'Wrong refreshToken'; data?: any; status: 401 } 13 | | { name: 'Headers schema validation error'; status: 422; data: any } 14 | | { name: 'Axios Error'; status: 400; data: any } 15 | | { name: 'Internal Server Error'; status: 500 }; 16 | } 17 | > => { 18 | const res = await Fetch({ 19 | ...data, 20 | method: 'POST', 21 | path: '/user/refreshToken', 22 | }); 23 | 24 | if (res.error && typeof res.error.status === 'number') 25 | return { data: null, error: res.error }; 26 | else return { data: res, error: null }; 27 | }; 28 | -------------------------------------------------------------------------------- /packages/bridge-studio/sdk/user/signinWithPassword.ts: -------------------------------------------------------------------------------- 1 | import Fetch from '../bridgeFetchMethod'; 2 | 3 | export default async (data: { 4 | body: { email: string; password: string }; 5 | }): Promise< 6 | | { data: any; error: null } 7 | | { 8 | data: null; 9 | error: 10 | | { name: 'Axios Error'; status: 400; data: any } 11 | | { name: 'Internal Server Error'; status: 500 }; 12 | } 13 | > => { 14 | const res = await Fetch({ 15 | ...data, 16 | method: 'POST', 17 | path: '/user/signinWithPassword', 18 | }); 19 | 20 | if (res.error && typeof res.error.status === 'number') 21 | return { data: null, error: res.error }; 22 | else return { data: res, error: null }; 23 | }; 24 | -------------------------------------------------------------------------------- /packages/bridge-studio/sdk/user/updateForgottenPassword.ts: -------------------------------------------------------------------------------- 1 | import Fetch from '../bridgeFetchMethod'; 2 | 3 | export default async (data: { 4 | body: { email: string; password: string }; 5 | query: { code: string }; 6 | }): Promise< 7 | | { data: { refreshToken: string; accessToken: string }; error: null } 8 | | { 9 | data: null; 10 | error: 11 | | { name: 'Expired code'; data?: any; status: 401 } 12 | | { name: 'Invalid code'; data?: any; status: 401 } 13 | | { 14 | name: 'user not found' | 'admin not found'; 15 | data?: any; 16 | status: 404; 17 | } 18 | | { name: 'Body schema validation error'; status: 422; data: any } 19 | | { name: 'Query schema validation error'; status: 422; data: any } 20 | | { name: 'Axios Error'; status: 400; data: any } 21 | | { name: 'Internal Server Error'; status: 500 }; 22 | } 23 | > => { 24 | const res = await Fetch({ 25 | ...data, 26 | method: 'POST', 27 | path: '/user/updateForgottenPassword', 28 | }); 29 | 30 | if (res.error && typeof res.error.status === 'number') 31 | return { data: null, error: res.error }; 32 | else return { data: res, error: null }; 33 | }; 34 | -------------------------------------------------------------------------------- /packages/bridge-studio/sdk/user/updateMyPassword.ts: -------------------------------------------------------------------------------- 1 | import Fetch from '../bridgeFetchMethod'; 2 | 3 | export default async (data: { 4 | body: { oldPassword: string; newPassword: string }; 5 | headers: { refreshtoken: string }; 6 | }): Promise< 7 | | { data: any; error: null } 8 | | { 9 | data: null; 10 | error: 11 | | { name: 'Axios Error'; status: 400; data: any } 12 | | { name: 'Internal Server Error'; status: 500 }; 13 | } 14 | > => { 15 | const res = await Fetch({ 16 | ...data, 17 | method: 'POST', 18 | path: '/user/updateMyPassword', 19 | }); 20 | 21 | if (res.error && typeof res.error.status === 'number') 22 | return { data: null, error: res.error }; 23 | else return { data: res, error: null }; 24 | }; 25 | -------------------------------------------------------------------------------- /packages/bridge/.gitignore: -------------------------------------------------------------------------------- 1 | /dist 2 | /node_modules 3 | pnpm-lock.yaml 4 | 5 | # Logs 6 | logs 7 | *.log 8 | npm-debug.log* 9 | pnpm-debug.log* 10 | yarn-debug.log* 11 | yarn-error.log* 12 | lerna-debug.log* 13 | 14 | # OS 15 | .DS_Store 16 | 17 | # Tests 18 | /coverage 19 | /.nyc_output 20 | 21 | # IDEs and editors 22 | /.idea 23 | .project 24 | .classpath 25 | .c9/ 26 | *.launch 27 | .settings/ 28 | *.sublime-workspace 29 | 30 | # IDE - VSCode 31 | .vscode/* 32 | !.vscode/settings.json 33 | !.vscode/tasks.json 34 | !.vscode/launch.json 35 | !.vscode/extensions.json -------------------------------------------------------------------------------- /packages/bridge/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bridge", 3 | "version": "2.0.73", 4 | "description": "Bridge is a Typescript Node.js framework that provides an easy and scalable way to create REST APIs while generating the client code.", 5 | "author": "Bridge Team ", 6 | "license": "MIT", 7 | "main": "dist/source/index.js", 8 | "types": "dist/source/index.d.ts", 9 | "bugs": "https://github.com/bridge-codes/bridge/issues", 10 | "homepage": "https://bridge.codes", 11 | "files": [ 12 | "/dist" 13 | ], 14 | "scripts": { 15 | "build": "tsc", 16 | "publish-minor": "tsc && npm version patch && npm publish" 17 | }, 18 | "repository": { 19 | "type": "git", 20 | "url": "git+https://github.com/bridge-codes/bridge.git", 21 | "directory": "packages/bridge" 22 | }, 23 | "keywords": [ 24 | "express", 25 | "http", 26 | "api", 27 | "sdk", 28 | "typescript", 29 | "generation", 30 | "type", 31 | "node", 32 | "framework", 33 | "rest" 34 | ], 35 | "devDependencies": { 36 | "@types/adm-zip": "^0.5.0", 37 | "@types/express": "^4.17.17", 38 | "@types/node": "^20.2.5", 39 | "nodemon": "^2.0.22", 40 | "ts-node": "^10.9.1", 41 | "typescript": "^5.1.3" 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /packages/bridge/source/bridge.ts: -------------------------------------------------------------------------------- 1 | import { BridgeRoutes } from './routes'; 2 | import { ErrorHandler } from './error'; 3 | import { RoutesToBridgeType } from './routes'; 4 | import type * as express from 'express'; 5 | import { createHttpHandler } from './server/adapters/node-http'; 6 | import http from 'http'; 7 | 8 | class Bridge { 9 | public bridgeType!: RoutesToBridgeType; 10 | 11 | constructor( 12 | public routes: Routes, 13 | private config: { errorHandler?: ErrorHandler; formidable?: any; logs?: boolean }, 14 | ) {} 15 | 16 | public expressMiddleware = (): express.Handler => createHttpHandler(this.routes, this.config); 17 | 18 | public HTTPServer = () => http.createServer(createHttpHandler(this.routes, this.config)); 19 | } 20 | 21 | export const initBridge = ({ 22 | routes, 23 | errorHandler, 24 | formidable, 25 | logs, 26 | }: { 27 | routes: Routes; 28 | formidable?: any; 29 | errorHandler?: ErrorHandler; 30 | logs?: boolean; 31 | }): Bridge => new Bridge(routes, { formidable, errorHandler, logs }); 32 | -------------------------------------------------------------------------------- /packages/bridge/source/core/handler.ts: -------------------------------------------------------------------------------- 1 | type AnyObject = Record; 2 | 3 | /** 4 | * This is the real data received by the client 5 | */ 6 | export interface HandlerParams { 7 | body: AnyObject; 8 | query: AnyObject; 9 | headers: AnyObject; 10 | files: AnyObject; 11 | middlewares: AnyObject; 12 | } 13 | 14 | export interface Handler { 15 | setNext(handler: Handler): Handler; 16 | 17 | handle: (p: HandlerParams) => any; 18 | 19 | getNextHandler: () => Handler | undefined; 20 | } 21 | 22 | export abstract class AbstractHandler implements Handler { 23 | protected nextHandler: Handler | undefined; 24 | 25 | public setNext(handler: Handler): Handler { 26 | this.nextHandler = handler; 27 | 28 | return handler; 29 | } 30 | 31 | public async handle(data: HandlerParams) { 32 | if (this.nextHandler) return this.nextHandler.handle(data); 33 | return data; 34 | } 35 | 36 | public getNextHandler = () => this.nextHandler; 37 | } 38 | 39 | export class FirstHandler extends AbstractHandler {} 40 | -------------------------------------------------------------------------------- /packages/bridge/source/core/handlers/bridge.ts: -------------------------------------------------------------------------------- 1 | import { AbstractHandler, Handler, FirstHandler } from '../handler'; 2 | import { MiddelwaresHandler } from './middleware'; 3 | import { Resolver } from './resolver'; 4 | import { DataParser, DataValidator } from './data-validator'; 5 | import { FilesConfig, FileValidator } from './file-validator'; 6 | 7 | export interface BridgeHandlerDocumentation { 8 | title?: string; 9 | text?: string; 10 | } 11 | 12 | export class BridgeHandler< 13 | Resolve extends (...args: any[]) => any = any, 14 | Middlewares extends ReadonlyArray = any, 15 | > extends AbstractHandler { 16 | private handler: Handler; 17 | public resolve: Resolve; 18 | 19 | public constructor( 20 | public config: { 21 | resolve: Resolve; 22 | bodySchema?: DataParser; 23 | querySchema?: DataParser; 24 | headersSchema?: DataParser; 25 | filesConfig?: FilesConfig; 26 | middlewares?: Middlewares; 27 | // documentation?: BridgeHandlerDocumentation; // NEED TO INFER FROM DATA TO DOCUMENTATE PARAMS 28 | }, 29 | ) { 30 | super(); 31 | 32 | this.resolve = config.resolve; 33 | 34 | if (config.bodySchema && config.filesConfig) 35 | throw Error("You can't get a JSON body and files in the same endpoint."); 36 | 37 | const firstHandler: Handler = new FirstHandler(); 38 | 39 | let handler = firstHandler; 40 | 41 | if (config.bodySchema) handler = handler.setNext(new DataValidator(config.bodySchema, 'body')); 42 | if (config.querySchema) 43 | handler = handler.setNext(new DataValidator(config.querySchema, 'query')); 44 | if (config.headersSchema) 45 | handler = handler.setNext(new DataValidator(config.headersSchema, 'headers')); 46 | if (config.filesConfig) handler = handler.setNext(new FileValidator(config.filesConfig)); 47 | 48 | if (config.middlewares) handler = handler.setNext(new MiddelwaresHandler(config.middlewares)); 49 | 50 | handler = handler.setNext(new Resolver(config.resolve)); 51 | 52 | this.handler = firstHandler; 53 | } 54 | 55 | /** 56 | * 57 | * If the middleware returns an error, we stop the chain and return it 58 | * otherwise we add the result in the mid data of the next handler 59 | * If there is no next handler, we return the last result 60 | */ 61 | public handle: Handler['handle'] = async (data) => { 62 | const res = await this.handler.handle(data); 63 | 64 | if (res && res.error) return res; 65 | 66 | data.middlewares = { ...res, ...data.middlewares }; 67 | 68 | if (this.nextHandler) return this.nextHandler.handle(data); 69 | 70 | return res; 71 | }; 72 | } 73 | 74 | export const isBridgeHandler = (data: any): data is BridgeHandler => 75 | data && 76 | typeof data === 'object' && 77 | typeof data.handler === 'object' && 78 | typeof data.handle === 'function' && 79 | typeof data.config === 'object' && 80 | typeof data.config.resolve === 'function'; 81 | -------------------------------------------------------------------------------- /packages/bridge/source/core/handlers/data-validator.ts: -------------------------------------------------------------------------------- 1 | // This code was strongly inspired from @trpc/server 2 | 3 | import { httpError, StatusCode } from '../../error'; 4 | import { AbstractHandler, Handler } from '../handler'; 5 | 6 | type YupParser = { validateSync: (input: unknown) => T }; 7 | type SuperstructParser = { create: (input: unknown) => T }; 8 | type ZodParser = { parse: (input: any) => T }; 9 | 10 | export type DataParser = YupParser | ZodParser | SuperstructParser; 11 | 12 | export type InferDataParser = Val extends DataParser 13 | ? Output 14 | : any; 15 | 16 | export const isZodParser = (parser: any): parser is ZodParser => 17 | typeof parser?.safeParse === 'function'; 18 | 19 | /** 20 | * This handler is used to validate data from the body, the 21 | */ 22 | export class DataValidator extends AbstractHandler { 23 | public output!: Output; 24 | 25 | constructor( 26 | private parser: DataParser, 27 | private dataToValidate: 'body' | 'query' | 'headers', 28 | ) { 29 | super(); 30 | } 31 | 32 | private isYupParser = (parser: any): parser is YupParser => 33 | typeof parser.validateSync === 'function'; 34 | 35 | private isSuperstructParser = (parser: any): parser is SuperstructParser => 36 | typeof parser.create === 'function'; 37 | 38 | private isZodParser = (parser: any): parser is ZodParser => 39 | typeof parser.safeParse === 'function'; 40 | 41 | public handle: Handler['handle'] = async (data) => { 42 | try { 43 | if (this.isYupParser(this.parser)) this.parser.validateSync(data[this.dataToValidate]); 44 | else if (this.isZodParser(this.parser)) this.parser.parse(data[this.dataToValidate]); 45 | else if (this.isSuperstructParser(this.parser)) this.parser.create(data[this.dataToValidate]); 46 | 47 | return super.handle(data); 48 | } catch (error) { 49 | switch (this.dataToValidate) { 50 | case 'body': 51 | return httpError(StatusCode.BAD_REQUEST, `Body schema validation error`, error); 52 | case 'query': 53 | return httpError(StatusCode.BAD_REQUEST, `Query schema validation error`, error); 54 | case 'headers': 55 | return httpError(StatusCode.BAD_REQUEST, `Headers schema validation error`, error); 56 | } 57 | } 58 | }; 59 | } 60 | -------------------------------------------------------------------------------- /packages/bridge/source/core/handlers/file-validator.ts: -------------------------------------------------------------------------------- 1 | import { AbstractHandler, Handler } from '../handler'; 2 | import { httpError, StatusCode } from '../../error'; 3 | 4 | export type FilesConfig = 'any' | ReadonlyArray; 5 | 6 | export class FileValidator extends AbstractHandler { 7 | constructor(private config: FilesConfig) { 8 | super(); 9 | } 10 | 11 | public handle: Handler['handle'] = async (data) => { 12 | const missingFiles: string[] = []; 13 | 14 | // req.body contains the files 15 | if (this.config !== 'any') 16 | for (const name of this.config) if (!data.files[name]) missingFiles.push(name); 17 | 18 | if (missingFiles.length > 0) 19 | return httpError(StatusCode.BAD_REQUEST, 'Files schema validation error', { 20 | missingFiles, 21 | }); 22 | 23 | return super.handle(data); 24 | }; 25 | } 26 | -------------------------------------------------------------------------------- /packages/bridge/source/core/handlers/index.ts: -------------------------------------------------------------------------------- 1 | export * from './bridge'; 2 | export * from './data-validator'; 3 | export * from './file-validator'; 4 | export * from './middleware'; 5 | export * from './resolver'; 6 | -------------------------------------------------------------------------------- /packages/bridge/source/core/handlers/middleware.ts: -------------------------------------------------------------------------------- 1 | import { Handler, AbstractHandler } from '../handler'; 2 | import { isBridgeError } from '../../error'; 3 | 4 | export class MiddelwaresHandler extends AbstractHandler { 5 | constructor(private handlers: ReadonlyArray) { 6 | super(); 7 | } 8 | 9 | public handle: Handler['handle'] = async (data) => { 10 | // All the middlewares are executed simultaneously 11 | const results = await Promise.all(this.handlers.map(async (handler) => handler.handle(data))); 12 | 13 | // If one middleware returns an error, we directly return the error to the client 14 | for (const result of results) if (isBridgeError(result)) return result; 15 | 16 | return super.handle({ ...data, middlewares: Object.assign({}, ...results) }); 17 | }; 18 | } 19 | -------------------------------------------------------------------------------- /packages/bridge/source/core/handlers/resolver.ts: -------------------------------------------------------------------------------- 1 | import { AbstractHandler, Handler, HandlerParams } from '../handler'; 2 | 3 | export class Resolver extends AbstractHandler { 4 | public isResolver = true; 5 | 6 | constructor(private resolve: (p: HandlerParams) => any) { 7 | super(); 8 | } 9 | 10 | public handle: Handler['handle'] = async (data) => this.resolve(data); 11 | } 12 | -------------------------------------------------------------------------------- /packages/bridge/source/core/index.ts: -------------------------------------------------------------------------------- 1 | import { CreateHandler } from './types'; 2 | import { BridgeHandler } from './handlers'; 3 | 4 | // This is a BridgeHandler 5 | export const handler: CreateHandler = (routeParams) => { 6 | return new BridgeHandler({ 7 | bodySchema: routeParams.body, 8 | querySchema: routeParams.query, 9 | headersSchema: routeParams.headers, 10 | filesConfig: routeParams.files, 11 | middlewares: routeParams.middlewares, 12 | resolve: routeParams.resolve, 13 | }); 14 | }; 15 | 16 | export * from './types'; 17 | export * from './handlers'; 18 | export * from './handler'; 19 | -------------------------------------------------------------------------------- /packages/bridge/source/error/index.ts: -------------------------------------------------------------------------------- 1 | import { ErrorStatus } from './status'; 2 | 3 | interface Error { 4 | name: string; 5 | status: number; 6 | data?: any; 7 | } 8 | 9 | export const isBridgeError = (object: any): object is { error: Error } => 10 | typeof object === 'object' && 11 | typeof object.error === 'object' && 12 | typeof object.error.name === 'string' && 13 | typeof object.error.status === 'number'; 14 | 15 | export const httpError = ( 16 | status: Status, 17 | name: Name, 18 | data?: Data, 19 | ): { error: { name: Name; data?: any; status: Status } } => { 20 | return { error: { status, name, data } }; 21 | }; 22 | 23 | export * from './listener'; 24 | export * from './status'; 25 | -------------------------------------------------------------------------------- /packages/bridge/source/error/listener.ts: -------------------------------------------------------------------------------- 1 | export type ErrorHandler = (p: { 2 | error: { status: number; data?: any; name: string }; 3 | path: string; 4 | }) => void; 5 | 6 | type OnError = (p: T) => T; 7 | 8 | export const onError: OnError = (p) => p; 9 | -------------------------------------------------------------------------------- /packages/bridge/source/index.ts: -------------------------------------------------------------------------------- 1 | export { handler } from './core'; 2 | export { httpError, onError, StatusCode, isBridgeError as isError } from './error'; 3 | export { apply } from './utilities'; 4 | export { initBridge } from './bridge'; 5 | export { method } from './routes/method'; 6 | -------------------------------------------------------------------------------- /packages/bridge/source/routes/convert.ts: -------------------------------------------------------------------------------- 1 | import { BridgeRoutes, ServerRoutes, isBridgeRoutes } from './types'; 2 | import { isBridgeHandler } from '../core'; 3 | import { isBridgeMethod } from './method'; 4 | 5 | const serverRoutes: ServerRoutes = {}; 6 | 7 | export const convertBridgeRoutesToServerRoutes = ( 8 | routes: BridgeRoutes, 9 | prefix: string, 10 | hasFormidable: boolean, 11 | ): ServerRoutes => { 12 | for (const [key, value] of Object.entries(routes)) { 13 | if (!value) continue; 14 | else if (isBridgeMethod(value)) { 15 | serverRoutes[`${prefix}/${key}`] = value.methods; 16 | Object.values(value.methods).forEach((handler) => { 17 | if (handler.config.filesConfig && !hasFormidable) 18 | throw new Error( 19 | `You need to install formidable and to give it to Bridge in order to use files.`, 20 | ); 21 | }); 22 | } else if (isBridgeHandler(value)) { 23 | serverRoutes[`${prefix}/${key}`] = { POST: value }; 24 | if (value.config.filesConfig && !hasFormidable) 25 | throw new Error( 26 | `You need to install formidable and pass it to the initBridge function in order to use files. 27 | \nCheck the documentation: https://bridge.codes/docs/file \n`, 28 | ); 29 | } else if (isBridgeRoutes(value)) 30 | convertBridgeRoutesToServerRoutes(value, `${prefix}/${key}`, hasFormidable); 31 | } 32 | 33 | return serverRoutes; 34 | }; 35 | -------------------------------------------------------------------------------- /packages/bridge/source/routes/index.ts: -------------------------------------------------------------------------------- 1 | export * from './types'; 2 | export * from './convert'; 3 | -------------------------------------------------------------------------------- /packages/bridge/source/routes/method.ts: -------------------------------------------------------------------------------- 1 | import { BridgeHandler } from '../core'; 2 | 3 | export class BridgeMethod< 4 | BHGet extends BridgeHandler | null, 5 | BHPost extends BridgeHandler | null, 6 | BHPatch extends BridgeHandler | null, 7 | BHPut extends BridgeHandler | null, 8 | BHDelete extends BridgeHandler | null, 9 | > { 10 | public isBridgeMethodObject = true; 11 | public type!: { 12 | getBridgeMehthodSDK: BHGet; 13 | postBridgeMehthodSDK: BHPost; 14 | patchBridgeMehthodSDK: BHPatch; 15 | putBridgeMehthodSDK: BHPut; 16 | deleteBridgeMehthodSDK: BHDelete; 17 | }; 18 | 19 | constructor( 20 | public methods: { 21 | GET?: BHGet; 22 | POST?: BHPost; 23 | PATCH?: BHPatch; 24 | PUT?: BHPut; 25 | DELETE?: BHDelete; 26 | }, 27 | ) { 28 | if (methods.GET?.config.bodySchema) throw Error("You can't have a body with a GET endpoint"); 29 | } 30 | } 31 | 32 | export const isBridgeMethod = (obj: any): obj is BridgeMethod => 33 | typeof obj === 'object' && obj.isBridgeMethodObject === true; 34 | 35 | export const method = < 36 | BHGet extends BridgeHandler | null = null, 37 | BHPost extends BridgeHandler | null = null, 38 | BHPatch extends BridgeHandler | null = null, 39 | BHPut extends BridgeHandler | null = null, 40 | BHDelete extends BridgeHandler | null = null, 41 | >(p: { 42 | GET?: BHGet; 43 | POST?: BHPost; 44 | PATCH?: BHPatch; 45 | PUT?: BHPut; 46 | DELETE?: BHDelete; 47 | }) => new BridgeMethod(p); 48 | -------------------------------------------------------------------------------- /packages/bridge/source/routes/types.ts: -------------------------------------------------------------------------------- 1 | import { BridgeHandler } from '../core'; 2 | import { BridgeMethod } from './method'; 3 | 4 | export type Method = 'POST' | 'PATCH' | 'GET' | 'DELETE' | 'PUT'; 5 | 6 | export type BridgeRoutes = { 7 | [key: string]: Object | BridgeHandler | any; 8 | }; 9 | 10 | export const isBridgeRoutes = (data: any): data is BridgeRoutes => typeof data === 'object'; 11 | 12 | export interface ServerRoutes { 13 | [key: string]: { 14 | GET?: BridgeHandler; 15 | POST?: BridgeHandler; 16 | PATCH?: BridgeHandler; 17 | PUT?: BridgeHandler; 18 | DELETE?: BridgeHandler; 19 | }; 20 | } 21 | 22 | type BridgeHandlerReturnType = H extends BridgeHandler< 23 | infer ResolveFct, 24 | infer Middlewares 25 | > 26 | ? { 27 | body: Parameters[0]['body']; 28 | query: Parameters[0]['query']; 29 | headers: Parameters[0]['headers']; 30 | files: Parameters[0]['files']; 31 | return: 32 | | (ReturnType extends Promise 33 | ? RetWithoutPromise 34 | : ReturnType) 35 | | Extract['return'], { error: any }>; 36 | } 37 | : {}; 38 | 39 | export type RoutesToBridgeType = { 40 | [key in keyof T]: T[key] extends BridgeHandler 41 | ? BridgeHandlerReturnType 42 | : T[key] extends BridgeMethod 43 | ? { 44 | GET_BRIDGE_METHOD: T[key]['type']['getBridgeMehthodSDK'] extends null 45 | ? null 46 | : BridgeHandlerReturnType; 47 | POST_BRIDGE_METHOD: T[key]['type']['postBridgeMehthodSDK'] extends null 48 | ? null 49 | : BridgeHandlerReturnType; 50 | PATCH_BRIDGE_METHOD: T[key]['type']['patchBridgeMehthodSDK'] extends null 51 | ? null 52 | : BridgeHandlerReturnType; 53 | PUT_BRIDGE_METHOD: T[key]['type']['putBridgeMehthodSDK'] extends null 54 | ? null 55 | : BridgeHandlerReturnType; 56 | DELETE_BRIDGE_METHOD: T[key]['type']['deleteBridgeMehthodSDK'] extends null 57 | ? null 58 | : BridgeHandlerReturnType; 59 | } 60 | : T[key] extends BridgeRoutes 61 | ? RoutesToBridgeType 62 | : never; 63 | }; 64 | -------------------------------------------------------------------------------- /packages/bridge/source/server/adapters/express.ts: -------------------------------------------------------------------------------- 1 | import { BridgeRoutes } from '../../routes'; 2 | import { ErrorHandler } from '../../error'; 3 | import type * as express from 'express'; 4 | 5 | import { createHttpHandler } from './node-http'; 6 | 7 | export const createExpressMiddleware = ( 8 | routes: BridgeRoutes, 9 | config?: { errorHandler?: ErrorHandler; formidable?: any; logs?: boolean }, 10 | ): express.Handler => createHttpHandler(routes, config); 11 | -------------------------------------------------------------------------------- /packages/bridge/source/server/adapters/index.ts: -------------------------------------------------------------------------------- 1 | export * from './express'; 2 | export * from './node-http'; 3 | -------------------------------------------------------------------------------- /packages/bridge/source/server/adapters/serverless.ts: -------------------------------------------------------------------------------- 1 | // Coming soon 2 | -------------------------------------------------------------------------------- /packages/bridge/source/server/http-transormers/body-json.ts: -------------------------------------------------------------------------------- 1 | import { IncomingMessage } from 'http'; 2 | 3 | export const getJSONDataFromRequestStream = (request: IncomingMessage): Promise => 4 | new Promise((resolve) => { 5 | if (typeof (request as any).body === 'object') resolve((request as any).body); 6 | const chunks: any[] = []; 7 | request.on('data', (chunk) => { 8 | chunks.push(chunk); 9 | }); 10 | request.on('end', () => { 11 | try { 12 | resolve(JSON.parse(Buffer.concat(chunks).toString())); 13 | } catch (err) { 14 | resolve(JSON.parse('{}')); 15 | } 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /packages/bridge/source/server/http-transormers/formidable-async.ts: -------------------------------------------------------------------------------- 1 | import { FormidableFile } from '../../utilities'; 2 | import { IncomingMessage } from 'http'; 3 | 4 | export const formidableAsyncParseFiles = async ( 5 | req: IncomingMessage, 6 | formidable: any, 7 | ): Promise<{ 8 | [file: string]: FormidableFile | FormidableFile[]; 9 | }> => { 10 | let form = formidable({ multiples: true }); 11 | 12 | return new Promise((resolve, reject) => { 13 | form.parse(req, function (error: any, fields: any, files: any) { 14 | if (error) { 15 | reject(error); 16 | return; 17 | } 18 | 19 | resolve(files); 20 | }); 21 | }); 22 | }; 23 | -------------------------------------------------------------------------------- /packages/bridge/source/server/http-transormers/index.ts: -------------------------------------------------------------------------------- 1 | export * from './body-json'; 2 | export * from './query-json'; 3 | export * from './formidable-async'; 4 | -------------------------------------------------------------------------------- /packages/bridge/source/server/http-transormers/query-json.ts: -------------------------------------------------------------------------------- 1 | export const getJSONQueryFromURL = (queryUrl: string): Record => { 2 | const queryJSON: Record = {}; 3 | try { 4 | if (!queryUrl) return queryJSON; 5 | 6 | const queries = queryUrl.replace('?', '&').split('&'); 7 | 8 | queries.forEach((query: string) => { 9 | const [key, value] = query.split('='); 10 | queryJSON[key] = value; 11 | }); 12 | 13 | return queryJSON; 14 | } catch (err) { 15 | console.error(err); 16 | return queryJSON; 17 | } 18 | }; 19 | -------------------------------------------------------------------------------- /packages/bridge/source/server/index.ts: -------------------------------------------------------------------------------- 1 | export * from './adapters'; 2 | -------------------------------------------------------------------------------- /packages/bridge/source/utilities/apply.ts: -------------------------------------------------------------------------------- 1 | import { BridgeHandler } from '../core'; 2 | import { UnionToArray } from './types'; 3 | 4 | // transform an array into an array as const 5 | type Apply = Record>, T extends Array>( 6 | ...args: T 7 | ) => B extends string ? UnionToArray : UnionToArray; 8 | 9 | export const apply: Apply = (...args) => args as any; 10 | -------------------------------------------------------------------------------- /packages/bridge/source/utilities/dirExists.ts: -------------------------------------------------------------------------------- 1 | import fs from 'fs'; 2 | 3 | export function directoryExists(path: string): boolean { 4 | try { 5 | return fs.statSync(path).isDirectory(); 6 | } catch (err: any) { 7 | if (err.code === 'ENOENT') { 8 | // The directory does not exist 9 | return false; 10 | } else { 11 | // Something else went wrong 12 | console.error(err); 13 | return false; 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /packages/bridge/source/utilities/formidable.ts: -------------------------------------------------------------------------------- 1 | export interface FormidableFile { 2 | /** 3 | * The size of the uploaded file in bytes. If the file is still being uploaded (see `'fileBegin'` 4 | * event), this property says how many bytes of the file have been written to disk yet. 5 | */ 6 | size: number; 7 | 8 | /** 9 | * The path this file is being written to. You can modify this in the `'fileBegin'` event in case 10 | * you are unhappy with the way formidable generates a temporary path for your files. 11 | */ 12 | filepath: string; 13 | 14 | /** 15 | * The name this file had according to the uploading client. 16 | */ 17 | originalFilename: string | null; 18 | 19 | /** 20 | * Calculated based on options provided 21 | */ 22 | newFilename: string; 23 | 24 | /** 25 | * The mime type of this file, according to the uploading client. 26 | */ 27 | mimetype: string | null; 28 | 29 | /** 30 | * A Date object (or `null`) containing the time this file was last written to. Mostly here for 31 | * compatibility with the [W3C File API Draft](http://dev.w3.org/2006/webapi/FileAPI/). 32 | */ 33 | mtime?: Date | null | undefined; 34 | 35 | hashAlgorithm: false | 'sha1' | 'md5' | 'sha256'; 36 | 37 | /** 38 | * If `options.hashAlgorithm` calculation was set, you can read the hex digest out of this var 39 | * (at the end it will be a string). 40 | */ 41 | hash?: string | null; 42 | 43 | /** 44 | * This method returns a JSON-representation of the file, allowing you to JSON.stringify() the 45 | * file which is useful for logging and responding to requests. 46 | * 47 | * @link https://github.com/node-formidable/formidable#filetojson 48 | */ 49 | toJSON(): FileJSON; 50 | 51 | toString(): string; 52 | } 53 | 54 | interface FileJSON 55 | extends Pick { 56 | length: number; 57 | mimetype: string | null; 58 | mtime: Date | null; 59 | } 60 | -------------------------------------------------------------------------------- /packages/bridge/source/utilities/index.ts: -------------------------------------------------------------------------------- 1 | export * from './types'; 2 | export * from './apply'; 3 | export * from './formidable'; 4 | export * from './dirExists'; 5 | -------------------------------------------------------------------------------- /packages/bridge/source/utilities/types.ts: -------------------------------------------------------------------------------- 1 | import { BridgeHandler } from '../core/handlers'; 2 | 3 | export type Pretify = { 4 | [K in keyof T]: T[K]; 5 | } & {}; 6 | 7 | ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 8 | 9 | export type UnionToIntersection = (U extends any ? (k: U) => void : never) extends ( 10 | k: infer I, 11 | ) => void 12 | ? I 13 | : never; 14 | 15 | export type KeysWithValNotNever = keyof { [P in keyof T as T[P] extends never ? never : P]: P }; 16 | 17 | export type ExcludeNeverKeys = { [key in KeysWithValNotNever & keyof T]: T[key] }; 18 | 19 | export type ExcludeNeverKeysObj = { [key in KeysWithValNotEmptyObject & keyof T]: T[key] }; 20 | 21 | export type KeysWithValNotEmptyObject = keyof { 22 | [P in keyof T as keyof T[P] extends never ? never : P]: P; 23 | }; 24 | 25 | ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 26 | // MidsReturnsIntersection 27 | ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 28 | 29 | type Values = T[keyof T]; 30 | 31 | type Unfoo = T extends { foo: any } ? T['foo'] : never; 32 | 33 | type RemoveError = T extends { error: any } | Promise<{ error: any }> ? never : T; 34 | 35 | type NFooWithoutError> = { 36 | [K in keyof T]: T[K] extends BridgeHandler<(arg: any) => infer Output, any> 37 | ? { foo: RemoveError } 38 | : never; 39 | }; 40 | 41 | type NFooWithoutErrorParams> = { 42 | [K in keyof T]: T[K] extends BridgeHandler<(arg: infer Input) => any, any> 43 | ? { foo: RemoveError } 44 | : never; 45 | }; 46 | 47 | export type MidsReturnsIntersection> = Unfoo< 48 | UnionToIntersection>> 49 | >; 50 | 51 | export type MidsParams> = Unfoo< 52 | UnionToIntersection>> 53 | >; 54 | 55 | ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 56 | // APPLY --> Take an array and make it as const 57 | ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 58 | 59 | type UnionToOvlds = UnionToIntersection void : never>; 60 | 61 | type PopUnion = UnionToOvlds extends (a: infer A) => void ? A : never; 62 | type IsUnion = [T] extends [UnionToIntersection] ? false : true; 63 | export type UnionToArray = IsUnion extends true 64 | ? UnionToArray>, [PopUnion, ...A]> 65 | : [T, ...A]; 66 | -------------------------------------------------------------------------------- /packages/bridge/test.ts: -------------------------------------------------------------------------------- 1 | // import { handler, apply, httpError } from './source'; 2 | // import { BridgeHandler } from './source/core'; 3 | // import { z } from 'zod'; 4 | 5 | // const middleware = handler({ 6 | // resolve: () => ({ status: 200, res: 'Hello, world!' }), 7 | // }); 8 | 9 | // const middleware2 = handler({ 10 | // body: z.object({ last: z.string() }), 11 | // resolve: ({ body }) => { 12 | // if (body.last === 'last') return httpError(400, 'last is not allowed'); 13 | // return { brooo: 'brooo' }; 14 | // }, 15 | // }); 16 | 17 | // const query = handler({ 18 | // middlewares: [middleware, middleware2] as const, 19 | // body: z.object({ name: z.string() }), 20 | // resolve: (data) => {}, 21 | // }); 22 | 23 | // type TEST = readonly [ 24 | // BridgeHandler< 25 | // () => { 26 | // brooo: string; 27 | // }, 28 | // never 29 | // >, 30 | // BridgeHandler< 31 | // () => { 32 | // status: number; 33 | // body: string; 34 | // }, 35 | // never 36 | // >, 37 | // ]; 38 | 39 | // export type UnionToIntersection = (U extends any ? (k: U) => void : never) extends ( 40 | // k: infer I, 41 | // ) => void 42 | // ? I 43 | // : never; 44 | 45 | // // Generic type to get the intersection of return types of an array of BridgeHandler 46 | // type UnionOfResolveReturnTypes>> = 47 | // T extends ReadonlyArray> ? ReturnType : never; 48 | 49 | // type Result = UnionToIntersection>; 50 | -------------------------------------------------------------------------------- /packages/create-bridge-app/.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /dist 3 | package-lock.json 4 | pnpm-lock.yaml -------------------------------------------------------------------------------- /packages/create-bridge-app/README.md: -------------------------------------------------------------------------------- 1 | # Create Bridge App 2 | 3 | The easiest way to get started with Bridge is by using `create-bridge-app`. This CLI tool enables you to quickly start building a new Bridge application, with everything set up for you. 4 | 5 | ### Interactive 6 | 7 | You can create a new project interactively by running: 8 | 9 | ```bash 10 | npx create-bridge-app@latest 11 | # or 12 | yarn create bridge-app 13 | # or 14 | pnpm create bridge-app 15 | ``` 16 | 17 | You will be asked for the name of your project. 18 | 19 | ### Why use Create Bridge App? 20 | 21 | `create-bridge-app` allows you to create a new Bridge app within seconds. It is officially maintained by the creators of Bridge, and includes a number of benefits: 22 | 23 | - **Interactive Experience**: Running `npx create-bridge-app@latest` (with no arguments) launches an interactive experience that guides you through setting up a project. 24 | - **Zero Dependencies**: Initializing a project is as quick as one second. Create Bridge App has zero dependencies. -------------------------------------------------------------------------------- /packages/create-bridge-app/bin/index.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | import os from 'os'; 4 | import path from 'path'; 5 | import prompts from 'prompts'; 6 | import fs from 'fs'; 7 | import { execSync } from 'child_process'; 8 | import { copy } from './renameSync'; 9 | 10 | const launch = async () => { 11 | const slugRegex = /^[a-zA-Z0-9-]+$/; 12 | 13 | const { projectName } = await prompts({ 14 | type: 'text', 15 | name: 'projectName', 16 | message: `What's your project's name?`, 17 | validate: (text) => 18 | slugRegex.test(text) ? true : 'You can only use alphanumeric characters and -', 19 | }); 20 | 21 | if (fs.existsSync(`./${projectName}`)) { 22 | const { override } = await prompts({ 23 | type: 'confirm', 24 | name: 'override', 25 | message: `A folder in ./${projectName} already exists. Do you want to override it?`, 26 | }); 27 | 28 | if (!override) process.exit(1); 29 | fs.rmSync(`./${projectName}`, { recursive: true }); 30 | } 31 | 32 | const { template } = await prompts({ 33 | type: 'select', 34 | choices: [ 35 | { title: 'minimal-express', value: 'minimal-express' }, 36 | { title: 'minimal-http', value: 'minimal-http' }, 37 | ], 38 | name: 'template', 39 | message: `Which template do you want to use?`, 40 | validate: (text) => 41 | slugRegex.test(text) ? true : 'You can only use alphanumeric characters and -', 42 | }); 43 | 44 | const tempRepoPath = path.join(os.tmpdir(), 'bridgeRepoTemp'); 45 | 46 | execSync(`git clone https://github.com/bridge-codes/bridge.git ${tempRepoPath} -q`); 47 | 48 | await copy(path.join(tempRepoPath, path.join('examples', template)), projectName); 49 | 50 | const packageJSONPath = path.join(projectName, 'package.json'); 51 | fs.readFile(packageJSONPath, 'utf-8', (err, data) => { 52 | if (err) { 53 | console.error(err); 54 | process.exit(1); 55 | } 56 | 57 | const regexStr = `"name": "${template}",`; 58 | const result = data.replace(new RegExp(regexStr, 'g'), `"name": "${projectName}",`); 59 | 60 | fs.writeFile(packageJSONPath, result, 'utf8', function (err) { 61 | if (err) return console.log(err); 62 | }); 63 | }); 64 | 65 | fs.rmSync(tempRepoPath, { recursive: true }); 66 | }; 67 | 68 | launch(); 69 | -------------------------------------------------------------------------------- /packages/create-bridge-app/bin/renameSync.ts: -------------------------------------------------------------------------------- 1 | import * as fs from 'fs'; 2 | import * as util from 'util'; 3 | import * as path from 'path'; 4 | 5 | const readdir = util.promisify(fs.readdir); 6 | const mkdir = util.promisify(fs.mkdir); 7 | const rmdir = util.promisify(fs.rmdir); 8 | const readFile = util.promisify(fs.readFile); 9 | const writeFile = util.promisify(fs.writeFile); 10 | 11 | export async function copy(src: string, dest: string): Promise { 12 | const filenames = await readdir(src); 13 | 14 | await mkdir(dest); 15 | 16 | for (const filename of filenames) { 17 | const srcPath = path.join(src, filename); 18 | const destPath = path.join(dest, filename); 19 | 20 | const stat = await fs.promises.stat(srcPath); 21 | 22 | if (stat.isDirectory()) { 23 | await copy(srcPath, destPath); 24 | } else { 25 | const data = await readFile(srcPath); 26 | await writeFile(destPath, data); 27 | } 28 | } 29 | } 30 | 31 | // export async function renameFolder(oldPath: string, newPath: string): Promise { 32 | // try { 33 | // throw new Error('sd'); 34 | // await fs.promises.rename(oldPath, newPath); 35 | // // console.log(`Successfully renamed '${oldPath}' to '${newPath}'`); 36 | // } catch (err: any) { 37 | // await copy(oldPath, newPath); 38 | // // await rmdir(oldPath); 39 | // } 40 | // } 41 | -------------------------------------------------------------------------------- /packages/create-bridge-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "create-bridge-app", 3 | "version": "1.0.35", 4 | "description": "Create Bridge-powered Express app with one command", 5 | "author": "Bridge Team ", 6 | "license": "MIT", 7 | "bin": { 8 | "create-bridge-app": "./dist/index.js" 9 | }, 10 | "bugs": "https://github.com/bridge-codes/bridge/issues", 11 | "homepage": "https://bridge.codes", 12 | "files": [ 13 | "/dist" 14 | ], 15 | "scripts": { 16 | "build": "tsc", 17 | "publish-minor": "tsc && npm version patch && npm publish" 18 | }, 19 | "repository": { 20 | "type": "git", 21 | "url": "git+https://github.com/bridge-codes/bridge.git", 22 | "directory": "packages/create-bridge-app" 23 | }, 24 | "devDependencies": { 25 | "@types/prettier": "^2.7.1", 26 | "@types/prompts": "^2.4.2", 27 | "ts-node": "^10.9.1", 28 | "typescript": "^4.9.4" 29 | }, 30 | "dependencies": { 31 | "prettier": "^2.8.1", 32 | "prompts": "^2.4.2" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /packages/fetch-bridge-sdk/.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /dist 3 | package-lock.json 4 | pnpm-lock.yaml -------------------------------------------------------------------------------- /packages/fetch-bridge-sdk/README.md: -------------------------------------------------------------------------------- 1 | # Fetch Bridge SDK 2 | 3 | You can fetch your bridge project sdk with the following commands: 4 | 5 | ```bash 6 | npx fetch-bridge-sdk@latest {username}/{project} 7 | # or 8 | yarn fetch-bridge-sdk {username}/{project} 9 | # or 10 | pnpm fetch-bridge-sdk {username}/{project} 11 | ``` 12 | 13 | If you haven't set up a Bridge project or linked it to the Bridge Studio, please visit our website for instructions: https://bridge.codes. -------------------------------------------------------------------------------- /packages/fetch-bridge-sdk/bin/index.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import axios from 'axios'; 3 | import AdmZip from 'adm-zip'; 4 | import { execSync } from 'child_process'; 5 | import fs from 'fs'; 6 | 7 | var argv = require('minimist')(process.argv.slice(2)); 8 | 9 | const launch = async () => { 10 | const serverUrl = argv._[0]; 11 | 12 | if (!serverUrl.includes('/')) { 13 | console.error( 14 | 'You have to give the server url as parameter, example: npx fetch-bridge-sdk@latest http://localhost:8080', 15 | ); 16 | process.exit(1); 17 | } 18 | 19 | const directory = 'sdk'; 20 | 21 | const { data } = await axios({ 22 | method: 'get', 23 | url: `${serverUrl}/sdk`, 24 | responseType: 'arraybuffer', 25 | }); 26 | 27 | const zipFolder = new AdmZip(data); 28 | zipFolder.extractAllTo(directory, true); 29 | 30 | const packageJSON = JSON.parse(fs.readFileSync('./package.json', 'utf-8')); 31 | 32 | let pnpmLock: boolean; 33 | try { 34 | fs.readFileSync('./pnpm-lock.yaml', 'utf-8'); 35 | pnpmLock = true; 36 | } catch (e) { 37 | pnpmLock = false; 38 | } 39 | 40 | const installCommand = pnpmLock ? 'pnpm i' : 'npm i'; 41 | 42 | if (!packageJSON) throw new Error('package.json not found.'); 43 | 44 | if (!packageJSON.dependencies.axios) 45 | execSync(`echo Installing axios`) && execSync(`${installCommand} axios`); 46 | if (!packageJSON.dependencies['form-data']) 47 | execSync(`echo Installing form-data`) && execSync(`${installCommand} form-data`); 48 | }; 49 | 50 | launch(); 51 | -------------------------------------------------------------------------------- /packages/fetch-bridge-sdk/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fetch-bridge-sdk", 3 | "version": "1.0.12", 4 | "description": "Fetch Bridge SDK with one command", 5 | "author": "Bridge Team ", 6 | "bin": { 7 | "fetch-bridge-sdk": "./dist/index.js" 8 | }, 9 | "files": [ 10 | "/dist" 11 | ], 12 | "scripts": { 13 | "build": "tsc", 14 | "publish-minor": "tsc && npm version patch && npm publish" 15 | }, 16 | "license": "MIT", 17 | "bugs": "https://github.com/bridge-codes/bridge/issues", 18 | "homepage": "https://bridge.codes", 19 | "repository": { 20 | "type": "git", 21 | "url": "git+https://github.com/bridge-codes/bridge.git", 22 | "directory": "packages/fetch-bridge-sdk" 23 | }, 24 | "dependencies": { 25 | "adm-zip": "^0.5.10", 26 | "axios": "^1.2.3", 27 | "form-data": "^4.0.0", 28 | "minimist": "^1.2.7", 29 | "prompts": "^2.4.2" 30 | }, 31 | "devDependencies": { 32 | "@types/adm-zip": "^0.5.0", 33 | "@types/prompts": "^2.4.2", 34 | "ts-node": "^10.9.1", 35 | "typescript": "^4.9.4" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /www/.gitignore: -------------------------------------------------------------------------------- 1 | # Dependencies 2 | /node_modules 3 | 4 | package-lock.json 5 | pnpm-lock.yaml 6 | 7 | # Production 8 | /build 9 | 10 | # Generated files 11 | .docusaurus 12 | .cache-loader 13 | 14 | # Misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | 25 | .vercel 26 | -------------------------------------------------------------------------------- /www/README.md: -------------------------------------------------------------------------------- 1 | Docusaurus 2 | -------------------------------------------------------------------------------- /www/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [require.resolve('@docusaurus/core/lib/babel/preset')], 3 | }; 4 | -------------------------------------------------------------------------------- /www/blog/authors.yml: -------------------------------------------------------------------------------- 1 | endi: 2 | name: Endilie Yacop Sucipto 3 | title: Maintainer of Docusaurus 4 | url: https://github.com/endiliey 5 | image_url: https://github.com/endiliey.png 6 | 7 | yangshun: 8 | name: Yangshun Tay 9 | title: Front End Engineer @ Facebook 10 | url: https://github.com/yangshun 11 | image_url: https://github.com/yangshun.png 12 | 13 | slorber: 14 | name: Sébastien Lorber 15 | title: Docusaurus maintainer 16 | url: https://sebastienlorber.com 17 | image_url: https://github.com/slorber.png 18 | 19 | raul: 20 | name: Raul Mihaila 21 | title: Bridge team 22 | image_url: https://avatars.githubusercontent.com/u/67507353?s=40&v=4 23 | -------------------------------------------------------------------------------- /www/blog/trpc.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | slug: bridge-vs-trpc 3 | title: Comparison Bridge and trpc 4 | authors: [raul] 5 | --- 6 | 7 | We often are asked how Bridge is different from trpc. They can be splitted into 2 differents branches: differences at client level and server level. 8 | 9 | ## The way you would consume the client 10 | Bridge Studio (the tool used to build the SDK of a Bridge api) is a more traditional REST codegen app. It's way of functioning is closer to something like [Graphql codegen](https://the-guild.dev/graphql/codegen) or [OpenAPI Generator](https://openapi-generator.tech/). 11 | 12 | tRPC on the other hand lets you see real-time updated of your servers's routes and types. It has no build or compile steps, meaning no code generation, runtime bloat or build step. It makes it a great choice when building app as a full stack developer as everything is close. 13 | 14 | This difference leads to other significant variances: 15 | - tRPC couples your server & website/app more tightly: you're less likely to have missmatched versions between your server/client 16 | - tRPC requires the backend and frontend code on the same machine (thus making it great for monorepos!) 17 | - because it's less coupled than tRPC, Bridge lets you build any public-faced API (meant to be used by other external developers). It can be published as an NPM package. 18 | - Bridge can build SDK in more languages. It's great for apps that have different (or multiple) languages between frontend and backend. 19 | 20 | ## The way you would write your server 21 | We can highlight some other differences in the synthax used to write your sever code. 22 | 23 | Bridge relies more on objects while tRPC is more functional (it uses function chaining). OOP can be used with Bridge. 24 | Here is the same request with Bridge (a handler) and tRPC (a procedure). 25 | 26 | Bridge: 27 | ```ts 28 | import { z } from "zod" 29 | import { handler } from 'bridge'; 30 | 31 | const helloHandler = handler({ 32 | body: z.object({ name: z.string() }), 33 | resolve: ({ body }) => `Hello ${body.name}` 34 | }) 35 | ``` 36 | 37 | Trpc: 38 | ```ts 39 | import { initTRPC, router } from '@trpc/server'; 40 | import { z } from "zod"; 41 | 42 | const appRouter = router({ 43 | greet: publicProcedure 44 | .input(z.object({ name: z.string() })) 45 | .query(({ input }) => ({ greeting: `hello, ${input}!` })), 46 | }); 47 | ``` 48 | 49 | Moreover, Bridge can be used with classes (for those who like object-oriented programmation) and its capabilities (like inheritance or polymorphisms). 50 | 51 | ```ts 52 | class Session { 53 | login: handler({ 54 | // ... 55 | }) 56 | } 57 | 58 | // the user class now inherits the "login" handler from the session 59 | class User extends Session { 60 | getUser = handler({ 61 | //... 62 | }) 63 | } 64 | 65 | const routes = { 66 | // ... 67 | user: new User(), 68 | // ... 69 | } 70 | ``` 71 | -------------------------------------------------------------------------------- /www/docs/bridge-client/dart.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_label: 'Dart (Coming Soon)' 3 | --- 4 | 5 | # Coming Soon 6 | 7 | If you would like your client SDK to be available in a specific language sooner or want to contribute, please contact us [on discord](https://discord.gg/yxjrwm7Bfr). 8 | -------------------------------------------------------------------------------- /www/docs/bridge-client/dart/coming-soon.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_label: 'Coming Soon' 3 | --- 4 | 5 | # Coming Soon 6 | -------------------------------------------------------------------------------- /www/docs/bridge-client/kotlin.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_label: 'Kotlin (Coming Soon)' 3 | --- 4 | 5 | # Coming Soon 6 | 7 | If you would like your client SDK to be available in a specific language sooner or want to contribute, please contact us [on discord](https://discord.gg/yxjrwm7Bfr). -------------------------------------------------------------------------------- /www/docs/bridge-client/kotlin/coming-soon.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_label: 'Coming Soon' 3 | --- 4 | 5 | # Coming Soon 6 | -------------------------------------------------------------------------------- /www/docs/bridge-client/python.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_label: 'Python (Coming Soon)' 3 | --- 4 | 5 | # Coming Soon 6 | 7 | If you would like your client SDK to be available in a specific language sooner or want to contribute, please contact us [on discord](https://discord.gg/yxjrwm7Bfr). -------------------------------------------------------------------------------- /www/docs/bridge-client/python/coming-soon.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_label: 'Coming Soon' 3 | --- 4 | 5 | # Coming Soon 6 | -------------------------------------------------------------------------------- /www/docs/bridge-client/swift.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_label: 'Swift (Coming Soon)' 3 | --- 4 | 5 | # Coming Soon 6 | 7 | If you would like your client SDK to be available in a specific language sooner or want to contribute, please contact us [on discord](https://discord.gg/yxjrwm7Bfr). -------------------------------------------------------------------------------- /www/docs/bridge-client/swift/coming-soon.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_label: 'Coming Soon' 3 | --- 4 | 5 | # Coming Soon 6 | -------------------------------------------------------------------------------- /www/docs/bridge-client/typescript/fetch.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_label: 'Fetch your Client SDK' 3 | --- 4 | 5 | # Fetch your Client SDK 6 | 7 | Refer to the [Bridge Studio documentation](../../bridge-studio.md) to learn how to deploy your Bridge API and retrieve your client SDK from Bridge Studio. 8 | 9 | **To deploy your project on Bridge Studio:** 10 | ```bash title='terminal' 11 | npx bridge-studio@latest 12 | ``` 13 | 14 | **To fetch the latest typescript client sdk:** 15 | ```bash title='terminal' 16 | npx fetch-bridge-sdk@latest {username}{projectName} 17 | ``` 18 | 19 | If you do not have `axios` and `form-data` installed in your project, the command line will automatically install them for you. 20 | 21 | The upcoming version of the command line will allow you to select your preferred HTTP client library, either axios or fetch, and the required packages will be automatically installed if they are not already present in your project. -------------------------------------------------------------------------------- /www/docs/bridge-client/typescript/how-to-use.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_label: 'How to use your SDK' 3 | --- 4 | 5 | # SDK in the client code 6 | 7 | ## Import API from sdk 8 | 9 | The Typescript client SDK exports an API constant which can be easily imported into your code and used to access every endpoint of the Bridge API. 10 | 11 | ## Example 12 | 13 | ```ts twoslash title='client.ts' live 14 | 15 | const API = { 16 | user: { 17 | getMe: async (p: { 18 | headers: { token: string } 19 | }): Promise< 20 | | { 21 | data: { 22 | _id: string 23 | username: string; 24 | email: string; 25 | age: number; 26 | language: 'english' | 'french'; 27 | avatar?: string; 28 | createdAt: Date; 29 | }; 30 | error: undefined; 31 | } 32 | | { 33 | data: undefined; 34 | error: 35 | | { name: 'Wrong permission'; data?: any; status: 401 } 36 | | { status: 404; name: 'User not found' } 37 | | { name: 'Headers schema validation error'; status: 422; data: any } 38 | | { name: 'Axios Error'; status: 400; data: any } 39 | | { name: 'Internal Server Error'; status: 500 }; 40 | } 41 | > => '' as any 42 | } 43 | } 44 | 45 | // ---cut--- 46 | 47 | async () => { 48 | 49 | const { data, error } = await API.user.getMe({ 50 | headers: { token: 'secretToken' } 51 | }) 52 | 53 | if (data) console.log(data) 54 | // ^? 55 | 56 | 57 | if (error) { 58 | switch (error.name) { 59 | case "User not found": 60 | //... 61 | break; 62 | } 63 | 64 | console.log(error) 65 | // ^? 66 | } 67 | } 68 | ``` 69 | 70 | Please visit https://bridge.codes/studio for more information. -------------------------------------------------------------------------------- /www/docs/bridge-studio.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_label: 'Bridge Studio' 3 | --- 4 | 5 | # Bridge Studio 6 | 7 | ![Bridge Studio Schema](../static/studio/studio-header.svg) 8 | 9 | 10 | ## Connecting your Bridge API to Bridge Studio 11 | 12 | 13 | ### Using the CLI 14 | 15 | ```bash title='terminal' 16 | npx bridge-studio@latest 17 | # or 18 | pnpx bridge-studio@latest 19 | ``` 20 | 21 | You'll be redirected to the [github login](https://github.com/login/oauth/authorize?client_id=Iv1.61c158472b4a85b9). Sign in with your GitHub account, then copy and paste the secretToken on the Studio Bridge page. 22 | 23 | This command-line tool will generate a `bridge.config.json` file for you. To keep your secret token private, consider adding this file to your `.gitignore` before publishing to GitHub. 24 | 25 | If you didn't have a Studio Bridge account, this process will create one for you. You can log in to your profile to view and manage your projects at https://studio.bridge.codes. 26 | 27 | ### Using the plateform 28 | 29 | Connect to https://studio.bridge.codes using your GitHub account to import a project from your GitHub repository. The project will be linked to your repository. To recompile the client SDK, simply click the "Recompile" button on the platform. 30 | 31 | ## Fetch your client SDK 32 | 33 | ```bash title='terminal' 34 | npx fetch-bridge-sdk@latest {username}/{projectName} 35 | ``` 36 | 37 | If you do not have `axios` and `form-data` installed in your project, the command line will automatically install them for you. 38 | 39 | The upcoming version of the command line will allow you to select your preferred HTTP client library, either axios or fetch, and the required packages will be automatically installed if they are not already present in your project. 40 | 41 | 42 | ## Access your generated documentation 43 | 44 | You'll be able to access your complete generated documentation on https://studio.bridge.codes soon. 45 | 46 | Please visit https://bridge.codes/studio for more information. -------------------------------------------------------------------------------- /www/docs/bridge/adapters/express.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_label: 'Express' 3 | --- 4 | 5 | # Express 6 | 7 | **Example** 8 | 9 | ```ts twoslash title='server.ts' 10 | import { initBridge, handler } from 'bridge'; 11 | import express from 'express'; 12 | 13 | const hello = handler({ 14 | resolve: () => 'hello', 15 | }); 16 | 17 | const bridge = initBridge({ routes: { hello } }); 18 | 19 | const port = 8080; 20 | 21 | const app = express(); 22 | 23 | app.use('', bridge.expressMiddleware()); 24 | 25 | app.listen(port, () => { 26 | `Listening on port ${port}`; 27 | }); 28 | ``` 29 | -------------------------------------------------------------------------------- /www/docs/bridge/adapters/fastify.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_label: 'Fastify' 3 | --- 4 | 5 | # Fastify 6 | 7 | Coming soon... 8 | -------------------------------------------------------------------------------- /www/docs/bridge/adapters/http.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_label: 'HTTP' 3 | --- 4 | 5 | # HTTP 6 | 7 | **Example** 8 | 9 | ```ts twoslash title='server.ts' 10 | import { initBridge, handler } from 'bridge'; 11 | 12 | const hello = handler({ 13 | resolve: () => 'hello', 14 | }); 15 | 16 | const bridge = initBridge({ routes: { hello } }); 17 | 18 | const port = 8080; 19 | 20 | bridge.HTTPServer().listen(port, () => { 21 | `Listening on port ${port}`; 22 | }); 23 | ``` 24 | -------------------------------------------------------------------------------- /www/docs/bridge/automatic_documentation.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_label: 'Documentation Generation' 3 | --- 4 | 5 | # Documentation Generation 6 | 7 | Bridge App allows you to easily create a comprehensive online documentation for your code without any extra effort. Simply join our **[Discord community](https://discord.gg/yxjrwm7Bfr)** to learn more and join the **[Beta](https://discord.gg/yxjrwm7Bfr)**! 8 | -------------------------------------------------------------------------------- /www/docs/bridge/client_code.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_label: 'Client Code Generation' 3 | --- 4 | 5 | # Client Code Generation 6 | 7 | Bridge App allows you to easily create a fully typed client code for any frontend langauge with minimal effort. No extra metadata is required. Simply join our **[Discord community](https://discord.gg/yxjrwm7Bfr)** to learn more and join the **[Beta](https://discord.gg/yxjrwm7Bfr)**! 8 | -------------------------------------------------------------------------------- /www/docs/bridge/data_validation.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_label: 'Data Validation' 3 | --- 4 | 5 | # Data Validation 6 | 7 | You have the option to validate and process the body, query parameters, and headers sent by the client using the Zod, Superstruct, or Yup libraries. While all three are effective options, we highly recommend using Zod for the best developer experience. 8 | 9 | - `body`: Record (always a JSON object) 10 | - `query`: Record 11 | - `headers`: Record 12 | - `files`: apply(...string[]) (check [files](files)) 13 | 14 | ## With [Zod](https://github.com/colinhacks/zod) 15 | 16 | ```ts twoslash 17 | import { handler } from 'bridge'; 18 | import z from 'zod'; 19 | 20 | const updateMe = handler({ 21 | headers: z.object({ token: z.string().min(5) }), 22 | query: z.object({ _id: z.string() }), 23 | body: z.object({ 24 | name: z.string(), 25 | age: z.number().optional(), 26 | type: z.enum(['admin', 'user']).optional(), 27 | address: z.object({ 28 | street: z.string(), 29 | }) 30 | }), 31 | resolve: (data) => { 32 | // ^? 33 | const { body, query, headers } = data; 34 | 35 | return { success: true }; 36 | }, 37 | }); 38 | ``` 39 | 40 | ## With [Superstruct](https://github.com/ianstormtaylor/superstruct) 41 | 42 | ```ts twoslash 43 | import { handler } from 'bridge'; 44 | import { object, number, string, enums, size, optional } from 'superstruct'; 45 | 46 | const updateMe = handler({ 47 | headers: object({ token: size(string(), 5) }), 48 | query: object({ _id: string() }), 49 | body: object({ 50 | name: optional(string()), 51 | age: optional(number()), 52 | type: optional(enums(['admin', 'user'])), 53 | }), 54 | resolve: ({ body, query, headers }) => { 55 | return { success: true }; 56 | }, 57 | }); 58 | ``` 59 | 60 | ## With [Yup](https://github.com/jquense/yup) 61 | 62 | ```ts twoslash 63 | import { handler } from 'bridge'; 64 | import * as yup from 'yup'; 65 | 66 | const updateMe = handler({ 67 | headers: yup.object({ token: yup.string().min(5).required() }), 68 | query: yup.object({ _id: yup.string().required() }), 69 | body: yup.object({ 70 | name: yup.string(), 71 | age: yup.number(), 72 | type: yup.mixed().oneOf(['admin', 'user']), 73 | }), 74 | resolve: ({ body, query, headers }) => { 75 | return { success: true }; 76 | }, 77 | }); 78 | ``` 79 | 80 | ## Error Example 81 | 82 | If the data submitted by the user is not in the correct format, Bridge will return a 400 Bad Request error, labeled as "Body|Query|Headers schema validation error". Here is an example using **Zod**: 83 | 84 | ```json 85 | { 86 | "error": { 87 | "status": 400, 88 | "name": "Body schema validation error", 89 | "data": { 90 | "issues": [ 91 | { 92 | "code": "invalid_type", 93 | "expected": "string", 94 | "received": "undefined", 95 | "path": ["name"], 96 | "message": "Required" 97 | } 98 | ], 99 | "name": "ZodError" 100 | } 101 | } 102 | } 103 | ``` 104 | -------------------------------------------------------------------------------- /www/docs/bridge/examples/advanced.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_label: 'Advanced Example' 3 | --- 4 | 5 | # Advanced Example 6 | -------------------------------------------------------------------------------- /www/docs/bridge/examples/basic.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_label: 'Basic Example' 3 | --- 4 | 5 | # Basic Example 6 | -------------------------------------------------------------------------------- /www/docs/bridge/examples/medium.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_label: 'Medium Example' 3 | --- 4 | 5 | # Medium Example 6 | -------------------------------------------------------------------------------- /www/docs/bridge/files.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_label: 'Files' 3 | --- 4 | 5 | # Files 6 | 7 | Bridge integrates seamlessly with `formidable` to receive and handle files on your server. Simply install `formidable` and pass it to `initBridge` to enable this feature. You can use the `apply` function in Bridge to specify the names of specific files you want your client to send, or use "any" to accept any file. 8 | 9 | ## Install [formidable](https://www.npmjs.com/package/formidable) 10 | 11 | 12 | ``` bash title='terminal' 13 | npm i formidable 14 | npm i --save-dev formidable 15 | ``` 16 | 17 | ## Example 18 | 19 | ```ts twoslash title='server.ts' 20 | import { initBridge, handler, apply } from 'bridge'; 21 | import formidable from 'formidable'; 22 | 23 | const sendAnyFiles = handler({ 24 | files: 'any', 25 | resolve: ({ files }) => { 26 | // ^? 27 | console.log(files); 28 | return { success: true }; 29 | }, 30 | }); 31 | 32 | const sendSpecificFiles = handler({ 33 | files: apply('profilePicture', 'coverPicture'), 34 | resolve: ({ files }) => { 35 | // ^? 36 | console.log(files); 37 | return { success: true }; 38 | }, 39 | }); 40 | 41 | const routes = { 42 | sendAnyFiles, 43 | sendSpecificFiles, 44 | }; 45 | 46 | const bridge = initBridge({ routes, formidable }); 47 | ``` 48 | 49 | ## Error Example 50 | 51 | "If the user submits files with incorrect names, Bridge will respond with a 400 Bad Request error, denoted as "Files schema validation error". Here is an example: 52 | 53 | ```json 54 | { 55 | "error": { 56 | "status": 400, 57 | "name": "Files schema validation error", 58 | "data": { 59 | "missingFiles": ["profilePicture"] 60 | } 61 | } 62 | } 63 | ``` 64 | -------------------------------------------------------------------------------- /www/docs/bridge/handler.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_label: 'Handler' 3 | --- 4 | 5 | # Handler 6 | 7 | The `handler` is the core concept of Bridge. This function helps you create endpoints and middlewares for your Bridge server. 8 | 9 | ## Resolve Function 10 | 11 | The `resolve` function is the only required parameter of a handler. You can either return a json object, a string or a number with the resolve function. If the function returns void, the client will receive an empty object in response. 12 | 13 | ```ts 14 | import { handler } from 'bridge'; 15 | 16 | // Returns the string "Hello World" with an http status code 200 17 | const handler1 = handler({ 18 | resolve: () => 'Hello World', 19 | }); 20 | 21 | // Returns the number 42 with an http status code 200 22 | const handler2 = handler({ 23 | resolve: () => 42, 24 | }); 25 | 26 | // Returns the json { name: "John", age: 42 } with an http status code 200 27 | const handler3 = handler({ 28 | resolve: () => ({ name: 'John', age: 42 }), 29 | }); 30 | 31 | // Returns the empty json {} with an http status code 200 32 | const handler3 = handler({ 33 | resolve: () => { 34 | console.log('hello'); 35 | }, 36 | }); 37 | ``` 38 | -------------------------------------------------------------------------------- /www/docs/bridge/launch_server/express copy 3.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_label: 'With Express' 3 | --- 4 | 5 | # Launch your server 6 | 7 | ## With HTTP 8 | 9 | **Example** 10 | 11 | ```ts twoslash title='server.ts' 12 | import { initBridge, handler } from 'bridge'; 13 | 14 | const hello = handler({ 15 | resolve: () => 'hello', 16 | }); 17 | 18 | const bridge = initBridge({ routes: { hello } }); 19 | 20 | const port = 8080; 21 | 22 | bridge.HTTPServer().listen(port, () => { 23 | `Listening on port ${port}`; 24 | }); 25 | ``` 26 | 27 | ## With Express 28 | 29 | **Example** 30 | 31 | ```ts twoslash title='server.ts' 32 | import { initBridge, handler } from 'bridge'; 33 | import express from 'express'; 34 | 35 | const hello = handler({ 36 | resolve: () => 'hello', 37 | }); 38 | 39 | const bridge = initBridge({ routes: { hello } }); 40 | 41 | const port = 8080; 42 | 43 | const app = express(); 44 | 45 | app.use('', bridge.expressMiddleware()); 46 | 47 | app.listen(port, () => { 48 | `Listening on port ${port}`; 49 | }); 50 | ``` 51 | 52 | ## With Fastify 53 | 54 | Coming soon... 55 | -------------------------------------------------------------------------------- /www/docs/bridge/launch_server/express.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_label: 'With Express' 3 | --- 4 | 5 | # With Express 6 | 7 | ## Install [express](https://www.npmjs.com/package/express) 8 | 9 | ```bash title=terminal 10 | npm i express 11 | npm i --save-dev @types/express 12 | ``` 13 | 14 | ## Example 15 | 16 | ```ts twoslash title='server.ts' 17 | import { initBridge, handler } from 'bridge'; 18 | import express from 'express'; 19 | 20 | const hello = handler({ 21 | resolve: () => 'hello', 22 | }); 23 | 24 | const bridge = initBridge({ routes: { hello } }); 25 | 26 | const port = 8080; 27 | 28 | const app = express(); 29 | 30 | app.use('', bridge.expressMiddleware()); 31 | 32 | app.listen(port, () => { 33 | `Listening on port ${port}`; 34 | }); 35 | ``` 36 | 37 | -------------------------------------------------------------------------------- /www/docs/bridge/launch_server/fastify.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_label: 'With Fastify' 3 | --- 4 | 5 | # With Fastify 6 | 7 | Coming soon... 8 | -------------------------------------------------------------------------------- /www/docs/bridge/launch_server/node-http.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_label: 'With Node HTTP' 3 | --- 4 | 5 | # With Node HTTP 6 | 7 | ## Example 8 | 9 | 10 | ```ts twoslash title='server.ts' 11 | import { initBridge, handler } from 'bridge'; 12 | 13 | const hello = handler({ 14 | resolve: () => 'hello', 15 | }); 16 | 17 | const bridge = initBridge({ routes: { hello } }); 18 | 19 | const port = 8080; 20 | 21 | bridge.HTTPServer().listen(port, () => { 22 | `Listening on port ${port}`; 23 | }); 24 | ``` 25 | -------------------------------------------------------------------------------- /www/docs/bridge/middlewares.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_label: 'Middlewares' 3 | --- 4 | 5 | # Middlewares 6 | 7 | ## Creating a middleware 8 | 9 | Creating a middleware is just as easy as creating a handler. In fact, a middleware is a handler, so it can validate data and return errors in the same way as usual. 10 | 11 | The calling handler's resolve function receives the return value of the middleware in the mid object. Its type is inferred automatically. If the middleware returns an httpError, it will send an error to the client and the calling handler's resolve function will not be executed. 12 | 13 | **Example** 14 | 15 | ```ts twoslash 16 | import { apply, handler, httpError, StatusCode } from 'bridge'; 17 | import z from 'zod'; 18 | 19 | const authMiddleware = handler({ 20 | headers: z.object({ token: z.string() }), 21 | resolve: ({ headers }) => { 22 | if (headers.token !== 'private_token') return httpError(StatusCode.UNAUTHORIZED, 'Wrong token'); 23 | else return { firstName: 'John', name: 'Doe', age: 21 }; 24 | }, 25 | }); 26 | 27 | const updateUser = handler({ 28 | middlewares: apply(authMiddleware), 29 | body: z.object({ age: z.number() }), 30 | resolve: ({ middlewares, body }) => { 31 | // ^? 32 | const user = middlewares; 33 | user.age = body.age; 34 | return user; 35 | }, 36 | }); 37 | ``` 38 | 39 | ## Multiple middlewares 40 | 41 | Multiple middlewares are run simultaneously, and their return values are merged into the `mid` object of the calling handler. 42 | 43 | **Example** 44 | 45 | ```ts twoslash 46 | import { apply, handler } from 'bridge'; 47 | 48 | const middleware1 = handler({ 49 | resolve: () => ({ firstName: 'John' }), 50 | }); 51 | 52 | const middleware2 = handler({ 53 | resolve: () => ({ lastName: 'Doe' }), 54 | }); 55 | 56 | const getMe = handler({ 57 | middlewares: apply(middleware1, middleware2), 58 | resolve: ({ middlewares }) => { 59 | // ^? 60 | return middlewares; 61 | }, 62 | }); 63 | ``` 64 | 65 | ## Nested middlewares 66 | 67 | To run middlewares one after the other, you can nest them by adding a middleware inside another middleware. 68 | 69 | **Example** 70 | 71 | ```ts twoslash 72 | import { apply, handler } from 'bridge'; 73 | 74 | const mid1 = handler({ 75 | resolve: () => ({ firstName: 'John' }), 76 | }); 77 | 78 | const mid2 = handler({ 79 | middlewares: apply(mid1), 80 | resolve: ({ middlewares }) => ({ fullName: `${middlewares.firstName} Doe` }), 81 | }); 82 | 83 | const mainHandler = handler({ 84 | middlewares: apply(mid2), 85 | resolve: ({ middlewares }) => middlewares, 86 | // ^? 87 | }); 88 | ``` 89 | 90 | The mainHandler will return 91 | 92 | ```json 93 | { 94 | "fullName": "John Doe" 95 | } 96 | ``` 97 | -------------------------------------------------------------------------------- /www/docs/bridge/routes.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_label: 'Routes' 3 | --- 4 | 5 | # Routes 6 | 7 | ## Definition 8 | 9 | Defining the routes for your Bridge project is simple – it's a object with your handlers at the leaf nodes of the object tree. 10 | 11 | In addition to defining individual routes, you can create nested routes by adding new objects to your router. Nested routes let you group related routes together for a more complex and organized API. 12 | 13 | **Example** 14 | 15 | ```ts twoslash title='server.ts' 16 | import { handler, initBridge } from 'bridge'; 17 | 18 | const helloHandler = handler({ 19 | resolve: () => 'Hello', 20 | }); 21 | 22 | const byeHandler = handler({ 23 | resolve: () => 'Bye', 24 | }); 25 | 26 | const routes = { 27 | hello: helloHandler, // POST /hello 28 | bye: byeHandler, // POST /bye 29 | user: { 30 | getMe: helloHandler, // POST /user/getMe 31 | friends: { 32 | byeHandler, // POST /user/friends/byeHandler 33 | }, 34 | }, 35 | }; 36 | 37 | initBridge({ routes }) 38 | .HTTPServer() 39 | .listen(8080, () => {}); 40 | ``` 41 | 42 | :::tip 43 | 44 | For the best experience with Bridge, we recommend sticking to POST methods for your endpoints. This aligns with our goal of treating the API like a client code library and removes the need for methods. However, if you need to change an endpoint method or have multiple endpoints for the same route, you can use the `method` function in Bridge. 45 | 46 | ::: 47 | 48 | ## Multiple endpoints for one route 49 | 50 | ```ts 51 | import { method } from 'Bridge'; 52 | 53 | const routes = { 54 | user: method({ 55 | GET: handler1, // GET /user 56 | POST: handler2, // POST /user 57 | PATCH: handler3, // PATCH /user 58 | PUT: handler4, // PUT /user 59 | DELETE: handler5, // DELETE /user 60 | }), 61 | }; 62 | ``` 63 | 64 | 87 | 88 | ## Route Not Found 89 | 90 | If the endpoint's route is not found, Bridge will respond to the client with a 404 error and the following object: 91 | 92 | ```json 93 | { 94 | "error": { 95 | "status": 404, 96 | "name": "Route not found" 97 | } 98 | } 99 | ``` 100 | 101 | :::info 102 | 103 | If you prefer using OOP and want to define your handlers inside classes, it's easy to do so by adding an instance of the class to the routes object. 104 | 105 | ::: 106 | -------------------------------------------------------------------------------- /www/docs/bridge/server.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_label: 'Launch your server' 3 | --- 4 | 5 | # Launch your server 6 | 7 | ## With Node HTTP 8 | 9 | ### Example 10 | 11 | 12 | ```ts twoslash title='server.ts' 13 | import { initBridge, handler } from 'bridge'; 14 | 15 | const hello = handler({ 16 | resolve: () => 'hello', 17 | }); 18 | 19 | const bridge = initBridge({ routes: { hello } }); 20 | 21 | const port = 8080; 22 | 23 | bridge.HTTPServer().listen(port, () => { 24 | `Listening on port ${port}`; 25 | }); 26 | ``` 27 | 28 | ## With Express 29 | 30 | ### Install [express](https://www.npmjs.com/package/express) 31 | 32 | ```bash title=terminal 33 | npm i express 34 | npm i --save-dev @types/express 35 | ``` 36 | 37 | ### Example 38 | 39 | ```ts twoslash title='server.ts' 40 | import { initBridge, handler } from 'bridge'; 41 | import express from 'express'; 42 | 43 | const hello = handler({ 44 | resolve: () => 'hello', 45 | }); 46 | 47 | const bridge = initBridge({ routes: { hello } }); 48 | 49 | const port = 8080; 50 | 51 | const app = express(); 52 | 53 | app.use('', bridge.expressMiddleware()); 54 | 55 | app.listen(port, () => { 56 | `Listening on port ${port}`; 57 | }); 58 | ``` 59 | 60 | ## With Fastify 61 | 62 | Coming soon... 63 | 64 | ## Lambda 65 | 66 | Coming soon... 67 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /www/docs/bridge/server_calls.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_label: 'Server Side Calls' 3 | --- 4 | 5 | # Server Side Calls 6 | 7 | You can directly access the resolve function of any handler. Its type will be inferred automatically. 8 | 9 | ## Example 10 | 11 | ```ts twoslash 12 | import { handler, httpError } from 'bridge'; 13 | import z from 'zod'; 14 | 15 | const users: Record = { 16 | '1': { 17 | firstName: 'John', 18 | lastName: 'Doe', 19 | }, 20 | }; 21 | 22 | const getMyIdWithToken = handler({ 23 | headers: z.object({ token: z.string() }), 24 | resolve: ({ headers }) => { 25 | if (headers.token === 'token') return { id: '1' }; 26 | else return httpError(400, 'Wrong token'); 27 | }, 28 | }); 29 | 30 | const getProfileWithToken = handler({ 31 | headers: z.object({ token: z.string() }), 32 | resolve: ({ headers }) => { 33 | const res = getMyIdWithToken.resolve({ headers }); 34 | // ^? 35 | if ('error' in res) return res; 36 | return users[res.id]; 37 | }, 38 | }); 39 | ``` 40 | 41 | This example shows two handlers that can be used as endpoints and called separately. `getProfileWithToken` uses the resolve function of `getMyIdWithToken`. 42 | 43 | :::caution 44 | The above example doesn't make much sense, it would be more logical to use getMyIdWithToken as a middleware for getProfileWithToken. This would make the code shorter and clearer. Here is the revised code: 45 | ::: 46 | 47 | ```ts twoslash 48 | import { handler, httpError, apply } from 'bridge'; 49 | import z from 'zod'; 50 | 51 | const users: Record = { 52 | '1': { 53 | firstName: 'John', 54 | lastName: 'Doe', 55 | }, 56 | }; 57 | 58 | const getMyIdWithToken = handler({ 59 | headers: z.object({ token: z.string() }), 60 | resolve: ({ headers }) => { 61 | if (headers.token === 'token') return { id: '1' }; 62 | else return httpError(400, 'Wrong token'); 63 | }, 64 | }); 65 | 66 | const getProfileWithToken = handler({ 67 | middlewares: apply(getMyIdWithToken), 68 | resolve: ({ middlewares }) => users[middlewares.id], 69 | }); 70 | ``` 71 | -------------------------------------------------------------------------------- /www/docs/bridge/socket.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_label: 'Socket' 3 | --- 4 | 5 | # Socket 6 | 7 | Coming soon... 8 | -------------------------------------------------------------------------------- /www/docs/coming-soon.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_label: 'Coming Soon' 3 | --- 4 | 5 | # Coming Soon 6 | -------------------------------------------------------------------------------- /www/docs/compilation.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_label: 'Compilation 🚀' 3 | --- 4 | 5 | # Compilation 6 | 7 | Use the following command line to compile your Bridge project: 8 | 9 | ```bash title='terminal' 10 | npx bridge-compile@latest 11 | # or 12 | pnpx bridge-compile@latest 13 | ``` 14 | 15 | This command line generates 3 things: 16 | - JSONType.json file 17 | - openapi.json file 18 | - sdk folder 19 | 20 | The JSONType file contains all the types of your project compiled by Bridge. This file was the input to create the openapi specification and to generate the typescript sdk folder. You can develop your own compiler using this file as an input to generate sdk in other languages. -------------------------------------------------------------------------------- /www/docs/documentation.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_label: 'OpenAPI Documentation' 3 | --- 4 | 5 | # Browse OpenAPI documentation 6 | 7 | After compiling your Bridge project, you can browse the openapi specification generated easily with ExpressJS. 8 | 9 | 10 | ```ts title='server.ts' showLineNumbers 11 | import { handler, initBridge } from 'bridge'; 12 | import express from 'express'; 13 | import swaggerUi from 'swagger-ui-express'; 14 | 15 | const openApiDocumentation = require('./openapi.json'); 16 | 17 | const routes = { 18 | hello: handler({ 19 | resolve: () => 'Hello World', 20 | }), 21 | }; 22 | 23 | const port = 8080; 24 | const app = express(); 25 | const bridge = initBridge({ routes }); 26 | 27 | app.use('', bridge.expressMiddleware()); 28 | 29 | app.use('/docs', swaggerUi.serve, swaggerUi.setup(openApiDocumentation)); 30 | 31 | app.listen(port, () => { 32 | console.log(`Listening on port ${port}`); 33 | }); 34 | ``` 35 | -------------------------------------------------------------------------------- /www/docs/examples/example.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_label: 'Example' 3 | --- 4 | 5 | # Example 6 | 7 | ```ts twoslash title='server.ts' showLineNumbers 8 | import { initBridge, handler, onError, StatusCode, httpError, apply } from 'bridge'; 9 | import express from 'express'; 10 | // You can also use Yup or Superstruct for data validation 11 | import z from 'zod'; 12 | 13 | const port = 8080; 14 | 15 | // A middleware is also a handler 16 | const authMiddleware = handler({ 17 | headers: z.object({ token: z.string().min(5) }), 18 | resolve: ({ headers }) => { 19 | // ^? 20 | if (headers.token !== 'private_token') return httpError(StatusCode.UNAUTHORIZED, 'Wrong token'); 21 | else return { firstName: 'John', name: 'Doe', age: 21 }; 22 | }, 23 | }); 24 | 25 | // A handler can be used as an endpoint 26 | const updateUser = handler({ 27 | middlewares: apply(authMiddleware), 28 | body: z.object({ age: z.number() }), 29 | resolve: (data) => { 30 | // ^? 31 | const user = data.middlewares; 32 | user.age = data.body.age; 33 | return user; 34 | }, 35 | }); 36 | 37 | // You can have multiple endpoints for the same route with different methods with the method function 38 | const routes = { 39 | hey: handler({ resolve: () => 'hey' }), // POST /hey 40 | hello: handler({ 41 | query: z.object({ name: z.string().optional() }), 42 | resolve: ({ query }) => `Hello ${query.name}`, 43 | }), // POST /hello 44 | user: { 45 | update: updateUser, // POST /user/update 46 | }, 47 | }; 48 | 49 | const errorHandler = onError(({ error, path }) => { 50 | // The error object can be modified here before it is sent to the client 51 | if (error.name === 'Internal server error') console.log(path, error); // Send to bug reporting 52 | else console.log(path, error.status, error.name); 53 | }); 54 | 55 | // It is also possible to use HTTP Server 56 | const app = express(); 57 | 58 | app.use('', initBridge({ routes, errorHandler }).expressMiddleware()); 59 | 60 | app.listen(port, () => { 61 | console.log(`Listening on port ${port}`); 62 | }); 63 | ``` 64 | -------------------------------------------------------------------------------- /www/docs/introduction.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_label: 'Introduction' 3 | --- 4 | 5 | # Introduction 6 | 7 | Welcome to the Bridge documentation! 8 | 9 | Bridge is the most straightforward yet powerful framework for creating simple or complex APIs using the full power of TypeScript, even for developers with little experience. Give it a try and see how easy it is to build your dream API! 10 | 11 | If you have any questions about anything related to Bridge or if you want to discuss with us and the community, you are welcome to join our **[discord](https://discord.gg/yxjrwm7Bfr)**. 12 | 13 | ## Our Mission 14 | 15 | Our mission at Bridge is to revolutionize the way developers build and integrate APIs. Through our open-source project and SAAS platform, we aim to provide the best developer experience ever by simplifying the process of developing and integrating APIs, and helping developers create value faster. We believe that developers deserve tools that are easy to use, efficient, and powerful, and we are committed to helping our clients achieve their goals and succeed in the digital world by providing innovative solutions that reduce development time and increase value creation. 16 | 17 | ## Chat with us! 18 | 19 | We are dedicated to creating a fantastic project that offers the best developer experience possible, and we would love for you to be a part of it! Please join our **[Discord Community](https://discord.gg/yxjrwm7Bfr)** and chat with us about your thoughts on Bridge and ask us any questions. We are always happy to have a discussion with you! 20 | 21 | 26 | -------------------------------------------------------------------------------- /www/docs/typescript-sdk.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_label: 'Typescript SDK' 3 | --- 4 | 5 | # Typescript SDK 6 | 7 | ## Fetch your Client SDK 8 | 9 | You can simply download the typescript client code with the following command line: 10 | 11 | ```bash title='terminal' 12 | npx fetch-bridge-sdk $serverUrl 13 | ``` 14 | 15 | If you do not have axios and form-data installed in your project, the command line will automatically install them for you. 16 | 17 | The upcoming version of the command line will allow you to select your preferred HTTP client library, either axios or fetch, and the required packages will be automatically installed if they are not already present in your project. 18 | 19 | 20 | ## Use it in the frontend 21 | 22 | The Typescript client SDK exports an API constant which can be easily imported into your code and used to access every endpoint of the Bridge API. 23 | 24 | ### Example 25 | 26 | ```ts twoslash title='client.ts' live 27 | 28 | const API = { 29 | user: { 30 | getMe: async (p: { 31 | headers: { token: string } 32 | }): Promise< 33 | | { 34 | data: { 35 | _id: string 36 | username: string; 37 | email: string; 38 | age: number; 39 | language: 'english' | 'french'; 40 | avatar?: string; 41 | createdAt: Date; 42 | }; 43 | error: undefined; 44 | } 45 | | { 46 | data: undefined; 47 | error: 48 | | { name: 'Wrong permission'; data?: any; status: 401 } 49 | | { status: 404; name: 'User not found' } 50 | | { name: 'Headers schema validation error'; status: 422; data: any } 51 | | { name: 'Axios Error'; status: 400; data: any } 52 | | { name: 'Internal Server Error'; status: 500 }; 53 | } 54 | > => '' as any 55 | } 56 | } 57 | 58 | // ---cut--- 59 | // import { API } from './api' 60 | 61 | async () => { 62 | 63 | const { data, error } = await API.user.getMe({ 64 | headers: { token: 'secretToken' } 65 | }) 66 | 67 | if (data) console.log(data) 68 | // ^? 69 | 70 | 71 | if (error) { 72 | switch (error.name) { 73 | case "User not found": 74 | //... 75 | break; 76 | } 77 | 78 | console.log(error) 79 | // ^? 80 | } 81 | } 82 | ``` -------------------------------------------------------------------------------- /www/docusaurus.preferredTheme.js: -------------------------------------------------------------------------------- 1 | import ExecutionEnvironment from '@docusaurus/ExecutionEnvironment'; 2 | 3 | const darkTheme = 'dark'; 4 | const lightTheme = 'light'; 5 | 6 | // if (!window.location.pathname.includes('docs')) { 7 | // console.log("yes") 8 | // document.querySelector('.navbar').classList.add('navbarE'); 9 | // // document.navbar?.style.setProperty('position', 'sticky') 10 | // } 11 | 12 | // if (window.location.pathname.includes('docs')) { 13 | // document.querySelector('.header').style.backgroundColor = '#F7F7F7'; 14 | // } 15 | 16 | if (ExecutionEnvironment.canUseDOM) { 17 | const mediaMatch = window.matchMedia('(prefers-color-scheme: dark)'); 18 | const htmlElement = document.querySelector('html'); 19 | 20 | const setInitialTheme = () => { 21 | const newTheme = mediaMatch.matches ? darkTheme : darkTheme; 22 | htmlElement?.setAttribute('data-theme', newTheme); 23 | }; 24 | setInitialTheme(); 25 | 26 | const colorSchemeChangeListener = (e) => { 27 | const newTheme = e.matches ? darkTheme : lightTheme; 28 | htmlElement?.setAttribute('data-theme', newTheme); 29 | }; 30 | mediaMatch.addEventListener('change', colorSchemeChangeListener); 31 | 32 | 33 | } 34 | -------------------------------------------------------------------------------- /www/global.d.ts: -------------------------------------------------------------------------------- 1 | declare module '@docusaurus/plugin-content-docs/client'; 2 | -------------------------------------------------------------------------------- /www/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "www", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "docusaurus": "docusaurus", 7 | "start": "docusaurus start", 8 | "build": "docusaurus build", 9 | "swizzle": "docusaurus swizzle", 10 | "deploy": "docusaurus deploy", 11 | "clear": "docusaurus clear", 12 | "serve": "docusaurus serve", 13 | "write-translations": "docusaurus write-translations", 14 | "write-heading-ids": "docusaurus write-heading-ids", 15 | "typecheck": "tsc" 16 | }, 17 | "directories": { 18 | "sdk": "sdk" 19 | }, 20 | "dependencies": { 21 | "@docusaurus/core": "2.2.0", 22 | "@docusaurus/plugin-google-analytics": "^2.2.0", 23 | "@docusaurus/plugin-sitemap": "^2.3.1", 24 | "@docusaurus/preset-classic": "2.2.0", 25 | "@heroicons/react": "^2.0.13", 26 | "@mdx-js/react": "^1.6.22", 27 | "@tailwindcss/line-clamp": "^0.4.2", 28 | "@vercel/analytics": "^0.1.8", 29 | "autoprefixer": "^10.4.13", 30 | "axios": "^1.3.2", 31 | "bridge": "^2.0.42", 32 | "bridge-react-query": "^1.0.8", 33 | "clsx": "^1.2.1", 34 | "docusaurus-preset-shiki-twoslash": "^1.1.38", 35 | "form-data": "^4.0.0", 36 | "formidable": "^2.1.1", 37 | "mixpanel-browser": "^2.45.0", 38 | "postcss": "^8.4.20", 39 | "prism-react-renderer": "^1.3.5", 40 | "react": "^17.0.2", 41 | "react-dom": "^17.0.2", 42 | "react-syntax-highlighter": "^15.5.0", 43 | "superstruct": "^1.0.3", 44 | "tailwindcss": "^3.2.4", 45 | "yup": "^0.32.11", 46 | "zod": "^3.20.2" 47 | }, 48 | "devDependencies": { 49 | "@docusaurus/module-type-aliases": "2.2.0", 50 | "@tsconfig/docusaurus": "^1.0.5", 51 | "@types/formidable": "^2.0.5", 52 | "@types/yup": "^0.32.0", 53 | "typescript": "^4.7.4" 54 | }, 55 | "browserslist": { 56 | "production": [ 57 | ">0.5%", 58 | "not dead", 59 | "not op_mini all" 60 | ], 61 | "development": [ 62 | "last 1 chrome version", 63 | "last 1 firefox version", 64 | "last 1 safari version" 65 | ] 66 | }, 67 | "engines": { 68 | "node": ">=16.14" 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /www/src/analytics/mixpanel.ts: -------------------------------------------------------------------------------- 1 | import mixpanel from "mixpanel-browser" 2 | 3 | export const track = (name: string, data: any) => { 4 | if (!process.env.NODE_ENV || process.env.NODE_ENV === 'development') { 5 | // dev code 6 | } else { 7 | // production code 8 | mixpanel.track(name, data) 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /www/src/components/HomepageFeatures/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import clsx from 'clsx'; 3 | import styles from './styles.module.css'; 4 | 5 | type FeatureItem = { 6 | title: string; 7 | Svg: React.ComponentType>; 8 | description: JSX.Element; 9 | }; 10 | 11 | const FeatureList: FeatureItem[] = [ 12 | { 13 | title: 'Easy to Use', 14 | Svg: require('@site/static/img/undraw_docusaurus_mountain.svg').default, 15 | description: ( 16 | <> 17 | Docusaurus was designed from the ground up to be easily installed and 18 | used to get your website up and running quickly. 19 | 20 | ), 21 | }, 22 | { 23 | title: 'Focus on What Matters', 24 | Svg: require('@site/static/img/undraw_docusaurus_tree.svg').default, 25 | description: ( 26 | <> 27 | Docusaurus lets you focus on your docs, and we'll do the chores. Go 28 | ahead and move your docs into the docs directory. 29 | 30 | ), 31 | }, 32 | { 33 | title: 'Powered by React', 34 | Svg: require('@site/static/img/undraw_docusaurus_react.svg').default, 35 | description: ( 36 | <> 37 | Extend or customize your website layout by reusing React. Docusaurus can 38 | be extended while reusing the same header and footer. 39 | 40 | ), 41 | }, 42 | ]; 43 | 44 | function Feature({title, Svg, description}: FeatureItem) { 45 | return ( 46 |
47 |
48 | 49 |
50 |
51 |

{title}

52 |

{description}

53 |
54 |
55 | ); 56 | } 57 | 58 | export default function HomepageFeatures(): JSX.Element { 59 | return ( 60 |
61 |
62 |
63 | {FeatureList.map((props, idx) => ( 64 | 65 | ))} 66 |
67 |
68 |
69 | ); 70 | } 71 | -------------------------------------------------------------------------------- /www/src/components/HomepageFeatures/styles.module.css: -------------------------------------------------------------------------------- 1 | .features { 2 | display: flex; 3 | align-items: center; 4 | padding: 2rem 0; 5 | width: 100%; 6 | } 7 | 8 | .featureSvg { 9 | height: 200px; 10 | width: 200px; 11 | } 12 | -------------------------------------------------------------------------------- /www/src/components/Layout.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export const Layout = (props) => { 4 | console.log("layout") 5 | return ( 6 |
{props.children}
7 | ) 8 | } 9 | -------------------------------------------------------------------------------- /www/src/components/Newsletter.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export const NewsLetter = (): JSX.Element => { 4 | return ( 5 |
6 |
7 |
8 |

9 | Bridge is getting better every day —
don’t miss out anything. 10 |

11 |

12 | Join the Bridge newsletter and stay updated on new releases, features and guides. 13 |

14 |
15 | 16 | 17 |
18 |
22 |
23 | Join our Discord 24 |
25 |
26 |
27 |
28 |
29 |
30 | ); 31 | }; 32 | -------------------------------------------------------------------------------- /www/src/pages/index.module.css: -------------------------------------------------------------------------------- 1 | /** 2 | * CSS files with the .module.css suffix will be treated as CSS modules 3 | * and scoped locally. 4 | */ 5 | 6 | .heroBanner { 7 | padding: 4rem 0; 8 | text-align: center; 9 | position: relative; 10 | overflow: hidden; 11 | } 12 | 13 | @media screen and (max-width: 996px) { 14 | .heroBanner { 15 | padding: 2rem; 16 | } 17 | } 18 | 19 | .buttons { 20 | display: flex; 21 | align-items: center; 22 | justify-content: center; 23 | } 24 | -------------------------------------------------------------------------------- /www/src/pages/markdown-page.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Markdown page example 3 | --- 4 | 5 | # Markdown page example 6 | 7 | You don't need React to write simple standalone pages. 8 | -------------------------------------------------------------------------------- /www/src/pages/ok.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export default function Page() { 4 | return(
) 5 | } 6 | -------------------------------------------------------------------------------- /www/src/theme/Layout/index.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from 'react'; 2 | import Layout from '@theme-original/Layout'; 3 | import mixpanel from 'mixpanel-browser'; 4 | import { useLocation } from '@docusaurus/router'; 5 | import { inject } from "@vercel/analytics" 6 | 7 | import { Analytics } from '@vercel/analytics/react'; 8 | export default function LayoutWrapper(props) { 9 | if (!process.env.NODE_ENV || process.env.NODE_ENV === 'development') { 10 | // dev code 11 | } else { 12 | // production code 13 | mixpanel.init('0c100fcfe97943ac4ded12a1d1a8cc7f'); 14 | } 15 | 16 | const location = useLocation() 17 | // useEffect(() => { 18 | // if (location.pathname.includes('docs')) { 19 | // const element = document.getElementsByClassName("navbar") 20 | // for (let navItem of element) { 21 | // navItem.style.background = "#111111" 22 | // navItem.style.borderBottom = "solid 1px #272727" 23 | // } 24 | // } 25 | // }, [location]) 26 | 27 | return ( 28 | <> 29 | 30 | 31 | 32 | ); 33 | } 34 | 35 | -------------------------------------------------------------------------------- /www/static/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bridge-codes/bridge/094896601a59d956929eb571e8fe6f5835e3970a/www/static/favicon.png -------------------------------------------------------------------------------- /www/static/img/bg-lines.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bridge-codes/bridge/094896601a59d956929eb571e8fe6f5835e3970a/www/static/img/bg-lines.png -------------------------------------------------------------------------------- /www/static/img/bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bridge-codes/bridge/094896601a59d956929eb571e8fe6f5835e3970a/www/static/img/bg.png -------------------------------------------------------------------------------- /www/static/img/dashboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bridge-codes/bridge/094896601a59d956929eb571e8fe6f5835e3970a/www/static/img/dashboard.png -------------------------------------------------------------------------------- /www/static/img/doc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bridge-codes/bridge/094896601a59d956929eb571e8fe6f5835e3970a/www/static/img/doc.png -------------------------------------------------------------------------------- /www/static/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bridge-codes/bridge/094896601a59d956929eb571e8fe6f5835e3970a/www/static/img/favicon.ico -------------------------------------------------------------------------------- /www/static/img/globe.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bridge-codes/bridge/094896601a59d956929eb571e8fe6f5835e3970a/www/static/img/globe.png -------------------------------------------------------------------------------- /www/static/img/globe.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /www/static/img/logo_b.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /www/static/img/logo_b_round.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /www/static/img/logo_w.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /www/static/img/logo_w_round.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /www/static/studio/customcard.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /www/static/studio/documentation-logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /www/static/studio/dots.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /www/static/studio/fetch-danger.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /www/static/studio/lights.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /www/static/studio/receive.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /www/static/studio/sdk-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /www/static/studio/send.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /www/static/studio/speed.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /www/static/studio/studio-header.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bridge-codes/bridge/094896601a59d956929eb571e8fe6f5835e3970a/www/static/studio/studio-header.png -------------------------------------------------------------------------------- /www/static/studio/tests/bg-main.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bridge-codes/bridge/094896601a59d956929eb571e8fe6f5835e3970a/www/static/studio/tests/bg-main.png -------------------------------------------------------------------------------- /www/static/twitter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bridge-codes/bridge/094896601a59d956929eb571e8fe6f5835e3970a/www/static/twitter.png -------------------------------------------------------------------------------- /www/tailwind.config.js: -------------------------------------------------------------------------------- 1 | // tailwind.config.js 2 | /** @type {import('tailwindcss').Config} */ 3 | module.exports = { 4 | content: [ 5 | // 6 | './src/**/*.{js,jsx,ts,tsx,md,mdx}', 7 | './docs/**/*.{js,jsx,ts,tsx,md,mdx}', 8 | ], 9 | theme: { 10 | extend: { 11 | colors: { 12 | primary: { 13 | dark: 'var(--ifm-color-primary-dark)', 14 | darker: 'var(--ifm-color-primary-darker)', 15 | darkest: 'var(--ifm-color-primary-darkest)', 16 | DEFAULT: 'var(--ifm-color-primary)', 17 | light: 'var(--ifm-color-primary-light)', 18 | lighter: 'var(--ifm-color-primary-lighter)', 19 | lightest: 'var(--ifm-color-primary-lightest)', 20 | }, 21 | t: { 22 | main: '#A175FF', 23 | }, 24 | }, 25 | }, 26 | backgroundImage: (theme) => ({ 27 | grad: 'linear-gradient(76.03deg, rgba(10, 117, 255, 0) 0%, rgba(255, 117, 255, 0.5) 19.01%, rgba(120, 117, 255, 0) 45.83%, rgba(120, 117, 255, 0.4) 73.44%, rgba(120, 117, 255, 0.25) 100%)', 28 | "grad-yellow-green": 'linear-gradient(263.08deg, #75E0A2 0%, rgba(117, 224, 162, 0.25) 21.88%, rgba(117, 224, 162, 0.9) 42.71%, rgba(244, 248, 92, 0.3) 65.1%, rgba(244, 248, 92, 0.9) 84.38%, rgba(244, 248, 92, 0.25) 100%)', 29 | grad2: 30 | 'linear-gradient(76.03deg, rgba(120, 117, 255, 0) 0%, rgba(120, 117, 255, 0.5) 19.01%, rgba(120, 117, 255, 0) 45.83%, rgba(120, 117, 255, 0.4) 73.44%, rgba(120, 117, 255, 0.25) 100%)', 31 | "grad-blue-green": "linear-gradient(253.54deg, #59F7C8 0%, rgba(94, 233, 202, 0.5) 17.71%, rgba(99, 220, 204, 0.9) 34.37%, rgba(105, 200, 207, 0.3) 51.04%, rgba(112, 182, 209, 0.6) 68.23%, rgba(116, 170, 211, 0.3) 84.9%, rgba(120, 158, 213, 0) 91.67%, rgba(125, 144, 215, 0.75) 100%)", 32 | 'gradient-conic': 'conic-gradient(var(--tw-gradient-stops))', 33 | }), 34 | keyframes: { 35 | disco: { 36 | '0%': { transform: 'translateY(-50%) rotate(0deg)' }, 37 | '100%': { transform: 'translateY(-50%) rotate(360deg)' }, 38 | }, 39 | }, 40 | animation: { 41 | disco: 'disco 1.5s linear infinite', 42 | }, 43 | }, 44 | darkMode: ['class', '[data-theme="dark"]'], 45 | plugins: [require('@tailwindcss/line-clamp')], 46 | }; 47 | -------------------------------------------------------------------------------- /www/test.ts: -------------------------------------------------------------------------------- 1 | import { API } from 'sdk'; 2 | -------------------------------------------------------------------------------- /www/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | // This file is not used in compilation. It is here just for a nice editor experience. 3 | "extends": "@tsconfig/docusaurus/tsconfig.json", 4 | "compilerOptions": { 5 | "baseUrl": "." 6 | } 7 | } 8 | --------------------------------------------------------------------------------