├── .eslintignore ├── .eslintrc.js ├── .gitattributes ├── .gitignore ├── .npmignore ├── .travis.yml ├── CLIENT-ERRORS-SAMPLE.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── jest.config.js ├── lib ├── cli.d.ts ├── cli.js ├── cli.js.map ├── commands │ ├── ask │ │ ├── ask.d.ts │ │ ├── ask.js │ │ ├── ask.js.map │ │ ├── createRawFileFromEntries.d.ts │ │ ├── createRawFileFromEntries.js │ │ ├── createRawFileFromEntries.js.map │ │ ├── index.d.ts │ │ ├── index.js │ │ ├── index.js.map │ │ ├── makeHeaders.d.ts │ │ ├── makeHeaders.js │ │ ├── makeHeaders.js.map │ │ ├── queryServer.d.ts │ │ ├── queryServer.js │ │ └── queryServer.js.map │ ├── generate │ │ ├── checkEntries.d.ts │ │ ├── checkEntries.js │ │ ├── checkEntries.js.map │ │ ├── createError.d.ts │ │ ├── createError.js │ │ ├── createError.js.map │ │ ├── generate.d.ts │ │ ├── generate.js │ │ ├── generate.js.map │ │ ├── generateRawClass.d.ts │ │ ├── generateRawClass.js │ │ ├── generateRawClass.js.map │ │ ├── index.d.ts │ │ ├── index.js │ │ └── index.js.map │ ├── index.d.ts │ ├── index.js │ └── index.js.map ├── index.d.ts ├── index.js ├── index.js.map ├── types.d.ts ├── types.js ├── types.js.map ├── utils │ ├── fs-prophecy.d.ts │ ├── fs-prophecy.js │ ├── fs-prophecy.js.map │ ├── index.d.ts │ ├── index.js │ └── index.js.map ├── writeClassFile.d.ts ├── writeClassFile.js └── writeClassFile.js.map ├── package-lock.json ├── package.json ├── src ├── _specs-utils │ ├── index.ts │ └── jest-fs.ts ├── cli.ts ├── commands │ ├── ask │ │ ├── ask.ts │ │ ├── createRawFileFromEntries.spec.ts │ │ ├── createRawFileFromEntries.ts │ │ ├── index.ts │ │ ├── makeHeaders.spec.ts │ │ ├── makeHeaders.ts │ │ └── queryServer.ts │ ├── generate │ │ ├── checkEntries.spec.ts │ │ ├── checkEntries.ts │ │ ├── createError.spec.ts │ │ ├── createError.ts │ │ ├── generate.spec.ts │ │ ├── generate.ts │ │ ├── generateRawClass.spec.ts │ │ ├── generateRawClass.ts │ │ └── index.ts │ └── index.ts ├── index.ts ├── types.ts ├── utils │ ├── fs-prophecy.ts │ └── index.ts ├── writeClassFile.spec.ts └── writeClassFile.ts └── tsconfig.json /.eslintignore: -------------------------------------------------------------------------------- 1 | lib/ -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { 4 | node: true, 5 | es6: true, 6 | "jest/globals": true 7 | }, 8 | parser: '@typescript-eslint/parser', 9 | parserOptions: { 10 | project: './tsconfig.json', 11 | }, 12 | plugins: [ 13 | '@typescript-eslint', 14 | ], 15 | extends: [ 16 | 'airbnb-typescript' 17 | ], 18 | }; -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .env* 2 | node_modules 3 | .idea 4 | .vscode 5 | *.log 6 | _generated/ 7 | _generated/Errors.ts 8 | .nyc_output 9 | coverage 10 | Errors.ts 11 | errors.json 12 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | src -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: '8' 3 | cache: 4 | directories: 5 | - node_modules 6 | after_success: npm run coverage -------------------------------------------------------------------------------- /CLIENT-ERRORS-SAMPLE.md: -------------------------------------------------------------------------------- 1 | Here you will find an example of client-side generated `Errors.ts`; 2 | 3 | ```ts 4 | /* tslint:disable */ 5 | import { ApolloError } from "apollo-client"; 6 | import { GraphQLError } from "graphql"; 7 | 8 | export enum PropheticErrorCode { 9 | CodeLessError = 'NONE', 10 | UnknownError = "UNKNOWN", 11 | ForbiddenError = "FORBIDDEN", 12 | AuthenticationRequiredError = "AUTH_REQUIRED" 13 | } 14 | 15 | export class PropheticError { 16 | constructor(public codes: string[]){} 17 | 18 | private inCodes(code: PropheticErrorCode){ return this.codes.indexOf(code) > -1; } 19 | 20 | get isCodeLessError() { return this.inCodes(PropheticErrorCode.CodeLessError); } 21 | get isUnknownError() { return this.inCodes(PropheticErrorCode.UnknownError); } 22 | get isForbiddenError() { return this.inCodes(PropheticErrorCode.ForbiddenError); } 23 | get isAuthenticationRequiredError() { return this.inCodes(PropheticErrorCode.AuthenticationRequiredError); } 24 | } 25 | 26 | export interface Handler { 27 | (): any 28 | } 29 | 30 | export class PropheticErrorHandled { 31 | private handler: Handler = () => {} 32 | 33 | constructor(public codes: string[]){} 34 | 35 | private inCodes(code: PropheticErrorCode, handler: Handler){ 36 | if(this.codes.indexOf(code) > -1){ 37 | this.handler = handler 38 | } 39 | 40 | return this; 41 | } 42 | 43 | CodeLessError(handler: Handler) { return this.inCodes(PropheticErrorCode.CodeLessError, handler); } 44 | UnknownError(handler: Handler) { return this.inCodes(PropheticErrorCode.UnknownError, handler); } 45 | ForbiddenError(handler: Handler) { return this.inCodes(PropheticErrorCode.ForbiddenError, handler); } 46 | AuthenticationRequiredError(handler: Handler) { return this.inCodes(PropheticErrorCode.AuthenticationRequiredError, handler); } 47 | handle() { return this.handler(); } 48 | } 49 | 50 | const CODE_LESS_EXTENSION = { code: 'NONE'}; 51 | const findCodes = (error: ApolloError | GraphQLError): PropheticErrorCode[] => { 52 | if(error instanceof ApolloError) { 53 | return error.graphQLErrors.map((gError) => findCodes(gError)[0]); 54 | } else if(error.extensions) { 55 | const { extensions: { code } = CODE_LESS_EXTENSION } = error; 56 | return [code]; 57 | } 58 | 59 | return [PropheticErrorCode.CodeLessError]; 60 | } 61 | 62 | export const errorHere = (error: ApolloError | GraphQLError | undefined ) => { 63 | if(!error) { 64 | return new PropheticError([]); 65 | } 66 | const codes = findCodes(error); 67 | return new PropheticError(codes); 68 | } 69 | 70 | export const isThis = (error: ApolloError | GraphQLError | undefined) => { 71 | if(!error) { 72 | return new PropheticErrorHandled([]); 73 | } 74 | const codes = findCodes(error); 75 | return new PropheticErrorHandled(codes); 76 | } 77 | ``` -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## Contributing 2 | [![Build status](https://travis-ci.com/theGlenn/apollo-prophecy.svg?branch=master&style=flat-square)](https://travis-ci.com/theGlenn/apollo-prophecy) 3 | 4 | > **Working on your first Pull Request?**
5 | > You can learn how from this *free* series [How to Contribute to an Open Source Project on GitHub](https://egghead.io/series/how-to-contribute-to-an-open-source-project-on-github) 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
Grab an issue
🍴Fork develop
👨‍💻Code
🛠Test
📩Pull Request
15 |

💥💥💥

16 |
17 | 18 | ### TODO 19 | 20 | * See [#2][i2]: Add support for third-party errors libraries like [apollo-errors](https://github.com/thebigredgeek/apollo-errors) good first issue 21 | * See [#12][i12]: Add `errors.json` file as ask command argument good first issue 22 | * See [#16][i16]: Add language specific code generation [Java/Kotlin][i13]/[Swift][i14]/[Javascript][i15] 23 | * See [#13][i13]: Java/Kotlin good first issue 24 | * See [#14][i14]: Swift good first issue 25 | * See [#15][i15]: Javascript good first issue 26 | 27 | 28 | [i2]: https://github.com/theGlenn/apollo-prophecy/issues/2 29 | [i12]: https://github.com/theGlenn/apollo-prophecy/issues/12 30 | 31 | [i13]: https://github.com/theGlenn/apollo-prophecy/issues/13 32 | [i14]: https://github.com/theGlenn/apollo-prophecy/issues/14 33 | [i15]: https://github.com/theGlenn/apollo-prophecy/issues/15 34 | 35 | [i16]: https://github.com/theGlenn/apollo-prophecy/issues/16 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Glenn Sonna 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. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 |

Apollo Prophecy

3 | 4 |
5 | 👁📟👁 6 |
You shall fail... successfully 7 |
8 | 9 |
10 | Command tool to generate errors files for your Appolo Server and Client 11 |
12 | 13 |
14 | 15 | 16 | NPM version 18 | 19 | 20 | 21 | 22 | Build Status 24 | 25 | 26 | 27 | 28 | Coverage Status 30 | 31 | 32 | 33 | 34 | License 36 | 37 | 38 | 39 | 40 | Contribute 42 | 43 | 44 | 45 | 46 | GitHub good first issue 48 | 49 |
50 |
51 | 52 | follow on Twitter 53 |
54 | 55 | ## 📟 Features 56 | 57 | * Generate **Server-side** **throwable** errors in your resolvers like `throw new NotAProphetError()` 58 | * Expose **machine readable** graphql errors through your api documentation 59 | * Generate **Client-side** Apollo errors **consumable** like `errorHere(error).isNotAProphetError ?` 60 | 61 | # 📋 Table of Contents 62 | 63 | * [Installation](#installation) 64 | * [Usage](#usage) 65 | * [Server Side](#server) 66 | * [Client Side](#client) 67 | * [Contributing](#contributing) 68 | * [TODO](#todo) 69 | * [Test and Miscellaneous](#run-tests) 70 | 71 | ## Installation 72 | 73 | Install with npm: 74 | 75 | ```sh 76 | npm install -g apollo-prophecy 77 | ``` 78 | 79 | Install with yarn: 80 | 81 | ```sh 82 | yarn global add apollo-prophecy 83 | ``` 84 | 85 | ## Usage 86 | 87 | ``` 88 | Usage: apollo-prophecy [command] 89 | 90 | Commands: 91 | apollo-prophecy generate [--out] 92 | apollo-prophecy ask [--type] [--out] 93 | 94 | Options: 95 | -h, --help Show help [boolean] 96 | -v, --version Display version number [boolean] 97 | ``` 98 | 99 | ### Server 100 | > ⚠️ This Project is *Only* compatible with Apollo-Server v2 101 | 102 | #### `generate` command 103 | 104 | Creates `Error.ts` from a `JSON` input file. Using `--out` param you can change the name and location. 105 | 106 | ```sh 107 | apollo-prophecy generate errors.json 108 | ``` 109 | 110 | Input file entries should at least contains the keys `message` and `code`. 111 | 112 | For example given the following `errors.json` as input: 113 | 114 | ```json 115 | { 116 | "AuthenticationRequiredError": { 117 | "message": "You must be logged in to do this", 118 | "code": "AUTH_REQUIRED" 119 | }, 120 | "UserNotFoundError": { 121 | "message": "No user found", 122 | "code": "USER_NOT_FOUND" 123 | }, 124 | } 125 | ``` 126 | 127 | Apollo Prophecy will generate the following `Errors.ts` 128 | 129 | ```ts 130 | ... 131 | export class AuthenticationRequiredError extends ProphecyError { 132 | constructor(properties?: Record) { 133 | super("AuthenticationRequiredError", "You must be logged in to do this","AUTH_REQUIRED", properties); 134 | } 135 | } 136 | 137 | export class UserNotFoundError extends ProphecyError { 138 | constructor(properties?: Record) { 139 | super("UserNotFoundError", "No user found", "USER_NOT_FOUND", properties); 140 | } 141 | } 142 | ... 143 | ``` 144 | 145 | Now you can use it the following way `throw new UserNotFoundError()` in your resolvers. 146 | 147 | `apollo-prophecy` also exposes a `definitions` object and a graphql type definition named `PropheticError` so that you can expose all your errors descriptions through a resolver, [see Client](###client). 148 | 149 | ```ts 150 | ... 151 | export const definitions = [{ 152 | "name": "AuthenticationRequiredError" 153 | "message": "You must be logged in to do this", 154 | "extensions": { 155 | "code": "AUTH_REQUIRED" 156 | } 157 | }, { 158 | "name": "UserNotFoundError" 159 | "message": "No user found", 160 | "extensions": { 161 | "code": "USER_NOT_FOUND" 162 | } 163 | } 164 | }]; 165 | 166 | export const errorType = ` 167 | type PropheticErrorExtensions { 168 | code: String 169 | } 170 | 171 | type PropheticError { 172 | message: String? 173 | extensions: PropheticErrorExtensions 174 | } 175 | `; 176 | ... 177 | ``` 178 | 179 | ### Client 180 | 181 | #### `ask` command 182 | 183 | Queries the `errors` field on the specified graphql endpoint and creates an `Errors.ts` file containing helpers with **check methods** ([see Helpers](#helpers)) for all the errors exposed through the server api documentation. 184 | 185 | ```sh 186 | apollo-prophecy ask http://localhost:3000/graphql 187 | ``` 188 | 189 | #### Helpers 190 | 191 | In order to easily handle erros with **Apollo-Client**, the generated `Errors.ts` exposes two helpers methods `errorHere` and `isThis`, both methods takes one paramater of type `ApolloError` or `GraphQLError`. 192 | 193 | ##### `errorHere()` function 194 | 195 | `errorHere` returns an object that has a **property** named after each errors. 196 | You can perform a simple `boolean` check on the `error` argument by calling the approiate *key*. 197 | 198 | ```ts 199 | import { errorHere } from `./_generated/Errors.ts`; 200 | 201 | ...(error) => { 202 | if(errorHere(error).isUserNotFoundError){ 203 | // Do something 204 | } else if(errorHere(error).isNotAProphetError){ 205 | // Do something else 206 | } 207 | } 208 | ``` 209 | 210 | ##### `isThis()` function 211 | 212 | `isThis` returns an object that has a **handler** method for each errors. 213 | It perfoms a simple check on the `error` argument, if the it succeed the corresponding handler is called otherwise nothing happens. 214 | 215 | Note: Handlers can return a values. 216 | 217 | ```ts 218 | import { isThis } from `./_generated/Errors.ts`; 219 | 220 | ...(error) => { 221 | isThis(error) 222 | .UserNotFoundError(() => ...) 223 | .NotAProphetError(() => ...) 224 | .handle() 225 | } 226 | ``` 227 | 228 | React example: 229 | 230 | ```tsx 231 | import { isThis } from `./_generated/Errors.ts`; 232 | 233 | ...(error) => { 234 | return

235 | { 236 | isThis(error) 237 | .UserNotFoundError(() => Could not find a user with tha name) 238 | .NotAProphetError(() => Only Prophets can perfom this kind of actions...) 239 | .handle(); 240 | } 241 |

242 | } 243 | ``` 244 | 245 | ## Contributing 246 | [![Build status](https://travis-ci.com/theGlenn/apollo-prophecy.svg?branch=master&style=flat-square)](https://travis-ci.com/theGlenn/apollo-prophecy) 247 | 248 |
249 | 250 | 251 | 252 | 253 | 254 | 255 |
Grab an issue
🍴fork develop
👨‍💻Code
🛠Test
📩Pull Request
256 |

💥💥💥

257 |
258 | 259 | ### TODO 260 | 261 | * See [#2][i2]: Add support for third-party errors libraries like [apollo-errors](https://github.com/thebigredgeek/apollo-errors) good first issue 262 | * See [#12][i12]: Add `errors.json` file as ask command argument good first issue 263 | * See [#16][i16]: Add language specific code generation [Java/Kotlin][i13]/[Swift][i14]/[Javascript][i15] 264 | * See [#13][i13]: Java/Kotlin good first issue 265 | * See [#14][i14]: Swift good first issue 266 | * See [#15][i15]: Javascript good first issue 267 | 268 | 269 | [i2]: https://github.com/theGlenn/apollo-prophecy/issues/2 270 | [i12]: https://github.com/theGlenn/apollo-prophecy/issues/12 271 | 272 | [i13]: https://github.com/theGlenn/apollo-prophecy/issues/13 273 | [i14]: https://github.com/theGlenn/apollo-prophecy/issues/14 274 | [i15]: https://github.com/theGlenn/apollo-prophecy/issues/15 275 | 276 | [i16]: https://github.com/theGlenn/apollo-prophecy/issues/16 277 | 278 | ### Run tests 279 | 280 | ```sh 281 | npm test 282 | ``` 283 | 284 | ```sh 285 | yarn test 286 | ``` -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | const jestOverwrites = { 2 | roots: ['src'], 3 | testMatch: ['**/*.spec.ts'], 4 | watchPathIgnorePatterns: ['/node_modules'], 5 | }; 6 | 7 | module.exports = { 8 | ...jestOverwrites, 9 | clearMocks: true, 10 | collectCoverage: true, 11 | preset: 'ts-jest' 12 | }; -------------------------------------------------------------------------------- /lib/cli.d.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | export {}; 3 | -------------------------------------------------------------------------------- /lib/cli.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | "use strict"; 3 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 4 | return new (P || (P = Promise))(function (resolve, reject) { 5 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 6 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 7 | function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } 8 | step((generator = generator.apply(thisArg, _arguments || [])).next()); 9 | }); 10 | }; 11 | var __generator = (this && this.__generator) || function (thisArg, body) { 12 | var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; 13 | return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; 14 | function verb(n) { return function (v) { return step([n, v]); }; } 15 | function step(op) { 16 | if (f) throw new TypeError("Generator is already executing."); 17 | while (_) try { 18 | if (f = 1, y && (t = y[op[0] & 2 ? "return" : op[0] ? "throw" : "next"]) && !(t = t.call(y, op[1])).done) return t; 19 | if (y = 0, t) op = [0, t.value]; 20 | switch (op[0]) { 21 | case 0: case 1: t = op; break; 22 | case 4: _.label++; return { value: op[1], done: false }; 23 | case 5: _.label++; y = op[1]; op = [0]; continue; 24 | case 7: op = _.ops.pop(); _.trys.pop(); continue; 25 | default: 26 | if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } 27 | if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } 28 | if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } 29 | if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } 30 | if (t[2]) _.ops.pop(); 31 | _.trys.pop(); continue; 32 | } 33 | op = body.call(thisArg, _); 34 | } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } 35 | if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; 36 | } 37 | }; 38 | var _this = this; 39 | Object.defineProperty(exports, "__esModule", { value: true }); 40 | var yargs = require("yargs"); 41 | var _1 = require("./"); 42 | var path_1 = require("path"); 43 | function handleError(error) { 44 | console.error(error); 45 | process.exit(1); 46 | } 47 | process.on('unhandledRejection', function (error) { throw error; }); 48 | process.on('uncaughtException', handleError); 49 | ; 50 | yargs 51 | .command(['generate [jsonFile]', 'prophecy'], 'Generate a Typescript file containg all application errors from a JSON input', { 52 | out: { 53 | demand: true, 54 | describe: 'Output path for error file', 55 | default: './Errors.ts', 56 | normalize: true, 57 | coerce: path_1.resolve 58 | }, 59 | }, function (argv) { return __awaiter(_this, void 0, void 0, function () { 60 | var _a, jsonFile, outputFilePath, intputFilePath, generatedOutputPath; 61 | return __generator(this, function (_b) { 62 | switch (_b.label) { 63 | case 0: 64 | _a = argv.jsonFile, jsonFile = _a === void 0 ? './errors.json' : _a, outputFilePath = argv.out; 65 | intputFilePath = path_1.resolve(jsonFile); 66 | return [4 /*yield*/, _1.commands.generate({ intputFilePath: intputFilePath, outputFilePath: outputFilePath })]; 67 | case 1: 68 | generatedOutputPath = _b.sent(); 69 | console.info('🔮 You will fail... but successfully'); 70 | console.info("\u2514\u2500\u2500 \uD83D\uDC41 Prophecy available at " + generatedOutputPath); 71 | return [2 /*return*/]; 72 | } 73 | }); 74 | }); }) 75 | .command(['ask ', 'oracles'], 'Generate a .ts file that exposes helpers for all the errors exposed through the server api', { 76 | field: { 77 | demandOption: true, 78 | describe: 'Field to query on Query type', 79 | default: 'errors', 80 | }, 81 | out: { 82 | demandOption: true, 83 | describe: 'Output path for error file', 84 | default: './Errors.ts', 85 | normalize: true, 86 | coerce: path_1.resolve 87 | }, 88 | }, function (argv) { return __awaiter(_this, void 0, void 0, function () { 89 | var input, outputFilePath, errorField, outputPath; 90 | return __generator(this, function (_a) { 91 | switch (_a.label) { 92 | case 0: 93 | console.info('🔮 Connecting with the oracles...'); 94 | input = argv.schema, outputFilePath = argv.out, errorField = argv.field; 95 | return [4 /*yield*/, _1.commands.ask({ input: input, errorField: errorField, outputFilePath: outputFilePath })]; 96 | case 1: 97 | outputPath = _a.sent(); 98 | console.info('├── 🙏 You will fail... but successfully'); 99 | console.info("\u2514\u2500\u2500 \uD83D\uDC41 All you need to know is available at " + outputPath); 100 | return [2 /*return*/]; 101 | } 102 | }); 103 | }); }) 104 | .fail(function (message, error) { return handleError(error ? error : new Error(message)); }) 105 | .help() 106 | .version() 107 | .strict() 108 | .argv; 109 | //# sourceMappingURL=cli.js.map -------------------------------------------------------------------------------- /lib/cli.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AACA,iBAuEQ;;AAvER,6BAA+B;AAE/B,uBAA8B;AAC9B,6BAA+B;AAE/B,qBAAqB,KAAK;IACxB,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IACrB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC;AAED,OAAO,CAAC,EAAE,CAAC,oBAAoB,EAAE,UAAC,KAAK,IAAO,MAAM,KAAK,CAAA,CAAC,CAAC,CAAC,CAAC;AAC7D,OAAO,CAAC,EAAE,CAAC,mBAAmB,EAAE,WAAW,CAAC,CAAC;AAQ5C,CAAC;AAEF,KAAK;KACF,OAAO,CACN,CAAC,qBAAqB,EAAE,UAAU,CAAC,EACnC,8EAA8E,EAC9E;IACE,GAAG,EAAE;QACH,MAAM,EAAE,IAAI;QACZ,QAAQ,EAAE,4BAA4B;QACtC,OAAO,EAAE,aAAa;QACtB,SAAS,EAAE,IAAI;QACf,MAAM,EAAE,cAAO;KAChB;CACF,EACD,UAAO,IAAU;;;;;gBACP,KAAoD,IAAI,SAA9B,EAA1B,QAAQ,mBAAG,eAAe,KAAA,EAAO,cAAc,GAAK,IAAI,IAAT,CAAU;gBAC3D,cAAc,GAAG,cAAO,CAAC,QAAQ,CAAC,CAAC;gBACb,qBAAM,WAAQ,CAAC,QAAQ,CAAC,EAAE,cAAc,gBAAA,EAAE,cAAc,gBAAA,EAAE,CAAC,EAAA;;gBAAjF,mBAAmB,GAAG,SAA2D;gBACvF,OAAO,CAAC,IAAI,CAAC,sCAAsC,CAAC,CAAC;gBACrD,OAAO,CAAC,IAAI,CAAC,2DAAgC,mBAAqB,CAAC,CAAC;;;;KACrE,CACF;KACA,OAAO,CACN,CAAC,cAAc,EAAE,SAAS,CAAC,EAC3B,4FAA4F,EAC5F;IACE,KAAK,EAAE;QACL,YAAY,EAAE,IAAI;QAClB,QAAQ,EAAE,8BAA8B;QACxC,OAAO,EAAE,QAAQ;KAClB;IACD,GAAG,EAAE;QACH,YAAY,EAAE,IAAI;QAClB,QAAQ,EAAE,4BAA4B;QACtC,OAAO,EAAE,aAAa;QACtB,SAAS,EAAE,IAAI;QACf,MAAM,EAAE,cAAO;KAChB;CACF,EACD,UAAO,IAAU;;;;;gBACf,OAAO,CAAC,IAAI,CAAC,mCAAmC,CAAC,CAAC;gBAClC,KAAK,GAA6C,IAAI,OAAjD,EAAO,cAAc,GAAwB,IAAI,IAA5B,EAAS,UAAU,GAAK,IAAI,MAAT,CAAU;gBACpD,qBAAM,WAAQ,CAAC,GAAG,CAAC,EAAE,KAAK,OAAA,EAAE,UAAU,YAAA,EAAE,cAAc,gBAAA,EAAE,CAAC,EAAA;;gBAAtE,UAAU,GAAG,SAAyD;gBAC5E,OAAO,CAAC,IAAI,CAAC,0CAA0C,CAAC,CAAC;gBACzD,OAAO,CAAC,IAAI,CAAC,0EAA+C,UAAY,CAAC,CAAC;;;;KAC3E,CACF;KACA,IAAI,CAAC,UAAC,OAAO,EAAE,KAAK,IAAK,OAAA,WAAW,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,EAA/C,CAA+C,CAAC;KACzE,IAAI,EAAE;KACN,OAAO,EAAE;KACT,MAAM,EAAE;KACR,IAAI,CAAC"} -------------------------------------------------------------------------------- /lib/commands/ask/ask.d.ts: -------------------------------------------------------------------------------- 1 | export interface AskArgs { 2 | input?: string; 3 | outputFilePath?: string; 4 | errorField?: string; 5 | headers?: string[]; 6 | } 7 | export default function ask({ input, errorField, headers, outputFilePath }: AskArgs): Promise; 8 | -------------------------------------------------------------------------------- /lib/commands/ask/ask.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 3 | return new (P || (P = Promise))(function (resolve, reject) { 4 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 5 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 6 | function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } 7 | step((generator = generator.apply(thisArg, _arguments || [])).next()); 8 | }); 9 | }; 10 | var __generator = (this && this.__generator) || function (thisArg, body) { 11 | var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; 12 | return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; 13 | function verb(n) { return function (v) { return step([n, v]); }; } 14 | function step(op) { 15 | if (f) throw new TypeError("Generator is already executing."); 16 | while (_) try { 17 | if (f = 1, y && (t = y[op[0] & 2 ? "return" : op[0] ? "throw" : "next"]) && !(t = t.call(y, op[1])).done) return t; 18 | if (y = 0, t) op = [0, t.value]; 19 | switch (op[0]) { 20 | case 0: case 1: t = op; break; 21 | case 4: _.label++; return { value: op[1], done: false }; 22 | case 5: _.label++; y = op[1]; op = [0]; continue; 23 | case 7: op = _.ops.pop(); _.trys.pop(); continue; 24 | default: 25 | if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } 26 | if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } 27 | if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } 28 | if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } 29 | if (t[2]) _.ops.pop(); 30 | _.trys.pop(); continue; 31 | } 32 | op = body.call(thisArg, _); 33 | } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } 34 | if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; 35 | } 36 | }; 37 | Object.defineProperty(exports, "__esModule", { value: true }); 38 | var createRawFileFromEntries_1 = require("./createRawFileFromEntries"); 39 | var writeClassFile_1 = require("../../writeClassFile"); 40 | var makeHeaders_1 = require("./makeHeaders"); 41 | var queryServer_1 = require("./queryServer"); 42 | function ask(_a) { 43 | var input = _a.input, errorField = _a.errorField, headers = _a.headers, outputFilePath = _a.outputFilePath; 44 | return __awaiter(this, void 0, void 0, function () { 45 | var urlRegex, errorEntries, rawFileContent; 46 | return __generator(this, function (_b) { 47 | switch (_b.label) { 48 | case 0: 49 | urlRegex = /^https?:\/\//i; 50 | errorEntries = []; 51 | if (!urlRegex.test(input)) return [3 /*break*/, 2]; 52 | return [4 /*yield*/, queryServer_1.default(input, errorField, makeHeaders_1.makeHeaders(headers))]; 53 | case 1: 54 | errorEntries = _b.sent(); 55 | _b.label = 2; 56 | case 2: 57 | rawFileContent = createRawFileFromEntries_1.default(errorEntries); 58 | return [2 /*return*/, writeClassFile_1.default(rawFileContent, outputFilePath)]; 59 | } 60 | }); 61 | }); 62 | } 63 | exports.default = ask; 64 | //# sourceMappingURL=ask.js.map -------------------------------------------------------------------------------- /lib/commands/ask/ask.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"ask.js","sourceRoot":"","sources":["../../../src/commands/ask/ask.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,uEAA+D;AAC/D,uDAAkD;AAElD,6CAA4C;AAC5C,6CAAwC;AASxC,aAAkC,EAAuD;QAArD,gBAAK,EAAE,0BAAU,EAAE,oBAAO,EAAE,kCAAc;;;;;;oBACtE,QAAQ,GAAG,eAAe,CAAC;oBAE7B,YAAY,GAAuB,EAAE,CAAA;yBACrC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,EAApB,wBAAoB;oBACP,qBAAM,qBAAW,CAAC,KAAK,EAAE,UAAU,EAAE,yBAAW,CAAC,OAAO,CAAC,CAAC,EAAA;;oBAAzE,YAAY,GAAG,SAA0D,CAAC;;;oBAEtE,cAAc,GAAG,kCAAqB,CAAC,YAAY,CAAC,CAAC;oBAC3D,sBAAO,wBAAc,CAAC,cAAc,EAAE,cAAc,CAAC,EAAC;;;;CACvD;AATD,sBASC"} -------------------------------------------------------------------------------- /lib/commands/ask/createRawFileFromEntries.d.ts: -------------------------------------------------------------------------------- 1 | import { ErrorOutputEntry } from "../../types"; 2 | export default function (entries: ErrorOutputEntry[]): string; 3 | -------------------------------------------------------------------------------- /lib/commands/ask/createRawFileFromEntries.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | var types_1 = require("../../types"); 4 | var TopMostImportsAndConfigDef = "/* tslint:disable */ \nimport { ApolloError } from \"apollo-client\";\nimport { GraphQLError } from \"graphql\";"; 5 | var PropheticErrorCodeEnumDef = "\nexport enum PropheticErrorCode {\n CodeLessError = 'NONE',\n _VALUES_\n}"; 6 | var PropheticErrorDef = "\nexport class PropheticError {\n constructor(public codes: string[]){}\n\n private inCodes(code: PropheticErrorCode){ return this.codes.indexOf(code) > -1; }\n\n get isCodeLessError() { return this.inCodes(PropheticErrorCode.CodeLessError); }\n _FUNCTIONS_\n}"; 7 | var PropheticErrorGetterDef = "get is$_ENUM_$() { return this.inCodes(PropheticErrorCode.$_ENUM_$); }"; 8 | var PropheticErrorHandledDef = "\nexport interface Handler {\n (): any\n}\n\nexport class PropheticErrorHandled {\n private handler: Handler = () => {}\n\n constructor(public codes: string[]){}\n\n private inCodes(code: PropheticErrorCode, handler: Handler){\n if(this.codes.indexOf(code) > -1){\n this.handler = handler\n }\n\n return this;\n }\n\n CodeLessError(handler: Handler) { return this.inCodes(PropheticErrorCode.CodeLessError, handler); }\n _FUNCTIONS_\n handle() { return this.handler(); }\n}"; 9 | var PropheticErrorHandlerDef = "$_ENUM_$(handler: Handler) { return this.inCodes(PropheticErrorCode.$_ENUM_$, handler); }"; 10 | var BottomExportedMethods = "\nconst CODE_LESS_EXTENSION = { code: 'NONE'};\nconst findCodes = (error: ApolloError | GraphQLError): PropheticErrorCode[] => {\n if(error instanceof ApolloError) {\n return error.graphQLErrors.map((gError) => findCodes(gError)[0]);\n } else if(error.extensions) {\n const { extensions: { code } = CODE_LESS_EXTENSION } = error;\n return [code];\n }\n\n return [PropheticErrorCode.CodeLessError];\n}\n\nexport const errorHere = (error: ApolloError | GraphQLError | undefined ) => {\n if(!error) {\n return new PropheticError([]);\n }\n const codes = findCodes(error);\n return new PropheticError(codes);\n}\n\nexport const isThis = (error: ApolloError | GraphQLError | undefined) => {\n if(!error) {\n return new PropheticErrorHandled([]);\n }\n const codes = findCodes(error);\n return new PropheticErrorHandled(codes);\n}"; 11 | var toNameAndCodeTupleArray = function (entriesArray) { 12 | var entries = types_1.toErrorEntries(entriesArray); 13 | return Object.keys(entries).map(function (key) { 14 | var _a = entries[key].extensions, extensions = _a === void 0 ? { code: undefined } : _a; 15 | return [key, extensions.code]; 16 | }).filter(function (_a) { 17 | var _ = _a[0], code = _a[1]; 18 | return code !== undefined; 19 | }); 20 | }; 21 | function default_1(entries) { 22 | var nameAndCodeTuples = toNameAndCodeTupleArray(entries); 23 | var enums = nameAndCodeTuples.map(function (_a) { 24 | var name = _a[0], code = _a[1]; 25 | return name + " = \"" + code + "\""; 26 | }).join(',\n '); 27 | var errorDefFun = nameAndCodeTuples.map(function (_a) { 28 | var name = _a[0]; 29 | return "" + PropheticErrorGetterDef.replace("$_ENUM_$", name).replace("$_ENUM_$", name); 30 | }).join('\n '); 31 | var errorHandledFun = nameAndCodeTuples.map(function (_a) { 32 | var name = _a[0]; 33 | return "" + PropheticErrorHandlerDef.replace("$_ENUM_$", name).replace("$_ENUM_$", name); 34 | }).join('\n '); 35 | return TopMostImportsAndConfigDef + "\n " + PropheticErrorCodeEnumDef.replace('_VALUES_', enums) + "\n " + PropheticErrorDef.replace('_FUNCTIONS_', errorDefFun) + "\n " + PropheticErrorHandledDef.replace('_FUNCTIONS_', errorHandledFun) + "\n " + BottomExportedMethods; 36 | } 37 | exports.default = default_1; 38 | //# sourceMappingURL=createRawFileFromEntries.js.map -------------------------------------------------------------------------------- /lib/commands/ask/createRawFileFromEntries.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"createRawFileFromEntries.js","sourceRoot":"","sources":["../../../src/commands/ask/createRawFileFromEntries.ts"],"names":[],"mappings":";;AAAA,qCAAwF;AAExF,IAAM,0BAA0B,GAAG,kHAEK,CAAA;AAExC,IAAM,yBAAyB,GAAG,8EAIhC,CAAC;AAEH,IAAM,iBAAiB,GAAG,0QAQxB,CAAC;AAEH,IAAM,uBAAuB,GAAG,wEAAwE,CAAC;AAEzG,IAAM,wBAAwB,GAAG,gfAqB/B,CAAA;AACF,IAAM,wBAAwB,GAAG,2FAA2F,CAAC;AAE7H,IAAM,qBAAqB,GAAG,q1BA2B5B,CAAA;AAEF,IAAM,uBAAuB,GAAG,UAAC,YAAgC;IAC/D,IAAM,OAAO,GAAG,sBAAc,CAAC,YAAY,CAAC,CAAC;IAC7C,OAAO,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,UAAC,GAAG;QAC1B,IAAA,4BAAgC,EAAhC,qDAAgC,CAAkB;QAC1D,OAAO,CAAC,GAAG,EAAE,UAAU,CAAC,IAAI,CAAqB,CAAA;IACnD,CAAC,CAAC,CAAC,MAAM,CAAC,UAAC,EAAS;YAAR,SAAC,EAAE,YAAI;QAAM,OAAA,IAAI,KAAK,SAAS;IAAlB,CAAkB,CAAC,CAAC;AAC/C,CAAC,CAAA;AAED,mBAAwB,OAA2B;IACjD,IAAM,iBAAiB,GAAG,uBAAuB,CAAC,OAAO,CAAC,CAAC;IAC3D,IAAM,KAAK,GAAG,iBAAiB,CAAC,GAAG,CAAC,UAAC,EAAY;YAAX,YAAI,EAAE,YAAI;QAAM,OAAG,IAAI,aAAO,IAAI,OAAG;IAArB,CAAqB,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAE3F,IAAM,WAAW,GAAG,iBAAiB,CAAC,GAAG,CAAC,UAAC,EAAM;YAAL,YAAI;QAC9C,OAAO,KAAG,uBAAuB,CAAC,OAAO,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC,OAAO,CAAC,UAAU,EAAE,IAAI,CAAG,CAAC;IAC1F,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAEhB,IAAM,eAAe,GAAG,iBAAiB,CAAC,GAAG,CAAC,UAAC,EAAM;YAAL,YAAI;QAClD,OAAO,KAAG,wBAAwB,CAAC,OAAO,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC,OAAO,CAAC,UAAU,EAAE,IAAI,CAAG,CAAC;IAC3F,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAEhB,OAAU,0BAA0B,YAClC,yBAAyB,CAAC,OAAO,CAAC,UAAU,EAAE,KAAK,CAAC,YACpD,iBAAiB,CAAC,OAAO,CAAC,aAAa,EAAE,WAAW,CAAC,YACrD,wBAAwB,CAAC,OAAO,CAAC,aAAa,EAAE,eAAe,CAAC,YAChE,qBAAuB,CAAC;AAC5B,CAAC;AAjBD,4BAiBC"} -------------------------------------------------------------------------------- /lib/commands/ask/index.d.ts: -------------------------------------------------------------------------------- 1 | export { default as ask } from "./ask"; 2 | -------------------------------------------------------------------------------- /lib/commands/ask/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | var ask_1 = require("./ask"); 4 | exports.ask = ask_1.default; 5 | //# sourceMappingURL=index.js.map -------------------------------------------------------------------------------- /lib/commands/ask/index.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/commands/ask/index.ts"],"names":[],"mappings":";;AAAA,6BAAuC;AAA9B,oBAAA,OAAO,CAAO"} -------------------------------------------------------------------------------- /lib/commands/ask/makeHeaders.d.ts: -------------------------------------------------------------------------------- 1 | export declare const makeHeaders: (headers?: string[]) => {}; 2 | -------------------------------------------------------------------------------- /lib/commands/ask/makeHeaders.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | // https://github.com/apollographql/apollo-codegen/blob/master/packages/apollo-codegen/src/cli.ts 4 | exports.makeHeaders = function (headers) { 5 | if (headers === void 0) { headers = []; } 6 | var additionalHeaders = {}; 7 | for (var _i = 0, headers_1 = headers; _i < headers_1.length; _i++) { 8 | var header = headers_1[_i]; 9 | var separator = header.indexOf(":"); 10 | var name_1 = header.substring(0, separator).trim(); 11 | var value = header.substring(separator + 1).trim(); 12 | if (!(name_1 && value)) { 13 | throw new Error('Headers should be specified as "Name: Value"'); 14 | } 15 | additionalHeaders[name_1] = value; 16 | } 17 | return additionalHeaders; 18 | }; 19 | //# sourceMappingURL=makeHeaders.js.map -------------------------------------------------------------------------------- /lib/commands/ask/makeHeaders.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"makeHeaders.js","sourceRoot":"","sources":["../../../src/commands/ask/makeHeaders.ts"],"names":[],"mappings":";;AAAA,iGAAiG;AACpF,QAAA,WAAW,GAAG,UAAC,OAAsB;IAAtB,wBAAA,EAAA,YAAsB;IAChD,IAAI,iBAAiB,GAAG,EAAE,CAAC;IAC3B,KAAqB,UAAO,EAAP,mBAAO,EAAP,qBAAO,EAAP,IAAO,EAAE;QAAzB,IAAM,MAAM,gBAAA;QACf,IAAM,SAAS,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QACtC,IAAM,MAAI,GAAG,MAAM,CAAC,SAAS,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC,IAAI,EAAE,CAAC;QACnD,IAAM,KAAK,GAAG,MAAM,CAAC,SAAS,CAAC,SAAS,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QACrD,IAAI,CAAC,CAAC,MAAI,IAAI,KAAK,CAAC,EAAE;YACpB,MAAM,IAAI,KAAK,CAAC,8CAA8C,CAAC,CAAC;SACjE;QACD,iBAAiB,CAAC,MAAI,CAAC,GAAG,KAAK,CAAC;KACjC;IACD,OAAO,iBAAiB,CAAC;AAC3B,CAAC,CAAA"} -------------------------------------------------------------------------------- /lib/commands/ask/queryServer.d.ts: -------------------------------------------------------------------------------- 1 | import { ErrorOutputEntry } from '../../types'; 2 | declare type Headers = { 3 | [name: string]: string; 4 | }; 5 | export default function (serverUri: string, field: string, headers: Headers): Promise; 6 | export {}; 7 | -------------------------------------------------------------------------------- /lib/commands/ask/queryServer.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __makeTemplateObject = (this && this.__makeTemplateObject) || function (cooked, raw) { 3 | if (Object.defineProperty) { Object.defineProperty(cooked, "raw", { value: raw }); } else { cooked.raw = raw; } 4 | return cooked; 5 | }; 6 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 7 | return new (P || (P = Promise))(function (resolve, reject) { 8 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 9 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 10 | function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } 11 | step((generator = generator.apply(thisArg, _arguments || [])).next()); 12 | }); 13 | }; 14 | var __generator = (this && this.__generator) || function (thisArg, body) { 15 | var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; 16 | return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; 17 | function verb(n) { return function (v) { return step([n, v]); }; } 18 | function step(op) { 19 | if (f) throw new TypeError("Generator is already executing."); 20 | while (_) try { 21 | if (f = 1, y && (t = y[op[0] & 2 ? "return" : op[0] ? "throw" : "next"]) && !(t = t.call(y, op[1])).done) return t; 22 | if (y = 0, t) op = [0, t.value]; 23 | switch (op[0]) { 24 | case 0: case 1: t = op; break; 25 | case 4: _.label++; return { value: op[1], done: false }; 26 | case 5: _.label++; y = op[1]; op = [0]; continue; 27 | case 7: op = _.ops.pop(); _.trys.pop(); continue; 28 | default: 29 | if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } 30 | if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } 31 | if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } 32 | if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } 33 | if (t[2]) _.ops.pop(); 34 | _.trys.pop(); continue; 35 | } 36 | op = body.call(thisArg, _); 37 | } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } 38 | if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; 39 | } 40 | }; 41 | Object.defineProperty(exports, "__esModule", { value: true }); 42 | var graphql_tag_1 = require("graphql-tag"); 43 | var node_fetch_1 = require("node-fetch"); 44 | var apollo_link_1 = require("apollo-link"); 45 | var apollo_link_http_1 = require("apollo-link-http"); 46 | var makeOperation = function (erroType) { return ({ 47 | query: graphql_tag_1.default(templateObject_1 || (templateObject_1 = __makeTemplateObject(["query {\n ", " {\n name\n message\n extensions {\n code\n }\n }\n }\n "], ["query {\n ", " {\n name\n message\n extensions {\n code\n }\n }\n }\n "])), erroType), 48 | }); }; 49 | function exeuteServerQuery(serverUri, type, headers) { 50 | var operation = makeOperation(type); 51 | var link = new apollo_link_http_1.HttpLink({ uri: serverUri, fetch: node_fetch_1.default, headers: headers }); 52 | return apollo_link_1.makePromise(apollo_link_1.execute(link, operation)); 53 | } 54 | function default_1(serverUri, field, headers) { 55 | if (field === void 0) { field = 'errors'; } 56 | return __awaiter(this, void 0, void 0, function () { 57 | var result, e_1, errors; 58 | return __generator(this, function (_a) { 59 | switch (_a.label) { 60 | case 0: 61 | _a.trys.push([0, 2, , 3]); 62 | return [4 /*yield*/, exeuteServerQuery(serverUri, field, headers)]; 63 | case 1: 64 | result = _a.sent(); 65 | return [3 /*break*/, 3]; 66 | case 2: 67 | e_1 = _a.sent(); 68 | throw e_1; 69 | case 3: 70 | if (result.errors) { 71 | throw new Error("Errors occured querying \"" + field + "\": " + result.errors); 72 | } 73 | if (!result.data || !result.data[field]) { 74 | throw new Error("No Errors found to generate on query field " + field); 75 | } 76 | errors = result.data[field]; 77 | return [2 /*return*/, errors]; 78 | } 79 | }); 80 | }); 81 | } 82 | exports.default = default_1; 83 | var templateObject_1; 84 | //# sourceMappingURL=queryServer.js.map -------------------------------------------------------------------------------- /lib/commands/ask/queryServer.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"queryServer.js","sourceRoot":"","sources":["../../../src/commands/ask/queryServer.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,2CAA8B;AAC9B,yCAA+B;AAC/B,2CAAmD;AACnD,qDAA4C;AAG5C,IAAM,aAAa,GAAG,UAAC,QAAgB,IAAK,OAAA,CAAC;IAC3C,KAAK,EAAE,qBAAG,8KAAA,eACN,EAAQ,0FAQX,KARG,QAAQ,CAQX;CACF,CAAC,EAX0C,CAW1C,CAAC;AAIH,2BAA2B,SAAiB,EAAE,IAAY,EAAE,OAAgB;IAC1E,IAAM,SAAS,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC;IACtC,IAAM,IAAI,GAAG,IAAI,2BAAQ,CAAC,EAAE,GAAG,EAAE,SAAS,EAAE,KAAK,sBAAA,EAAE,OAAO,SAAA,EAAE,CAAC,CAAC;IAE9D,OAAO,yBAAW,CAAC,qBAAO,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC,CAAC;AAC/C,CAAC;AAED,mBAA8B,SAAiB,EAAE,KAAwB,EAAE,OAAgB;IAA1C,sBAAA,EAAA,gBAAwB;;;;;;;oBAG5D,qBAAM,iBAAiB,CAAC,SAAS,EAAE,KAAK,EAAE,OAAO,CAAC,EAAA;;oBAA3D,MAAM,GAAG,SAAkD,CAAC;;;;oBAE5D,MAAM,GAAC,CAAC;;oBAGV,IAAI,MAAM,CAAC,MAAM,EAAE;wBACjB,MAAM,IAAI,KAAK,CAAC,+BAA4B,KAAK,YAAM,MAAM,CAAC,MAAQ,CAAC,CAAC;qBACzE;oBAED,IAAI,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE;wBACvC,MAAM,IAAI,KAAK,CAAC,gDAA8C,KAAO,CAAC,CAAC;qBACxE;oBAEK,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;oBAClC,sBAAO,MAA4B,EAAC;;;;CACrC;AAlBD,4BAkBC"} -------------------------------------------------------------------------------- /lib/commands/generate/checkEntries.d.ts: -------------------------------------------------------------------------------- 1 | import { JsonInputErrorEntryRecord } from '../../types'; 2 | declare const _default: (inputEntries: JsonInputErrorEntryRecord) => JsonInputErrorEntryRecord; 3 | export default _default; 4 | -------------------------------------------------------------------------------- /lib/commands/generate/checkEntries.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | exports.default = (function (inputEntries) { 4 | var checks = Object.keys(inputEntries).map(function (key) { 5 | var entry = inputEntries[key]; 6 | return { name: key, hasCodeKey: entry.code !== undefined }; 7 | }).filter(function (_a) { 8 | var hasCodeKey = _a.hasCodeKey; 9 | return !hasCodeKey; 10 | }); 11 | if (checks.length > 0) { 12 | var concernedNames = checks.map(function (_a) { 13 | var name = _a.name; 14 | return name; 15 | }).join(); 16 | throw Error("[CodeKeyImperative] No \"code\" key found for: [" + concernedNames + "]"); 17 | } 18 | return inputEntries; 19 | }); 20 | //# sourceMappingURL=checkEntries.js.map -------------------------------------------------------------------------------- /lib/commands/generate/checkEntries.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"checkEntries.js","sourceRoot":"","sources":["../../../src/commands/generate/checkEntries.ts"],"names":[],"mappings":";;AAEA,mBAAe,UAAC,YAAuC;IACrD,IAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,GAAG,CAAC,UAAA,GAAG;QAC9C,IAAM,KAAK,GAAG,YAAY,CAAC,GAAG,CAAC,CAAC;QAChC,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,UAAU,EAAE,KAAK,CAAC,IAAI,KAAK,SAAS,EAAE,CAAA;IAC5D,CAAC,CAAC,CAAC,MAAM,CAAC,UAAC,EAAc;YAAZ,0BAAU;QAAO,OAAA,CAAC,UAAU;IAAX,CAAW,CAAC,CAAC;IAE3C,IAAG,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE;QACpB,IAAM,cAAc,GAAG,MAAM,CAAC,GAAG,CAAC,UAAC,EAAQ;gBAAN,cAAI;YAAO,OAAA,IAAI;QAAJ,CAAI,CAAC,CAAC,IAAI,EAAE,CAAC;QAC7D,MAAM,KAAK,CAAC,qDAAiD,cAAc,MAAG,CAAC,CAAC;KACjF;IACD,OAAO,YAAY,CAAC;AACtB,CAAC,EAAA"} -------------------------------------------------------------------------------- /lib/commands/generate/createError.d.ts: -------------------------------------------------------------------------------- 1 | export declare const PropheticErrorTextDef = "class PropheticError extends ApolloError {\n constructor(name: string, message: string, code?: string, properties?: Record) {\n super(message, code, properties);\n\n // Set the prototype explicitly.\n // https://stackoverflow.com/a/41102306\n Object.setPrototypeOf(this, SyntaxError.prototype);\n Object.defineProperty(this, 'name', { value: name });\n }\n}"; 2 | export default function (name: string, message: string, code: string, properties?: Record): string; 3 | -------------------------------------------------------------------------------- /lib/commands/generate/createError.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | exports.PropheticErrorTextDef = "class PropheticError extends ApolloError {\n constructor(name: string, message: string, code?: string, properties?: Record) {\n super(message, code, properties);\n\n // Set the prototype explicitly.\n // https://stackoverflow.com/a/41102306\n Object.setPrototypeOf(this, SyntaxError.prototype);\n Object.defineProperty(this, 'name', { value: name });\n }\n}"; 4 | function default_1(name, message, code, properties) { 5 | return "export class " + name + " extends PropheticError {\n constructor(properties?: Record) {\n super(\"" + name + "\", \"" + message + "\", \"" + code + "\", properties);\n }\n}"; 6 | } 7 | exports.default = default_1; 8 | //# sourceMappingURL=createError.js.map -------------------------------------------------------------------------------- /lib/commands/generate/createError.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"createError.js","sourceRoot":"","sources":["../../../src/commands/generate/createError.ts"],"names":[],"mappings":";;AAOa,QAAA,qBAAqB,GAAG,qYASnC,CAAA;AAEF,mBAAwB,IAAY,EAAE,OAAe,EAAE,IAAY,EAAE,UAAgC;IACnG,OAAO,kBAAgB,IAAI,kGAEhB,IAAI,cAAO,OAAO,cAAO,IAAI,6BAExC,CAAA;AACF,CAAC;AAND,4BAMC"} -------------------------------------------------------------------------------- /lib/commands/generate/generate.d.ts: -------------------------------------------------------------------------------- 1 | export interface ProphecyArgs { 2 | intputFilePath: string; 3 | outputFilePath?: string; 4 | } 5 | export default function generate(args: ProphecyArgs): Promise; 6 | -------------------------------------------------------------------------------- /lib/commands/generate/generate.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 3 | return new (P || (P = Promise))(function (resolve, reject) { 4 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 5 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 6 | function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } 7 | step((generator = generator.apply(thisArg, _arguments || [])).next()); 8 | }); 9 | }; 10 | var __generator = (this && this.__generator) || function (thisArg, body) { 11 | var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; 12 | return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; 13 | function verb(n) { return function (v) { return step([n, v]); }; } 14 | function step(op) { 15 | if (f) throw new TypeError("Generator is already executing."); 16 | while (_) try { 17 | if (f = 1, y && (t = y[op[0] & 2 ? "return" : op[0] ? "throw" : "next"]) && !(t = t.call(y, op[1])).done) return t; 18 | if (y = 0, t) op = [0, t.value]; 19 | switch (op[0]) { 20 | case 0: case 1: t = op; break; 21 | case 4: _.label++; return { value: op[1], done: false }; 22 | case 5: _.label++; y = op[1]; op = [0]; continue; 23 | case 7: op = _.ops.pop(); _.trys.pop(); continue; 24 | default: 25 | if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } 26 | if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } 27 | if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } 28 | if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } 29 | if (t[2]) _.ops.pop(); 30 | _.trys.pop(); continue; 31 | } 32 | op = body.call(thisArg, _); 33 | } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } 34 | if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; 35 | } 36 | }; 37 | Object.defineProperty(exports, "__esModule", { value: true }); 38 | var generateRawClass_1 = require("./generateRawClass"); 39 | var writeClassFile_1 = require("../../writeClassFile"); 40 | var checkEntries_1 = require("./checkEntries"); 41 | var utils_1 = require("./../../utils"); 42 | function generate(args) { 43 | return __awaiter(this, void 0, void 0, function () { 44 | var intputFilePath, outputFilePath, entries, rawClassContent; 45 | return __generator(this, function (_a) { 46 | intputFilePath = args.intputFilePath, outputFilePath = args.outputFilePath; 47 | entries = checkEntries_1.default(utils_1.fs.readJsonDef(intputFilePath)); 48 | rawClassContent = generateRawClass_1.default(entries); 49 | return [2 /*return*/, writeClassFile_1.default(rawClassContent, outputFilePath)]; 50 | }); 51 | }); 52 | } 53 | exports.default = generate; 54 | //# sourceMappingURL=generate.js.map -------------------------------------------------------------------------------- /lib/commands/generate/generate.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"generate.js","sourceRoot":"","sources":["../../../src/commands/generate/generate.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,uDAAkD;AAClD,uDAAkD;AAClD,+CAA0C;AAC1C,uCAAmC;AAOnC,kBAAuC,IAAkB;;;;YAC/C,cAAc,GAAsB,IAAI,eAA1B,EAAE,cAAc,GAAM,IAAI,eAAV,CAAW;YAC3C,OAAO,GAAG,sBAAY,CAAC,UAAE,CAAC,WAAW,CAAC,cAAc,CAAC,CAAC,CAAC;YACvD,eAAe,GAAG,0BAAgB,CAAC,OAAO,CAAC,CAAC;YAClD,sBAAO,wBAAc,CAAC,eAAe,EAAE,cAAc,CAAC,EAAC;;;CACxD;AALD,2BAKC"} -------------------------------------------------------------------------------- /lib/commands/generate/generateRawClass.d.ts: -------------------------------------------------------------------------------- 1 | import { JsonInputErrorEntryRecord, JsonInputErrorEntry } from '../../types'; 2 | export declare type ScalarType = "Boolean" | "Int" | "Float" | "String"; 3 | export declare type ScalarTypesMap = { 4 | [key: string]: ScalarType; 5 | }; 6 | export declare function toRawClassesArray(entries: JsonInputErrorEntryRecord): string[]; 7 | export declare const exludeMessageAndName: (entry: JsonInputErrorEntry) => string[]; 8 | export declare function toScalarTypesMap(entries: JsonInputErrorEntryRecord): ScalarTypesMap[]; 9 | export declare function generatePropheticErrorType(typesArray: ScalarTypesMap[]): string; 10 | export default function (entries: JsonInputErrorEntryRecord): string; 11 | -------------------------------------------------------------------------------- /lib/commands/generate/generateRawClass.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __rest = (this && this.__rest) || function (s, e) { 3 | var t = {}; 4 | for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) 5 | t[p] = s[p]; 6 | if (s != null && typeof Object.getOwnPropertySymbols === "function") 7 | for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) if (e.indexOf(p[i]) < 0) 8 | t[p[i]] = s[p[i]]; 9 | return t; 10 | }; 11 | Object.defineProperty(exports, "__esModule", { value: true }); 12 | var createError_1 = require("./createError"); 13 | var types_1 = require("../../types"); 14 | function scalarGQLTpeFromValue(value) { 15 | switch (typeof value) { 16 | case "string": 17 | return "String"; 18 | case "boolean": 19 | return "Boolean"; 20 | case "number": 21 | if (value % 1 === 0) { 22 | return "Int"; 23 | } 24 | else { 25 | return "Float"; 26 | } 27 | default: 28 | return null; 29 | } 30 | } 31 | function toRawClassesArray(entries) { 32 | return Object.keys(entries).map(function (key) { 33 | var _a = entries[key], message = _a.message, code = _a.code, rest = __rest(_a, ["message", "code"]); 34 | return createError_1.default(key, message, code, rest); 35 | }); 36 | } 37 | exports.toRawClassesArray = toRawClassesArray; 38 | exports.exludeMessageAndName = function (entry) { return Object.keys(entry).filter(function (key) { return key !== 'message' || 'name'; }); }; 39 | function toScalarTypesMap(entries) { 40 | return Object.values(entries).map(function (entry) { return Object.keys(entry).filter(function (key) { return key !== 'message'; }).reduce(function (prev, entryFieldKey) { 41 | var fieldValue = entry[entryFieldKey]; 42 | var fieldType = scalarGQLTpeFromValue(fieldValue); 43 | prev[entryFieldKey] = fieldType; 44 | return prev; 45 | }, {}); }, {}); 46 | } 47 | exports.toScalarTypesMap = toScalarTypesMap; 48 | function generatePropheticErrorType(typesArray) { 49 | var fieldsReduced = typesArray.filter(function (type) { return type; }).reduce(function (prev, typesMap) { 50 | var isFirst = prev === {}; 51 | for (var key in typesMap) { 52 | var fieldType = typesMap[key]; 53 | var isNullableType = true; 54 | if (!isFirst && prev[key] && prev[key].isNullableType) { 55 | if (prev[key].isNullableType !== true) { 56 | prev[key].isNullableType = prev[key].isNullableType; 57 | } 58 | else { 59 | prev[key].isNullableType = false; 60 | } 61 | } 62 | var value = { type: fieldType, isNullableType: isNullableType }; 63 | prev[key] = value; 64 | } 65 | return prev; 66 | }, {}); 67 | var fields = Object.entries(fieldsReduced).map(function (_a) { 68 | var name = _a[0], _b = _a[1], type = _b.type, isNullableType = _b.isNullableType; 69 | return name + ": " + type + (isNullableType ? '?' : ''); 70 | }); 71 | return "\n type PropheticErrorExtensions {\n " + fields.join('\n ') + "\n }\n \n type PropheticError {\n name: String\n message: String?\n extensions: PropheticErrorExtensions\n }\n "; 72 | } 73 | exports.generatePropheticErrorType = generatePropheticErrorType; 74 | function default_1(entries) { 75 | var rawErrorClasses = toRawClassesArray(entries).join('\n'); 76 | var fieldsTypesMapArray = toScalarTypesMap(entries); 77 | var rawPropheticErrorAndExtensionsType = generatePropheticErrorType(fieldsTypesMapArray); 78 | var errorsList = types_1.toOutputErrors(entries); 79 | var classFile = "\n import { ApolloError } from 'apollo-server'\n\n export const errorsList = " + JSON.stringify(errorsList, null, 2) + ";\n export const errorType = `" + rawPropheticErrorAndExtensionsType + "`;\n \n " + createError_1.PropheticErrorTextDef + "\n \n " + rawErrorClasses + "\n "; 80 | return classFile; 81 | } 82 | exports.default = default_1; 83 | //# sourceMappingURL=generateRawClass.js.map -------------------------------------------------------------------------------- /lib/commands/generate/generateRawClass.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"generateRawClass.js","sourceRoot":"","sources":["../../../src/commands/generate/generateRawClass.ts"],"names":[],"mappings":";;;;;;;;;;;AAAA,6CAAkE;AAClE,qCAA6F;AAK7F,+BAA+B,KAAU;IACvC,QAAQ,OAAO,KAAK,EAAE;QACpB,KAAK,QAAQ;YACX,OAAO,QAAQ,CAAA;QACjB,KAAK,SAAS;YACZ,OAAO,SAAS,CAAA;QAClB,KAAK,QAAQ;YACX,IAAG,KAAK,GAAG,CAAC,KAAK,CAAC,EAAC;gBACjB,OAAO,KAAK,CAAA;aACb;iBAAM;gBACL,OAAO,OAAO,CAAA;aACf;QACH;YACE,OAAO,IAAI,CAAC;KACf;AACH,CAAC;AAED,2BAAkC,OAAkC;IAClE,OAAO,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,UAAC,GAAG;QAClC,IAAM,iBAAyC,EAAvC,oBAAO,EAAE,cAAI,EAAE,sCAAwB,CAAC;QAChD,OAAO,qBAAW,CAAC,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;IAC/C,CAAC,CAAC,CAAC;AACL,CAAC;AALD,8CAKC;AAEY,QAAA,oBAAoB,GAAG,UAAC,KAA0B,IAAK,OAAA,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,UAAA,GAAG,IAAI,OAAA,GAAG,KAAK,SAAS,IAAI,MAAM,EAA3B,CAA2B,CAAC,EAA7D,CAA6D,CAAA;AACjI,0BAAiC,OAAkC;IACjE,OAAO,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,UAAC,KAAK,IAAK,OAAA,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,UAAA,GAAG,IAAI,OAAA,GAAG,KAAK,SAAS,EAAjB,CAAiB,CAAC,CAAC,MAAM,CAAC,UAAC,IAAI,EAAE,aAAa;QAC1H,IAAM,UAAU,GAAG,KAAK,CAAC,aAAa,CAAC,CAAC;QACxC,IAAM,SAAS,GAAG,qBAAqB,CAAC,UAAU,CAAC,CAAC;QACpD,IAAI,CAAC,aAAa,CAAC,GAAG,SAAS,CAAC;QAChC,OAAO,IAAI,CAAC;IACd,CAAC,EAAE,EAAE,CAAC,EALuC,CAKvC,EAAC,EAAE,CAAC,CAAC;AACb,CAAC;AAPD,4CAOC;AAKD,oCAA2C,UAA4B;IACrE,IAAM,aAAa,GAAG,UAAU,CAAC,MAAM,CAAC,UAAA,IAAI,IAAI,OAAA,IAAI,EAAJ,CAAI,CAAC,CAAC,MAAM,CAAC,UAAC,IAAwB,EAAE,QAAQ;QAC9F,IAAM,OAAO,GAAG,IAAI,KAAK,EAAE,CAAA;QAE3B,KAAK,IAAI,GAAG,IAAI,QAAQ,EAAE;YACxB,IAAM,SAAS,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC;YAChC,IAAI,cAAc,GAAG,IAAI,CAAC;YAC1B,IAAG,CAAC,OAAO,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,cAAc,EAAE;gBACpD,IAAG,IAAI,CAAC,GAAG,CAAC,CAAC,cAAc,KAAK,IAAI,EAAE;oBACpC,IAAI,CAAC,GAAG,CAAC,CAAC,cAAc,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,cAAc,CAAC;iBACrD;qBAAK;oBACJ,IAAI,CAAC,GAAG,CAAC,CAAC,cAAc,GAAG,KAAK,CAAA;iBACjC;aACF;YAED,IAAM,KAAK,GAAG,EAAE,IAAI,EAAE,SAAS,EAAE,cAAc,gBAAA,EAAE,CAAC;YAClD,IAAI,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;SACnB;QACD,OAAO,IAAI,CAAC;IACd,CAAC,EAAC,EAAE,CAAuB,CAAA;IAG3B,IAAM,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC,GAAG,CAAC,UAAC,EAAiC;YAAhC,YAAI,EAAE,UAAwB,EAAtB,cAAI,EAAE,kCAAc;QAC7E,OAAU,IAAI,UAAK,IAAI,IAAG,cAAc,CAAC,CAAC,CAAC,GAAG,CAAA,CAAC,CAAC,EAAE,CAAE,CAAA;IACtD,CAAC,CAAC,CAAC;IAEH,OAAO,kDAED,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,gJAQ5B,CAAA;AACH,CAAC;AArCD,gEAqCC;AAED,mBAAyB,OAAkC;IACzD,IAAM,eAAe,GAAG,iBAAiB,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC9D,IAAM,mBAAmB,GAAG,gBAAgB,CAAC,OAAO,CAAC,CAAC;IACtD,IAAM,kCAAkC,GAAG,0BAA0B,CAAC,mBAAmB,CAAC,CAAC;IAC3F,IAAM,UAAU,GAAG,sBAAc,CAAC,OAAO,CAAC,CAAC;IAE3C,IAAM,SAAS,GAAG,oFAGU,IAAI,CAAC,SAAS,CAAC,UAAU,EAAE,IAAI,EAAE,CAAC,CAAC,uCAClC,kCAAkC,kBAE7D,mCAAqB,gBAErB,eAAe,SAChB,CAAA;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAjBD,4BAiBC"} -------------------------------------------------------------------------------- /lib/commands/generate/index.d.ts: -------------------------------------------------------------------------------- 1 | export { default as generate } from "./generate"; 2 | -------------------------------------------------------------------------------- /lib/commands/generate/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | var generate_1 = require("./generate"); 4 | exports.generate = generate_1.default; 5 | //# sourceMappingURL=index.js.map -------------------------------------------------------------------------------- /lib/commands/generate/index.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/commands/generate/index.ts"],"names":[],"mappings":";;AAAA,uCAAiD;AAAxC,8BAAA,OAAO,CAAY"} -------------------------------------------------------------------------------- /lib/commands/index.d.ts: -------------------------------------------------------------------------------- 1 | export { generate } from './generate'; 2 | export { ask } from './ask'; 3 | -------------------------------------------------------------------------------- /lib/commands/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | var generate_1 = require("./generate"); 4 | exports.generate = generate_1.generate; 5 | var ask_1 = require("./ask"); 6 | exports.ask = ask_1.ask; 7 | //# sourceMappingURL=index.js.map -------------------------------------------------------------------------------- /lib/commands/index.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/commands/index.ts"],"names":[],"mappings":";;AAAA,uCAAqC;AAA5B,8BAAA,QAAQ,CAAA;AACjB,6BAA2B;AAAlB,oBAAA,GAAG,CAAA"} -------------------------------------------------------------------------------- /lib/index.d.ts: -------------------------------------------------------------------------------- 1 | import * as commands from './commands'; 2 | import * as utils from './utils'; 3 | import * as types from './types'; 4 | export { commands, utils, types }; 5 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | var commands = require("./commands"); 4 | exports.commands = commands; 5 | var utils = require("./utils"); 6 | exports.utils = utils; 7 | var types = require("./types"); 8 | exports.types = types; 9 | //# sourceMappingURL=index.js.map -------------------------------------------------------------------------------- /lib/index.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;AAAA,qCAAuC;AAI9B,4BAAQ;AAHjB,+BAAiC;AAGd,sBAAK;AAFxB,+BAAiC;AAEP,sBAAK"} -------------------------------------------------------------------------------- /lib/types.d.ts: -------------------------------------------------------------------------------- 1 | export declare type JsonInputErrorEntry = { 2 | message: string; 3 | code: string; 4 | [key: string]: any; 5 | }; 6 | export declare type ErrorOutputEntry = { 7 | name: string; 8 | message?: string; 9 | extensions: { 10 | code: string; 11 | [key: string]: any; 12 | }; 13 | }; 14 | export declare type JsonInputErrorEntryRecord = { 15 | [key: string]: JsonInputErrorEntry; 16 | }; 17 | export declare type JsonOutputEntriesRecord = { 18 | [key: string]: ErrorOutputEntry; 19 | }; 20 | export declare function toErrorEntries(entries: ErrorOutputEntry[]): JsonOutputEntriesRecord; 21 | export declare function toOutputErrors(entries: JsonInputErrorEntryRecord): ErrorOutputEntry[]; 22 | -------------------------------------------------------------------------------- /lib/types.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __assign = (this && this.__assign) || Object.assign || function(t) { 3 | for (var s, i = 1, n = arguments.length; i < n; i++) { 4 | s = arguments[i]; 5 | for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) 6 | t[p] = s[p]; 7 | } 8 | return t; 9 | }; 10 | var __rest = (this && this.__rest) || function (s, e) { 11 | var t = {}; 12 | for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) 13 | t[p] = s[p]; 14 | if (s != null && typeof Object.getOwnPropertySymbols === "function") 15 | for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) if (e.indexOf(p[i]) < 0) 16 | t[p[i]] = s[p[i]]; 17 | return t; 18 | }; 19 | Object.defineProperty(exports, "__esModule", { value: true }); 20 | function toErrorEntries(entries) { 21 | return entries.reduce(function (map, entry) { 22 | map[entry.name] = entry; 23 | return map; 24 | }, {}); 25 | } 26 | exports.toErrorEntries = toErrorEntries; 27 | function toOutputErrors(entries) { 28 | return Object.keys(entries).map(function (key) { 29 | var _a = entries[key], name = _a.name, message = _a.message, code = _a.code, rest = __rest(_a, ["name", "message", "code"]); 30 | return { 31 | name: name || key, 32 | message: message, 33 | extensions: __assign({ code: code }, rest) 34 | }; 35 | }); 36 | } 37 | exports.toOutputErrors = toOutputErrors; 38 | //# sourceMappingURL=types.js.map -------------------------------------------------------------------------------- /lib/types.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;AAkBA,wBAA+B,OAA2B;IACxD,OAAO,OAAO,CAAC,MAAM,CAAC,UAAC,GAAG,EAAE,KAAK;QAC/B,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC;QACxB,OAAO,GAAG,CAAC;IACb,CAAC,EAAE,EAA6B,CAAC,CAAC;AACpC,CAAC;AALD,wCAKC;AAED,wBAA+B,OAAkC;IAC/D,OAAO,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,UAAC,GAAG;QAClC,IAAM,iBAA+C,EAA7C,cAAI,EAAE,oBAAO,EAAE,cAAI,EAAE,8CAAwB,CAAC;QACtD,OAAO;YACL,IAAI,EAAE,IAAI,IAAI,GAAG;YACjB,OAAO,EAAE,OAAO;YAChB,UAAU,aACR,IAAI,MAAA,IACD,IAAI,CACR;SACkB,CAAA;IACvB,CAAC,CAAC,CAAC;AACL,CAAC;AAZD,wCAYC"} -------------------------------------------------------------------------------- /lib/utils/fs-prophecy.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | import * as fs from 'fs'; 3 | import * as jsonfile from 'jsonfile'; 4 | import { JsonInputErrorEntry } from '../types'; 5 | export declare const writeFile: (path: fs.PathLike, content: string) => void; 6 | export declare const mkdirs: (path: fs.PathLike) => any; 7 | export declare const rmrf: (path: fs.PathLike) => any; 8 | export declare function readJsonDef(filePath: jsonfile.Path): JsonInputErrorEntry; 9 | -------------------------------------------------------------------------------- /lib/utils/fs-prophecy.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | var fs = require("fs"); 4 | var rimraf = require("rimraf"); 5 | var mkdirp = require("mkdirp"); 6 | var jsonfile = require("jsonfile"); 7 | exports.writeFile = function (path, content) { return fs.writeFileSync(path, content); }; 8 | exports.mkdirs = function (path) { return mkdirp.sync(path); }; 9 | exports.rmrf = function (path) { return rimraf.sync(path); }; 10 | function readJsonDef(filePath) { 11 | return jsonfile.readFileSync(filePath); 12 | } 13 | exports.readJsonDef = readJsonDef; 14 | //# sourceMappingURL=fs-prophecy.js.map -------------------------------------------------------------------------------- /lib/utils/fs-prophecy.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"fs-prophecy.js","sourceRoot":"","sources":["../../src/utils/fs-prophecy.ts"],"names":[],"mappings":";;AAAA,uBAAyB;AACzB,+BAAiC;AACjC,+BAAiC;AACjC,mCAAqC;AAGxB,QAAA,SAAS,GAAG,UAAC,IAAkB,EAAE,OAAe,IAAK,OAAA,EAAE,CAAC,aAAa,CAAC,IAAI,EAAE,OAAO,CAAC,EAA/B,CAA+B,CAAC;AACrF,QAAA,MAAM,GAAG,UAAC,IAAiB,IAAK,OAAA,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,EAAjB,CAAiB,CAAC;AAClD,QAAA,IAAI,GAAG,UAAC,IAAiB,IAAK,OAAA,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,EAAjB,CAAiB,CAAC;AAC7D,qBAA4B,QAAuB;IACjD,OAAO,QAAQ,CAAC,YAAY,CAAC,QAAQ,CAAwB,CAAC;AAChE,CAAC;AAFD,kCAEC"} -------------------------------------------------------------------------------- /lib/utils/index.d.ts: -------------------------------------------------------------------------------- 1 | import * as fs from './fs-prophecy'; 2 | export { fs }; 3 | -------------------------------------------------------------------------------- /lib/utils/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | var fs = require("./fs-prophecy"); 4 | exports.fs = fs; 5 | //# sourceMappingURL=index.js.map -------------------------------------------------------------------------------- /lib/utils/index.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":";;AAAA,kCAAoC;AAGlC,gBAAE"} -------------------------------------------------------------------------------- /lib/writeClassFile.d.ts: -------------------------------------------------------------------------------- 1 | declare const _default: (content: string, output?: string) => string; 2 | export default _default; 3 | -------------------------------------------------------------------------------- /lib/writeClassFile.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | var path_1 = require("path"); 4 | var utils_1 = require("./utils"); 5 | exports.default = (function (content, output) { 6 | if (output === void 0) { output = '_generated'; } 7 | var outputPath = path_1.normalize(output); 8 | var fileExtension = path_1.parse(outputPath).ext; 9 | var fileSpecified = (fileExtension !== null && fileExtension !== undefined) && fileExtension.length > 0; 10 | var fileIsTS = fileExtension === '.ts'; 11 | if (fileSpecified && !fileIsTS) { 12 | throw new Error('.ts file expected as output'); 13 | } 14 | var outputFile = fileSpecified ? path_1.parse(outputPath).base : 'Errors.ts'; 15 | var outputDirectory = fileSpecified ? path_1.parse(outputPath).dir : outputPath; 16 | utils_1.fs.mkdirs(outputDirectory); 17 | var outputFilePath = path_1.normalize(outputDirectory + "/" + outputFile); 18 | utils_1.fs.writeFile(outputFilePath, content); 19 | return outputFilePath; 20 | }); 21 | //# sourceMappingURL=writeClassFile.js.map -------------------------------------------------------------------------------- /lib/writeClassFile.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"writeClassFile.js","sourceRoot":"","sources":["../src/writeClassFile.ts"],"names":[],"mappings":";;AAAA,6BAAyC;AACzC,iCAA6B;AAE7B,mBAAe,UAAC,OAAe,EAAE,MAA6B;IAA7B,uBAAA,EAAA,qBAA6B;IAC5D,IAAM,UAAU,GAAG,gBAAS,CAAC,MAAM,CAAC,CAAC;IACrC,IAAM,aAAa,GAAG,YAAK,CAAC,UAAU,CAAC,CAAC,GAAG,CAAC;IAC5C,IAAM,aAAa,GAAG,CAAC,aAAa,KAAK,IAAI,IAAI,aAAa,KAAK,SAAS,CAAC,IAAI,aAAa,CAAC,MAAM,GAAG,CAAC,CAAC;IAC1G,IAAM,QAAQ,GAAG,aAAa,KAAK,KAAK,CAAC;IAEzC,IAAG,aAAa,IAAI,CAAC,QAAQ,EAAE;QAC7B,MAAM,IAAI,KAAK,CAAC,6BAA6B,CAAC,CAAC;KAChD;IAED,IAAM,UAAU,GAAG,aAAa,CAAC,CAAC,CAAC,YAAK,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,WAAW,CAAC;IACxE,IAAM,eAAe,GAAG,aAAa,CAAC,CAAC,CAAC,YAAK,CAAC,UAAU,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,UAAU,CAAC;IAE3E,UAAE,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC;IAE3B,IAAM,cAAc,GAAG,gBAAS,CAAI,eAAe,SAAI,UAAY,CAAC,CAAC;IACrE,UAAE,CAAC,SAAS,CAAC,cAAc,EAAE,OAAO,CAAC,CAAC;IAEtC,OAAO,cAAc,CAAC;AACxB,CAAC,EAAA"} -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "apollo-prophecy", 3 | "version": "0.2.3-0", 4 | "description": "Generate Spec compliant and shareable errors for Apollo Client/Server from JSON or Graphql Schema", 5 | "main": "./lib/index.js", 6 | "bin": "./lib/cli.js", 7 | "scripts": { 8 | "test": "jest --collectCoverage", 9 | "test:watch": "jest --watch --runInBand --detectOpenHandles --coverage --env=node", 10 | "coverage": "jest --collectCoverage && cat ./coverage/lcov.info | coveralls", 11 | "build": "rm -rf lib && tsc", 12 | "cli": "ts-node src/cli.ts", 13 | "preversion": "npm test", 14 | "version": "npm run build && git add -A lib", 15 | "postversion": "git push && git push --tags && rm -rf lib", 16 | "patch-release": "npm version patch && npm publish && git push --follow-tags", 17 | "prepatch-release": "npm version prepatch && npm publish && git push --follow-tags" 18 | }, 19 | "repository": { 20 | "type": "git", 21 | "url": "git+https://github.com/theGlenn/apollo-pythian.git" 22 | }, 23 | "keywords": [ 24 | "apollo", 25 | "graphql", 26 | "errors", 27 | "apollo-client", 28 | "apollo-server" 29 | ], 30 | "author": "Glenn Sonna", 31 | "license": "MIT", 32 | "bugs": { 33 | "url": "https://github.com/theGlenn/apollo-pythian/issues" 34 | }, 35 | "homepage": "https://github.com/theGlenn/apollo-pythian#readme", 36 | "dependencies": { 37 | "@types/rimraf": "^3.0.0", 38 | "apollo-link": "^1.2.2", 39 | "apollo-link-http": "^1.5.4", 40 | "apollo-server": "^2.0.0-beta.5", 41 | "graphql": "^15.3.0", 42 | "graphql-tag": "^2.9.2", 43 | "jsonfile": "^4.0.0", 44 | "mkdirp": "^1.0.4", 45 | "node-fetch": "^2.1.2", 46 | "rimraf": "^2.6.2", 47 | "yargs": "^11.0.0" 48 | }, 49 | "devDependencies": { 50 | "@types/chai": "^4.1.3", 51 | "@types/chai-fs": "^2.0.2", 52 | "@types/express": "^4.16.0", 53 | "@types/graphql": "^0.13.1", 54 | "@types/jest": "^24.0.21", 55 | "@types/jsonfile": "^4.0.1", 56 | "@types/minimist": "^1.2.0", 57 | "@types/mkdirp": "^1.0.1", 58 | "@types/mocha": "^5.2.0", 59 | "@types/node": "^10.3.0", 60 | "@types/yargs": "^11.0.0", 61 | "@typescript-eslint/eslint-plugin": "^4.5.0", 62 | "@typescript-eslint/parser": "^4.5.0", 63 | "chai": "^4.1.2", 64 | "chai-fs": "^2.0.0", 65 | "coveralls": "^3.1.0", 66 | "eslint": "^7.12.0", 67 | "eslint-config-airbnb-typescript": "^12.0.0", 68 | "jest": "^24.9.0", 69 | "source-map-support": "^0.5.6", 70 | "ts-jest": "^24.1.0", 71 | "ts-node": "^8.3.0", 72 | "typescript": "^3.7.5" 73 | }, 74 | "resolutions": { 75 | "**@types/mocha": "^5.2.0", 76 | "**@types/express": "^4.16.3" 77 | }, 78 | "nyc": { 79 | "extension": [ 80 | ".ts", 81 | ".tsx" 82 | ], 83 | "exclude": [ 84 | "lib", 85 | "**/*.d.ts", 86 | "coverage" 87 | ], 88 | "reporter": [ 89 | "html" 90 | ], 91 | "all": true 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/_specs-utils/index.ts: -------------------------------------------------------------------------------- 1 | import * as os from 'os'; 2 | import * as osFs from 'fs'; 3 | import * as path from 'path'; 4 | 5 | export const mkdirTmp = () => { 6 | const tmpDir = os.tmpdir(); 7 | const dirLocation = path.join(tmpDir, 'apollo-prophetic-test-'); 8 | return osFs.mkdtempSync(dirLocation); 9 | }; 10 | 11 | export const tmpErrorFilePath = (fileName: string) => path.join(mkdirTmp(), fileName); 12 | export const removeWhiteSpaces = (text: string) => text.replace(/[^a-zA-Z]/g, ""); 13 | 14 | export const entries = { 15 | UnknownError: { 16 | "message": "An unknown error has occurred! Please try again later", 17 | "code": "UNKNOWN" 18 | }, 19 | ForbiddenError: { 20 | "message": "You are not allowed to do this", 21 | "code": "FORBIDDEN", 22 | }, 23 | }; 24 | 25 | export * as jestFS from './jest-fs'; -------------------------------------------------------------------------------- /src/_specs-utils/jest-fs.ts: -------------------------------------------------------------------------------- 1 | import fs from "fs"; 2 | 3 | declare global { 4 | namespace jest { 5 | interface Matchers { 6 | toBePath(): R; 7 | toBeDirectory(): R; 8 | toBeDirectoryWithFiles(files: string[]): R; 9 | } 10 | } 11 | } 12 | 13 | expect.extend({ 14 | toBePath(received) { 15 | const pass = fs.existsSync(received); 16 | const failMessage = this.isNot 17 | ? "expected #{this} not to exist" 18 | : "expected #{this} to exist"; 19 | 20 | const message = pass ? "" : failMessage; 21 | return { pass, actual: received, message: () => message }; 22 | }, 23 | 24 | toBeDirectory(received) { 25 | expect(received).toEqual(expect.any(String)); 26 | expect(received).toBePath(); 27 | 28 | const pass = fs.statSync(received).isDirectory(); 29 | const failMessage = this.isNot 30 | ? "Expected Not: to be directory" 31 | : "Expected: to be directory"; 32 | 33 | const message = pass ? "good" : failMessage; 34 | return { pass, actual: received, message: () => message }; 35 | }, 36 | 37 | toBeDirectoryWithFiles(received, actual) { 38 | expect(received).toBeDirectory(); 39 | 40 | const files = fs.readdirSync(received); 41 | expect(files).toEqual(expect.arrayContaining(actual)); 42 | return { pass: true, actual: received, message: () => "good" }; 43 | }, 44 | }); 45 | -------------------------------------------------------------------------------- /src/cli.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import * as yargs from 'yargs'; 3 | import { ParsedArgs } from 'minimist'; 4 | import { commands } from './'; 5 | import { resolve } from 'path'; 6 | 7 | function handleError(error) { 8 | console.error(error); 9 | process.exit(1); 10 | } 11 | 12 | process.on('unhandledRejection', (error) => { throw error }); 13 | process.on('uncaughtException', handleError); 14 | 15 | interface Args extends ParsedArgs { 16 | jsonFile?: string 17 | 18 | out?: string 19 | field?: string 20 | headers?: string 21 | }; 22 | 23 | yargs 24 | .command( 25 | ['generate [jsonFile]', 'prophecy'], 26 | 'Generate a Typescript file containg all application errors from a JSON input', 27 | { 28 | out: { 29 | demand: true, 30 | describe: 'Output path for error file', 31 | default: './Errors.ts', 32 | normalize: true, 33 | coerce: resolve 34 | }, 35 | }, 36 | async (argv: Args) => { 37 | const { jsonFile = './errors.json', out: outputFilePath } = argv; 38 | const intputFilePath = resolve(jsonFile); 39 | const generatedOutputPath = await commands.generate({ intputFilePath, outputFilePath }); 40 | console.info('🔮 You will fail... but successfully'); 41 | console.info(`└── 👁 Prophecy available at ${generatedOutputPath}`); 42 | } 43 | ) 44 | .command( 45 | ['ask ', 'oracles'], 46 | 'Generate a .ts file that exposes helpers for all the errors exposed through the server api', 47 | { 48 | field: { 49 | demandOption: true, 50 | describe: 'Field to query on Query type', 51 | default: 'errors', 52 | }, 53 | out: { 54 | demandOption: true, 55 | describe: 'Output path for error file', 56 | default: './Errors.ts', 57 | normalize: true, 58 | coerce: resolve 59 | }, 60 | }, 61 | async (argv: Args) => { 62 | console.info('🔮 Connecting with the oracles...'); 63 | const { schema: input, out: outputFilePath, field: errorField } = argv; 64 | const outputPath = await commands.ask({ input, errorField, outputFilePath }); 65 | console.info('├── 🙏 You will fail... but successfully'); 66 | console.info(`└── 👁 All you need to know is available at ${outputPath}`); 67 | } 68 | ) 69 | .fail((message, error) => handleError(error ? error : new Error(message))) 70 | .help() 71 | .version() 72 | .strict() 73 | .argv; -------------------------------------------------------------------------------- /src/commands/ask/ask.ts: -------------------------------------------------------------------------------- 1 | import createFileFromEntries from "./createRawFileFromEntries"; 2 | import writeClassFile from "../../writeClassFile"; 3 | import { ErrorOutputEntry } from "../../types"; 4 | import { makeHeaders } from "./makeHeaders"; 5 | import queryServer from "./queryServer"; 6 | 7 | export interface AskArgs { 8 | input?: string, 9 | outputFilePath?: string, 10 | errorField?: string 11 | headers?: string[] 12 | } 13 | 14 | export default async function ask({ input, errorField, headers, outputFilePath }: AskArgs) { 15 | const urlRegex = /^https?:\/\//i; 16 | 17 | let errorEntries: ErrorOutputEntry[] = [] 18 | if (urlRegex.test(input)) { 19 | errorEntries = await queryServer(input, errorField, makeHeaders(headers)); 20 | } 21 | const rawFileContent = createFileFromEntries(errorEntries); 22 | return writeClassFile(rawFileContent, outputFilePath); 23 | } -------------------------------------------------------------------------------- /src/commands/ask/createRawFileFromEntries.spec.ts: -------------------------------------------------------------------------------- 1 | import createRawFileFromEntries from './createRawFileFromEntries' 2 | 3 | const errors = [{ 4 | "name": "UnknownError", 5 | "message": "An unknown error has occurred! Please try again later", 6 | extensions: { 7 | "code": "UNKNOWN" 8 | } 9 | }, { 10 | "name": "ForbiddenError", 11 | "message": "You are not allowed to do this", 12 | extensions: { 13 | "code": "FORBIDDEN" 14 | } 15 | }, { 16 | "name": "AuthenticationRequiredError", 17 | "message": "You must be logged in to do this", 18 | extensions: { 19 | "code": "AUTH_REQUIRED" 20 | } 21 | }]; 22 | 23 | const expectedFile = `/* tslint:disable */ 24 | import { ApolloError } from "apollo-client"; 25 | import { GraphQLError } from "graphql"; 26 | 27 | export enum PropheticErrorCode { 28 | CodeLessError = 'NONE', 29 | UnknownError = "UNKNOWN", 30 | ForbiddenError = "FORBIDDEN", 31 | AuthenticationRequiredError = "AUTH_REQUIRED" 32 | } 33 | 34 | export class PropheticError { 35 | constructor(public codes: string[]){} 36 | 37 | private inCodes(code: PropheticErrorCode){ return this.codes.indexOf(code) > -1; } 38 | 39 | get isCodeLessError() { return this.inCodes(PropheticErrorCode.CodeLessError); } 40 | get isUnknownError() { return this.inCodes(PropheticErrorCode.UnknownError); } 41 | get isForbiddenError() { return this.inCodes(PropheticErrorCode.ForbiddenError); } 42 | get isAuthenticationRequiredError() { return this.inCodes(PropheticErrorCode.AuthenticationRequiredError); } 43 | } 44 | 45 | export interface Handler { 46 | (): any 47 | } 48 | 49 | export class PropheticErrorHandled { 50 | private handler: Handler = () => {} 51 | 52 | constructor(public codes: string[]){} 53 | 54 | private inCodes(code: PropheticErrorCode, handler: Handler){ 55 | if(this.codes.indexOf(code) > -1){ 56 | this.handler = handler 57 | } 58 | 59 | return this; 60 | } 61 | 62 | CodeLessError(handler: Handler) { return this.inCodes(PropheticErrorCode.CodeLessError, handler); } 63 | UnknownError(handler: Handler) { return this.inCodes(PropheticErrorCode.UnknownError, handler); } 64 | ForbiddenError(handler: Handler) { return this.inCodes(PropheticErrorCode.ForbiddenError, handler); } 65 | AuthenticationRequiredError(handler: Handler) { return this.inCodes(PropheticErrorCode.AuthenticationRequiredError, handler); } 66 | handle() { return this.handler(); } 67 | } 68 | 69 | const CODE_LESS_EXTENSION = { code: 'NONE'}; 70 | const findCodes = (error: ApolloError | GraphQLError): PropheticErrorCode[] => { 71 | if(error instanceof ApolloError) { 72 | return error.graphQLErrors.map((gError) => findCodes(gError)[0]); 73 | } else if(error.extensions) { 74 | const { extensions: { code } = CODE_LESS_EXTENSION } = error; 75 | return [code]; 76 | } 77 | 78 | return [PropheticErrorCode.CodeLessError]; 79 | } 80 | 81 | export const errorHere = (error: ApolloError | GraphQLError | undefined ) => { 82 | if(!error) { 83 | return new PropheticError([]); 84 | } 85 | const codes = findCodes(error); 86 | return new PropheticError(codes); 87 | } 88 | 89 | export const isThis = (error: ApolloError | GraphQLError | undefined) => { 90 | if(!error) { 91 | return new PropheticErrorHandled([]); 92 | } 93 | const codes = findCodes(error); 94 | return new PropheticErrorHandled(codes); 95 | }` 96 | 97 | describe('createRawFile', () => { 98 | it('Should correctly generate raw file', () => { 99 | const errorsRawFile = createRawFileFromEntries(errors); 100 | expect(errorsRawFile).toEqual(expectedFile); 101 | }); 102 | }); -------------------------------------------------------------------------------- /src/commands/ask/createRawFileFromEntries.ts: -------------------------------------------------------------------------------- 1 | import { JsonOutputEntriesRecord, ErrorOutputEntry, toErrorEntries } from "../../types"; 2 | 3 | const TopMostImportsAndConfigDef = `/* tslint:disable */ 4 | import { ApolloError } from "apollo-client"; 5 | import { GraphQLError } from "graphql";` 6 | 7 | const PropheticErrorCodeEnumDef = ` 8 | export enum PropheticErrorCode { 9 | CodeLessError = 'NONE', 10 | _VALUES_ 11 | }`; 12 | 13 | const PropheticErrorDef = ` 14 | export class PropheticError { 15 | constructor(public codes: string[]){} 16 | 17 | private inCodes(code: PropheticErrorCode){ return this.codes.indexOf(code) > -1; } 18 | 19 | get isCodeLessError() { return this.inCodes(PropheticErrorCode.CodeLessError); } 20 | _FUNCTIONS_ 21 | }`; 22 | 23 | const PropheticErrorGetterDef = "get is$_ENUM_$() { return this.inCodes(PropheticErrorCode.$_ENUM_$); }"; 24 | 25 | const PropheticErrorHandledDef = ` 26 | export interface Handler { 27 | (): any 28 | } 29 | 30 | export class PropheticErrorHandled { 31 | private handler: Handler = () => {} 32 | 33 | constructor(public codes: string[]){} 34 | 35 | private inCodes(code: PropheticErrorCode, handler: Handler){ 36 | if(this.codes.indexOf(code) > -1){ 37 | this.handler = handler 38 | } 39 | 40 | return this; 41 | } 42 | 43 | CodeLessError(handler: Handler) { return this.inCodes(PropheticErrorCode.CodeLessError, handler); } 44 | _FUNCTIONS_ 45 | handle() { return this.handler(); } 46 | }` 47 | const PropheticErrorHandlerDef = "$_ENUM_$(handler: Handler) { return this.inCodes(PropheticErrorCode.$_ENUM_$, handler); }"; 48 | 49 | const BottomExportedMethods = ` 50 | const CODE_LESS_EXTENSION = { code: 'NONE'}; 51 | const findCodes = (error: ApolloError | GraphQLError): PropheticErrorCode[] => { 52 | if(error instanceof ApolloError) { 53 | return error.graphQLErrors.map((gError) => findCodes(gError)[0]); 54 | } else if(error.extensions) { 55 | const { extensions: { code } = CODE_LESS_EXTENSION } = error; 56 | return [code]; 57 | } 58 | 59 | return [PropheticErrorCode.CodeLessError]; 60 | } 61 | 62 | export const errorHere = (error: ApolloError | GraphQLError | undefined ) => { 63 | if(!error) { 64 | return new PropheticError([]); 65 | } 66 | const codes = findCodes(error); 67 | return new PropheticError(codes); 68 | } 69 | 70 | export const isThis = (error: ApolloError | GraphQLError | undefined) => { 71 | if(!error) { 72 | return new PropheticErrorHandled([]); 73 | } 74 | const codes = findCodes(error); 75 | return new PropheticErrorHandled(codes); 76 | }` 77 | 78 | const toNameAndCodeTupleArray = (entriesArray: ErrorOutputEntry[]) => { 79 | const entries = toErrorEntries(entriesArray); 80 | return Object.keys(entries).map((key) => { 81 | const { extensions = { code: undefined } } = entries[key]; 82 | return [key, extensions.code] as [string, string] 83 | }).filter(([_, code]) => code !== undefined); 84 | } 85 | 86 | export default function(entries: ErrorOutputEntry[]) { 87 | const nameAndCodeTuples = toNameAndCodeTupleArray(entries); 88 | const enums = nameAndCodeTuples.map(([name, code]) => `${name} = "${code}"`).join(',\n '); 89 | 90 | const errorDefFun = nameAndCodeTuples.map(([name]) => { 91 | return `${PropheticErrorGetterDef.replace("$_ENUM_$", name).replace("$_ENUM_$", name)}`; 92 | }).join('\n '); 93 | 94 | const errorHandledFun = nameAndCodeTuples.map(([name]) => { 95 | return `${PropheticErrorHandlerDef.replace("$_ENUM_$", name).replace("$_ENUM_$", name)}`; 96 | }).join('\n '); 97 | 98 | return `${TopMostImportsAndConfigDef} 99 | ${PropheticErrorCodeEnumDef.replace('_VALUES_', enums)} 100 | ${PropheticErrorDef.replace('_FUNCTIONS_', errorDefFun)} 101 | ${PropheticErrorHandledDef.replace('_FUNCTIONS_', errorHandledFun)} 102 | ${BottomExportedMethods}`; 103 | } -------------------------------------------------------------------------------- /src/commands/ask/index.ts: -------------------------------------------------------------------------------- 1 | export { default as ask } from "./ask"; 2 | -------------------------------------------------------------------------------- /src/commands/ask/makeHeaders.spec.ts: -------------------------------------------------------------------------------- 1 | import { makeHeaders } from './makeHeaders'; 2 | 3 | describe('makeHeader', () => { 4 | it('Should create header', () => { 5 | const headers = makeHeaders(['Authorization: Bearer uoauzeaouhenaoea']); 6 | expect(headers).toEqual({ 7 | Authorization: 'Bearer uoauzeaouhenaoea', 8 | }); 9 | }); 10 | 11 | it('Should create empty header', () => { 12 | const headers = makeHeaders(); 13 | expect(headers).toEqual({}); 14 | }); 15 | 16 | it('Should throw -> Headers should be specified as "Name: Value"', () => { 17 | expect(() => makeHeaders(['Authorization Bearer uoauzeaouhenaoea'])).toThrow('Headers should be specified as "Name: Value"'); 18 | }); 19 | }); -------------------------------------------------------------------------------- /src/commands/ask/makeHeaders.ts: -------------------------------------------------------------------------------- 1 | // https://github.com/apollographql/apollo-codegen/blob/master/packages/apollo-codegen/src/cli.ts 2 | export const makeHeaders = (headers: string[] = []) => { 3 | let additionalHeaders: { [key: string]: string } = {}; 4 | for (const header of headers) { 5 | const separator = header.indexOf(":"); 6 | const name = header.substring(0, separator).trim(); 7 | const value = header.substring(separator + 1).trim(); 8 | if (!(name && value)) { 9 | throw new Error('Headers should be specified as "Name: Value"'); 10 | } 11 | additionalHeaders[name] = value; 12 | } 13 | return additionalHeaders; 14 | }; 15 | -------------------------------------------------------------------------------- /src/commands/ask/queryServer.ts: -------------------------------------------------------------------------------- 1 | import gql from 'graphql-tag'; 2 | import fetch from 'node-fetch'; 3 | import { execute, makePromise } from 'apollo-link'; 4 | import { HttpLink } from 'apollo-link-http'; 5 | import { toErrorEntries, ErrorOutputEntry } from '../../types'; 6 | 7 | const makeOperation = (erroType: string) => ({ 8 | query: gql`query { 9 | ${erroType} { 10 | name 11 | message 12 | extensions { 13 | code 14 | } 15 | } 16 | } 17 | `, 18 | }); 19 | 20 | type Headers = {[name: string]: string} 21 | 22 | function exeuteServerQuery(serverUri: string, type: string, headers: Headers) { 23 | const operation = makeOperation(type); 24 | const link = new HttpLink({ uri: serverUri, fetch, headers }); 25 | 26 | return makePromise(execute(link, operation)); 27 | } 28 | 29 | export default async function(serverUri: string, field: string = 'errors', headers: Headers) { 30 | let result; 31 | try { 32 | result = await exeuteServerQuery(serverUri, field, headers); 33 | } catch(e) { 34 | throw e; 35 | } 36 | 37 | if (result.errors) { 38 | throw new Error(`Errors occured querying "${field}": ${result.errors}`); 39 | } 40 | 41 | if (!result.data || !result.data[field]) { 42 | throw new Error(`No Errors found to generate on query field ${field}`); 43 | } 44 | 45 | const errors = result.data[field]; 46 | return errors as ErrorOutputEntry[]; 47 | } -------------------------------------------------------------------------------- /src/commands/generate/checkEntries.spec.ts: -------------------------------------------------------------------------------- 1 | import checkEntries from "./checkEntries"; 2 | import { entries } from '../../_specs-utils'; 3 | 4 | const mockReadFromFile = () => JSON.parse(`{ 5 | "UnknownError": { 6 | "message": "An unknown error has occurred! Please try again later", 7 | "code": "UNKNOWN" 8 | }, 9 | "NoCodeError": { 10 | "message": "You are not allowed to do this" 11 | } 12 | }`); 13 | 14 | describe('checkEntries', () => { 15 | it('Should positevely check the entries and return them', () => { 16 | expect(checkEntries(entries)).toEqual(entries); 17 | }); 18 | 19 | it('Should throw No "code" key found', () => { 20 | const checkEntriesMock = () => checkEntries(mockReadFromFile()) 21 | expect(checkEntriesMock).toThrow('[CodeKeyImperative] No "code" key found for: [NoCodeError]'); 22 | }); 23 | }); -------------------------------------------------------------------------------- /src/commands/generate/checkEntries.ts: -------------------------------------------------------------------------------- 1 | import { JsonInputErrorEntryRecord } from '../../types'; 2 | 3 | export default (inputEntries: JsonInputErrorEntryRecord) => { 4 | const checks = Object.keys(inputEntries).map(key => { 5 | const entry = inputEntries[key]; 6 | return { name: key, hasCodeKey: entry.code !== undefined } 7 | }).filter(({ hasCodeKey }) => !hasCodeKey); 8 | 9 | if(checks.length > 0) { 10 | const concernedNames = checks.map(({ name }) => name).join(); 11 | throw Error(`[CodeKeyImperative] No "code" key found for: [${concernedNames}]`); 12 | } 13 | return inputEntries; 14 | } 15 | -------------------------------------------------------------------------------- /src/commands/generate/createError.spec.ts: -------------------------------------------------------------------------------- 1 | import createError from './createError' 2 | import { removeWhiteSpaces } from '../../_specs-utils'; 3 | 4 | describe('createError', () => { 5 | it('Should create Special error classs string', () => { 6 | const SpecialError = createError("SpecialError", "Very special", "SPECIAL"); 7 | const expectedClass = ` 8 | export class SpecialError extends PropheticError { 9 | constructor(properties?: Record) { 10 | super("SpecialError", "Very special", "SPECIAL", properties); 11 | } 12 | } 13 | `; 14 | expect(removeWhiteSpaces(SpecialError)).toEqual(removeWhiteSpaces(expectedClass)); 15 | }); 16 | }); -------------------------------------------------------------------------------- /src/commands/generate/createError.ts: -------------------------------------------------------------------------------- 1 | import { 2 | ApolloServer, 3 | ApolloError, 4 | toApolloError, 5 | gql, 6 | } from 'apollo-server'; 7 | 8 | export const PropheticErrorTextDef = `class PropheticError extends ApolloError { 9 | constructor(name: string, message: string, code?: string, properties?: Record) { 10 | super(message, code, properties); 11 | 12 | // Set the prototype explicitly. 13 | // https://stackoverflow.com/a/41102306 14 | Object.setPrototypeOf(this, SyntaxError.prototype); 15 | Object.defineProperty(this, 'name', { value: name }); 16 | } 17 | }` 18 | 19 | export default function(name: string, message: string, code: string, properties?: Record) { 20 | return `export class ${name} extends PropheticError { 21 | constructor(properties?: Record) { 22 | super("${name}", "${message}", "${code}", properties); 23 | } 24 | }` 25 | } -------------------------------------------------------------------------------- /src/commands/generate/generate.spec.ts: -------------------------------------------------------------------------------- 1 | import '../../_specs-utils/jest-fs'; 2 | import fs from 'fs'; 3 | import generate from './generate'; 4 | 5 | import * as path from 'path'; 6 | import { entries, mkdirTmp } from '../../_specs-utils'; 7 | import { fs as fsu } from '../../utils'; 8 | 9 | 10 | describe('generate', () => { 11 | let tmpFolder: string; 12 | let errorsJsonInputFile: string; 13 | let errorsTsOutputFile: string; 14 | 15 | beforeAll(() => { 16 | tmpFolder = mkdirTmp(); 17 | errorsJsonInputFile = path.join(tmpFolder, 'errors.json'); 18 | errorsTsOutputFile = path.join(tmpFolder, 'Errors.ts'); 19 | fsu.mkdirs(tmpFolder); 20 | fsu.writeFile(errorsJsonInputFile, JSON.stringify(entries)); 21 | }); 22 | 23 | it(`Should create the "Errors.ts" file in tmp folder`, async () => { 24 | const pathName = await generate({ intputFilePath: errorsJsonInputFile, outputFilePath: errorsTsOutputFile }); 25 | 26 | expect(tmpFolder).toBeDirectoryWithFiles(['Errors.ts', 'errors.json']); 27 | // ((expect(tmpFolder).to.be.a) as any).directory(tmpFolder).and.not.empty; 28 | // ((expect(tmpFolder).to.be.a) as any).directory(tmpFolder).with.files(['Errors.ts', 'errors.json']); 29 | }); 30 | }); -------------------------------------------------------------------------------- /src/commands/generate/generate.ts: -------------------------------------------------------------------------------- 1 | import generateRawClass from './generateRawClass'; 2 | import writeClassFile from '../../writeClassFile'; 3 | import checkEntries from './checkEntries'; 4 | import { fs } from './../../utils'; 5 | 6 | export interface ProphecyArgs { 7 | intputFilePath: string, 8 | outputFilePath?: string, 9 | } 10 | 11 | export default async function generate(args: ProphecyArgs) { 12 | const { intputFilePath, outputFilePath } = args; 13 | const entries = checkEntries(fs.readJsonDef(intputFilePath)); 14 | const rawClassContent = generateRawClass(entries); 15 | return writeClassFile(rawClassContent, outputFilePath); 16 | } -------------------------------------------------------------------------------- /src/commands/generate/generateRawClass.spec.ts: -------------------------------------------------------------------------------- 1 | import { toRawClassesArray,toScalarTypesMap, generatePropheticErrorType } from './generateRawClass' 2 | import { removeWhiteSpaces } from '../../_specs-utils'; 3 | 4 | const errors = { 5 | UnknownError: { 6 | "message": "An unknown error has occurred! Please try again later", 7 | "code": "UNKNOWN" 8 | }, 9 | ForbiddenError: { 10 | "message": "You are not allowed to do this", 11 | "code": "FORBIDDEN", 12 | }, 13 | AuthenticationRequiredError: { 14 | "message": "You must be logged in to do this", 15 | "code": "AUTH_REQUIRED" 16 | }, 17 | }; 18 | 19 | const expectedTypes = [ { code: "String" }, { code: "String" }, { code: "String" }]; 20 | const expectedOutputClass = `export class ForbiddenError extends PropheticError { 21 | constructor(properties?: Record) { 22 | super("ForbiddenError", "You are not allowed to do this", "FORBIDDEN", properties); 23 | } 24 | }` 25 | describe('generateRawClass', () => { 26 | it('toRawClassesArray should correctly returns string classes definition', () => { 27 | const rawClasses = toRawClassesArray(errors); 28 | expect(rawClasses).toHaveLength(3); 29 | expect(rawClasses[1]).toEqual(expectedOutputClass); 30 | }); 31 | 32 | it('Should correctly create the GraphQL Type Definition string', () => { 33 | const scalarTypesArray = toScalarTypesMap(errors); 34 | const rawGraphqlTypes = generatePropheticErrorType(scalarTypesArray); 35 | 36 | expect(removeWhiteSpaces(rawGraphqlTypes)).toEqual(removeWhiteSpaces(` 37 | type PropheticErrorExtensions { 38 | code: String? 39 | } 40 | 41 | type PropheticError { 42 | name: String 43 | message: String? 44 | extensions: PropheticErrorExtensions 45 | }`)); 46 | }); 47 | 48 | it('Should correctly map object fields to the right type', () => { 49 | const scalarTypesArray = toScalarTypesMap(errors); 50 | expect(scalarTypesArray).toEqual(expectedTypes); 51 | }); 52 | }); -------------------------------------------------------------------------------- /src/commands/generate/generateRawClass.ts: -------------------------------------------------------------------------------- 1 | import createError, { PropheticErrorTextDef } from './createError' 2 | import { JsonInputErrorEntryRecord, JsonInputErrorEntry, toOutputErrors } from '../../types'; 3 | 4 | export type ScalarType = "Boolean" | "Int" | "Float" | "String" 5 | export type ScalarTypesMap = {[key: string]: ScalarType}; 6 | 7 | function scalarGQLTpeFromValue(value: any): ScalarType | null { 8 | switch (typeof value) { 9 | case "string": 10 | return "String" 11 | case "boolean": 12 | return "Boolean" 13 | case "number": 14 | if(value % 1 === 0){ 15 | return "Int" 16 | } else { 17 | return "Float" 18 | } 19 | default: 20 | throw new Error(`Non scalar type define for "${value}" type is ${typeof value}`); 21 | } 22 | } 23 | 24 | export function toRawClassesArray(entries: JsonInputErrorEntryRecord) { 25 | return Object.keys(entries).map((key) => { 26 | const { message, code, ...rest } = entries[key]; 27 | return createError(key, message, code, rest); 28 | }); 29 | } 30 | 31 | export function toScalarTypesMap(entries: JsonInputErrorEntryRecord): ScalarTypesMap[] { 32 | const initial: ScalarTypesMap = {}; 33 | return Object.values(entries).map((entry) => Object.keys(entry).filter(key => key !== 'message').reduce((prev, entryFieldKey) => { 34 | const fieldValue = entry[entryFieldKey]; 35 | const fieldType = scalarGQLTpeFromValue(fieldValue); 36 | if (fieldType) prev[entryFieldKey] = fieldType; 37 | 38 | return prev; 39 | }, initial),{}); 40 | } 41 | 42 | type TypeDefinition = { type: string, isNullableType: boolean } 43 | type TypeDefinitionsMap = {[key: string]: TypeDefinition } 44 | 45 | export function generatePropheticErrorType(typesArray: ScalarTypesMap[]) { 46 | const fieldsReduced = typesArray.filter(type => type).reduce((prev: TypeDefinitionsMap, typesMap) => { 47 | const isFirst = prev === {} 48 | 49 | for (let key in typesMap) { 50 | const fieldType = typesMap[key]; 51 | let isNullableType = true; 52 | if(!isFirst && prev[key] && prev[key].isNullableType) { 53 | if(prev[key].isNullableType !== true) { 54 | prev[key].isNullableType = prev[key].isNullableType; 55 | } else{ 56 | prev[key].isNullableType = false 57 | } 58 | } 59 | 60 | const value = { type: fieldType, isNullableType }; 61 | prev[key] = value; 62 | } 63 | return prev; 64 | },{}) as TypeDefinitionsMap 65 | 66 | 67 | const fields = Object.entries(fieldsReduced).map(([name, { type, isNullableType } ]) => { 68 | return `${name}: ${type}${isNullableType ? '?': ''}` 69 | }); 70 | 71 | return ` 72 | type PropheticErrorExtensions { 73 | ${fields.join('\n ')} 74 | } 75 | 76 | type PropheticError { 77 | name: String 78 | message: String? 79 | extensions: PropheticErrorExtensions 80 | } 81 | ` 82 | } 83 | 84 | export default function generateRawClass (entries: JsonInputErrorEntryRecord) { 85 | const rawErrorClasses = toRawClassesArray(entries).join('\n'); 86 | const fieldsTypesMapArray = toScalarTypesMap(entries); 87 | const rawPropheticErrorAndExtensionsType = generatePropheticErrorType(fieldsTypesMapArray); 88 | const errorsList = toOutputErrors(entries); 89 | 90 | const classFile = ` 91 | import { ApolloError } from 'apollo-server' 92 | 93 | export const errorsList = ${JSON.stringify(errorsList, null, 2)}; 94 | export const errorType = \`${rawPropheticErrorAndExtensionsType}\`; 95 | 96 | ${PropheticErrorTextDef} 97 | 98 | ${rawErrorClasses} 99 | ` 100 | return classFile; 101 | } -------------------------------------------------------------------------------- /src/commands/generate/index.ts: -------------------------------------------------------------------------------- 1 | export { default as generate } from "./generate"; 2 | -------------------------------------------------------------------------------- /src/commands/index.ts: -------------------------------------------------------------------------------- 1 | export { generate } from './generate' 2 | export { ask } from './ask' 3 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import * as commands from './commands'; 2 | import * as utils from './utils'; 3 | import * as types from './types'; 4 | 5 | export { commands, utils, types }; -------------------------------------------------------------------------------- /src/types.ts: -------------------------------------------------------------------------------- 1 | export type JsonInputErrorEntry = { 2 | message: string 3 | code: string 4 | [key:string]: any 5 | } 6 | 7 | export type ErrorOutputEntry = { 8 | name: string 9 | message?: string 10 | extensions: { 11 | code: string 12 | [key:string]: any 13 | } 14 | } 15 | 16 | export type JsonInputErrorEntryRecord = { [key:string]: JsonInputErrorEntry } 17 | export type JsonOutputEntriesRecord = { [key:string]: ErrorOutputEntry } 18 | 19 | export function toErrorEntries(entries: ErrorOutputEntry[]) { 20 | return entries.reduce((map, entry) => { 21 | map[entry.name] = entry; 22 | return map; 23 | }, {} as JsonOutputEntriesRecord); 24 | } 25 | 26 | export function toOutputErrors(entries: JsonInputErrorEntryRecord) { 27 | return Object.keys(entries).map((key) => { 28 | const { name, message, code, ...rest } = entries[key]; 29 | return { 30 | name: name || key, 31 | message: message, 32 | extensions: { 33 | code, 34 | ...rest 35 | } 36 | } as ErrorOutputEntry 37 | }); 38 | } -------------------------------------------------------------------------------- /src/utils/fs-prophecy.ts: -------------------------------------------------------------------------------- 1 | import * as fs from 'fs'; 2 | import * as rimraf from 'rimraf'; 3 | import * as mkdirp from 'mkdirp'; 4 | import * as jsonfile from 'jsonfile'; 5 | import { JsonInputErrorEntry } from '../types'; 6 | 7 | export const writeFile = (path: string, content: string) => fs.writeFileSync(path, content); 8 | export const mkdirs = (path: string) => mkdirp.sync(path); 9 | export const rmrf = (path: string) => rimraf.sync(path); 10 | export function readJsonDef(filePath: jsonfile.Path) { 11 | return jsonfile.readFileSync(filePath) as JsonInputErrorEntry; 12 | } -------------------------------------------------------------------------------- /src/utils/index.ts: -------------------------------------------------------------------------------- 1 | import * as fs from './fs-prophecy'; 2 | 3 | export { 4 | fs 5 | } 6 | -------------------------------------------------------------------------------- /src/writeClassFile.spec.ts: -------------------------------------------------------------------------------- 1 | import * as path from 'path'; 2 | import * as chai from 'chai'; 3 | import chaiFS from 'chai-fs'; 4 | 5 | import writeClassFile from './writeClassFile' 6 | import { fs } from './utils'; 7 | 8 | import { mkdirTmp, tmpErrorFilePath } from './_specs-utils'; 9 | 10 | chai.use(chaiFS); 11 | const { expect } = chai; 12 | 13 | describe('createClassFile', () => { 14 | it('Should create a folder with the "Errors.ts" file in it', () => { 15 | const tmpDirPath = mkdirTmp(); 16 | const tmpErrorsPath = path.join(tmpDirPath, 'Errors.ts'); 17 | writeClassFile("class SpecialError {}", tmpDirPath); 18 | ((expect(tmpDirPath).to.be.a) as any).directory(tmpDirPath).and.not.empty; 19 | ((expect(tmpDirPath).to.be.a) as any).directory(tmpDirPath).with.files(['Errors.ts']); 20 | ((expect(tmpErrorsPath).to.be.a) as any).to.be.a.file().with.content("class SpecialError {}"); 21 | 22 | fs.rmrf(tmpDirPath); 23 | }); 24 | 25 | it('Should create a folder with the "Errs.ts file" in it', () => { 26 | const tmpDirPath = tmpErrorFilePath('Errs.ts'); 27 | writeClassFile("class SpecialError {}", tmpDirPath); 28 | ((expect(tmpDirPath).to.be.a) as any).to.be.a.file().and.not.empty; 29 | ((expect(tmpDirPath).to.be.a) as any).to.be.a.file().with.content("class SpecialError {}"); 30 | 31 | fs.rmrf(tmpDirPath); 32 | }); 33 | 34 | it('Should throw .ts file expected as output', () => { 35 | const tmpDirPath = tmpErrorFilePath('Errors.js'); 36 | const writeClassFileThrow = () => writeClassFile("class SpecialError {}", tmpDirPath); 37 | expect(writeClassFileThrow).to.throw(".ts file expected as output"); 38 | 39 | fs.rmrf(tmpDirPath); 40 | }); 41 | }); -------------------------------------------------------------------------------- /src/writeClassFile.ts: -------------------------------------------------------------------------------- 1 | import { parse , normalize } from 'path'; 2 | import { fs } from './utils'; 3 | 4 | export default (content: string, output: string = '_generated') => { 5 | const outputPath = normalize(output); 6 | const fileExtension = parse(outputPath).ext; 7 | const fileSpecified = (fileExtension !== null && fileExtension !== undefined) && fileExtension.length > 0; 8 | const fileIsTS = fileExtension === '.ts'; 9 | 10 | if(fileSpecified && !fileIsTS) { 11 | throw new Error('.ts file expected as output'); 12 | } 13 | 14 | const outputFile = fileSpecified ? parse(outputPath).base : 'Errors.ts'; 15 | const outputDirectory = fileSpecified ? parse(outputPath).dir : outputPath; 16 | 17 | fs.mkdirs(outputDirectory); 18 | 19 | const outputFilePath = normalize(`${outputDirectory}/${outputFile}`); 20 | fs.writeFile(outputFilePath, content); 21 | 22 | return outputFilePath; 23 | } -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es6", 4 | "module": "commonjs", 5 | "types": ["node", "jest"], 6 | "moduleResolution": "node", 7 | "lib": [ "es6", "esnext" ], 8 | "esModuleInterop": true, 9 | "noImplicitAny": true, 10 | "declaration": true, 11 | "sourceMap": true, 12 | "pretty": true, 13 | "rootDir": "src", 14 | "outDir": "lib", 15 | "skipLibCheck": true, 16 | "resolveJsonModule": true, 17 | "strict": true 18 | }, 19 | "exclude": [ 20 | "lib", 21 | "test", 22 | "**/*.spec.ts", 23 | "**/*.check.ts", 24 | "./node_modules", 25 | "node_modules", 26 | "_generated", 27 | "_specs-utils" 28 | ] 29 | } --------------------------------------------------------------------------------