├── .babelrc ├── .eslintignore ├── .eslintrc ├── .eslintrc.build.json ├── .eslintrc.shared.json ├── .eslintrc.spec.json ├── .github └── workflows │ └── test.yml ├── .gitignore ├── .gitlab-ci.yml ├── .npmignore ├── .travis.yml ├── .vscode ├── launch.json └── settings.json ├── CHANGELOG.md ├── LICENSE.md ├── README.md ├── build-scripts ├── copy-as-mjs.js └── pkg.json ├── docs ├── external-deps │ ├── deps-rt.ts.txt │ └── deps.ts.txt ├── playground │ ├── assets │ │ ├── script │ │ │ ├── playground.js │ │ │ └── playground2.js │ │ └── style │ │ │ ├── index.css │ │ │ ├── playground.css │ │ │ └── playground2.css │ ├── playground.html │ └── playground2.html ├── repository-open-graph-no-text.png ├── repository-open-graph.png └── tynder.svg ├── examples └── schema │ ├── _compiled │ ├── fields.json │ ├── fields.ts │ ├── filesys.json │ ├── filesys.ts │ ├── primitives.json │ ├── primitives.ts │ ├── tynder.json │ └── tynder.ts │ ├── csharp │ ├── fields.cs │ ├── filesys.cs │ ├── primitives.cs │ └── tynder.cs │ ├── graphql │ ├── fields.graphql │ ├── filesys.graphql │ ├── primitives.graphql │ └── tynder.graphql │ ├── json-schema │ ├── fields.json │ ├── fields.ts │ ├── filesys.json │ ├── filesys.ts │ ├── primitives.json │ ├── primitives.ts │ ├── tynder.json │ └── tynder.ts │ ├── proto3 │ ├── fields.proto │ ├── filesys.proto │ ├── primitives.proto │ └── tynder.proto │ ├── ts │ ├── fields.d.ts │ ├── filesys.d.ts │ ├── primitives.d.ts │ └── tynder.d.ts │ └── tynder │ ├── fields.tss │ ├── filesys.tss │ ├── primitives.tss │ └── tynder.tss ├── package-lock.json ├── package.json ├── schema └── .gitkeep ├── spec ├── helpers │ └── reporter.js └── support │ └── jasmine.json ├── src ├── _spec │ ├── compiler-1.spec.ts │ ├── compiler-2.spec.ts │ ├── compiler-3.spec.ts │ ├── compiler-4.spec.ts │ ├── compiler-5.spec.ts │ ├── compiler-6.spec.ts │ ├── compiler-7.spec.ts │ ├── compiler-8.spec.ts │ ├── compiler-9.spec.ts │ ├── fixes-1.spec.ts │ ├── fixes-2.spec.ts │ ├── fixes-3.spec.ts │ ├── fixes-4.spec.ts │ ├── fixes-5.spec.ts │ ├── foo.spec.ts │ ├── index.ts │ ├── json-schema-1.spec.ts │ └── operators.spec.ts ├── cli.ts ├── codegen.ts ├── compiler.ts ├── constraints │ └── unique.ts ├── index-rt.ts ├── index.ts ├── lib │ ├── cli.ts │ ├── codegen │ │ ├── csharp.ts │ │ ├── graphql.ts │ │ ├── json-schema.ts │ │ ├── proto3.ts │ │ └── typescript.ts │ ├── compiler.ts │ ├── errors.ts │ ├── escape.ts │ ├── protection.ts │ ├── reporter.ts │ ├── resolver.ts │ └── util.ts ├── operators.ts ├── picker.ts ├── serializer.ts ├── stereotypes │ ├── date.ts │ └── noop.ts ├── types.ts ├── types │ ├── json-schema-types.ts │ └── tynder-schema-types.ts └── validator.ts ├── tsconfig.build.json ├── tsconfig.json ├── tsconfig.spec.json ├── tslint.json ├── webpack.config.js ├── webpack.dist.config.js └── webpack.spec.config.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "sourceMaps": true, 3 | "presets": [ 4 | ["@babel/preset-env", { 5 | "targets": { 6 | "node": "current" 7 | } 8 | }] 9 | ], 10 | "ignore": [] 11 | } 12 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "./.eslintrc.shared.json" 4 | ], 5 | "parserOptions": { 6 | "project": "./tsconfig.json", 7 | "tsconfigRootDir": "." 8 | }, 9 | "rules": { 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /.eslintrc.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "./.eslintrc.shared.json" 4 | ], 5 | "parserOptions": { 6 | "project": "./tsconfig.build.json", 7 | "tsconfigRootDir": "." 8 | }, 9 | "rules": { 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /.eslintrc.shared.json: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "@typescript-eslint/parser", 3 | "plugins": ["@typescript-eslint"], 4 | "extends": [ 5 | "eslint:recommended", 6 | "plugin:@typescript-eslint/eslint-recommended", 7 | "plugin:@typescript-eslint/recommended", 8 | "plugin:@typescript-eslint/recommended-requiring-type-checking" 9 | ], 10 | "rules": { 11 | "no-useless-escape": 0, 12 | "no-unexpected-multiline": 0, 13 | "no-fallthrough": 0, 14 | "no-control-regex": 0, 15 | "no-prototype-builtins": 0, 16 | "no-inner-declarations": 0, 17 | "no-useless-catch": 0, 18 | "@typescript-eslint/no-var-requires": 0, 19 | "@typescript-eslint/no-unused-vars": 0, 20 | "@typescript-eslint/no-use-before-define": 0, 21 | "@typescript-eslint/explicit-function-return-type": 0, 22 | "@typescript-eslint/no-explicit-any": 0, 23 | "@typescript-eslint/no-unnecessary-type-assertion": 0, 24 | "@typescript-eslint/member-delimiter-style": 0, 25 | 26 | "@typescript-eslint/no-unsafe-return": 0, 27 | "@typescript-eslint/no-unsafe-assignment": 0, 28 | "@typescript-eslint/no-unsafe-member-access": 0, 29 | "@typescript-eslint/no-implied-eval": 0, 30 | "@typescript-eslint/no-unsafe-call": 0, 31 | "@typescript-eslint/restrict-template-expressions": 0, 32 | "@typescript-eslint/explicit-module-boundary-types": 0, 33 | "@typescript-eslint/restrict-plus-operands": 0, 34 | "@typescript-eslint/ban-types": 0 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /.eslintrc.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "./.eslintrc.shared.json" 4 | ], 5 | "parserOptions": { 6 | "project": "./tsconfig.spec.json", 7 | "tsconfigRootDir": "." 8 | }, 9 | "rules": { 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | on: [push, pull_request] 2 | 3 | defaults: 4 | run: 5 | shell: bash 6 | 7 | jobs: 8 | test: 9 | name: Test on node ${{ matrix.node_version }} and ${{ matrix.os }} 10 | runs-on: ${{ matrix.os }} 11 | strategy: 12 | matrix: 13 | node_version: [10, 12, 14, 15] 14 | os: [ubuntu-latest, windows-latest, macos-latest] 15 | 16 | steps: 17 | - uses: actions/checkout@v1 18 | 19 | - name: Use Node.js ${{ matrix.node_version }} 20 | uses: actions/setup-node@v1 21 | with: 22 | node-version: ${{ matrix.node_version }} 23 | 24 | - name: npm install, build and test 25 | run: | 26 | npm install -g npm 27 | npm ci 28 | npm run clean 29 | npm run lint 30 | npm run build 31 | npm run build:dist:dev 32 | npm run build:dist:prod 33 | npm test 34 | 35 | artifact: 36 | name: Build artifact 37 | runs-on: ubuntu-latest 38 | needs: [test] 39 | 40 | steps: 41 | - uses: actions/checkout@v1 42 | 43 | - name: Use Node.js ${{ 12 }} 44 | uses: actions/setup-node@v1 45 | with: 46 | node-version: ${{ 12 }} 47 | 48 | - name: npm install, build 49 | run: | 50 | npm install -g npm 51 | npm ci 52 | npm run build:dist:dev 53 | npm run build:dist:prod 54 | - uses: actions/upload-artifact@master 55 | with: 56 | name: production-build 57 | path: dist 58 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Runtime data 7 | pids 8 | *.pid 9 | *.seed 10 | 11 | # Directory for instrumented libs generated by jscoverage/JSCover 12 | lib-cov 13 | 14 | # Coverage directory used by tools like istanbul 15 | coverage 16 | 17 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 18 | .grunt 19 | 20 | # node-waf configuration 21 | .lock-wscript 22 | 23 | # Optional npm cache directory 24 | .npm 25 | 26 | # Optional REPL history 27 | .node_repl_history 28 | 29 | # Compiled binary addons (http://nodejs.org/api/addons.html) 30 | build/Release 31 | 32 | 33 | # Visual Studio files 34 | /.vs/ 35 | 36 | # VS Code cache files 37 | /.vscode/.browse.VC* 38 | 39 | # Dependency directory 40 | # https://docs.npmjs.com/misc/faq#should-i-check-my-node-modules-folder-into-git 41 | /node_modules/ 42 | 43 | # Bin directory 44 | /bin/ 45 | /bin.cli/ 46 | /bin.test/ 47 | 48 | # ES2015 modules (for webpack) 49 | /modules/ 50 | 51 | # Declarations directory 52 | /declarations/ 53 | 54 | # Dist directory 55 | /dist/ 56 | 57 | # Debug output directory 58 | /debug/ 59 | 60 | # NPM files 61 | .npmrc 62 | -------------------------------------------------------------------------------- /.gitlab-ci.yml: -------------------------------------------------------------------------------- 1 | image: node:12 2 | 3 | before_script: 4 | - npm ci 5 | 6 | #cache: 7 | # paths: 8 | # - node_modules/ 9 | 10 | test:lint: 11 | script: 12 | - npm run lint 13 | 14 | test:node:15: 15 | image: node:15 16 | script: 17 | - npm run clean 18 | - npm run build 19 | - npm run build:dist:dev 20 | - npm run build:dist:prod 21 | - npm test 22 | 23 | # LTS 24 | test:node:14: 25 | image: node:14 26 | script: 27 | - npm run clean 28 | - npm run build 29 | - npm run build:dist:dev 30 | - npm run build:dist:prod 31 | - npm test 32 | 33 | # LTS 34 | test:node:12: 35 | script: 36 | - npm run clean 37 | - npm run build 38 | - npm run build:dist:dev 39 | - npm run build:dist:prod 40 | - npm test 41 | 42 | # LTS 43 | test:node:10: 44 | image: node:10 45 | script: 46 | - npm run clean 47 | - npm run build 48 | - npm run build:dist:dev 49 | - npm run build:dist:prod 50 | - npm test 51 | 52 | publish:artifacts: 53 | stage: deploy 54 | # only: 55 | # - tags 56 | # - triggers 57 | script: 58 | - npm run clean 59 | - npm run build:dist:dev 60 | - npm run build:dist:prod 61 | artifacts: 62 | paths: 63 | - dist/ 64 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Runtime data 7 | pids 8 | *.pid 9 | *.seed 10 | 11 | # Directory for instrumented libs generated by jscoverage/JSCover 12 | lib-cov 13 | 14 | # Coverage directory used by tools like istanbul 15 | coverage 16 | 17 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 18 | .grunt 19 | 20 | # node-waf configuration 21 | .lock-wscript 22 | 23 | # Optional npm cache directory 24 | .npm 25 | 26 | # Optional REPL history 27 | .node_repl_history 28 | 29 | # Compiled binary addons (http://nodejs.org/api/addons.html) 30 | build/Release 31 | 32 | 33 | # Visual Studio files 34 | /.vs/ 35 | 36 | # VS Code cache files 37 | /.vscode/.browse.VC* 38 | 39 | # Dependency directory 40 | # https://docs.npmjs.com/misc/faq#should-i-check-my-node-modules-folder-into-git 41 | /node_modules/ 42 | 43 | # Bin directory 44 | # /bin/ 45 | # /bin.cli/ 46 | /bin.test/ 47 | 48 | # ES2015 modules (for webpack) 49 | # /modules/ 50 | 51 | # Declarations directory 52 | # /declarations/ 53 | 54 | # Dist directory 55 | /dist/ 56 | 57 | # Debug output directory 58 | /debug/ 59 | 60 | # NPM files 61 | .npmrc 62 | 63 | 64 | 65 | ################### 66 | #### npmignore #### 67 | 68 | /.github/ 69 | /.vscode/ 70 | /spec/ 71 | .babelrc 72 | .gitlab-ci.yml 73 | .travis.yml 74 | tsconfig.json 75 | tsconfig.spec.json 76 | tslint.json 77 | webpack.config.js 78 | 79 | /src.dist/ 80 | webpack.dist.config.js 81 | webpack.spec.config.js 82 | 83 | /examples/ 84 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "10" 4 | - "12" 5 | - "14" 6 | # - "15" 7 | 8 | #branches: 9 | # only: 10 | # - master 11 | 12 | sudo: false 13 | 14 | before_script: 15 | - npm run clean 16 | - npm run lint 17 | - npm run build 18 | - npm run build:dist:dev 19 | - npm run build:dist:prod 20 | - npm test 21 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "type": "node", 6 | "request": "launch", 7 | "name": "Launch Program", 8 | "program": "${workspaceRoot}/node_modules/jasmine/bin/jasmine.js", 9 | "outFiles": [ 10 | "${workspaceRoot}/bin.test/index.spec.js" 11 | ] 12 | }, 13 | { 14 | "type": "node", 15 | "request": "launch", 16 | "name": "Launch Program (cli)", 17 | "program": "${workspaceRoot}/src/cli.ts", 18 | "outFiles": [ 19 | "${workspaceRoot}/bin.cli/tynder.js" 20 | ], 21 | "args": [ 22 | "compile", 23 | "--indir", 24 | "./examples/schema/tynder", 25 | "--outdir", 26 | "./examples/schema/_compiled", 27 | ] 28 | }, 29 | { 30 | "type": "node", 31 | "request": "launch", 32 | "name": "Launch Program (cli) cs", 33 | "program": "${workspaceRoot}/src/cli.ts", 34 | "outFiles": [ 35 | "${workspaceRoot}/bin.cli/tynder.js" 36 | ], 37 | "args": [ 38 | "gen-csharp", 39 | "--indir", 40 | "./examples/schema/tynder", 41 | "--outdir", 42 | "./examples/schema/csharp", 43 | ] 44 | } 45 | ] 46 | } 47 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.associations": { 3 | "*.tss": "typescript" 4 | } 5 | } -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## v0.7.0 4 | 5 | * Update dependencies. 6 | * Update CI configurations. 7 | * Migrate to webpack 5. 8 | 9 | 10 | --- 11 | 12 | 13 | ## v0.6.6 14 | 15 | * Add fall back processing to get `globalThis`, `Object`, and `Function`. (to check for unsafe keywords) 16 | * Update dependencies. 17 | 18 | 19 | ## v0.6.5 20 | 21 | * Update dependencies. 22 | * Migrate to TypeScript 4.0. 23 | 24 | 25 | ## v0.6.4 26 | 27 | * Fix empty string literal parsing. 28 | * Update dependencies. 29 | * Update CI configurations. 30 | 31 | 32 | ## v0.6.3 33 | 34 | * Add `assertType(data, ty, ctx?)` assertion type guard function. 35 | * Generate the type name const enum `Schema` when serializing the schema. 36 | * Edit README. 37 | * Update dev dependencies. 38 | 39 | 40 | ## v0.6.2 41 | 42 | * Add `declare type|interface|enum|const enum` statement. 43 | * Refactoring: 44 | * Move and separate files. 45 | * Extract a compiler docComment function. 46 | * Fix README. 47 | * Update dev dependencies. 48 | 49 | 50 | ## v0.6.1 51 | 52 | * Fix typo. 53 | 54 | 55 | ## v0.6.0 56 | 57 | * Add pass-through `declare var|let|const` statement. 58 | * Add `const enum` definition. 59 | * Add `/* @tynder-pass-through ... */` directive. 60 | * Typed `external` statement and `/* @tynder-external ... */` directive. 61 | 62 | 63 | ---- 64 | 65 | 66 | ## v0.5.2 67 | 68 | * `[FIX]` Fix codegen: Fix type alias, repeated types and oneOf types (CSharp, Protobuf, GraphQL). 69 | 70 | 71 | ## v0.5.1 72 | 73 | * `[FIX]` Fix and improve codegen output (CSharp, Protobuf3). 74 | * Update CI configurations. 75 | 76 | 77 | ## v0.5.0 78 | 79 | * Add `C#` type definition code generation. 80 | * Improve code generation (TypeScript, Protobuf, GraphQL). 81 | 82 | 83 | ---- 84 | 85 | 86 | ## v0.4.1 87 | 88 | * `[FIX]` Fix TypeScript code generation: object member non-ascii symbol name should be enclose with quotation. 89 | 90 | 91 | ## v0.4.0 92 | 93 | * Add type guard functions `isType`. 94 | * `[FIX]` Fix `date`, `lcdate`, `datetime`, `lcdatetime` stereotypes. 95 | * Constructor returns wrong month if passed month's day of month is shorted than current month. 96 | 97 | ### _Breaking changes_ 98 | * Type of `ValidationError::ctx` is changed to `Partial`. 99 | 100 | 101 | ---- 102 | 103 | 104 | ## v0.3.11 105 | 106 | * `[FIX]` Fix d.ts code generation: empty objects generate invalid code. 107 | 108 | 109 | ## v0.3.10 110 | 111 | * `[FIX]` Fix d.ts code generation: additional-props entry is not output when other member entries are not exist. 112 | 113 | 114 | ## v0.3.9 115 | 116 | * Add `unique-non-null` constraint. 117 | * `[FIX]` Fix `unique` and `unique-non-null` constraint bug. 118 | 119 | 120 | ## v0.3.8 121 | 122 | * Add `@constraint` decorator and `unique` constraint implementation. 123 | * Add spec codes. 124 | 125 | 126 | ## v0.3.7 127 | 128 | * Add `@meta` decorator. 129 | * User defined custom properties (meta informations). 130 | * Output to the compiled schema. 131 | * Add spec codes. 132 | * Update dev dependencies. 133 | * Add eslint configurations. 134 | * Edit github actions CI configuration. 135 | 136 | 137 | ## v0.3.6 138 | 139 | * Add `first-date-of-fy(mo)` operator to `data` and `datetime` stereotype formula. 140 | * fiscal year operator. 141 | * Add spec codes. 142 | 143 | 144 | ## v0.3.5 145 | 146 | * `[FIX]` Fix stereotype, forceCast, and recordType decorators: 147 | when optional type, it should be decorated with ty.optional. 148 | * `v0.3.4` fix is broken. 149 | 150 | 151 | ## v0.3.4 152 | 153 | * `[FIX]` Fix stereotype, forceCast, and recordType decorators: 154 | when optional type, it should be decorated with ty.optional. 155 | 156 | 157 | ## v0.3.3 158 | 159 | * `[FIX]` Fix compiler: Fix character class that can be used in symbols. 160 | 161 | 162 | ## v0.3.2 163 | 164 | * Improve one-of assertion validation error message. 165 | * Add `@recordType` decorator. 166 | * If the decorated member field of object is validated, the union type is determined. 167 | 168 | 169 | ## v0.3.1 170 | 171 | * `[FIX]` Fix error handling and reporting of stereotype. 172 | * Improve playground. 173 | 174 | 175 | ## v0.3.0 176 | 177 | * Add `@stereotype` decorator. 178 | * Perform custom validation. 179 | * Add standard stereotypes. 180 | * `date` 181 | * date (UTC timezone) 182 | * `lcdate` 183 | * date (local timezone) 184 | * `datetime` 185 | * datetime (UTC timezone) 186 | * `lcdatetime` 187 | * datetime (local timezone) 188 | * Add `@forceCast` decorator. 189 | * Validate after forcibly casting to the assertion's type. 190 | * Add spec codes. 191 | 192 | 193 | ---- 194 | 195 | 196 | ## v0.2.3 197 | 198 | * `[FIX]` Fix serializer & deserializer bugs. 199 | * RegExp pattern additonal props are not serialize/deserialize correctly. 200 | * Add spec codes. 201 | 202 | 203 | ## v0.2.2 204 | 205 | * `[FIX]` Fix serializer & deserializer bugs. 206 | * Object member meta info format is wrong. (serializer) 207 | * Optional info is lost in named type case. (serializer) 208 | * Extended interface has unexpected duplicated member properties. (deserializer) 209 | * Add spec codes. 210 | 211 | 212 | ## v0.2.1 213 | 214 | * A grammar for referencing other interface members has been added. 215 | * `[FIX]` Fix meta info lost during serialization. 216 | * Occurs when serializing named non-primitive types. 217 | * Update dependencies. 218 | 219 | 220 | ## v0.2.0 221 | 222 | * `[FIX]` Fix validation reporting. (incorrect parent name) 223 | 224 | ### _Breaking changes_ 225 | * Change the `dataPath` format for validation errors. 226 | * Path separator after `type` is changed from `.` to `:`. 227 | * before changed: `File.acl.(0:repeated).ACL.target` 228 | * after changed: `File:acl.(0:repeated).ACL:target` 229 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # ISC License (ISC) 2 | 3 | ### Copyright (c) 2019-2020 Shellyl_N and Authors 4 | #### https://github.com/shellyln 5 | 6 | Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby 7 | granted, provided that the above copyright notice and this permission notice appear in all copies. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING 10 | ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, 11 | DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 12 | WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE 13 | OR PERFORMANCE OF THIS SOFTWARE. 14 | -------------------------------------------------------------------------------- /build-scripts/copy-as-mjs.js: -------------------------------------------------------------------------------- 1 | 2 | const fs = require('fs'); 3 | const path = require('path'); 4 | 5 | 6 | 7 | function copyAsMjs(srcDir, destDir, options) { 8 | const opts = Object.assign({}, { 9 | srcExt: '.js', 10 | destExt: '.mjs', 11 | }, options || {}); 12 | 13 | if (! fs.existsSync(destDir)) { 14 | fs.mkdirSync(destDir, { recursive: true }); 15 | } 16 | 17 | if (fs.lstatSync(srcDir).isDirectory()) { 18 | const files = fs.readdirSync(srcDir); 19 | for (const entry of files) { 20 | const srcEntryPath = path.join(srcDir, entry); 21 | if (fs.lstatSync(srcEntryPath).isDirectory()) { 22 | copyAsMjs(srcEntryPath, path.join(destDir, entry), opts); 23 | } else { 24 | if (entry.toLowerCase().endsWith(opts.srcExt)) { 25 | fs.copyFileSync(srcEntryPath, path.join(destDir, entry.slice(0, -(opts.srcExt.length)) + opts.destExt)); 26 | } 27 | } 28 | } 29 | } 30 | } 31 | 32 | exports.copyAsMjs = copyAsMjs; 33 | -------------------------------------------------------------------------------- /build-scripts/pkg.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "module" 3 | } 4 | -------------------------------------------------------------------------------- /docs/external-deps/deps-rt.ts.txt: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 Shellyl_N and Authors 2 | // license: ISC 3 | // https://github.com/shellyln 4 | 5 | 6 | // External dependencies 7 | -------------------------------------------------------------------------------- /docs/external-deps/deps.ts.txt: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 Shellyl_N and Authors 2 | // license: ISC 3 | // https://github.com/shellyln 4 | 5 | 6 | // External dependencies 7 | 8 | export { existsSync, 9 | mkdirSync, 10 | lstatSync, 11 | readdirSync, 12 | readFileSync } from 'fs'; 13 | export { join } from 'path'; 14 | 15 | export { parserInput, 16 | ParserInputWithCtx, 17 | ParserFnWithCtx } from 'fruitsconfits/modules/lib/types'; 18 | export { formatErrorMessage } from 'fruitsconfits/modules/lib/parser'; 19 | export { getStringParsers } from 'fruitsconfits/modules/lib/string-parser'; 20 | export { getObjectParsers } from 'fruitsconfits/modules/lib/object-parser'; 21 | 22 | export { SxTokenChild, 23 | SxToken, 24 | SxSymbol, 25 | SxParserConfig } from 'liyad/modules/s-exp/types'; 26 | export { default as installCore } from 'liyad/modules/s-exp/operators/core'; 27 | export { SExpression } from 'liyad/modules/s-exp/interpreters'; 28 | export { defaultConfig } from 'liyad/modules/s-exp/defaults'; 29 | -------------------------------------------------------------------------------- /docs/playground/assets/style/index.css: -------------------------------------------------------------------------------- 1 | 2 | 3 | body { 4 | display: flex; 5 | min-height: 100vh; 6 | flex-direction: column; 7 | } 8 | 9 | nav { 10 | font-family: 'Ubuntu Mono', monospace; 11 | } 12 | 13 | main { 14 | flex: 1 0 auto; 15 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif; 16 | background: radial-gradient(#F2B9A1, #EA6264); 17 | } 18 | 19 | footer { 20 | font-family: 'Ubuntu Mono', monospace; 21 | } 22 | 23 | .dots { 24 | border-width: 0 0 8px; 25 | border-style: solid; 26 | border-image: url('data:image/svg+xml,') 0 0 100% repeat; 27 | width: 216px; 28 | } 29 | -------------------------------------------------------------------------------- /docs/playground/assets/style/playground.css: -------------------------------------------------------------------------------- 1 | 2 | 3 | body { 4 | font-family: 'Ubuntu Mono', monospace; 5 | display: flex; 6 | min-height: 100vh; 7 | flex-direction: column; 8 | } 9 | 10 | main { 11 | flex: 1 0 auto; 12 | } 13 | 14 | .AceEditorOuterWrap { 15 | width: calc(50% - 10px); 16 | min-width: 400px; 17 | margin: 8px 4px 4px 4px; 18 | } 19 | 20 | .AceEditorOuterWrap div.AceEditorDiv { 21 | font-size: 12pt; 22 | } 23 | 24 | .AceEditorDiv { 25 | width: 100%; 26 | height: calc(100vh - 64px - 220px - 55px - 15px); 27 | min-height: 300px; 28 | } 29 | 30 | .OutletDiv { 31 | width: calc(50% - 10px); 32 | min-width: 400px; 33 | height: calc(100vh - 64px - 220px - 55px - 15px); 34 | min-height: 300px; 35 | margin: 8px 4px 4px 4px; 36 | overflow: auto; 37 | } 38 | 39 | @media only screen and (max-width: 1200px) { 40 | .AceEditorOuterWrap { 41 | width: 100%; 42 | } 43 | .AceEditorDiv { 44 | height: calc(100vh - 130px); 45 | } 46 | .OutletDiv { 47 | width: 100%; 48 | height: calc(100vh - 20px); 49 | } 50 | } 51 | 52 | @media print { 53 | .AceEditorOuterWrap { 54 | width: 100%; 55 | } 56 | .OutletDiv { 57 | width: 100%; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /docs/playground/assets/style/playground2.css: -------------------------------------------------------------------------------- 1 | 2 | 3 | body { 4 | font-family: 'Ubuntu Mono', monospace; 5 | display: flex; 6 | min-height: 100vh; 7 | flex-direction: column; 8 | } 9 | 10 | main { 11 | flex: 1 0 auto; 12 | } 13 | 14 | .AceEditorOuterWrap { 15 | width: calc(50% - 10px); 16 | min-width: 400px; 17 | margin: 8px 4px 4px 4px; 18 | } 19 | 20 | .AceEditorOuterWrap div.AceEditorDiv { 21 | font-size: 12pt; 22 | } 23 | 24 | .AceEditorDiv { 25 | width: 100%; 26 | height: calc(100vh - 64px - 220px - 55px - 15px); 27 | min-height: 300px; 28 | } 29 | 30 | .OutletDiv { 31 | width: calc(100% - 10px); 32 | min-width: 400px; 33 | height: 100px; 34 | min-height: 100px; 35 | margin: 4px; 36 | overflow: auto; 37 | } 38 | 39 | #targetEntryNameLabel.active { 40 | margin-top: 10px !important; 41 | } 42 | 43 | @media only screen and (max-width: 1200px) { 44 | .AceEditorOuterWrap { 45 | width: 100%; 46 | } 47 | .AceEditorDiv { 48 | height: calc(100vh - 130px); 49 | } 50 | } 51 | 52 | @media print { 53 | .AceEditorOuterWrap { 54 | width: 100%; 55 | } 56 | .OutletDiv { 57 | width: 100%; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /docs/playground/playground.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 14 | 15 | 16 | Tynder - Playground 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 59 | 60 |
61 | 62 | 120 | 121 | 124 | -------------------------------------------------------------------------------- /docs/playground/playground2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 14 | 15 | 16 | Tynder - Playground 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 59 | 60 |
61 | 62 | 101 | 102 | 105 | -------------------------------------------------------------------------------- /docs/repository-open-graph-no-text.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shellyln/tynder/d3961f737343dbfdba8c13515b9bf82ba67f62f1/docs/repository-open-graph-no-text.png -------------------------------------------------------------------------------- /docs/repository-open-graph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shellyln/tynder/d3961f737343dbfdba8c13515b9bf82ba67f62f1/docs/repository-open-graph.png -------------------------------------------------------------------------------- /examples/schema/_compiled/filesys.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "tynder/1.0", 3 | "ns": { 4 | ".": { 5 | "ACL": { 6 | "kind": "object", 7 | "members": [ 8 | [ 9 | "target", 10 | { 11 | "kind": "primitive", 12 | "primitiveName": "string", 13 | "name": "target" 14 | } 15 | ], 16 | [ 17 | "value", 18 | { 19 | "kind": "primitive", 20 | "primitiveName": "string", 21 | "name": "value" 22 | } 23 | ] 24 | ], 25 | "typeName": "ACL", 26 | "name": "ACL" 27 | }, 28 | "EntryBase": { 29 | "kind": "object", 30 | "members": [ 31 | [ 32 | "name", 33 | { 34 | "kind": "primitive", 35 | "primitiveName": "string", 36 | "name": "name" 37 | }, 38 | false, 39 | "Entry name" 40 | ], 41 | [ 42 | "acl", 43 | { 44 | "kind": "repeated", 45 | "min": null, 46 | "max": null, 47 | "repeated": { 48 | "kind": "symlink", 49 | "symlinkTargetName": "ACL", 50 | "typeName": "ACL", 51 | "name": "ACL" 52 | }, 53 | "name": "acl" 54 | }, 55 | false, 56 | "ACL infos" 57 | ] 58 | ], 59 | "docComment": "Entry base", 60 | "typeName": "EntryBase", 61 | "name": "EntryBase" 62 | }, 63 | "File": { 64 | "kind": "object", 65 | "members": [ 66 | [ 67 | "type", 68 | { 69 | "kind": "primitive-value", 70 | "value": "file", 71 | "name": "type" 72 | }, 73 | false, 74 | "Entry type" 75 | ], 76 | [ 77 | "name", 78 | { 79 | "kind": "primitive", 80 | "primitiveName": "string", 81 | "name": "name" 82 | }, 83 | true, 84 | "Entry name" 85 | ], 86 | [ 87 | "acl", 88 | { 89 | "kind": "repeated", 90 | "min": null, 91 | "max": null, 92 | "repeated": { 93 | "kind": "symlink", 94 | "symlinkTargetName": "ACL", 95 | "typeName": "ACL", 96 | "name": "ACL" 97 | }, 98 | "name": "acl" 99 | }, 100 | true, 101 | "ACL infos" 102 | ] 103 | ], 104 | "baseTypes": [ 105 | { 106 | "kind": "symlink", 107 | "symlinkTargetName": "EntryBase", 108 | "typeName": "EntryBase", 109 | "name": "EntryBase", 110 | "docComment": "Entry base" 111 | } 112 | ], 113 | "docComment": "File entry", 114 | "typeName": "File", 115 | "name": "File" 116 | }, 117 | "Folder": { 118 | "kind": "object", 119 | "members": [ 120 | [ 121 | "type", 122 | { 123 | "kind": "primitive-value", 124 | "value": "folder", 125 | "name": "type" 126 | }, 127 | false, 128 | "Entry type" 129 | ], 130 | [ 131 | "entries", 132 | { 133 | "kind": "repeated", 134 | "min": null, 135 | "max": null, 136 | "repeated": { 137 | "kind": "symlink", 138 | "symlinkTargetName": "Entry", 139 | "typeName": "Entry", 140 | "name": "Entry" 141 | }, 142 | "name": "entries" 143 | }, 144 | false, 145 | "Child entries" 146 | ], 147 | [ 148 | "name", 149 | { 150 | "kind": "primitive", 151 | "primitiveName": "string", 152 | "name": "name" 153 | }, 154 | true, 155 | "Entry name" 156 | ], 157 | [ 158 | "acl", 159 | { 160 | "kind": "repeated", 161 | "min": null, 162 | "max": null, 163 | "repeated": { 164 | "kind": "symlink", 165 | "symlinkTargetName": "ACL", 166 | "typeName": "ACL", 167 | "name": "ACL" 168 | }, 169 | "name": "acl" 170 | }, 171 | true, 172 | "ACL infos" 173 | ] 174 | ], 175 | "baseTypes": [ 176 | { 177 | "kind": "symlink", 178 | "symlinkTargetName": "EntryBase", 179 | "typeName": "EntryBase", 180 | "name": "EntryBase", 181 | "docComment": "Entry base" 182 | } 183 | ], 184 | "docComment": "Folder entry", 185 | "typeName": "Folder", 186 | "name": "Folder" 187 | }, 188 | "Entry": { 189 | "kind": "one-of", 190 | "oneOf": [ 191 | { 192 | "kind": "symlink", 193 | "symlinkTargetName": "File", 194 | "typeName": "File", 195 | "name": "File", 196 | "docComment": "File entry" 197 | }, 198 | { 199 | "kind": "symlink", 200 | "symlinkTargetName": "Folder", 201 | "typeName": "Folder", 202 | "name": "Folder", 203 | "docComment": "Folder entry" 204 | } 205 | ], 206 | "docComment": "Entry (union type)", 207 | "typeName": "Entry", 208 | "name": "Entry" 209 | } 210 | } 211 | } 212 | } -------------------------------------------------------------------------------- /examples/schema/_compiled/filesys.ts: -------------------------------------------------------------------------------- 1 | 2 | // tslint:disable: object-literal-key-quotes 3 | const schema = { 4 | "version": "tynder/1.0", 5 | "ns": { 6 | ".": { 7 | "ACL": { 8 | "kind": "object", 9 | "members": [ 10 | [ 11 | "target", 12 | { 13 | "kind": "primitive", 14 | "primitiveName": "string", 15 | "name": "target" 16 | } 17 | ], 18 | [ 19 | "value", 20 | { 21 | "kind": "primitive", 22 | "primitiveName": "string", 23 | "name": "value" 24 | } 25 | ] 26 | ], 27 | "typeName": "ACL", 28 | "name": "ACL" 29 | }, 30 | "EntryBase": { 31 | "kind": "object", 32 | "members": [ 33 | [ 34 | "name", 35 | { 36 | "kind": "primitive", 37 | "primitiveName": "string", 38 | "name": "name" 39 | }, 40 | false, 41 | "Entry name" 42 | ], 43 | [ 44 | "acl", 45 | { 46 | "kind": "repeated", 47 | "min": null, 48 | "max": null, 49 | "repeated": { 50 | "kind": "symlink", 51 | "symlinkTargetName": "ACL", 52 | "typeName": "ACL", 53 | "name": "ACL" 54 | }, 55 | "name": "acl" 56 | }, 57 | false, 58 | "ACL infos" 59 | ] 60 | ], 61 | "docComment": "Entry base", 62 | "typeName": "EntryBase", 63 | "name": "EntryBase" 64 | }, 65 | "File": { 66 | "kind": "object", 67 | "members": [ 68 | [ 69 | "type", 70 | { 71 | "kind": "primitive-value", 72 | "value": "file", 73 | "name": "type" 74 | }, 75 | false, 76 | "Entry type" 77 | ], 78 | [ 79 | "name", 80 | { 81 | "kind": "primitive", 82 | "primitiveName": "string", 83 | "name": "name" 84 | }, 85 | true, 86 | "Entry name" 87 | ], 88 | [ 89 | "acl", 90 | { 91 | "kind": "repeated", 92 | "min": null, 93 | "max": null, 94 | "repeated": { 95 | "kind": "symlink", 96 | "symlinkTargetName": "ACL", 97 | "typeName": "ACL", 98 | "name": "ACL" 99 | }, 100 | "name": "acl" 101 | }, 102 | true, 103 | "ACL infos" 104 | ] 105 | ], 106 | "baseTypes": [ 107 | { 108 | "kind": "symlink", 109 | "symlinkTargetName": "EntryBase", 110 | "typeName": "EntryBase", 111 | "name": "EntryBase", 112 | "docComment": "Entry base" 113 | } 114 | ], 115 | "docComment": "File entry", 116 | "typeName": "File", 117 | "name": "File" 118 | }, 119 | "Folder": { 120 | "kind": "object", 121 | "members": [ 122 | [ 123 | "type", 124 | { 125 | "kind": "primitive-value", 126 | "value": "folder", 127 | "name": "type" 128 | }, 129 | false, 130 | "Entry type" 131 | ], 132 | [ 133 | "entries", 134 | { 135 | "kind": "repeated", 136 | "min": null, 137 | "max": null, 138 | "repeated": { 139 | "kind": "symlink", 140 | "symlinkTargetName": "Entry", 141 | "typeName": "Entry", 142 | "name": "Entry" 143 | }, 144 | "name": "entries" 145 | }, 146 | false, 147 | "Child entries" 148 | ], 149 | [ 150 | "name", 151 | { 152 | "kind": "primitive", 153 | "primitiveName": "string", 154 | "name": "name" 155 | }, 156 | true, 157 | "Entry name" 158 | ], 159 | [ 160 | "acl", 161 | { 162 | "kind": "repeated", 163 | "min": null, 164 | "max": null, 165 | "repeated": { 166 | "kind": "symlink", 167 | "symlinkTargetName": "ACL", 168 | "typeName": "ACL", 169 | "name": "ACL" 170 | }, 171 | "name": "acl" 172 | }, 173 | true, 174 | "ACL infos" 175 | ] 176 | ], 177 | "baseTypes": [ 178 | { 179 | "kind": "symlink", 180 | "symlinkTargetName": "EntryBase", 181 | "typeName": "EntryBase", 182 | "name": "EntryBase", 183 | "docComment": "Entry base" 184 | } 185 | ], 186 | "docComment": "Folder entry", 187 | "typeName": "Folder", 188 | "name": "Folder" 189 | }, 190 | "Entry": { 191 | "kind": "one-of", 192 | "oneOf": [ 193 | { 194 | "kind": "symlink", 195 | "symlinkTargetName": "File", 196 | "typeName": "File", 197 | "name": "File", 198 | "docComment": "File entry" 199 | }, 200 | { 201 | "kind": "symlink", 202 | "symlinkTargetName": "Folder", 203 | "typeName": "Folder", 204 | "name": "Folder", 205 | "docComment": "Folder entry" 206 | } 207 | ], 208 | "docComment": "Entry (union type)", 209 | "typeName": "Entry", 210 | "name": "Entry" 211 | } 212 | } 213 | } 214 | }; 215 | export default schema; 216 | 217 | export const enum Schema { 218 | ACL = 'ACL', 219 | EntryBase = 'EntryBase', 220 | File = 'File', 221 | Folder = 'Folder', 222 | Entry = 'Entry', 223 | } 224 | // tslint:enable: object-literal-key-quotes 225 | -------------------------------------------------------------------------------- /examples/schema/_compiled/primitives.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "tynder/1.0", 3 | "ns": { 4 | ".": { 5 | "NumberType": { 6 | "kind": "primitive", 7 | "primitiveName": "number", 8 | "typeName": "NumberType", 9 | "name": "NumberType" 10 | }, 11 | "IntegerType": { 12 | "kind": "primitive", 13 | "primitiveName": "integer", 14 | "typeName": "IntegerType", 15 | "name": "IntegerType" 16 | }, 17 | "BigIntType": { 18 | "kind": "primitive", 19 | "primitiveName": "bigint", 20 | "typeName": "BigIntType", 21 | "name": "BigIntType" 22 | }, 23 | "StringType": { 24 | "kind": "primitive", 25 | "primitiveName": "string", 26 | "typeName": "StringType", 27 | "name": "StringType" 28 | }, 29 | "BooleanType": { 30 | "kind": "primitive", 31 | "primitiveName": "boolean", 32 | "typeName": "BooleanType", 33 | "name": "BooleanType" 34 | }, 35 | "NullType": { 36 | "kind": "primitive", 37 | "primitiveName": "null", 38 | "typeName": "NullType", 39 | "name": "NullType" 40 | }, 41 | "UndefinedType": { 42 | "kind": "primitive", 43 | "primitiveName": "undefined", 44 | "typeName": "UndefinedType", 45 | "name": "UndefinedType" 46 | }, 47 | "AnyType": { 48 | "kind": "any", 49 | "typeName": "AnyType", 50 | "name": "AnyType" 51 | }, 52 | "UnknownType": { 53 | "kind": "unknown", 54 | "typeName": "UnknownType", 55 | "name": "UnknownType" 56 | }, 57 | "NeverType": { 58 | "kind": "never", 59 | "typeName": "NeverType", 60 | "name": "NeverType" 61 | }, 62 | "NumberValueType": { 63 | "kind": "primitive-value", 64 | "value": 3, 65 | "typeName": "NumberValueType", 66 | "name": "NumberValueType" 67 | }, 68 | "IntegerValueType": { 69 | "kind": "primitive", 70 | "primitiveName": "integer", 71 | "typeName": "IntegerValueType", 72 | "name": "IntegerValueType" 73 | }, 74 | "BigIntValueType": { 75 | "kind": "primitive-value", 76 | "value": "7", 77 | "typeName": "BigIntValueType", 78 | "name": "BigIntValueType", 79 | "primitiveName": "bigint" 80 | }, 81 | "StringValueType": { 82 | "kind": "primitive-value", 83 | "value": "XB", 84 | "typeName": "StringValueType", 85 | "name": "StringValueType" 86 | }, 87 | "BooleanValueType": { 88 | "kind": "primitive-value", 89 | "value": true, 90 | "typeName": "BooleanValueType", 91 | "name": "BooleanValueType" 92 | } 93 | } 94 | } 95 | } -------------------------------------------------------------------------------- /examples/schema/_compiled/primitives.ts: -------------------------------------------------------------------------------- 1 | 2 | // tslint:disable: object-literal-key-quotes 3 | const schema = { 4 | "version": "tynder/1.0", 5 | "ns": { 6 | ".": { 7 | "NumberType": { 8 | "kind": "primitive", 9 | "primitiveName": "number", 10 | "typeName": "NumberType", 11 | "name": "NumberType" 12 | }, 13 | "IntegerType": { 14 | "kind": "primitive", 15 | "primitiveName": "integer", 16 | "typeName": "IntegerType", 17 | "name": "IntegerType" 18 | }, 19 | "BigIntType": { 20 | "kind": "primitive", 21 | "primitiveName": "bigint", 22 | "typeName": "BigIntType", 23 | "name": "BigIntType" 24 | }, 25 | "StringType": { 26 | "kind": "primitive", 27 | "primitiveName": "string", 28 | "typeName": "StringType", 29 | "name": "StringType" 30 | }, 31 | "BooleanType": { 32 | "kind": "primitive", 33 | "primitiveName": "boolean", 34 | "typeName": "BooleanType", 35 | "name": "BooleanType" 36 | }, 37 | "NullType": { 38 | "kind": "primitive", 39 | "primitiveName": "null", 40 | "typeName": "NullType", 41 | "name": "NullType" 42 | }, 43 | "UndefinedType": { 44 | "kind": "primitive", 45 | "primitiveName": "undefined", 46 | "typeName": "UndefinedType", 47 | "name": "UndefinedType" 48 | }, 49 | "AnyType": { 50 | "kind": "any", 51 | "typeName": "AnyType", 52 | "name": "AnyType" 53 | }, 54 | "UnknownType": { 55 | "kind": "unknown", 56 | "typeName": "UnknownType", 57 | "name": "UnknownType" 58 | }, 59 | "NeverType": { 60 | "kind": "never", 61 | "typeName": "NeverType", 62 | "name": "NeverType" 63 | }, 64 | "NumberValueType": { 65 | "kind": "primitive-value", 66 | "value": 3, 67 | "typeName": "NumberValueType", 68 | "name": "NumberValueType" 69 | }, 70 | "IntegerValueType": { 71 | "kind": "primitive", 72 | "primitiveName": "integer", 73 | "typeName": "IntegerValueType", 74 | "name": "IntegerValueType" 75 | }, 76 | "BigIntValueType": { 77 | "kind": "primitive-value", 78 | "value": "7", 79 | "typeName": "BigIntValueType", 80 | "name": "BigIntValueType", 81 | "primitiveName": "bigint" 82 | }, 83 | "StringValueType": { 84 | "kind": "primitive-value", 85 | "value": "XB", 86 | "typeName": "StringValueType", 87 | "name": "StringValueType" 88 | }, 89 | "BooleanValueType": { 90 | "kind": "primitive-value", 91 | "value": true, 92 | "typeName": "BooleanValueType", 93 | "name": "BooleanValueType" 94 | } 95 | } 96 | } 97 | }; 98 | export default schema; 99 | 100 | export const enum Schema { 101 | NumberType = 'NumberType', 102 | IntegerType = 'IntegerType', 103 | BigIntType = 'BigIntType', 104 | StringType = 'StringType', 105 | BooleanType = 'BooleanType', 106 | NullType = 'NullType', 107 | UndefinedType = 'UndefinedType', 108 | AnyType = 'AnyType', 109 | UnknownType = 'UnknownType', 110 | NeverType = 'NeverType', 111 | NumberValueType = 'NumberValueType', 112 | IntegerValueType = 'IntegerValueType', 113 | BigIntValueType = 'BigIntValueType', 114 | StringValueType = 'StringValueType', 115 | BooleanValueType = 'BooleanValueType', 116 | } 117 | // tslint:enable: object-literal-key-quotes 118 | -------------------------------------------------------------------------------- /examples/schema/csharp/filesys.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | 3 | namespace Tynder.UserSchema 4 | { 5 | /** Entry (union type) */ 6 | using Entry = System.Object; 7 | 8 | 9 | public class ACL 10 | { 11 | [Required] 12 | public string target { get; set; } 13 | 14 | [Required] 15 | public string value { get; set; } 16 | } 17 | 18 | 19 | /** Entry base */ 20 | public class EntryBase 21 | { 22 | /** Entry name */ 23 | [Required] 24 | public string name { get; set; } 25 | 26 | /** ACL infos */ 27 | [Required] 28 | public ACL[] acl { get; set; } 29 | } 30 | 31 | 32 | /** File entry */ 33 | public class File : EntryBase 34 | { 35 | /** Entry type */ 36 | [Required] 37 | public string type { get; set; } 38 | } 39 | 40 | 41 | /** Folder entry */ 42 | public class Folder : EntryBase 43 | { 44 | /** Entry type */ 45 | [Required] 46 | public string type { get; set; } 47 | 48 | /** Child entries */ 49 | [Required] 50 | public Entry[] entries { get; set; } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /examples/schema/csharp/primitives.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | 3 | namespace Tynder.UserSchema 4 | { 5 | using NumberType = System.Double; 6 | 7 | using IntegerType = System.Int32; 8 | 9 | using BigIntType = System.Decimal; 10 | 11 | using StringType = System.String; 12 | 13 | using BooleanType = System.Boolean; 14 | 15 | using NullType = System.Object; 16 | 17 | using UndefinedType = System.Object; 18 | 19 | using AnyType = System.Object; 20 | 21 | using UnknownType = System.Object; 22 | 23 | using NeverType = System.Object; 24 | 25 | using NumberValueType = System.Double; 26 | 27 | using IntegerValueType = System.Int32; 28 | 29 | using BigIntValueType = System.Object; 30 | 31 | using StringValueType = System.String; 32 | 33 | using BooleanValueType = System.Boolean; 34 | 35 | } 36 | -------------------------------------------------------------------------------- /examples/schema/csharp/tynder.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | 3 | namespace Tynder.UserSchema 4 | { 5 | using RegExp = System.Object; 6 | 7 | using Map = System.Object; 8 | 9 | using PrimitiveValueTypes = System.Object; 10 | 11 | using PrimitiveValueTypeNames = System.Object; 12 | 13 | using OptionalPrimitiveValueTypeNames = System.Object; 14 | 15 | using PlaceholderTypeNames = System.Object; 16 | 17 | using OptionalPlaceholderTypeNames = System.Object; 18 | 19 | using ObjectAssertionMember = System.Object; 20 | 21 | using AdditionalPropsKey = System.Object; 22 | 23 | using AdditionalPropsMember = System.Object; 24 | 25 | using TypeAssertion = System.Object; 26 | 27 | using TypeAssertionMap = System.Object; 28 | 29 | 30 | public static class ErrorTypes 31 | { 32 | /** comment */ 33 | public static double InvalidDefinition { get { return 1; } } 34 | 35 | /** comment */ 36 | public static double Required { get { return 2; } } 37 | 38 | /** comment */ 39 | public static double TypeUnmatched { get { return 3; } } 40 | 41 | /** comment */ 42 | public static double RepeatQtyUnmatched { get { return 4; } } 43 | 44 | public static double SequenceUnmatched { get { return 5; } } 45 | 46 | public static double ValueRangeUnmatched { get { return 6; } } 47 | 48 | public static double ValuePatternUnmatched { get { return 7; } } 49 | 50 | public static double ValueLengthUnmatched { get { return 8; } } 51 | 52 | public static double ValueUnmatched { get { return 9; } } 53 | } 54 | 55 | 56 | public class ErrorMessages 57 | { 58 | public string invalidDefinition { get; set; } 59 | 60 | public string required { get; set; } 61 | 62 | public string typeUnmatched { get; set; } 63 | 64 | public string repeatQtyUnmatched { get; set; } 65 | 66 | public string sequenceUnmatched { get; set; } 67 | 68 | public string valueRangeUnmatched { get; set; } 69 | 70 | public string valuePatternUnmatched { get; set; } 71 | 72 | public string valueLengthUnmatched { get; set; } 73 | 74 | public string valueUnmatched { get; set; } 75 | } 76 | 77 | 78 | public class TypeAssertionErrorMessageConstraints 79 | { 80 | public object minValue { get; set; } 81 | 82 | public object maxValue { get; set; } 83 | 84 | public object greaterThanValue { get; set; } 85 | 86 | public object lessThanValue { get; set; } 87 | 88 | public double? minLength { get; set; } 89 | 90 | public double? maxLength { get; set; } 91 | 92 | public double? min { get; set; } 93 | 94 | public double? max { get; set; } 95 | 96 | public string pattern { get; set; } 97 | } 98 | 99 | 100 | public class TypeAssertionErrorMessage 101 | { 102 | [Required] 103 | public string code { get; set; } 104 | 105 | [Required] 106 | public string message { get; set; } 107 | 108 | [Required] 109 | public string dataPath { get; set; } 110 | 111 | [Required] 112 | public TypeAssertionErrorMessageConstraints constraints { get; set; } 113 | 114 | public object value { get; set; } 115 | } 116 | 117 | 118 | public class TypeAssertionBase 119 | { 120 | public string messageId { get; set; } 121 | 122 | public string message { get; set; } 123 | 124 | public ErrorMessages messages { get; set; } 125 | 126 | public string name { get; set; } 127 | 128 | public string typeName { get; set; } 129 | 130 | public string docComment { get; set; } 131 | 132 | public string passThruCodeBlock { get; set; } 133 | 134 | public bool? noOutput { get; set; } 135 | } 136 | 137 | 138 | public class NeverTypeAssertion : TypeAssertionBase 139 | { 140 | [Required] 141 | public string kind { get; set; } 142 | } 143 | 144 | 145 | public class AnyTypeAssertion : TypeAssertionBase 146 | { 147 | [Required] 148 | public string kind { get; set; } 149 | } 150 | 151 | 152 | public class UnknownTypeAssertion : TypeAssertionBase 153 | { 154 | [Required] 155 | public string kind { get; set; } 156 | } 157 | 158 | 159 | public class PrimitiveTypeAssertionConstraints 160 | { 161 | public object minValue { get; set; } 162 | 163 | public object maxValue { get; set; } 164 | 165 | public object greaterThanValue { get; set; } 166 | 167 | public object lessThanValue { get; set; } 168 | 169 | public double? minLength { get; set; } 170 | 171 | public double? maxLength { get; set; } 172 | 173 | public RegExp pattern { get; set; } 174 | } 175 | 176 | 177 | public class PrimitiveTypeAssertion : TypeAssertionBase, PrimitiveTypeAssertionConstraints 178 | { 179 | [Required] 180 | public string kind { get; set; } 181 | 182 | [Required] 183 | public PrimitiveValueTypeNames primitiveName { get; set; } 184 | } 185 | 186 | 187 | public class PrimitiveValueTypeAssertion : TypeAssertionBase 188 | { 189 | [Required] 190 | public string kind { get; set; } 191 | 192 | [Required] 193 | public PrimitiveValueTypes value { get; set; } 194 | } 195 | 196 | 197 | public class RepeatedAssertionConstraints 198 | { 199 | public double? min { get; set; } 200 | 201 | public double? max { get; set; } 202 | } 203 | 204 | 205 | public class RepeatedAssertion : TypeAssertionBase, RepeatedAssertionConstraints 206 | { 207 | [Required] 208 | public string kind { get; set; } 209 | 210 | [Required] 211 | public TypeAssertion repeated { get; set; } 212 | } 213 | 214 | 215 | public class SpreadAssertion : TypeAssertionBase, RepeatedAssertionConstraints 216 | { 217 | [Required] 218 | public string kind { get; set; } 219 | 220 | [Required] 221 | public TypeAssertion spread { get; set; } 222 | } 223 | 224 | 225 | public class SequenceAssertion : TypeAssertionBase 226 | { 227 | [Required] 228 | public string kind { get; set; } 229 | 230 | [Required] 231 | public TypeAssertion[] sequence { get; set; } 232 | } 233 | 234 | 235 | public class OneOfAssertion : TypeAssertionBase 236 | { 237 | [Required] 238 | public string kind { get; set; } 239 | 240 | [Required] 241 | public TypeAssertion[] oneOf { get; set; } 242 | } 243 | 244 | 245 | public class OptionalAssertion : TypeAssertionBase 246 | { 247 | [Required] 248 | public string kind { get; set; } 249 | 250 | [Required] 251 | public TypeAssertion optional { get; set; } 252 | } 253 | 254 | 255 | public class EnumAssertion : TypeAssertionBase 256 | { 257 | [Required] 258 | public string kind { get; set; } 259 | 260 | [Required] 261 | public object[] values { get; set; } 262 | } 263 | 264 | 265 | public class ObjectAssertion : TypeAssertionBase 266 | { 267 | [Required] 268 | public string kind { get; set; } 269 | 270 | [Required] 271 | public ObjectAssertionMember[] members { get; set; } 272 | 273 | public AdditionalPropsMember[] additionalProps { get; set; } 274 | 275 | public object[] baseTypes { get; set; } 276 | } 277 | 278 | 279 | public class AssertionSymlink : TypeAssertionBase 280 | { 281 | [Required] 282 | public string kind { get; set; } 283 | 284 | [Required] 285 | public string symlinkTargetName { get; set; } 286 | } 287 | 288 | 289 | public class ValidationContext 290 | { 291 | public bool? checkAll { get; set; } 292 | 293 | public bool? noAdditionalProps { get; set; } 294 | 295 | public ErrorMessages errorMessages { get; set; } 296 | 297 | [Required] 298 | public TypeAssertionErrorMessage[] errors { get; set; } 299 | 300 | [Required] 301 | public object[] typeStack { get; set; } 302 | 303 | public TypeAssertionMap schema { get; set; } 304 | } 305 | 306 | 307 | public class TypeAssertionSetValue 308 | { 309 | [Required] 310 | public TypeAssertion ty { get; set; } 311 | 312 | public bool exported { get; set; } 313 | 314 | public bool resolved { get; set; } 315 | } 316 | 317 | 318 | public class SymbolResolverContext 319 | { 320 | public double nestLevel { get; set; } 321 | 322 | [Required] 323 | public string[] symlinkStack { get; set; } 324 | } 325 | 326 | 327 | public class CodegenContext 328 | { 329 | public double nestLevel { get; set; } 330 | 331 | public TypeAssertionMap schema { get; set; } 332 | } 333 | } 334 | -------------------------------------------------------------------------------- /examples/schema/graphql/fields.graphql: -------------------------------------------------------------------------------- 1 | 2 | scalar Any 3 | union BigInt = String | Int 4 | 5 | union NumberType = Float 6 | 7 | type A { 8 | numberTypeField: NumberType! 9 | integerTypeField: Int! 10 | bigIntTypeField: BigInt! 11 | stringTypeField: String! 12 | booleanTypeField: Boolean! 13 | nullTypeField: Any! 14 | undefinedTypeField: Any! 15 | anyTypeField: Any! 16 | unknownTypeField: Any! 17 | neverTypeField: Any! 18 | numberValueTypeField: Float! 19 | integerValueTypeField: Int! 20 | bigIntValueTypeField: BigInt! 21 | stringValueTypeField: String! 22 | booleanValueTypeField: Boolean! 23 | } 24 | 25 | type B { 26 | numberTypeField: NumberType 27 | integerTypeField: Int 28 | bigIntTypeField: BigInt 29 | stringTypeField: String 30 | booleanTypeField: Boolean 31 | nullTypeField: Any 32 | undefinedTypeField: Any 33 | anyTypeField: Any 34 | unknownTypeField: Any 35 | neverTypeField: Any 36 | numberValueTypeField: Float 37 | integerValueTypeField: Int 38 | bigIntValueTypeField: BigInt 39 | stringValueTypeField: String 40 | booleanValueTypeField: Boolean 41 | } 42 | 43 | type C { 44 | numberTypeField: NumberType! 45 | integerTypeField: Int! 46 | bigIntTypeField: BigInt! 47 | stringTypeField: String! 48 | booleanTypeField: Boolean! 49 | nullTypeField: Any! 50 | undefinedTypeField: Any! 51 | anyTypeField: Any! 52 | unknownTypeField: Any! 53 | neverTypeField: Any! 54 | numberValueTypeField: Float! 55 | integerValueTypeField: Int! 56 | bigIntValueTypeField: BigInt! 57 | stringValueTypeField: String! 58 | booleanValueTypeField: Boolean! 59 | } 60 | 61 | type D { 62 | numberTypeField: NumberType 63 | integerTypeField: Int 64 | bigIntTypeField: BigInt 65 | stringTypeField: String 66 | booleanTypeField: Boolean 67 | nullTypeField: Any 68 | undefinedTypeField: Any 69 | anyTypeField: Any 70 | unknownTypeField: Any 71 | neverTypeField: Any 72 | numberValueTypeField: Float 73 | integerValueTypeField: Int 74 | bigIntValueTypeField: BigInt 75 | stringValueTypeField: String 76 | booleanValueTypeField: Boolean 77 | } 78 | 79 | type E {} 80 | 81 | type Z1 { 82 | foo: String! 83 | bar: String! 84 | baz: String! 85 | } 86 | 87 | type ACL { 88 | target: String! 89 | value: String! 90 | } 91 | 92 | type Z2 { 93 | foo: String! 94 | bar: String! 95 | baz: String! 96 | } 97 | 98 | enum ErrorTypes { 99 | /** comment */ 100 | InvalidDefinition 101 | /** comment */ 102 | Required 103 | /** comment */ 104 | TypeUnmatched 105 | /** comment */ 106 | RepeatQtyUnmatched 107 | SequenceUnmatched 108 | ValueRangeUnmatched 109 | ValuePatternUnmatched 110 | ValueLengthUnmatched 111 | ValueUnmatched 112 | Aaaaa 113 | Bbbbb 114 | } 115 | 116 | enum ErrorTypes2 { 117 | Aaaaa 118 | } 119 | 120 | enum ErrorTypes3 { 121 | Zzzzz 122 | Aaaaa 123 | } 124 | 125 | type Foo { 126 | name: String! 127 | email: String! 128 | } 129 | 130 | type Bar { 131 | foo: Foo! 132 | } 133 | 134 | type Baz { 135 | aaa1: String! 136 | aaa2: String! 137 | aaa3: String! 138 | aaa4: String! 139 | bbb1: Int! 140 | bbb2: Int! 141 | bbb3: Int! 142 | bbb4: Int! 143 | /** comment */ 144 | ccc1: Float! 145 | /** comment */ 146 | ccc2: Float! 147 | /** comment */ 148 | ccc3: Float! 149 | /** comment */ 150 | ccc4: Float! 151 | ddd1: [Any]! 152 | ddd2: [Any]! 153 | ddd3: [Any]! 154 | ddd4: [Any]! 155 | ddd5: [Any]! 156 | ddd6: [Any]! 157 | eee1: [String!]! 158 | eee2: [String!]! 159 | eee3: [String!]! 160 | eee4: [String!]! 161 | fff: ErrorTypes! 162 | ggg1: Bar! 163 | ggg2: [Bar!]! 164 | ggg3: [[Bar!]!]! 165 | ggg4: Bar 166 | ggg5: [Bar]! 167 | ggg6: [[Bar]!]! 168 | ggg7: Bar 169 | ggg8: [Bar]! 170 | ggg9: [[Bar]!]! 171 | ggg10: Bar 172 | ggg11: [Bar]! 173 | ggg12: [[Bar]!]! 174 | hhh1: Float! 175 | hhh2: [Float!]! 176 | hhh3: [[Float!]!]! 177 | hhh4: Float 178 | hhh5: [Float]! 179 | hhh6: [[Float]!]! 180 | hhh7: Float 181 | hhh8: [Float]! 182 | hhh9: [[Float]!]! 183 | hhh10: Float 184 | hhh11: [Float]! 185 | hhh12: [[Float]!]! 186 | } 187 | 188 | type User { 189 | userName: String! 190 | primaryEmail: String! 191 | primaryAliasName: String! 192 | aliasNames: [String!]! 193 | } 194 | 195 | -------------------------------------------------------------------------------- /examples/schema/graphql/filesys.graphql: -------------------------------------------------------------------------------- 1 | 2 | scalar Any 3 | union BigInt = String | Int 4 | 5 | type ACL { 6 | target: String! 7 | value: String! 8 | } 9 | 10 | /** Entry base */ 11 | type EntryBase { 12 | /** Entry name */ 13 | name: String! 14 | /** ACL infos */ 15 | acl: [ACL!]! 16 | } 17 | 18 | /** File entry */ 19 | type File { 20 | /** Entry type */ 21 | type: String! 22 | /** Entry name */ 23 | name: String! 24 | /** ACL infos */ 25 | acl: [ACL!]! 26 | } 27 | 28 | /** Folder entry */ 29 | type Folder { 30 | /** Entry type */ 31 | type: String! 32 | /** Child entries */ 33 | entries: [Entry!]! 34 | /** Entry name */ 35 | name: String! 36 | /** ACL infos */ 37 | acl: [ACL!]! 38 | } 39 | 40 | /** Entry (union type) */ 41 | union Entry = File | Folder 42 | 43 | -------------------------------------------------------------------------------- /examples/schema/graphql/primitives.graphql: -------------------------------------------------------------------------------- 1 | 2 | scalar Any 3 | union BigInt = String | Int 4 | 5 | union NumberType = Float 6 | 7 | union IntegerType = Int 8 | 9 | union BigIntType = BigInt 10 | 11 | union StringType = String 12 | 13 | union BooleanType = Boolean 14 | 15 | union NullType = Any 16 | 17 | union UndefinedType = Any 18 | 19 | union AnyType = Any 20 | 21 | union UnknownType = Any 22 | 23 | union NeverType = Any 24 | 25 | union NumberValueType = Float 26 | 27 | union IntegerValueType = Int 28 | 29 | union BigIntValueType = BigInt 30 | 31 | union StringValueType = String 32 | 33 | union BooleanValueType = Boolean 34 | 35 | -------------------------------------------------------------------------------- /examples/schema/graphql/tynder.graphql: -------------------------------------------------------------------------------- 1 | 2 | scalar Any 3 | union BigInt = String | Int 4 | 5 | scalar RegExp 6 | 7 | scalar Map 8 | 9 | union PrimitiveValueTypes = Float | BigInt | String | Boolean | Any | Any 10 | 11 | union PrimitiveValueTypeNames = String | String | String | String | String | String 12 | 13 | union OptionalPrimitiveValueTypeNames = String | String | String | String | String | String 14 | 15 | union PlaceholderTypeNames = String | String | String 16 | 17 | union OptionalPlaceholderTypeNames = String | String | String 18 | 19 | enum ErrorTypes { 20 | /** comment */ 21 | InvalidDefinition 22 | /** comment */ 23 | Required 24 | /** comment */ 25 | TypeUnmatched 26 | /** comment */ 27 | RepeatQtyUnmatched 28 | SequenceUnmatched 29 | ValueRangeUnmatched 30 | ValuePatternUnmatched 31 | ValueLengthUnmatched 32 | ValueUnmatched 33 | } 34 | 35 | type ErrorMessages { 36 | invalidDefinition: String 37 | required: String 38 | typeUnmatched: String 39 | repeatQtyUnmatched: String 40 | sequenceUnmatched: String 41 | valueRangeUnmatched: String 42 | valuePatternUnmatched: String 43 | valueLengthUnmatched: String 44 | valueUnmatched: String 45 | } 46 | 47 | type TypeAssertionErrorMessageConstraints { 48 | minValue: Any 49 | maxValue: Any 50 | greaterThanValue: Any 51 | lessThanValue: Any 52 | minLength: Float 53 | maxLength: Float 54 | min: Float 55 | max: Float 56 | pattern: String 57 | } 58 | 59 | type TypeAssertionErrorMessage { 60 | code: String! 61 | message: String! 62 | dataPath: String! 63 | constraints: TypeAssertionErrorMessageConstraints! 64 | value: Any 65 | } 66 | 67 | type TypeAssertionBase { 68 | messageId: String 69 | message: String 70 | messages: ErrorMessages 71 | name: String 72 | typeName: String 73 | docComment: String 74 | passThruCodeBlock: String 75 | noOutput: Boolean 76 | } 77 | 78 | type NeverTypeAssertion { 79 | kind: String! 80 | messageId: String 81 | message: String 82 | messages: ErrorMessages 83 | name: String 84 | typeName: String 85 | docComment: String 86 | passThruCodeBlock: String 87 | noOutput: Boolean 88 | } 89 | 90 | type AnyTypeAssertion { 91 | kind: String! 92 | messageId: String 93 | message: String 94 | messages: ErrorMessages 95 | name: String 96 | typeName: String 97 | docComment: String 98 | passThruCodeBlock: String 99 | noOutput: Boolean 100 | } 101 | 102 | type UnknownTypeAssertion { 103 | kind: String! 104 | messageId: String 105 | message: String 106 | messages: ErrorMessages 107 | name: String 108 | typeName: String 109 | docComment: String 110 | passThruCodeBlock: String 111 | noOutput: Boolean 112 | } 113 | 114 | type PrimitiveTypeAssertionConstraints { 115 | minValue: Any 116 | maxValue: Any 117 | greaterThanValue: Any 118 | lessThanValue: Any 119 | minLength: Float 120 | maxLength: Float 121 | pattern: RegExp 122 | } 123 | 124 | type PrimitiveTypeAssertion { 125 | kind: String! 126 | primitiveName: PrimitiveValueTypeNames! 127 | messageId: String 128 | message: String 129 | messages: ErrorMessages 130 | name: String 131 | typeName: String 132 | docComment: String 133 | passThruCodeBlock: String 134 | noOutput: Boolean 135 | minValue: Any 136 | maxValue: Any 137 | greaterThanValue: Any 138 | lessThanValue: Any 139 | minLength: Float 140 | maxLength: Float 141 | pattern: RegExp 142 | } 143 | 144 | type PrimitiveValueTypeAssertion { 145 | kind: String! 146 | value: PrimitiveValueTypes! 147 | messageId: String 148 | message: String 149 | messages: ErrorMessages 150 | name: String 151 | typeName: String 152 | docComment: String 153 | passThruCodeBlock: String 154 | noOutput: Boolean 155 | } 156 | 157 | type RepeatedAssertionConstraints { 158 | min: Float 159 | max: Float 160 | } 161 | 162 | type RepeatedAssertion { 163 | kind: String! 164 | repeated: TypeAssertion! 165 | messageId: String 166 | message: String 167 | messages: ErrorMessages 168 | name: String 169 | typeName: String 170 | docComment: String 171 | passThruCodeBlock: String 172 | noOutput: Boolean 173 | min: Float 174 | max: Float 175 | } 176 | 177 | type SpreadAssertion { 178 | kind: String! 179 | spread: TypeAssertion! 180 | messageId: String 181 | message: String 182 | messages: ErrorMessages 183 | name: String 184 | typeName: String 185 | docComment: String 186 | passThruCodeBlock: String 187 | noOutput: Boolean 188 | min: Float 189 | max: Float 190 | } 191 | 192 | type SequenceAssertion { 193 | kind: String! 194 | sequence: [TypeAssertion!]! 195 | messageId: String 196 | message: String 197 | messages: ErrorMessages 198 | name: String 199 | typeName: String 200 | docComment: String 201 | passThruCodeBlock: String 202 | noOutput: Boolean 203 | } 204 | 205 | type OneOfAssertion { 206 | kind: String! 207 | oneOf: [TypeAssertion!]! 208 | messageId: String 209 | message: String 210 | messages: ErrorMessages 211 | name: String 212 | typeName: String 213 | docComment: String 214 | passThruCodeBlock: String 215 | noOutput: Boolean 216 | } 217 | 218 | type OptionalAssertion { 219 | kind: String! 220 | optional: TypeAssertion! 221 | messageId: String 222 | message: String 223 | messages: ErrorMessages 224 | name: String 225 | typeName: String 226 | docComment: String 227 | passThruCodeBlock: String 228 | noOutput: Boolean 229 | } 230 | 231 | type EnumAssertion { 232 | kind: String! 233 | values: [[Any]!]! 234 | messageId: String 235 | message: String 236 | messages: ErrorMessages 237 | name: String 238 | typeName: String 239 | docComment: String 240 | passThruCodeBlock: String 241 | noOutput: Boolean 242 | } 243 | 244 | union ObjectAssertionMember = [Any] | [Any] | [Any] 245 | 246 | union AdditionalPropsKey = [Any!] 247 | 248 | union AdditionalPropsMember = [Any] | [Any] | [Any] 249 | 250 | type ObjectAssertion { 251 | kind: String! 252 | members: [ObjectAssertionMember!]! 253 | additionalProps: [AdditionalPropsMember!] 254 | baseTypes: [Any!] 255 | messageId: String 256 | message: String 257 | messages: ErrorMessages 258 | name: String 259 | typeName: String 260 | docComment: String 261 | passThruCodeBlock: String 262 | noOutput: Boolean 263 | } 264 | 265 | type AssertionSymlink { 266 | kind: String! 267 | symlinkTargetName: String! 268 | messageId: String 269 | message: String 270 | messages: ErrorMessages 271 | name: String 272 | typeName: String 273 | docComment: String 274 | passThruCodeBlock: String 275 | noOutput: Boolean 276 | } 277 | 278 | union TypeAssertion = NeverTypeAssertion | AnyTypeAssertion | UnknownTypeAssertion | PrimitiveTypeAssertion | PrimitiveValueTypeAssertion | RepeatedAssertion | SpreadAssertion | SequenceAssertion | OneOfAssertion | OptionalAssertion | EnumAssertion | ObjectAssertion | AssertionSymlink 279 | 280 | type ValidationContext { 281 | checkAll: Boolean 282 | noAdditionalProps: Boolean 283 | errorMessages: ErrorMessages 284 | errors: [TypeAssertionErrorMessage!]! 285 | typeStack: [Any!]! 286 | schema: TypeAssertionMap 287 | } 288 | 289 | type TypeAssertionSetValue { 290 | ty: TypeAssertion! 291 | exported: Boolean! 292 | resolved: Boolean! 293 | } 294 | 295 | union TypeAssertionMap = Any 296 | 297 | type SymbolResolverContext { 298 | nestLevel: Float! 299 | symlinkStack: [String!]! 300 | } 301 | 302 | type CodegenContext { 303 | nestLevel: Float! 304 | schema: TypeAssertionMap 305 | } 306 | 307 | -------------------------------------------------------------------------------- /examples/schema/json-schema/filesys.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-06/schema#", 3 | "definitions": { 4 | "ACL": { 5 | "type": "object", 6 | "properties": { 7 | "target": { 8 | "type": "string" 9 | }, 10 | "value": { 11 | "type": "string" 12 | } 13 | }, 14 | "required": [ 15 | "target", 16 | "value" 17 | ], 18 | "additionalProperties": false 19 | }, 20 | "EntryBase": { 21 | "type": "object", 22 | "properties": { 23 | "name": { 24 | "type": "string", 25 | "description": "Entry name" 26 | }, 27 | "acl": { 28 | "type": "array", 29 | "items": { 30 | "$ref": "#/definitions/ACL" 31 | }, 32 | "description": "ACL infos" 33 | } 34 | }, 35 | "required": [ 36 | "name", 37 | "acl" 38 | ], 39 | "additionalProperties": false, 40 | "description": "Entry base" 41 | }, 42 | "File": { 43 | "type": "object", 44 | "properties": { 45 | "type": { 46 | "type": "string", 47 | "enum": [ 48 | "file" 49 | ], 50 | "description": "Entry type" 51 | }, 52 | "name": { 53 | "type": "string", 54 | "description": "Entry name" 55 | }, 56 | "acl": { 57 | "type": "array", 58 | "items": { 59 | "$ref": "#/definitions/ACL" 60 | }, 61 | "description": "ACL infos" 62 | } 63 | }, 64 | "required": [ 65 | "type", 66 | "name", 67 | "acl" 68 | ], 69 | "additionalProperties": false, 70 | "description": "File entry" 71 | }, 72 | "Folder": { 73 | "type": "object", 74 | "properties": { 75 | "type": { 76 | "type": "string", 77 | "enum": [ 78 | "folder" 79 | ], 80 | "description": "Entry type" 81 | }, 82 | "entries": { 83 | "type": "array", 84 | "items": { 85 | "$ref": "#/definitions/Entry" 86 | }, 87 | "description": "Child entries" 88 | }, 89 | "name": { 90 | "type": "string", 91 | "description": "Entry name" 92 | }, 93 | "acl": { 94 | "type": "array", 95 | "items": { 96 | "$ref": "#/definitions/ACL" 97 | }, 98 | "description": "ACL infos" 99 | } 100 | }, 101 | "required": [ 102 | "type", 103 | "entries", 104 | "name", 105 | "acl" 106 | ], 107 | "additionalProperties": false, 108 | "description": "Folder entry" 109 | }, 110 | "Entry": { 111 | "anyOf": [ 112 | { 113 | "type": "object", 114 | "properties": { 115 | "type": { 116 | "type": "string", 117 | "enum": [ 118 | "file" 119 | ], 120 | "description": "Entry type" 121 | }, 122 | "name": { 123 | "type": "string", 124 | "description": "Entry name" 125 | }, 126 | "acl": { 127 | "type": "array", 128 | "items": { 129 | "$ref": "#/definitions/ACL" 130 | }, 131 | "description": "ACL infos" 132 | } 133 | }, 134 | "required": [ 135 | "type", 136 | "name", 137 | "acl" 138 | ], 139 | "additionalProperties": false, 140 | "description": "File entry" 141 | }, 142 | { 143 | "type": "object", 144 | "properties": { 145 | "type": { 146 | "type": "string", 147 | "enum": [ 148 | "folder" 149 | ], 150 | "description": "Entry type" 151 | }, 152 | "entries": { 153 | "type": "array", 154 | "items": { 155 | "$ref": "#/definitions/Entry" 156 | }, 157 | "description": "Child entries" 158 | }, 159 | "name": { 160 | "type": "string", 161 | "description": "Entry name" 162 | }, 163 | "acl": { 164 | "type": "array", 165 | "items": { 166 | "$ref": "#/definitions/ACL" 167 | }, 168 | "description": "ACL infos" 169 | } 170 | }, 171 | "required": [ 172 | "type", 173 | "entries", 174 | "name", 175 | "acl" 176 | ], 177 | "additionalProperties": false, 178 | "description": "Folder entry" 179 | } 180 | ], 181 | "description": "Entry (union type)" 182 | } 183 | } 184 | } -------------------------------------------------------------------------------- /examples/schema/json-schema/filesys.ts: -------------------------------------------------------------------------------- 1 | 2 | // tslint:disable: object-literal-key-quotes 3 | const schema = { 4 | "$schema": "http://json-schema.org/draft-06/schema#", 5 | "definitions": { 6 | "ACL": { 7 | "type": "object", 8 | "properties": { 9 | "target": { 10 | "type": "string" 11 | }, 12 | "value": { 13 | "type": "string" 14 | } 15 | }, 16 | "required": [ 17 | "target", 18 | "value" 19 | ], 20 | "additionalProperties": false 21 | }, 22 | "EntryBase": { 23 | "type": "object", 24 | "properties": { 25 | "name": { 26 | "type": "string", 27 | "description": "Entry name" 28 | }, 29 | "acl": { 30 | "type": "array", 31 | "items": { 32 | "$ref": "#/definitions/ACL" 33 | }, 34 | "description": "ACL infos" 35 | } 36 | }, 37 | "required": [ 38 | "name", 39 | "acl" 40 | ], 41 | "additionalProperties": false, 42 | "description": "Entry base" 43 | }, 44 | "File": { 45 | "type": "object", 46 | "properties": { 47 | "type": { 48 | "type": "string", 49 | "enum": [ 50 | "file" 51 | ], 52 | "description": "Entry type" 53 | }, 54 | "name": { 55 | "type": "string", 56 | "description": "Entry name" 57 | }, 58 | "acl": { 59 | "type": "array", 60 | "items": { 61 | "$ref": "#/definitions/ACL" 62 | }, 63 | "description": "ACL infos" 64 | } 65 | }, 66 | "required": [ 67 | "type", 68 | "name", 69 | "acl" 70 | ], 71 | "additionalProperties": false, 72 | "description": "File entry" 73 | }, 74 | "Folder": { 75 | "type": "object", 76 | "properties": { 77 | "type": { 78 | "type": "string", 79 | "enum": [ 80 | "folder" 81 | ], 82 | "description": "Entry type" 83 | }, 84 | "entries": { 85 | "type": "array", 86 | "items": { 87 | "$ref": "#/definitions/Entry" 88 | }, 89 | "description": "Child entries" 90 | }, 91 | "name": { 92 | "type": "string", 93 | "description": "Entry name" 94 | }, 95 | "acl": { 96 | "type": "array", 97 | "items": { 98 | "$ref": "#/definitions/ACL" 99 | }, 100 | "description": "ACL infos" 101 | } 102 | }, 103 | "required": [ 104 | "type", 105 | "entries", 106 | "name", 107 | "acl" 108 | ], 109 | "additionalProperties": false, 110 | "description": "Folder entry" 111 | }, 112 | "Entry": { 113 | "anyOf": [ 114 | { 115 | "type": "object", 116 | "properties": { 117 | "type": { 118 | "type": "string", 119 | "enum": [ 120 | "file" 121 | ], 122 | "description": "Entry type" 123 | }, 124 | "name": { 125 | "type": "string", 126 | "description": "Entry name" 127 | }, 128 | "acl": { 129 | "type": "array", 130 | "items": { 131 | "$ref": "#/definitions/ACL" 132 | }, 133 | "description": "ACL infos" 134 | } 135 | }, 136 | "required": [ 137 | "type", 138 | "name", 139 | "acl" 140 | ], 141 | "additionalProperties": false, 142 | "description": "File entry" 143 | }, 144 | { 145 | "type": "object", 146 | "properties": { 147 | "type": { 148 | "type": "string", 149 | "enum": [ 150 | "folder" 151 | ], 152 | "description": "Entry type" 153 | }, 154 | "entries": { 155 | "type": "array", 156 | "items": { 157 | "$ref": "#/definitions/Entry" 158 | }, 159 | "description": "Child entries" 160 | }, 161 | "name": { 162 | "type": "string", 163 | "description": "Entry name" 164 | }, 165 | "acl": { 166 | "type": "array", 167 | "items": { 168 | "$ref": "#/definitions/ACL" 169 | }, 170 | "description": "ACL infos" 171 | } 172 | }, 173 | "required": [ 174 | "type", 175 | "entries", 176 | "name", 177 | "acl" 178 | ], 179 | "additionalProperties": false, 180 | "description": "Folder entry" 181 | } 182 | ], 183 | "description": "Entry (union type)" 184 | } 185 | } 186 | }; 187 | export default schema; 188 | // tslint:enable: object-literal-key-quotes 189 | -------------------------------------------------------------------------------- /examples/schema/json-schema/primitives.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-06/schema#", 3 | "definitions": { 4 | "NumberType": { 5 | "type": "number" 6 | }, 7 | "IntegerType": { 8 | "type": "integer" 9 | }, 10 | "BigIntType": { 11 | "type": [ 12 | "integer", 13 | "string" 14 | ] 15 | }, 16 | "StringType": { 17 | "type": "string" 18 | }, 19 | "BooleanType": { 20 | "type": "boolean" 21 | }, 22 | "NullType": { 23 | "type": "null" 24 | }, 25 | "UndefinedType": { 26 | "type": "null" 27 | }, 28 | "AnyType": { 29 | "type": [ 30 | "null", 31 | "integer", 32 | "number", 33 | "string", 34 | "boolean", 35 | "array", 36 | "object" 37 | ] 38 | }, 39 | "UnknownType": { 40 | "type": [ 41 | "null", 42 | "integer", 43 | "number", 44 | "string", 45 | "boolean", 46 | "array", 47 | "object" 48 | ] 49 | }, 50 | "NeverType": { 51 | "type": "null" 52 | }, 53 | "NumberValueType": { 54 | "type": "number", 55 | "enum": [ 56 | 3 57 | ] 58 | }, 59 | "IntegerValueType": { 60 | "type": "integer" 61 | }, 62 | "BigIntValueType": { 63 | "type": [ 64 | "integer", 65 | "string" 66 | ], 67 | "enum": [ 68 | "7", 69 | 7 70 | ] 71 | }, 72 | "StringValueType": { 73 | "type": "string", 74 | "enum": [ 75 | "XB" 76 | ] 77 | }, 78 | "BooleanValueType": { 79 | "type": "boolean", 80 | "enum": [ 81 | true 82 | ] 83 | } 84 | } 85 | } -------------------------------------------------------------------------------- /examples/schema/json-schema/primitives.ts: -------------------------------------------------------------------------------- 1 | 2 | // tslint:disable: object-literal-key-quotes 3 | const schema = { 4 | "$schema": "http://json-schema.org/draft-06/schema#", 5 | "definitions": { 6 | "NumberType": { 7 | "type": "number" 8 | }, 9 | "IntegerType": { 10 | "type": "integer" 11 | }, 12 | "BigIntType": { 13 | "type": [ 14 | "integer", 15 | "string" 16 | ] 17 | }, 18 | "StringType": { 19 | "type": "string" 20 | }, 21 | "BooleanType": { 22 | "type": "boolean" 23 | }, 24 | "NullType": { 25 | "type": "null" 26 | }, 27 | "UndefinedType": { 28 | "type": "null" 29 | }, 30 | "AnyType": { 31 | "type": [ 32 | "null", 33 | "integer", 34 | "number", 35 | "string", 36 | "boolean", 37 | "array", 38 | "object" 39 | ] 40 | }, 41 | "UnknownType": { 42 | "type": [ 43 | "null", 44 | "integer", 45 | "number", 46 | "string", 47 | "boolean", 48 | "array", 49 | "object" 50 | ] 51 | }, 52 | "NeverType": { 53 | "type": "null" 54 | }, 55 | "NumberValueType": { 56 | "type": "number", 57 | "enum": [ 58 | 3 59 | ] 60 | }, 61 | "IntegerValueType": { 62 | "type": "integer" 63 | }, 64 | "BigIntValueType": { 65 | "type": [ 66 | "integer", 67 | "string" 68 | ], 69 | "enum": [ 70 | "7", 71 | 7 72 | ] 73 | }, 74 | "StringValueType": { 75 | "type": "string", 76 | "enum": [ 77 | "XB" 78 | ] 79 | }, 80 | "BooleanValueType": { 81 | "type": "boolean", 82 | "enum": [ 83 | true 84 | ] 85 | } 86 | } 87 | }; 88 | export default schema; 89 | // tslint:enable: object-literal-key-quotes 90 | -------------------------------------------------------------------------------- /examples/schema/proto3/fields.proto: -------------------------------------------------------------------------------- 1 | 2 | syntax = "proto3"; 3 | import "google/protobuf/wrappers.proto"; 4 | import "google/protobuf/any.proto"; 5 | 6 | message NumberType { 7 | double value = 1; 8 | } 9 | 10 | message A { 11 | NumberType numberTypeField = 1; 12 | int32 integerTypeField = 2; 13 | string bigIntTypeField = 3; 14 | string stringTypeField = 4; 15 | bool booleanTypeField = 5; 16 | google.protobuf.Any nullTypeField = 6; 17 | google.protobuf.Any undefinedTypeField = 7; 18 | google.protobuf.Any anyTypeField = 8; 19 | google.protobuf.Any unknownTypeField = 9; 20 | google.protobuf.Any neverTypeField = 10; 21 | double numberValueTypeField = 11; 22 | int32 integerValueTypeField = 12; 23 | string bigIntValueTypeField = 13; 24 | string stringValueTypeField = 14; 25 | bool booleanValueTypeField = 15; 26 | } 27 | 28 | message B { 29 | NumberType numberTypeField = 1; 30 | google.protobuf.Int32Value integerTypeField = 2; 31 | google.protobuf.StringValue bigIntTypeField = 3; 32 | google.protobuf.StringValue stringTypeField = 4; 33 | google.protobuf.BoolValue booleanTypeField = 5; 34 | google.protobuf.Any nullTypeField = 6; 35 | google.protobuf.Any undefinedTypeField = 7; 36 | google.protobuf.Any anyTypeField = 8; 37 | google.protobuf.Any unknownTypeField = 9; 38 | google.protobuf.Any neverTypeField = 10; 39 | google.protobuf.DoubleValue numberValueTypeField = 11; 40 | google.protobuf.Int32Value integerValueTypeField = 12; 41 | google.protobuf.StringValue bigIntValueTypeField = 13; 42 | google.protobuf.StringValue stringValueTypeField = 14; 43 | google.protobuf.BoolValue booleanValueTypeField = 15; 44 | } 45 | 46 | message C { 47 | NumberType numberTypeField = 1; 48 | int32 integerTypeField = 2; 49 | string bigIntTypeField = 3; 50 | string stringTypeField = 4; 51 | bool booleanTypeField = 5; 52 | google.protobuf.Any nullTypeField = 6; 53 | google.protobuf.Any undefinedTypeField = 7; 54 | google.protobuf.Any anyTypeField = 8; 55 | google.protobuf.Any unknownTypeField = 9; 56 | google.protobuf.Any neverTypeField = 10; 57 | double numberValueTypeField = 11; 58 | int32 integerValueTypeField = 12; 59 | string bigIntValueTypeField = 13; 60 | string stringValueTypeField = 14; 61 | bool booleanValueTypeField = 15; 62 | } 63 | 64 | message D { 65 | NumberType numberTypeField = 1; 66 | google.protobuf.Int32Value integerTypeField = 2; 67 | google.protobuf.StringValue bigIntTypeField = 3; 68 | google.protobuf.StringValue stringTypeField = 4; 69 | google.protobuf.BoolValue booleanTypeField = 5; 70 | google.protobuf.Any nullTypeField = 6; 71 | google.protobuf.Any undefinedTypeField = 7; 72 | google.protobuf.Any anyTypeField = 8; 73 | google.protobuf.Any unknownTypeField = 9; 74 | google.protobuf.Any neverTypeField = 10; 75 | google.protobuf.DoubleValue numberValueTypeField = 11; 76 | google.protobuf.Int32Value integerValueTypeField = 12; 77 | google.protobuf.StringValue bigIntValueTypeField = 13; 78 | google.protobuf.StringValue stringValueTypeField = 14; 79 | google.protobuf.BoolValue booleanValueTypeField = 15; 80 | } 81 | 82 | message E {} 83 | 84 | message Z1 { 85 | string foo = 1; 86 | string bar = 2; 87 | string baz = 3; 88 | } 89 | 90 | message ACL { 91 | string target = 1; 92 | string value = 2; 93 | } 94 | 95 | message Z2 { 96 | string foo = 1; 97 | string bar = 2; 98 | string baz = 3; 99 | } 100 | 101 | message ErrorTypes { 102 | google.protobuf.Any value = 1; 103 | } 104 | 105 | enum ErrorTypes2 { 106 | option allow_alias = true; 107 | ErrorTypes2__UNKNOWN__ = 0; 108 | ErrorTypes2_Aaaaa = 99; 109 | } 110 | 111 | enum ErrorTypes3 { 112 | option allow_alias = true; 113 | ErrorTypes3_Zzzzz = 0; 114 | ErrorTypes3_Aaaaa = 99; 115 | } 116 | 117 | message Foo { 118 | string name = 1; 119 | string email = 2; 120 | } 121 | 122 | message Bar { 123 | Foo foo = 1; 124 | } 125 | 126 | message Baz { 127 | string aaa1 = 1; 128 | string aaa2 = 2; 129 | string aaa3 = 3; 130 | string aaa4 = 4; 131 | int32 bbb1 = 5; 132 | int32 bbb2 = 6; 133 | int32 bbb3 = 7; 134 | int32 bbb4 = 8; 135 | /** comment */ 136 | double ccc1 = 9; 137 | /** comment */ 138 | double ccc2 = 10; 139 | /** comment */ 140 | double ccc3 = 11; 141 | /** comment */ 142 | double ccc4 = 12; 143 | repeated google.protobuf.Any ddd1 = 13; 144 | repeated google.protobuf.Any ddd2 = 14; 145 | repeated google.protobuf.Any ddd3 = 15; 146 | repeated google.protobuf.Any ddd4 = 16; 147 | repeated google.protobuf.Any ddd5 = 17; 148 | repeated google.protobuf.Any ddd6 = 18; 149 | repeated string eee1 = 19; 150 | repeated string eee2 = 20; 151 | repeated string eee3 = 21; 152 | repeated string eee4 = 22; 153 | ErrorTypes fff = 23; 154 | Bar ggg1 = 24; 155 | repeated Bar ggg2 = 25; 156 | repeated google.protobuf.Any ggg3 = 26; 157 | Bar ggg4 = 27; 158 | repeated Bar ggg5 = 28; 159 | repeated google.protobuf.Any ggg6 = 29; 160 | Bar ggg7 = 30; 161 | repeated Bar ggg8 = 31; 162 | repeated google.protobuf.Any ggg9 = 32; 163 | Bar ggg10 = 33; 164 | repeated Bar ggg11 = 34; 165 | repeated google.protobuf.Any ggg12 = 35; 166 | double hhh1 = 36; 167 | repeated double hhh2 = 37; 168 | repeated google.protobuf.Any hhh3 = 38; 169 | google.protobuf.DoubleValue hhh4 = 39; 170 | repeated google.protobuf.DoubleValue hhh5 = 40; 171 | repeated google.protobuf.Any hhh6 = 41; 172 | google.protobuf.DoubleValue hhh7 = 42; 173 | repeated google.protobuf.DoubleValue hhh8 = 43; 174 | repeated google.protobuf.Any hhh9 = 44; 175 | google.protobuf.DoubleValue hhh10 = 45; 176 | repeated google.protobuf.DoubleValue hhh11 = 46; 177 | repeated google.protobuf.Any hhh12 = 47; 178 | } 179 | 180 | message User { 181 | string userName = 1; 182 | string primaryEmail = 2; 183 | string primaryAliasName = 3; 184 | repeated string aliasNames = 4; 185 | } 186 | 187 | -------------------------------------------------------------------------------- /examples/schema/proto3/filesys.proto: -------------------------------------------------------------------------------- 1 | 2 | syntax = "proto3"; 3 | import "google/protobuf/wrappers.proto"; 4 | import "google/protobuf/any.proto"; 5 | 6 | message ACL { 7 | string target = 1; 8 | string value = 2; 9 | } 10 | 11 | /** Entry base */ 12 | message EntryBase { 13 | /** Entry name */ 14 | string name = 1; 15 | /** ACL infos */ 16 | repeated ACL acl = 2; 17 | } 18 | 19 | /** File entry */ 20 | message File { 21 | /** Entry type */ 22 | string type = 1; 23 | /** Entry name */ 24 | string name = 2; 25 | /** ACL infos */ 26 | repeated ACL acl = 3; 27 | } 28 | 29 | /** Folder entry */ 30 | message Folder { 31 | /** Entry type */ 32 | string type = 1; 33 | /** Child entries */ 34 | repeated Entry entries = 2; 35 | /** Entry name */ 36 | string name = 3; 37 | /** ACL infos */ 38 | repeated ACL acl = 4; 39 | } 40 | 41 | /** Entry (union type) */ 42 | message Entry { 43 | google.protobuf.Any value = 1; 44 | } 45 | 46 | -------------------------------------------------------------------------------- /examples/schema/proto3/primitives.proto: -------------------------------------------------------------------------------- 1 | 2 | syntax = "proto3"; 3 | import "google/protobuf/wrappers.proto"; 4 | import "google/protobuf/any.proto"; 5 | 6 | message NumberType { 7 | double value = 1; 8 | } 9 | 10 | message IntegerType { 11 | int32 value = 1; 12 | } 13 | 14 | message BigIntType { 15 | string value = 1; 16 | } 17 | 18 | message StringType { 19 | string value = 1; 20 | } 21 | 22 | message BooleanType { 23 | bool value = 1; 24 | } 25 | 26 | message NullType { 27 | google.protobuf.Any value = 1; 28 | } 29 | 30 | message UndefinedType { 31 | google.protobuf.Any value = 1; 32 | } 33 | 34 | message AnyType { 35 | google.protobuf.Any value = 1; 36 | } 37 | 38 | message UnknownType { 39 | google.protobuf.Any value = 1; 40 | } 41 | 42 | message NeverType { 43 | google.protobuf.Any value = 1; 44 | } 45 | 46 | message NumberValueType { 47 | double value = 1; 48 | } 49 | 50 | message IntegerValueType { 51 | int32 value = 1; 52 | } 53 | 54 | message BigIntValueType { 55 | string value = 1; 56 | } 57 | 58 | message StringValueType { 59 | string value = 1; 60 | } 61 | 62 | message BooleanValueType { 63 | bool value = 1; 64 | } 65 | 66 | -------------------------------------------------------------------------------- /examples/schema/ts/fields.d.ts: -------------------------------------------------------------------------------- 1 | type NumberType = number; 2 | 3 | interface A { 4 | numberTypeField: NumberType; 5 | integerTypeField: number; 6 | bigIntTypeField: bigint; 7 | stringTypeField: string; 8 | booleanTypeField: boolean; 9 | nullTypeField: null; 10 | undefinedTypeField: undefined; 11 | anyTypeField: any; 12 | unknownTypeField: unknown; 13 | neverTypeField: never; 14 | numberValueTypeField: 3; 15 | integerValueTypeField: number; 16 | bigIntValueTypeField: 7n; 17 | stringValueTypeField: 'XB'; 18 | booleanValueTypeField: true; 19 | } 20 | 21 | interface B { 22 | numberTypeField?: NumberType; 23 | integerTypeField?: number; 24 | bigIntTypeField?: bigint; 25 | stringTypeField?: string; 26 | booleanTypeField?: boolean; 27 | nullTypeField?: null; 28 | undefinedTypeField?: undefined; 29 | anyTypeField?: any; 30 | unknownTypeField?: unknown; 31 | neverTypeField?: never; 32 | numberValueTypeField?: 3; 33 | integerValueTypeField?: number; 34 | bigIntValueTypeField?: 7n; 35 | stringValueTypeField?: 'XB'; 36 | booleanValueTypeField?: true; 37 | } 38 | 39 | interface C extends A {} 40 | 41 | interface D { 42 | numberTypeField?: NumberType; 43 | integerTypeField?: number; 44 | bigIntTypeField?: bigint; 45 | stringTypeField?: string; 46 | booleanTypeField?: boolean; 47 | nullTypeField?: null; 48 | undefinedTypeField?: undefined; 49 | anyTypeField?: any; 50 | unknownTypeField?: unknown; 51 | neverTypeField?: never; 52 | numberValueTypeField?: 3; 53 | integerValueTypeField?: number; 54 | bigIntValueTypeField?: 7n; 55 | stringValueTypeField?: 'XB'; 56 | booleanValueTypeField?: true; 57 | } 58 | 59 | interface E { 60 | /** additional props */ 61 | [propName0: string]: any; 62 | } 63 | 64 | interface Z1 { 65 | foo: (ACL['target']); 66 | bar: (ACL['value']); 67 | baz: (ACL['target']); 68 | } 69 | 70 | interface ACL { 71 | target: string; 72 | value: string; 73 | } 74 | 75 | interface Z2 { 76 | foo: (ACL['target']); 77 | bar: (ACL['value']); 78 | baz: (ACL['target']); 79 | } 80 | 81 | export enum ErrorTypes { 82 | /** comment */ 83 | InvalidDefinition = 1, 84 | /** comment */ 85 | Required, 86 | /** comment */ 87 | TypeUnmatched, 88 | /** comment */ 89 | RepeatQtyUnmatched, 90 | SequenceUnmatched, 91 | ValueRangeUnmatched, 92 | ValuePatternUnmatched, 93 | ValueLengthUnmatched, 94 | ValueUnmatched, 95 | Aaaaa = 99, 96 | Bbbbb = 'string bbbbb', 97 | } 98 | 99 | export enum ErrorTypes2 { 100 | Aaaaa = 99, 101 | } 102 | 103 | export enum ErrorTypes3 { 104 | Zzzzz, 105 | Aaaaa = 99, 106 | } 107 | 108 | interface Foo { 109 | name: string; 110 | email: string; 111 | } 112 | 113 | interface Bar { 114 | foo: Foo; 115 | } 116 | 117 | interface Baz { 118 | aaa1: string; 119 | aaa2: string; 120 | aaa3: string; 121 | aaa4: string; 122 | bbb1: number; 123 | bbb2: number; 124 | bbb3: number; 125 | bbb4: number; 126 | /** comment */ 127 | ccc1: number; 128 | /** comment */ 129 | ccc2: number; 130 | /** comment */ 131 | ccc3: number; 132 | /** comment */ 133 | ccc4: number; 134 | ddd1: [number, string]; 135 | ddd2: any[]; 136 | ddd3: any[]; 137 | ddd4: any[]; 138 | ddd5: any[]; 139 | ddd6: any[]; 140 | eee1: string[]; 141 | eee2: string[]; 142 | eee3: string[]; 143 | eee4: string[]; 144 | fff: ErrorTypes; 145 | ggg1: Bar; 146 | ggg2: Bar[]; 147 | ggg3: Array; 148 | ggg4: (Bar | null); 149 | ggg5: Array<(Bar | null)>; 150 | ggg6: Array>; 151 | ggg7: (Bar | undefined); 152 | ggg8: Array<(Bar | undefined)>; 153 | ggg9: Array>; 154 | ggg10: (Bar | null | undefined); 155 | ggg11: Array<(Bar | null | undefined)>; 156 | ggg12: Array>; 157 | hhh1: number; 158 | hhh2: number[]; 159 | hhh3: Array; 160 | hhh4: (number | null); 161 | hhh5: Array<(number | null)>; 162 | hhh6: Array>; 163 | hhh7: (number | undefined); 164 | hhh8: Array<(number | undefined)>; 165 | hhh9: Array>; 166 | hhh10: (number | null | undefined); 167 | hhh11: Array<(number | null | undefined)>; 168 | hhh12: Array>; 169 | } 170 | 171 | interface User { 172 | userName: (Foo['name']); 173 | primaryEmail: (Foo['email']); 174 | primaryAliasName: (Bar['foo']['name']); 175 | aliasNames: (Bar['foo']['name'])[]; 176 | } 177 | 178 | -------------------------------------------------------------------------------- /examples/schema/ts/filesys.d.ts: -------------------------------------------------------------------------------- 1 | interface ACL { 2 | target: string; 3 | value: string; 4 | } 5 | 6 | /** Entry base */ 7 | interface EntryBase { 8 | /** Entry name */ 9 | name: string; 10 | /** ACL infos */ 11 | acl: ACL[]; 12 | } 13 | 14 | /** File entry */ 15 | interface File extends EntryBase { 16 | /** Entry type */ 17 | type: 'file'; 18 | } 19 | 20 | /** Folder entry */ 21 | interface Folder extends EntryBase { 22 | /** Entry type */ 23 | type: 'folder'; 24 | /** Child entries */ 25 | entries: Entry[]; 26 | } 27 | 28 | /** Entry (union type) */ 29 | type Entry = (File | Folder); 30 | 31 | -------------------------------------------------------------------------------- /examples/schema/ts/primitives.d.ts: -------------------------------------------------------------------------------- 1 | type NumberType = number; 2 | 3 | type IntegerType = number; 4 | 5 | type BigIntType = bigint; 6 | 7 | type StringType = string; 8 | 9 | type BooleanType = boolean; 10 | 11 | type NullType = null; 12 | 13 | type UndefinedType = undefined; 14 | 15 | type AnyType = any; 16 | 17 | type UnknownType = unknown; 18 | 19 | type NeverType = never; 20 | 21 | type NumberValueType = 3; 22 | 23 | type IntegerValueType = number; 24 | 25 | type BigIntValueType = 7n; 26 | 27 | type StringValueType = 'XB'; 28 | 29 | type BooleanValueType = true; 30 | 31 | -------------------------------------------------------------------------------- /examples/schema/ts/tynder.d.ts: -------------------------------------------------------------------------------- 1 | import * as fs from 'fs'; 2 | 3 | export type PrimitiveValueTypes = (number | bigint | string | boolean | null | undefined); 4 | 5 | export type PrimitiveValueTypeNames = ('number' | 'bigint' | 'string' | 'boolean' | 'null' | 'undefined'); 6 | 7 | export type OptionalPrimitiveValueTypeNames = ('number?' | 'bigint?' | 'string?' | 'boolean?' | 'null?' | 'undefined?'); 8 | 9 | export type PlaceholderTypeNames = ('never' | 'any' | 'unknown'); 10 | 11 | export type OptionalPlaceholderTypeNames = ('never?' | 'any?' | 'unknown?'); 12 | 13 | export enum ErrorTypes { 14 | /** comment */ 15 | InvalidDefinition = 1, 16 | /** comment */ 17 | Required, 18 | /** comment */ 19 | TypeUnmatched, 20 | /** comment */ 21 | RepeatQtyUnmatched, 22 | SequenceUnmatched, 23 | ValueRangeUnmatched, 24 | ValuePatternUnmatched, 25 | ValueLengthUnmatched, 26 | ValueUnmatched, 27 | } 28 | 29 | export interface ErrorMessages { 30 | invalidDefinition?: string; 31 | required?: string; 32 | typeUnmatched?: string; 33 | repeatQtyUnmatched?: string; 34 | sequenceUnmatched?: string; 35 | valueRangeUnmatched?: string; 36 | valuePatternUnmatched?: string; 37 | valueLengthUnmatched?: string; 38 | valueUnmatched?: string; 39 | } 40 | 41 | export interface TypeAssertionErrorMessageConstraints { 42 | minValue?: (number | string | null); 43 | maxValue?: (number | string | null); 44 | greaterThanValue?: (number | string | null); 45 | lessThanValue?: (number | string | null); 46 | minLength?: (number | null); 47 | maxLength?: (number | null); 48 | min?: (number | null); 49 | max?: (number | null); 50 | pattern?: string; 51 | } 52 | 53 | export interface TypeAssertionErrorMessage { 54 | code: string; 55 | message: string; 56 | dataPath: string; 57 | constraints: TypeAssertionErrorMessageConstraints; 58 | value?: any; 59 | } 60 | 61 | export interface TypeAssertionBase { 62 | messageId?: string; 63 | message?: string; 64 | messages?: ErrorMessages; 65 | name?: string; 66 | typeName?: string; 67 | docComment?: string; 68 | passThruCodeBlock?: string; 69 | noOutput?: boolean; 70 | } 71 | 72 | export interface NeverTypeAssertion extends TypeAssertionBase { 73 | kind: 'never'; 74 | } 75 | 76 | export interface AnyTypeAssertion extends TypeAssertionBase { 77 | kind: 'any'; 78 | } 79 | 80 | export interface UnknownTypeAssertion extends TypeAssertionBase { 81 | kind: 'unknown'; 82 | } 83 | 84 | export interface PrimitiveTypeAssertionConstraints { 85 | minValue?: (number | string | null); 86 | maxValue?: (number | string | null); 87 | greaterThanValue?: (number | string | null); 88 | lessThanValue?: (number | string | null); 89 | minLength?: (number | null); 90 | maxLength?: (number | null); 91 | pattern?: (RegExp | null); 92 | } 93 | 94 | export interface PrimitiveTypeAssertion extends TypeAssertionBase, PrimitiveTypeAssertionConstraints { 95 | kind: 'primitive'; 96 | primitiveName: PrimitiveValueTypeNames; 97 | } 98 | 99 | export interface PrimitiveValueTypeAssertion extends TypeAssertionBase { 100 | kind: 'primitive-value'; 101 | value: PrimitiveValueTypes; 102 | } 103 | 104 | export interface RepeatedAssertionConstraints { 105 | min: (number | null); 106 | max: (number | null); 107 | } 108 | 109 | export interface RepeatedAssertion extends TypeAssertionBase, RepeatedAssertionConstraints { 110 | kind: 'repeated'; 111 | repeated: TypeAssertion; 112 | } 113 | 114 | export interface SpreadAssertion extends TypeAssertionBase, RepeatedAssertionConstraints { 115 | kind: 'spread'; 116 | spread: TypeAssertion; 117 | } 118 | 119 | export interface SequenceAssertion extends TypeAssertionBase { 120 | kind: 'sequence'; 121 | sequence: TypeAssertion[]; 122 | } 123 | 124 | export interface OneOfAssertion extends TypeAssertionBase { 125 | kind: 'one-of'; 126 | oneOf: TypeAssertion[]; 127 | } 128 | 129 | export interface OptionalAssertion extends TypeAssertionBase { 130 | kind: 'optional'; 131 | optional: TypeAssertion; 132 | } 133 | 134 | export interface EnumAssertion extends TypeAssertionBase { 135 | kind: 'enum'; 136 | values: Array; 137 | } 138 | 139 | export type ObjectAssertionMember = ([string, TypeAssertion] | [string, TypeAssertion, boolean] | [string, TypeAssertion, boolean, string]); 140 | 141 | export type AdditionalPropsKey = Array<('string' | 'number' | RegExp)>; 142 | 143 | export type AdditionalPropsMember = ([AdditionalPropsKey, TypeAssertion] | [AdditionalPropsKey, TypeAssertion, boolean] | [AdditionalPropsKey, TypeAssertion, boolean, string]); 144 | 145 | export interface ObjectAssertion extends TypeAssertionBase { 146 | kind: 'object'; 147 | members: ObjectAssertionMember[]; 148 | additionalProps?: AdditionalPropsMember[]; 149 | baseTypes?: Array<(ObjectAssertion | AssertionSymlink)>; 150 | } 151 | 152 | export interface AssertionSymlink extends TypeAssertionBase { 153 | kind: 'symlink'; 154 | symlinkTargetName: string; 155 | } 156 | 157 | export type TypeAssertion = (NeverTypeAssertion | AnyTypeAssertion | UnknownTypeAssertion | PrimitiveTypeAssertion | PrimitiveValueTypeAssertion | RepeatedAssertion | SpreadAssertion | SequenceAssertion | OneOfAssertion | OptionalAssertion | EnumAssertion | ObjectAssertion | AssertionSymlink); 158 | 159 | export interface ValidationContext { 160 | checkAll?: boolean; 161 | noAdditionalProps?: boolean; 162 | errorMessages?: ErrorMessages; 163 | errors: TypeAssertionErrorMessage[]; 164 | typeStack: Array<(NeverTypeAssertion | AnyTypeAssertion | UnknownTypeAssertion | PrimitiveTypeAssertion | PrimitiveValueTypeAssertion | RepeatedAssertion | SpreadAssertion | SequenceAssertion | OneOfAssertion | OptionalAssertion | EnumAssertion | ObjectAssertion | AssertionSymlink | [TypeAssertion, (number | string | undefined)])>; 165 | schema?: TypeAssertionMap; 166 | } 167 | 168 | export interface TypeAssertionSetValue { 169 | ty: TypeAssertion; 170 | exported: boolean; 171 | resolved: boolean; 172 | } 173 | 174 | export type TypeAssertionMap = Map; 175 | 176 | export interface SymbolResolverContext { 177 | nestLevel: number; 178 | symlinkStack: string[]; 179 | } 180 | 181 | export interface CodegenContext { 182 | nestLevel: number; 183 | schema?: TypeAssertionMap; 184 | } 185 | 186 | -------------------------------------------------------------------------------- /examples/schema/tynder/fields.tss: -------------------------------------------------------------------------------- 1 | // @ts-nocheck 2 | 3 | type NumberType = number; 4 | 5 | interface A { 6 | numberTypeField: NumberType; 7 | integerTypeField: integer; 8 | bigIntTypeField: bigint; 9 | stringTypeField: string; 10 | booleanTypeField: boolean; 11 | nullTypeField: null; 12 | undefinedTypeField: undefined; 13 | anyTypeField: any; 14 | unknownTypeField: unknown; 15 | neverTypeField: never; 16 | numberValueTypeField: 3; 17 | integerValueTypeField: integer; 18 | bigIntValueTypeField: 7n; 19 | stringValueTypeField: 'XB'; 20 | booleanValueTypeField: true; 21 | } 22 | 23 | interface B { 24 | numberTypeField?: NumberType; 25 | integerTypeField?: integer; 26 | bigIntTypeField?: bigint; 27 | stringTypeField?: string; 28 | booleanTypeField?: boolean; 29 | nullTypeField?: null; 30 | undefinedTypeField?: undefined; 31 | anyTypeField?: any; 32 | unknownTypeField?: unknown; 33 | neverTypeField?: never; 34 | numberValueTypeField?: 3; 35 | integerValueTypeField?: integer; 36 | bigIntValueTypeField?: 7n; 37 | stringValueTypeField?: 'XB'; 38 | booleanValueTypeField?: true; 39 | } 40 | 41 | interface C extends A {} 42 | 43 | type D = Partial; 44 | 45 | interface E { 46 | /** additional props */ 47 | [propNames: string]: any; 48 | } 49 | 50 | 51 | interface Z1 { 52 | foo: ACL.target; 53 | bar: ACL.value; 54 | //@maxLength(10) // TODO: BUG: can't set to kind === symlink 55 | baz: ACL.target; 56 | } 57 | 58 | interface ACL { 59 | target: string; 60 | @minLength(0) 61 | value: string; 62 | } 63 | 64 | interface Z2 { 65 | foo: ACL.target; 66 | bar: ACL.value; 67 | @maxLength(10) 68 | baz: ACL.target; 69 | } 70 | 71 | export enum ErrorTypes { 72 | /** comment */ 73 | InvalidDefinition = 1, 74 | /** comment */ 75 | Required, // (all) 76 | /** comment */ 77 | TypeUnmatched, // Never/Unknown/Primitive/Object 78 | /** comment */ 79 | RepeatQtyUnmatched, // Repeated/Spread 80 | SequenceUnmatched, // Sequence 81 | ValueRangeUnmatched, // Primitive: minValue, maxValue, greaterThanValue, lessThanValue 82 | ValuePatternUnmatched, // Primitive: pattern 83 | ValueLengthUnmatched, // Primitive: minLength, maxLength 84 | ValueUnmatched, // PrimitiveValue 85 | Aaaaa = 99, 86 | Bbbbb = 'string bbbbb', 87 | } 88 | 89 | export enum ErrorTypes2 { 90 | Aaaaa = 99, 91 | } 92 | 93 | export enum ErrorTypes3 { 94 | Zzzzz = 0, 95 | Aaaaa = 99, 96 | } 97 | 98 | interface Foo { 99 | @match(/^[A-Za-z]+$/) 100 | name: string; 101 | @match(/^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/) 102 | email: string; 103 | } 104 | 105 | interface Bar { 106 | foo: Foo 107 | } 108 | 109 | interface Baz { 110 | @minValue('a') @maxValue('z') 111 | aaa1: string; 112 | @range('a', 'z') 113 | aaa2: string; 114 | @maxValue('z') 115 | aaa3: string; 116 | @minValue('a') 117 | aaa4: string; 118 | 119 | @minValue(-3) @maxValue(5) 120 | bbb1: integer; 121 | @range(-3, 5) 122 | bbb2: integer; 123 | @maxValue(-3) 124 | bbb3: integer; 125 | @minValue(5) 126 | bbb4: integer; 127 | 128 | /** comment */ 129 | @minValue(-3) @maxValue(5) 130 | ccc1: number; 131 | /** comment */ 132 | @range(-3, 5) 133 | ccc2: number; 134 | /** comment */ 135 | @maxValue(-3) 136 | ccc3: number; 137 | /** comment */ 138 | @minValue(5) 139 | ccc4: number; 140 | 141 | ddd1: [number, string]; 142 | ddd2: [..., string]; 143 | ddd3: [..., string]; 144 | ddd4: [..., string]; 145 | ddd5: [..., string]; 146 | ddd6: [number?, string]; 147 | 148 | eee1: string[]; 149 | eee2: string[10..]; 150 | eee3: string[..20]; 151 | eee4: string[10..20]; 152 | 153 | fff: ErrorTypes; 154 | 155 | ggg1: Bar; 156 | ggg2: Bar[]; 157 | ggg3: Bar[][]; 158 | ggg4: (Bar|null); 159 | ggg5: (Bar|null)[]; 160 | ggg6: (Bar|null)[][]; 161 | ggg7: (Bar|undefined); 162 | ggg8: (Bar|undefined)[]; 163 | ggg9: (Bar|undefined)[][]; 164 | ggg10: (Bar|null|undefined); 165 | ggg11: (Bar|null|undefined)[]; 166 | ggg12: (Bar|null|undefined)[][]; 167 | 168 | hhh1: number; 169 | hhh2: number[]; 170 | hhh3: number[][]; 171 | hhh4: (number|null); 172 | hhh5: (number|null)[]; 173 | hhh6: (number|null)[][]; 174 | hhh7: (number|undefined); 175 | hhh8: (number|undefined)[]; 176 | hhh9: (number|undefined)[][]; 177 | hhh10: (number|null|undefined); 178 | hhh11: (number|null|undefined)[]; 179 | hhh12: (number|null|undefined)[][]; 180 | } 181 | 182 | interface User { 183 | userName: Foo.name; 184 | primaryEmail: Foo.email; 185 | primaryAliasName: Bar.foo.name; 186 | aliasNames: Bar.foo.name[]; 187 | } 188 | -------------------------------------------------------------------------------- /examples/schema/tynder/filesys.tss: -------------------------------------------------------------------------------- 1 | interface ACL { 2 | target: string; 3 | value: string; 4 | } 5 | 6 | /** Entry base */ 7 | interface EntryBase { 8 | /** Entry name */ 9 | name: string; 10 | /** ACL infos */ 11 | acl: ACL[]; 12 | } 13 | 14 | /** File entry */ 15 | interface File extends EntryBase { 16 | /** Entry type */ 17 | type: 'file'; 18 | } 19 | 20 | /** Folder entry */ 21 | interface Folder extends EntryBase { 22 | /** Entry type */ 23 | type: 'folder'; 24 | /** Child entries */ 25 | entries: Entry[]; 26 | } 27 | 28 | /** Entry (union type) */ 29 | type Entry = File | Folder; -------------------------------------------------------------------------------- /examples/schema/tynder/primitives.tss: -------------------------------------------------------------------------------- 1 | type NumberType = number; 2 | type IntegerType = integer; 3 | type BigIntType = bigint; 4 | type StringType = string; 5 | type BooleanType = boolean; 6 | type NullType = null; 7 | type UndefinedType = undefined; 8 | type AnyType = any; 9 | type UnknownType = unknown; 10 | type NeverType = never; 11 | type NumberValueType = 3; 12 | type IntegerValueType = integer; 13 | type BigIntValueType = 7n; 14 | type StringValueType = 'XB'; 15 | type BooleanValueType = true; -------------------------------------------------------------------------------- /examples/schema/tynder/tynder.tss: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 Shellyl_N and Authors 2 | // license: ISC 3 | // https://github.com/shellyln 4 | 5 | 6 | // external RegExp, Map; 7 | /// @tynder-external RegExp, Map 8 | 9 | import * as fs from 'fs'; 10 | 11 | export type PrimitiveValueTypes = number | bigint | string | boolean | null | undefined; // TODO: function 12 | export type PrimitiveValueTypeNames = 'number' | 'bigint' | 'string' | 'boolean' | 'null' | 'undefined'; // TODO: function 13 | export type OptionalPrimitiveValueTypeNames = 'number?' | 'bigint?' | 'string?' | 'boolean?' | 'null?' | 'undefined?'; // TODO: function 14 | export type PlaceholderTypeNames = 'never' | 'any' | 'unknown'; 15 | export type OptionalPlaceholderTypeNames = 'never?' | 'any?' | 'unknown?'; 16 | 17 | 18 | 19 | export enum ErrorTypes { 20 | /** comment */ 21 | InvalidDefinition = 1, 22 | /** comment */ 23 | Required, // (all) 24 | /** comment */ 25 | TypeUnmatched, // Never/Unknown/Primitive/Object 26 | /** comment */ 27 | RepeatQtyUnmatched, // Repeated/Spread 28 | SequenceUnmatched, // Sequence 29 | ValueRangeUnmatched, // Primitive: minValue, maxValue, greaterThanValue, lessThanValue 30 | ValuePatternUnmatched, // Primitive: pattern 31 | ValueLengthUnmatched, // Primitive: minLength, maxLength 32 | ValueUnmatched, // PrimitiveValue 33 | } 34 | 35 | 36 | export type ErrorMessages = Partial<{ 37 | invalidDefinition: string, 38 | required: string, 39 | typeUnmatched: string, 40 | repeatQtyUnmatched: string, 41 | sequenceUnmatched: string, 42 | valueRangeUnmatched: string, 43 | valuePatternUnmatched: string, 44 | valueLengthUnmatched: string, 45 | valueUnmatched: string, 46 | }>; 47 | 48 | 49 | // TODO: BUG: parsed result is wrong! 50 | // evaluate ops before resolving backref? 51 | export type TypeAssertionErrorMessageConstraints = 52 | Partial & 54 | {pattern: string}>; 55 | 56 | 57 | export interface TypeAssertionErrorMessage { 58 | code: string; 59 | message: string; 60 | dataPath: string; 61 | constraints: TypeAssertionErrorMessageConstraints; 62 | value?: any; 63 | } 64 | 65 | 66 | export interface TypeAssertionBase { 67 | messageId?: string; 68 | message?: string; 69 | messages?: ErrorMessages; 70 | name?: string; // Member name or 'typeName' below. For error reporting and codegen. 71 | typeName?: string; // Named user defined 'type' or 'interface' name. For error reporting and codegen. 72 | docComment?: string; // Doc comment. 73 | passThruCodeBlock?: string; // Store a pass-thru code block (e.g. import statement). use it with kind===never 74 | noOutput?: boolean; // If true, skip code generation. 75 | } 76 | 77 | 78 | export interface NeverTypeAssertion extends TypeAssertionBase { 79 | kind: 'never'; 80 | } 81 | 82 | 83 | export interface AnyTypeAssertion extends TypeAssertionBase { 84 | kind: 'any'; 85 | } 86 | 87 | 88 | export interface UnknownTypeAssertion extends TypeAssertionBase { 89 | kind: 'unknown'; 90 | } 91 | 92 | 93 | export interface PrimitiveTypeAssertionConstraints { 94 | minValue?: number | string | null; // TODO: bigint 95 | maxValue?: number | string | null; // TODO: bigint 96 | greaterThanValue?: number | string | null; 97 | lessThanValue?: number | string | null; 98 | minLength?: number | null; 99 | maxLength?: number | null; 100 | pattern?: RegExp | null; 101 | } 102 | 103 | 104 | export interface PrimitiveTypeAssertion extends TypeAssertionBase, PrimitiveTypeAssertionConstraints { 105 | kind: 'primitive'; 106 | primitiveName: PrimitiveValueTypeNames; 107 | } 108 | 109 | 110 | export interface PrimitiveValueTypeAssertion extends TypeAssertionBase { 111 | kind: 'primitive-value'; 112 | value: PrimitiveValueTypes; 113 | } 114 | 115 | 116 | export interface RepeatedAssertionConstraints { 117 | min: number | null; 118 | max: number | null; 119 | } 120 | 121 | 122 | export interface RepeatedAssertion extends TypeAssertionBase, RepeatedAssertionConstraints { 123 | kind: 'repeated'; 124 | repeated: TypeAssertion; 125 | } 126 | 127 | 128 | export interface SpreadAssertion extends TypeAssertionBase, RepeatedAssertionConstraints { 129 | kind: 'spread'; 130 | spread: TypeAssertion; 131 | } 132 | 133 | 134 | export interface SequenceAssertion extends TypeAssertionBase { 135 | kind: 'sequence'; 136 | sequence: TypeAssertion[]; 137 | } 138 | 139 | 140 | export interface OneOfAssertion extends TypeAssertionBase { 141 | kind: 'one-of'; 142 | oneOf: TypeAssertion[]; 143 | } 144 | 145 | 146 | export interface OptionalAssertion extends TypeAssertionBase { 147 | kind: 'optional'; 148 | optional: TypeAssertion; 149 | } 150 | 151 | 152 | export interface EnumAssertion extends TypeAssertionBase { 153 | kind: 'enum'; 154 | values: Array<[ 155 | string, // enum key 156 | number | string, // enum value 157 | string?, // doc comment 158 | ]>; 159 | } 160 | 161 | 162 | export type ObjectAssertionMember = [ 163 | string, // name 164 | TypeAssertion, // type 165 | ] | [ 166 | string, // name 167 | TypeAssertion, // type 168 | boolean, // If true, defined by ancestor types 169 | ] | [ 170 | string, // name 171 | TypeAssertion, // type 172 | boolean, // If true, defined by ancestor types 173 | string, // doc comment 174 | ]; 175 | 176 | 177 | 178 | export type AdditionalPropsKey = Array<'string' | 'number' | RegExp>; 179 | 180 | 181 | export type AdditionalPropsMember = [ 182 | AdditionalPropsKey, // name 183 | TypeAssertion, // type 184 | ] | [ 185 | AdditionalPropsKey, // name 186 | TypeAssertion, // type 187 | boolean, // If true, defined by ancestor types 188 | ] | [ 189 | AdditionalPropsKey, // name 190 | TypeAssertion, // type 191 | boolean, // If true, defined by ancestor types 192 | string, // doc comment 193 | ]; 194 | 195 | 196 | export interface ObjectAssertion extends TypeAssertionBase { 197 | kind: 'object'; 198 | members: ObjectAssertionMember[]; 199 | additionalProps?: AdditionalPropsMember[]; 200 | baseTypes?: Array; 201 | } 202 | 203 | 204 | export interface AssertionSymlink extends TypeAssertionBase { 205 | kind: 'symlink'; 206 | symlinkTargetName: string; 207 | } 208 | 209 | 210 | export type TypeAssertion = 211 | NeverTypeAssertion | 212 | AnyTypeAssertion | 213 | UnknownTypeAssertion | 214 | PrimitiveTypeAssertion | 215 | PrimitiveValueTypeAssertion | 216 | RepeatedAssertion | 217 | SpreadAssertion | 218 | SequenceAssertion | 219 | OneOfAssertion | 220 | OptionalAssertion | 221 | EnumAssertion | 222 | ObjectAssertion | 223 | AssertionSymlink; 224 | 225 | 226 | export interface ValidationContext { 227 | checkAll?: boolean; 228 | noAdditionalProps?: boolean; 229 | errorMessages?: ErrorMessages; 230 | 231 | // maxDepth: number; 232 | // depth: number; 233 | // mapper?: (value: any, ty: TypeAssertion) => any; 234 | 235 | // === returned values === 236 | errors: TypeAssertionErrorMessage[]; 237 | 238 | // === internal use === 239 | typeStack: Array< // For error reporting (keyword substitutions) 240 | TypeAssertion | 241 | [TypeAssertion, number | string | undefined]>; // [1]: data index 242 | // NOTE: DO NOT reassign! Push or pop items instead of reassign. 243 | schema?: TypeAssertionMap; // To resolve 'symlink' assertion, the context need to have a schema instance. 244 | } 245 | 246 | 247 | export interface TypeAssertionSetValue { 248 | ty: TypeAssertion; 249 | exported: boolean; 250 | resolved: boolean; 251 | } 252 | 253 | 254 | // TODO: preserve right hand side type name on output ts file. 255 | // rhs is: {kind: primitive, primitiveName: 'any', name: 'TypeAssertionMap', typeName: 'TypeAssertionMap'} 256 | export type TypeAssertionMap = Map; 257 | 258 | 259 | export interface SymbolResolverContext { 260 | nestLevel: number; 261 | symlinkStack: string[]; // For detecting recursive type 262 | } 263 | 264 | 265 | export interface CodegenContext { 266 | nestLevel: number; 267 | schema?: TypeAssertionMap; // To resolve 'symlink' assertion, the context need to have a schema instance. 268 | } 269 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tynder", 3 | "private": false, 4 | "version": "0.7.0", 5 | "description": "TypeScript friendly Data validator for JavaScript.", 6 | "keywords": [ 7 | "data validator", 8 | "validator", 9 | "TypeScript", 10 | "validation", 11 | "schema validator", 12 | "validation library", 13 | "JSON Schema", 14 | "schema", 15 | "type checker", 16 | "ajv", 17 | "joi", 18 | "protobuf", 19 | "GraphQL", 20 | "tynder" 21 | ], 22 | "main": "./bin/index.js", 23 | "module": "./modules/index.js", 24 | "modules.root": "./modules", 25 | "types": "./bin/index.d.ts", 26 | "typings": "./bin/index.d.ts", 27 | "engines": { 28 | "node": ">=10.0" 29 | }, 30 | "bin": { 31 | "tynder": "bin.cli/tynder.js" 32 | }, 33 | "dependencies": { 34 | "fruitsconfits": "^0.5.0", 35 | "liyad": "^0.6.0" 36 | }, 37 | "devDependencies": { 38 | "@babel/cli": "^7.12.7", 39 | "@babel/core": "^7.12.7", 40 | "@babel/preset-env": "^7.12.7", 41 | "@types/jasmine": "^3.6.2", 42 | "@types/node": "^14.14.9", 43 | "@typescript-eslint/eslint-plugin": "^4.8.1", 44 | "@typescript-eslint/parser": "^4.8.1", 45 | "ajv": "^6.12.6", 46 | "babel-loader": "^8.2.1", 47 | "cross-env": "^7.0.2", 48 | "eslint": "^7.14.0", 49 | "jasmine": "^3.6.3", 50 | "jasmine-spec-reporter": "^6.0.0", 51 | "mkdirp": "^1.0.4", 52 | "npm-run-all": "^4.1.5", 53 | "rimraf": "^3.0.2", 54 | "shx": "^0.3.3", 55 | "source-map-loader": "^1.1.2", 56 | "ts-loader": "^8.0.11", 57 | "tslint": "^6.1.3", 58 | "typescript": "^4.1.2", 59 | "webpack": "^5.6.0", 60 | "webpack-cli": "^4.2.0" 61 | }, 62 | "scripts": { 63 | "clean": "run-s clean:cjs clean:esm clean:cli clean:spec clean:dist", 64 | "clean:cjs": "rimraf ./bin", 65 | "clean:esm": "rimraf ./modules", 66 | "clean:cli": "rimraf ./bin.cli", 67 | "clean:spec": "rimraf ./bin.test", 68 | "clean:dist": "rimraf ./dist", 69 | "build": "run-s build:cjs build:esm build:esm:2 build:esm:3 build:spec build:cli:dev", 70 | "build:cjs": "tsc --project tsconfig.build.json --module commonjs --target es2015 --outDir bin --declaration --declarationDir ./bin", 71 | "build:esm": "tsc --project tsconfig.build.json --outDir modules --declaration --declarationDir ./modules", 72 | "build:esm:2": "node -e \"require('./build-scripts/copy-as-mjs').copyAsMjs('modules','modules')\"", 73 | "build:esm:3": "shx cp ./build-scripts/pkg.json ./modules/package.json", 74 | "build:cli": "run-s build:cli:prod", 75 | "build:cli:dev": "webpack-cli --mode=development --config webpack.config.js", 76 | "build:cli:prod": "cross-env NODE_ENV=production webpack-cli --mode=production --config webpack.config.js", 77 | "build:spec": "webpack-cli --mode=development --config webpack.spec.config.js", 78 | "build:spec2": "tsc -p tsconfig.spec.json --module commonjs --target es2015 --outDir bin.test", 79 | "build:dist": "run-s build:dist:prod", 80 | "build:dist:prod": "run-s build:dist:prod:1", 81 | "build:dist:dev": "run-s build:dist:dev:1", 82 | "build:dist:prod:1": "cross-env NODE_ENV=production webpack-cli --mode=production --config webpack.dist.config.js", 83 | "build:dist:dev:1": "webpack-cli --mode=development --config webpack.dist.config.js", 84 | "lint": "tslint ./src/**/*.ts -t verbose", 85 | "lint:es": "run-s lint:es:main lint:es:spec", 86 | "lint:es:main": "eslint -c ./.eslintrc.build.json --ext .js,.ts --ignore-pattern src/_spec/ ./src/", 87 | "lint:es:spec": "eslint -c ./.eslintrc.spec.json --ext .js,.ts ./src/_spec/", 88 | "test": "jasmine", 89 | "example": "run-s example:0 example:1 example:2 example:3 example:4 example:5 example:6 example:7 example:8", 90 | "example:0": "node ./bin.cli/tynder.js", 91 | "example:1": "node ./bin.cli/tynder.js compile --indir ./examples/schema/tynder --outdir ./examples/schema/_compiled", 92 | "example:2": "node ./bin.cli/tynder.js compile-as-ts --indir ./examples/schema/tynder --outdir ./examples/schema/_compiled", 93 | "example:3": "node ./bin.cli/tynder.js gen-ts --indir ./examples/schema/tynder --outdir ./examples/schema/ts", 94 | "example:4": "node ./bin.cli/tynder.js gen-json-schema --indir ./examples/schema/tynder --outdir ./examples/schema/json-schema", 95 | "example:5": "node ./bin.cli/tynder.js gen-json-schema-as-ts --indir ./examples/schema/tynder --outdir ./examples/schema/json-schema", 96 | "example:6": "node ./bin.cli/tynder.js gen-proto3 --indir ./examples/schema/tynder --outdir ./examples/schema/proto3", 97 | "example:7": "node ./bin.cli/tynder.js gen-graphql --indir ./examples/schema/tynder --outdir ./examples/schema/graphql", 98 | "example:8": "node ./bin.cli/tynder.js gen-csharp --indir ./examples/schema/tynder --outdir ./examples/schema/csharp", 99 | "prepublishOnly": "run-s clean build test lint" 100 | }, 101 | "repository": { 102 | "type": "git", 103 | "url": "https://github.com/shellyln/tynder.git" 104 | }, 105 | "author": "shellyln", 106 | "homepage": "https://shellyln.github.io/", 107 | "bugs": { 108 | "url": "https://github.com/shellyln/tynder/issues" 109 | }, 110 | "license": "ISC" 111 | } 112 | -------------------------------------------------------------------------------- /schema/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shellyln/tynder/d3961f737343dbfdba8c13515b9bf82ba67f62f1/schema/.gitkeep -------------------------------------------------------------------------------- /spec/helpers/reporter.js: -------------------------------------------------------------------------------- 1 | const SpecReporter = require('jasmine-spec-reporter').SpecReporter; 2 | 3 | jasmine.getEnv().clearReporters(); // remove default reporter logs 4 | jasmine.getEnv().addReporter(new SpecReporter({ // add jasmine-spec-reporter 5 | spec: { 6 | displayPending: true 7 | } 8 | })); 9 | -------------------------------------------------------------------------------- /spec/support/jasmine.json: -------------------------------------------------------------------------------- 1 | { 2 | "spec_dir": "", 3 | "spec_files": [ 4 | "bin.test/**/*.[sS]pec.js" 5 | ], 6 | "helpers": [ 7 | "spec/helpers/**/*.js" 8 | ], 9 | "stopSpecOnExpectationFailure": false, 10 | "random": false 11 | } 12 | -------------------------------------------------------------------------------- /src/_spec/fixes-5.spec.ts: -------------------------------------------------------------------------------- 1 | 2 | import { TypeAssertion, 3 | ValidationContext } from '../types'; 4 | import { validate, 5 | isType, 6 | assertType, 7 | getType } from '../validator'; 8 | import { pick, 9 | patch } from '../picker'; 10 | import { compile } from '../compiler'; 11 | import { generateTypeScriptCode } from '../codegen'; 12 | import { serialize, 13 | deserialize, 14 | deserializeFromObject } from '../serializer'; 15 | import { stereotypes as dateStereotypes } from '../stereotypes/date'; 16 | import { constraints as uniqueConstraints } from '../constraints/unique'; 17 | import primitivesSchema, 18 | { Schema as PrimitivesSchema } from '../../examples/schema/_compiled/primitives'; 19 | 20 | 21 | 22 | describe("fix-5", function() { 23 | it("unique-1", function() { 24 | const ty = getType(deserializeFromObject(primitivesSchema), PrimitivesSchema.NumberType); 25 | expect(ty).toEqual({ 26 | kind: 'primitive', 27 | primitiveName: 'number', 28 | typeName: 'NumberType', 29 | name: 'NumberType', 30 | }); 31 | }); 32 | 33 | it("empty-string-literal-1", function() { 34 | const schemas = [compile(` 35 | type A = ''; 36 | type B = 'a'; 37 | `), compile(` 38 | type A = ""; 39 | type B = "a"; 40 | `), compile(` 41 | type A = \`\`; 42 | type B = \`a\`; 43 | `)]; 44 | for (const schema of schemas) { 45 | { 46 | expect(Array.from(schema.keys())).toEqual([ 47 | 'A', 'B', 48 | ]); 49 | } 50 | for (const ty of [getType(deserialize(serialize(schema)), 'A'), getType(schema, 'A')]) { 51 | const rhs: TypeAssertion = { 52 | name: 'A', 53 | typeName: 'A', 54 | kind: 'primitive-value', 55 | value: '', 56 | }; 57 | expect(ty).toEqual(rhs); 58 | expect(validate(0, ty)).toEqual(null); 59 | expect(validate(1, ty)).toEqual(null); 60 | expect(validate(1.1, ty)).toEqual(null); 61 | expect(validate(BigInt(0), ty)).toEqual(null); 62 | expect(validate(BigInt(1), ty)).toEqual(null); 63 | expect(validate('', ty)).toEqual({value: ''}); 64 | expect(validate('a', ty)).toEqual(null); 65 | expect(validate(false, ty)).toEqual(null); 66 | expect(validate(true, ty)).toEqual(null); 67 | expect(validate(null, ty)).toEqual(null); 68 | expect(validate(void 0, ty)).toEqual(null); 69 | expect(validate({}, ty)).toEqual(null); 70 | expect(validate([], ty)).toEqual(null); 71 | expect(validate(3, ty)).toEqual(null); 72 | expect(validate(BigInt(7), ty)).toEqual(null); 73 | expect(validate('XB', ty)).toEqual(null); 74 | expect(validate(true, ty)).toEqual(null); 75 | } 76 | for (const ty of [getType(deserialize(serialize(schema)), 'B'), getType(schema, 'B')]) { 77 | const rhs: TypeAssertion = { 78 | name: 'B', 79 | typeName: 'B', 80 | kind: 'primitive-value', 81 | value: 'a', 82 | }; 83 | expect(ty).toEqual(rhs); 84 | expect(validate(0, ty)).toEqual(null); 85 | expect(validate(1, ty)).toEqual(null); 86 | expect(validate(1.1, ty)).toEqual(null); 87 | expect(validate(BigInt(0), ty)).toEqual(null); 88 | expect(validate(BigInt(1), ty)).toEqual(null); 89 | expect(validate('', ty)).toEqual(null); 90 | expect(validate('a', ty)).toEqual({value: 'a'}); 91 | expect(validate(false, ty)).toEqual(null); 92 | expect(validate(true, ty)).toEqual(null); 93 | expect(validate(null, ty)).toEqual(null); 94 | expect(validate(void 0, ty)).toEqual(null); 95 | expect(validate({}, ty)).toEqual(null); 96 | expect(validate([], ty)).toEqual(null); 97 | expect(validate(3, ty)).toEqual(null); 98 | expect(validate(BigInt(7), ty)).toEqual(null); 99 | expect(validate('XB', ty)).toEqual(null); 100 | expect(validate(true, ty)).toEqual(null); 101 | } 102 | } 103 | }); 104 | }); 105 | -------------------------------------------------------------------------------- /src/_spec/index.ts: -------------------------------------------------------------------------------- 1 | // tslint:disable:no-var-requires 2 | 3 | function importAll(r: any) { 4 | r.keys().forEach(r); 5 | } 6 | 7 | importAll((require as any).context('./', true, /\.spec\.(tsx|ts|jsx|js)$/)); 8 | -------------------------------------------------------------------------------- /src/_spec/operators.spec.ts: -------------------------------------------------------------------------------- 1 | 2 | import {} from '../types'; 3 | import { validate } from '../validator'; 4 | import { pick } from '../picker'; 5 | import { picked, 6 | partial, 7 | intersect, 8 | oneOf, 9 | subtract, 10 | primitive, 11 | primitiveValue, 12 | optional, 13 | repeated, 14 | sequenceOf, 15 | spread, 16 | objectType, 17 | derived, 18 | withMsg as $$, 19 | withMsgId as $ } from '../operators'; 20 | 21 | 22 | 23 | describe("operator", function() { 24 | it("operator-1", function() { 25 | const myType = oneOf( 26 | derived( 27 | objectType( 28 | ['foo', 10], 29 | ['qoo', $$({valueUnmatched: ''})( 30 | optional(20))], 31 | ['zoo', $('msgid-myType-zoo')( 32 | optional('aaa'))], 33 | ['www', sequenceOf(10, 20, spread(primitive('string'), {min: 3, max: 10}), 50)], 34 | ), 35 | objectType( 36 | ['bar', optional(primitive('string'))], 37 | ['bar2', primitive('string?')], 38 | ['bar3', repeated('string', {min: 3, max: 10})], 39 | ['bar4', primitive('integer')], 40 | ['bar5', primitive('integer?')], 41 | [[/^[a-z][0-9]$/], optional(primitive('string'))], 42 | ), 43 | intersect( 44 | objectType( 45 | ['baz', 10], 46 | ), 47 | objectType( 48 | ['baz', 10], 49 | ), 50 | ), 51 | subtract( 52 | objectType( 53 | ['baz', 10], 54 | ), 55 | objectType( 56 | ['baz', 10], 57 | ), 58 | ), 59 | ), 60 | 10, 20, 30, 61 | primitive('string'), 62 | primitiveValue(50), 63 | ); 64 | 65 | expect(validate({ 66 | foo: 10, 67 | www: [10, 20, 'a', 'b', 'c', 50], 68 | bar3: ['a', 'b', 'c'], 69 | bar4: 11, 70 | bar5: 13, 71 | baz: 10, 72 | }, myType)).toEqual({value: { 73 | foo: 10, 74 | www: [10, 20, 'a', 'b', 'c', 50], 75 | bar3: ['a', 'b', 'c'], 76 | bar4: 11, 77 | bar5: 13, 78 | baz: 10, 79 | }}); 80 | 81 | /* 82 | console.log(JSON.stringify(myType, null, 2)); 83 | 84 | const myType2 = picked(myType, 'foo', 'baz'); 85 | console.log(JSON.stringify(myType2, null, 2)); 86 | 87 | const myType3 = partial(myType); 88 | console.log(JSON.stringify(myType3, null, 2)); 89 | 90 | expect(validate({ 91 | aaaaaa: 'ppppppppppppp', 92 | foo: 10, 93 | www: [10, 20, 'a', 'b', 'c', 50], 94 | bar3: ['a', 'b', 'c'], 95 | }, myType)).toEqual({} as any); 96 | pick({}, myType2); 97 | merge({}, {}); 98 | */ 99 | 100 | expect(1).toEqual(1); 101 | }); 102 | }); 103 | -------------------------------------------------------------------------------- /src/cli.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 Shellyl_N and Authors 2 | // license: ISC 3 | // https://github.com/shellyln 4 | 5 | 6 | import { run } from './lib/cli'; 7 | 8 | 9 | 10 | run(process.argv); 11 | -------------------------------------------------------------------------------- /src/codegen.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 Shellyl_N and Authors 2 | // license: ISC 3 | // https://github.com/shellyln 4 | 5 | 6 | export * from './lib/codegen/typescript'; 7 | export * from './lib/codegen/json-schema'; 8 | export * from './lib/codegen/proto3'; 9 | export * from './lib/codegen/graphql'; 10 | export * from './lib/codegen/csharp'; 11 | -------------------------------------------------------------------------------- /src/constraints/unique.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 Shellyl_N and Authors 2 | // license: ISC 3 | // https://github.com/shellyln 4 | 5 | 6 | import { CustomConstraintInfo } from '../types'; 7 | import { dummyTargetObject, 8 | isUnsafeVarNames } from '../lib/protection'; 9 | 10 | 11 | 12 | type MapperFn = (data: any, fields: string[]) => any[]; 13 | const mapperErrMsg = 'Unsafe symbol name is appeared in unique constraint assertion:'; 14 | 15 | 16 | const normalMapper: MapperFn = (data: any, fields: string[]) => { 17 | const ret: any[] = []; 18 | if (0 < fields.length) { 19 | for (const field of fields) { 20 | if (isUnsafeVarNames(dummyTargetObject, field)) { 21 | throw new Error(`${mapperErrMsg} ${field}`); 22 | } 23 | ret.push(data[field]); 24 | } 25 | } else { 26 | ret.push(data); 27 | } 28 | return ret; 29 | }; 30 | 31 | 32 | const nonNullMapper: MapperFn = (data: any, fields: string[]) => { 33 | const ret: any[] = []; 34 | if (0 < fields.length) { 35 | for (const field of fields) { 36 | if (isUnsafeVarNames(dummyTargetObject, field)) { 37 | throw new Error(`${mapperErrMsg} ${field}`); 38 | } 39 | ret.push(data[field] ?? NaN); 40 | } 41 | } else { 42 | ret.push(data); 43 | } 44 | return ret; 45 | }; 46 | 47 | 48 | const checkerGen = (mapper: MapperFn) => { 49 | return ((data: any, args: any) => { 50 | const errMsg = `evaluateFormula: invalid parameter ${args}`; 51 | if (! Array.isArray(data)) { 52 | throw new Error(errMsg); 53 | } 54 | 55 | const fields: string[] = []; 56 | if (typeof args === 'string') { 57 | fields.push(args); 58 | } else if (Array.isArray(args)) { 59 | for (const z of args) { 60 | if (typeof z !== 'string') { 61 | throw new Error(errMsg); 62 | } 63 | } 64 | fields.push(...args); 65 | } 66 | 67 | const mapped = data.map(x => mapper(x, fields)); 68 | for (let i = 0; i < mapped.length; i++) { 69 | CMP: for (let j = 0; j < mapped.length; j++) { 70 | if (i === j) { 71 | continue; 72 | } 73 | const a = mapped[i]; 74 | const b = mapped[j]; 75 | for (let k = 0; k < a.length; k++) { // TODO: this is slow! more better checking 76 | if (a[k] !== b[k]) { 77 | continue CMP; 78 | } 79 | } 80 | return false; 81 | } 82 | } 83 | 84 | return true; 85 | }); 86 | }; 87 | 88 | 89 | export const constraints: Array<[string, CustomConstraintInfo]> = [ 90 | ['unique', { 91 | kinds: ['repeated', 'sequence'], 92 | check: checkerGen(normalMapper), 93 | }], 94 | ['unique-non-null', { 95 | kinds: ['repeated', 'sequence'], 96 | check: checkerGen(nonNullMapper), 97 | }], 98 | ]; 99 | -------------------------------------------------------------------------------- /src/index-rt.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 Shellyl_N and Authors 2 | // license: ISC 3 | // https://github.com/shellyln 4 | 5 | 6 | import { Stereotype, 7 | CustomConstraintInfo } from './types'; 8 | import { stereotypes as dateStereotypes } from './stereotypes/date'; 9 | import { constraints as uniqueConstraints } from './constraints/unique'; 10 | 11 | export * from './types'; 12 | export * from './operators'; 13 | export * from './validator'; 14 | export * from './picker'; 15 | 16 | export const stereotypes: Array<[string, Stereotype]> = [ 17 | ...dateStereotypes, 18 | ]; 19 | 20 | export const customConstraints: Array<[string, CustomConstraintInfo]> = [ 21 | ...uniqueConstraints, 22 | ]; 23 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 Shellyl_N and Authors 2 | // license: ISC 3 | // https://github.com/shellyln 4 | 5 | 6 | import { Stereotype, 7 | CustomConstraintInfo } from './types'; 8 | import { stereotypes as dateStereotypes } from './stereotypes/date'; 9 | import { constraints as uniqueConstraints } from './constraints/unique'; 10 | 11 | export * from './types'; 12 | export * from './compiler'; 13 | export * from './operators'; 14 | export * from './codegen'; 15 | export * from './serializer'; 16 | export * from './validator'; 17 | export * from './picker'; 18 | 19 | export const stereotypes: Array<[string, Stereotype]> = [ 20 | ...dateStereotypes, 21 | ]; 22 | 23 | export const customConstraints: Array<[string, CustomConstraintInfo]> = [ 24 | ...uniqueConstraints, 25 | ]; 26 | -------------------------------------------------------------------------------- /src/lib/cli.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 Shellyl_N and Authors 2 | // license: ISC 3 | // https://github.com/shellyln 4 | 5 | 6 | import * as fs from 'fs'; 7 | import * as path from 'path'; 8 | import { TypeAssertionMap } from '../types'; 9 | import { compile } from '../compiler'; 10 | import { serialize } from '../serializer'; 11 | import { generateTypeScriptCode, 12 | generateJsonSchema, 13 | generateProto3Code, 14 | generateGraphQlCode, 15 | generateCSharpCode } from '../codegen'; 16 | 17 | 18 | 19 | interface CliOptions { 20 | srcExt: string; 21 | destExt: string; 22 | } 23 | 24 | 25 | function compileTo(fn: (types: TypeAssertionMap) => string, srcDir: string, destDir: string, options: CliOptions) { 26 | if (! fs.existsSync(destDir)) { 27 | fs.mkdirSync(destDir, { recursive: true }); 28 | } 29 | 30 | if (fs.lstatSync(srcDir).isDirectory()) { 31 | const files = fs.readdirSync(srcDir); 32 | for (const entry of files) { 33 | const srcEntryPath = path.join(srcDir, entry); 34 | if (fs.lstatSync(srcEntryPath).isDirectory()) { 35 | compileTo(fn, srcEntryPath, path.join(destDir, entry), options); 36 | } else { 37 | if (entry.toLowerCase().endsWith(options.srcExt)) { 38 | const code = fs.readFileSync(srcEntryPath, {encoding: 'utf8'}); 39 | let trans = ''; 40 | try { 41 | const typeMap = compile(code); 42 | trans = fn(typeMap); 43 | } catch (error) { 44 | throw SyntaxError(`${srcEntryPath} - ${error.message}`); 45 | } 46 | fs.writeFileSync( 47 | path.join(destDir, entry.slice(0, -(options.srcExt.length)) + options.destExt), 48 | trans, 49 | {encoding: 'utf8'}, 50 | ); 51 | } 52 | } 53 | } 54 | } 55 | } 56 | 57 | 58 | function compileToTynderCompiled(srcDir: string, destDir: string, options: Partial) { 59 | const opts: CliOptions = Object.assign({}, { 60 | srcExt: '.tss', 61 | destExt: '.json', 62 | }, options || {}); 63 | 64 | return compileTo(serialize, srcDir, destDir, opts); 65 | } 66 | 67 | 68 | function compileToTynderCompiledAsTs(srcDir: string, destDir: string, options: Partial) { 69 | const opts: CliOptions = Object.assign({}, { 70 | srcExt: '.tss', 71 | destExt: '.ts', 72 | }, options || {}); 73 | 74 | return compileTo((types: TypeAssertionMap) => serialize(types, true), srcDir, destDir, opts); 75 | } 76 | 77 | 78 | function compileToTypeScript(srcDir: string, destDir: string, options: Partial) { 79 | const opts: CliOptions = Object.assign({}, { 80 | srcExt: '.tss', 81 | destExt: '.d.ts', 82 | }, options || {}); 83 | 84 | return compileTo(generateTypeScriptCode, srcDir, destDir, opts); 85 | } 86 | 87 | 88 | function compileToJsonSchema(srcDir: string, destDir: string, options: Partial) { 89 | const opts: CliOptions = Object.assign({}, { 90 | srcExt: '.tss', 91 | destExt: '.json', 92 | }, options || {}); 93 | 94 | return compileTo(generateJsonSchema, srcDir, destDir, opts); 95 | } 96 | 97 | 98 | function compileToJsonSchemaAsTs(srcDir: string, destDir: string, options: Partial) { 99 | const opts: CliOptions = Object.assign({}, { 100 | srcExt: '.tss', 101 | destExt: '.ts', 102 | }, options || {}); 103 | 104 | return compileTo((types: TypeAssertionMap) => generateJsonSchema(types, true), srcDir, destDir, opts); 105 | } 106 | 107 | 108 | function compileToProto3(srcDir: string, destDir: string, options: Partial) { 109 | const opts: CliOptions = Object.assign({}, { 110 | srcExt: '.tss', 111 | destExt: '.proto', 112 | }, options || {}); 113 | 114 | return compileTo(generateProto3Code, srcDir, destDir, opts); 115 | } 116 | 117 | 118 | function compileToGraphQl(srcDir: string, destDir: string, options: Partial) { 119 | const opts: CliOptions = Object.assign({}, { 120 | srcExt: '.tss', 121 | destExt: '.graphql', 122 | }, options || {}); 123 | 124 | return compileTo(generateGraphQlCode, srcDir, destDir, opts); 125 | } 126 | 127 | 128 | function compileToCSharp(srcDir: string, destDir: string, options: Partial) { 129 | const opts: CliOptions = Object.assign({}, { 130 | srcExt: '.tss', 131 | destExt: '.cs', 132 | }, options || {}); 133 | 134 | return compileTo(generateCSharpCode, srcDir, destDir, opts); 135 | } 136 | 137 | 138 | export function printHelp() { 139 | console.log( 140 | `tynder - TypeScript friendly Data validator for JavaScript. 141 | 142 | Usage: 143 | tynder subcommand options... 144 | 145 | Subcommands: 146 | help 147 | Show this help. 148 | compile 149 | Compile schema and output as JSON files. 150 | * default input file extension is *.tss 151 | * default output file extension is *.json 152 | compile-as-ts 153 | Compile schema and output as JavaScript|TypeScript files. 154 | * default input file extension is *.tss 155 | * default output file extension is *.ts 156 | Generated code is: 157 | const schema = {...}; 158 | export default schema; 159 | gen-ts 160 | Compile schema and generate TypeScript type definition files. 161 | * default input file extension is *.tss 162 | * default output file extension is *.d.ts 163 | gen-json-schema 164 | Compile schema and generate 'JSON Schema' files. 165 | * default input file extension is *.tss 166 | * default output file extension is *.json 167 | gen-json-schema-as-ts 168 | Compile schema and generate 'JSON Schema' 169 | as JavaScript|TypeScript files. 170 | * default input file extension is *.tss 171 | * default output file extension is *.ts 172 | Generated code is: 173 | const schema = {...}; 174 | export default schema; 175 | gen-csharp 176 | Compile schema and generate 'C#' type definition files. 177 | * default input file extension is *.tss 178 | * default output file extension is *.cs 179 | gen-proto3 180 | Compile schema and generate 'Protocol Buffers 3' type definition files. 181 | * default input file extension is *.tss 182 | * default output file extension is *.proto 183 | gen-graphql 184 | Compile schema and generate 'GraphQL' type definition files. 185 | * default input file extension is *.tss 186 | * default output file extension is *.graphql 187 | 188 | Options: 189 | --indir dirname 190 | Input directory 191 | --outdir dirname 192 | Output directory 193 | --inext fileExtensionName 194 | Input files' extension 195 | --outext fileExtensionName 196 | Output files' extension 197 | 198 | ` 199 | ); 200 | } 201 | 202 | 203 | export function run(argv: string[]) { 204 | let inDir: string | null = null; 205 | let outDir: string | null = null; 206 | let inExt: string | null = null; 207 | let outExt: string | null = null; 208 | 209 | if (argv.length < 3) { 210 | printHelp(); 211 | process.exit(0); 212 | } 213 | 214 | try { 215 | for (let i = 3; i < argv.length; i++) { 216 | switch (argv[i]) { 217 | case '--indir': 218 | i++; 219 | if (argv.length <= i) { 220 | throw new Error(`Parameters are too short: ${argv[i]}.`); 221 | } 222 | inDir = argv[i]; 223 | break; 224 | case '--outdir': 225 | i++; 226 | if (argv.length <= i) { 227 | throw new Error(`Parameters are too short: ${argv[i]}.`); 228 | } 229 | outDir = argv[i]; 230 | break; 231 | case '--inext': 232 | i++; 233 | if (argv.length <= i) { 234 | throw new Error(`Parameters are too short: ${argv[i]}.`); 235 | } 236 | inExt = argv[i]; 237 | break; 238 | case '--outext': 239 | i++; 240 | if (argv.length <= i) { 241 | throw new Error(`Parameters are too short: ${argv[i]}.`); 242 | } 243 | outExt = argv[i]; 244 | break; 245 | default: 246 | throw new Error(`Unknown option: ${argv[i]}.`); 247 | } 248 | } 249 | 250 | if (! inDir) { 251 | throw new Error(`"--indir" is not set.`); 252 | } 253 | if (! outDir) { 254 | throw new Error(`"--indir" is not set.`); 255 | } 256 | 257 | const options: Partial = {}; 258 | if (inExt) { 259 | options.srcExt = inExt; 260 | } 261 | if (outExt) { 262 | options.destExt = outExt; 263 | } 264 | 265 | switch (argv[2]) { 266 | case 'compile': 267 | compileToTynderCompiled(inDir, outDir, options); 268 | break; 269 | case 'compile-as-ts': 270 | compileToTynderCompiledAsTs(inDir, outDir, options); 271 | break; 272 | case 'gen-ts': 273 | compileToTypeScript(inDir, outDir, options); 274 | break; 275 | case 'gen-json-schema': 276 | compileToJsonSchema(inDir, outDir, options); 277 | break; 278 | case 'gen-json-schema-as-ts': 279 | compileToJsonSchemaAsTs(inDir, outDir, options); 280 | break; 281 | case 'gen-proto3': 282 | compileToProto3(inDir, outDir, options); 283 | break; 284 | case 'gen-graphql': 285 | compileToGraphQl(inDir, outDir, options); 286 | break; 287 | case 'gen-csharp': 288 | compileToCSharp(inDir, outDir, options); 289 | break; 290 | case 'help': 291 | printHelp(); 292 | process.exit(0); 293 | default: 294 | throw new Error(`Unknown subcommand: ${argv[0]}.`); 295 | } 296 | } catch (e) { 297 | console.error(e.message); 298 | printHelp(); 299 | process.exit(-1); 300 | } 301 | } 302 | -------------------------------------------------------------------------------- /src/lib/codegen/graphql.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 Shellyl_N and Authors 2 | // license: ISC 3 | // https://github.com/shellyln 4 | 5 | 6 | import { TypeAssertion, 7 | PrimitiveTypeAssertion, 8 | PrimitiveValueTypeAssertion, 9 | RepeatedAssertion, 10 | SpreadAssertion, 11 | SequenceAssertion, 12 | OneOfAssertion, 13 | EnumAssertion, 14 | ObjectAssertion, 15 | TypeAssertionMap, 16 | CodegenContext } from '../../types'; 17 | 18 | 19 | 20 | function formatTypeName(ty: TypeAssertion, ctx: CodegenContext, typeName: string) { 21 | if (typeName.includes('.')) { 22 | return generateGraphQlCodeInner(ty, false, ctx, false); 23 | } 24 | return typeName; 25 | } 26 | 27 | 28 | function formatGraphQlCodeDocComment(ty: TypeAssertion | string, nestLevel: number) { 29 | let code = ''; 30 | const indent = ' '.repeat(nestLevel); 31 | const docComment = typeof ty === 'string' ? ty : ty.docComment; 32 | if (docComment) { 33 | if (0 <= docComment.indexOf('\n')) { 34 | code += `${indent}/**\n${indent} ${ 35 | docComment 36 | .split('\n') 37 | .map(x => x.trimLeft()) 38 | .join(`\n${indent} `)}\n${indent} */\n`; 39 | } else { 40 | code += `${indent}/** ${docComment} */\n`; 41 | } 42 | } 43 | return code; 44 | } 45 | 46 | 47 | function isNullableOneOf(ty: OneOfAssertion, ctx: CodegenContext) { 48 | const filtered = ty.oneOf.filter(x => !( 49 | x.kind === 'primitive' && (x.primitiveName === 'null' || x.primitiveName === 'undefined') || 50 | x.kind === 'primitive-value' && (x.value === null || x.value === void 0))); 51 | return (filtered.length === 1 && ty.oneOf.length !== 1 ? filtered[0] : null) ; 52 | } 53 | 54 | 55 | function generateGraphQlCodePrimitive(ty: PrimitiveTypeAssertion, ctx: CodegenContext) { 56 | switch (ty.primitiveName) { 57 | case 'number': 58 | return 'Float'; 59 | case 'integer': 60 | return 'Int'; 61 | case 'bigint': 62 | return 'BigInt'; 63 | case 'string': 64 | return 'String'; 65 | case 'boolean': 66 | return 'Boolean'; 67 | case 'undefined': case 'null': default: 68 | return 'Any'; // TODO: Any is invalid type. 69 | } 70 | // TODO: Function, DateStr, DateTimeStr 71 | } 72 | 73 | 74 | function generateGraphQlCodePrimitiveValue(ty: PrimitiveValueTypeAssertion, ctx: CodegenContext) { 75 | if (ty.value === null) { 76 | return 'Any'; // TODO: Any is invalid type. 77 | } 78 | if (ty.value === void 0) { 79 | return 'Any'; // TODO: Any is invalid type. 80 | } 81 | switch (typeof ty.value) { 82 | case 'number': 83 | return 'Float'; 84 | case 'bigint': 85 | return 'BigInt'; 86 | case 'string': 87 | return 'String'; 88 | case 'boolean': 89 | return 'Boolean'; 90 | default: 91 | return 'Any'; // TODO: Any is invalid type. 92 | } 93 | } 94 | 95 | 96 | function generateGraphQlCodeRepeated(ty: RepeatedAssertion, ctx: CodegenContext) { 97 | return (`[${ty.repeated.typeName ? 98 | formatTypeName(ty.repeated, ctx, ty.repeated.typeName) : 99 | generateGraphQlCodeInner(ty.repeated, false, ctx, false)}${ 100 | (ty.repeated.kind === 'optional' || 101 | ty.repeated.kind === 'one-of' && isNullableOneOf(ty.repeated, ctx)) ? 102 | '' : '!'}]` 103 | ); 104 | } 105 | 106 | 107 | function generateGraphQlCodeSpread(ty: SpreadAssertion, ctx: CodegenContext) { 108 | return ''; 109 | } 110 | 111 | 112 | function generateGraphQlCodeSequence(ty: SequenceAssertion, ctx: CodegenContext) { 113 | return '[Any]'; // TODO: Any is invalid type. 114 | } 115 | 116 | 117 | function generateGraphQlCodeOneOf(ty: OneOfAssertion, ctx: CodegenContext, isUnion: boolean) { 118 | const z = isNullableOneOf(ty, ctx); 119 | if (z) { 120 | return z.typeName ? 121 | z.typeName : 122 | generateGraphQlCodeInner(z, false, ctx, false); 123 | } else { 124 | if (isUnion) { 125 | return `${ty.oneOf 126 | .map(x => x.typeName ? 127 | x.typeName : 128 | generateGraphQlCodeInner(x, false, ctx, false)).join(' | ')}`; 129 | } else { 130 | return 'Any'; // TODO: Any is invalid type. 131 | } 132 | } 133 | } 134 | 135 | 136 | function generateGraphQlCodeEnum(ty: EnumAssertion, ctx: CodegenContext) { 137 | return (ty.typeName ? 138 | formatTypeName(ty, ctx, ty.typeName) : 139 | 'Any' 140 | ); 141 | } 142 | 143 | 144 | function generateGraphQlCodeObject(ty: ObjectAssertion, isInterface: boolean, ctx: CodegenContext) { 145 | if (ty.members.length === 0) { 146 | return '{}'; 147 | } 148 | const sep = '\n'; 149 | 150 | const memberLines = 151 | ty.members 152 | .map(x => 153 | `${formatGraphQlCodeDocComment(x[3] || '', ctx.nestLevel + 1)}${ 154 | ' '.repeat(ctx.nestLevel + 1)}${ 155 | x[0]}: ${ 156 | x[1].typeName ? 157 | formatTypeName(x[1], {...ctx, nestLevel: ctx.nestLevel + 1}, x[1].typeName) : 158 | generateGraphQlCodeInner(x[1], false, {...ctx, nestLevel: ctx.nestLevel + 1}, false)}${ 159 | (x[1].kind === 'optional' || 160 | x[1].kind === 'one-of' && isNullableOneOf(x[1], ctx)) ? 161 | '' : '!'}`); 162 | 163 | return ( 164 | `{\n${memberLines.join(sep)}${sep}${' '.repeat(ctx.nestLevel)}}` 165 | ); 166 | } 167 | 168 | 169 | function generateGraphQlCodeInner(ty: TypeAssertion, isInterface: boolean, ctx: CodegenContext, isUnion: boolean): string { 170 | let ret = ''; 171 | 172 | switch (ty.kind) { 173 | case 'optional': 174 | return generateGraphQlCodeInner(ty.optional, isInterface, ctx, false); 175 | case 'one-of': 176 | return generateGraphQlCodeOneOf(ty, ctx, isUnion); // TODO: inline union is invalid. 177 | case 'spread': 178 | return generateGraphQlCodeSpread(ty, ctx); 179 | case 'sequence': 180 | return generateGraphQlCodeSequence(ty, ctx); 181 | case 'never': 182 | ret = 'Any'; // TODO: Any is invalid type. 183 | break; 184 | case 'any': 185 | ret = 'Any'; // TODO: Any is invalid type. 186 | break; 187 | case 'unknown': 188 | ret = 'Any'; // TODO: Any is invalid type. 189 | break; 190 | case 'primitive': 191 | ret = generateGraphQlCodePrimitive(ty, ctx); 192 | break; 193 | case 'primitive-value': 194 | ret = generateGraphQlCodePrimitiveValue(ty, ctx); 195 | break; 196 | case 'repeated': 197 | ret = generateGraphQlCodeRepeated(ty, ctx); 198 | break; 199 | case 'enum': 200 | ret = generateGraphQlCodeEnum(ty, ctx); 201 | break; 202 | case 'object': 203 | ret = generateGraphQlCodeObject(ty, isInterface, ctx); 204 | break; 205 | case 'symlink': 206 | ret = ty.symlinkTargetName; 207 | break; 208 | case 'operator': 209 | throw new Error(`Unexpected type assertion: ${(ty as any).kind}`); 210 | default: 211 | throw new Error(`Unknown type assertion: ${(ty as any).kind}`); 212 | } 213 | return ret + ''; 214 | } 215 | 216 | 217 | export function generateGraphQlCode(types: TypeAssertionMap): string { 218 | let code = `\nscalar Any\nunion BigInt = String | Int\n\n`; 219 | 220 | const ctx = {nestLevel: 0}; 221 | for (const ty of types.entries()) { 222 | if (ty[1].ty.noOutput) { 223 | code += `scalar ${ty[0]}\n\n`; 224 | continue; 225 | } 226 | code += formatGraphQlCodeDocComment(ty[1].ty, ctx.nestLevel); 227 | if (ty[1].ty.kind === 'object') { 228 | code += `type ${ty[0]} ${ 229 | generateGraphQlCodeInner(ty[1].ty, true, ctx, false)}\n\n`; 230 | } else if (ty[1].ty.kind === 'enum') { 231 | const indent0 = ' '.repeat(ctx.nestLevel); 232 | const indent1 = ' '.repeat(ctx.nestLevel + 1); 233 | code += `enum ${ty[0]} {\n${ 234 | ty[1].ty.values 235 | .map(x => `${ 236 | formatGraphQlCodeDocComment(x[2] || '', ctx.nestLevel + 1)}${ 237 | indent1}${x[0]}\n`) 238 | .join('')}${indent0}}\n\n`; 239 | } else if (ty[1].ty.kind === 'never' && ty[1].ty.passThruCodeBlock) { 240 | // nothing to do 241 | } else { 242 | code += `union ${ty[0]} = ${generateGraphQlCodeInner(ty[1].ty, false, ctx, true)}\n\n`; 243 | } 244 | } 245 | return code; 246 | } 247 | -------------------------------------------------------------------------------- /src/lib/errors.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 Shellyl_N and Authors 2 | // license: ISC 3 | // https://github.com/shellyln 4 | 5 | 6 | import { TypeAssertion, 7 | ValidationContext } from '../types'; 8 | 9 | 10 | 11 | export class ValidationError extends Error { 12 | public ty?: TypeAssertion; 13 | public ctx?: Partial; 14 | public constructor(message: string, ty?: TypeAssertion, ctx?: Partial) { 15 | super(message); 16 | this.ty = ty; 17 | this.ctx = ctx; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/lib/escape.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 Shellyl_N and Authors 2 | // license: ISC 3 | // https://github.com/shellyln 4 | 5 | 6 | export function escapeString(s: string) { 7 | return (s 8 | .replace(/\x08/g, '\\b') 9 | .replace(/\f/g, '\\f') 10 | .replace(/\n/g, '\\n') 11 | .replace(/\r/g, '\\r') 12 | .replace(/\t/g, '\\t') 13 | .replace(/\v/g, '\\v') 14 | .replace(/\\/g, '\\\\') 15 | .replace(/\'/g, '\\\'') 16 | .replace(/\"/g, '\\\"') 17 | .replace(/\`/g, '\\\`') 18 | ); 19 | } 20 | -------------------------------------------------------------------------------- /src/lib/protection.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 Shellyl_N and Authors 2 | // license: ISC 3 | // https://github.com/shellyln 4 | 5 | 6 | 7 | // tslint:disable: no-shadowed-variable 8 | // tslint:disable: function-constructor 9 | 10 | 11 | export const dummyTargetObject = {}; 12 | 13 | 14 | // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment 15 | export const { g: globalObj, o: objConstructor, f: funConstructor } = (() => { 16 | let globalObj = null; 17 | try { 18 | // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-implied-eval 19 | globalObj = Function('return this')(); 20 | } catch (e) { 21 | // Nothing to do. 22 | } 23 | if (! globalObj) { 24 | // Fall back (for CSP, etc...) 25 | if (typeof window === 'object' && window) { 26 | globalObj = window; 27 | } else if (typeof global === 'object' && global) { 28 | globalObj = global; 29 | } else if (typeof globalThis === 'object' && globalThis) { 30 | globalObj = globalThis; 31 | } else { 32 | globalObj = dummyTargetObject; 33 | } 34 | } 35 | 36 | // NOTE: ({}).constructor === Object 37 | // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment 38 | let objConstructor: ObjectConstructor = null as any; 39 | try { 40 | // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment 41 | objConstructor = (({}).constructor ?? Object) as any; 42 | } catch (e) { 43 | // Nothing to do. 44 | } 45 | if (! objConstructor) { 46 | // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment 47 | objConstructor = dummyTargetObject as any; 48 | } 49 | 50 | // NOTE: ({}).toString.constructor === Function 51 | // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment 52 | let funConstructor: FunctionConstructor = null as any; 53 | try { 54 | // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment 55 | funConstructor = (({}).toString.constructor ?? Function) as any; 56 | } catch (e) { 57 | // Nothing to do. 58 | } 59 | if (! funConstructor) { 60 | // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment 61 | funConstructor = dummyTargetObject as any; 62 | } 63 | 64 | return ({ 65 | // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment 66 | g: globalObj, o: objConstructor, f: funConstructor 67 | }); 68 | })(); 69 | 70 | 71 | // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types 72 | export function isUnsafeVarNames(target: any, varName: string) { 73 | if (target === globalObj || 74 | varName === '__proto__' || 75 | varName === '__defineGetter__' || varName === '__defineSetter__' || 76 | varName === '__lookupGetter__' || varName === '__lookupSetter__') { 77 | return true; 78 | } 79 | if (varName === 'prototype' || varName === 'constructor') { 80 | if (target === null || target === void 0 || typeof target === 'function') { 81 | return true; 82 | } 83 | } 84 | if (target === null || target === void 0 || target === objConstructor) { 85 | if (Object.prototype.hasOwnProperty.call(objConstructor, varName)) { 86 | return true; 87 | } 88 | } 89 | if (target === null || target === void 0 || target === funConstructor) { 90 | // checking 'call', 'arguments', 'caller', ... 91 | let con: any = funConstructor; 92 | while (con) { 93 | // eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access 94 | if (Object.prototype.hasOwnProperty.call(con, varName)) { 95 | return true; 96 | } 97 | // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access 98 | con = con.__proto__; 99 | } 100 | } 101 | if (typeof target === 'function') { 102 | // eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access 103 | if (! Object.prototype.hasOwnProperty.call(target, varName)) { 104 | // function's prototypes' members 105 | return true; 106 | } 107 | } 108 | return false; 109 | } 110 | -------------------------------------------------------------------------------- /src/lib/util.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 Shellyl_N and Authors 2 | // license: ISC 3 | // https://github.com/shellyln 4 | 5 | 6 | 7 | export const SymbolPattern = /^[A-Za-z_$][A-Za-z0-9_$]*$/; 8 | export const NumberPattern = /^([\+\-]?\d*\.?\d+(?:[Ee][\+\-]?\d+)?)$/; 9 | export const DatePattern = /^(\d{4}-[01]\d-[0-3]\d)$/; 10 | export const DateTimePattern = 11 | /^((?:(?:\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+)|(?:\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d)|(?:\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d))(?:[+-][0-2]\d:[0-5]\d|Z))$/; 12 | export const DateTimeNoTzPattern = 13 | /^((?:\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+)|(?:\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d)|(?:\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d))$/; 14 | 15 | 16 | export function nvl(v: any, alt: any) { 17 | return ( 18 | v !== null && v !== void 0 ? v : alt 19 | ); 20 | } 21 | 22 | 23 | export function nvl2(v: any, f: (x: any) => any, alt: any) { 24 | return ( 25 | v !== null && v !== void 0 ? f(v) : alt 26 | ); 27 | } 28 | -------------------------------------------------------------------------------- /src/picker.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 Shellyl_N and Authors 2 | // license: ISC 3 | // https://github.com/shellyln 4 | 5 | 6 | import { RecursivePartial, 7 | TypeAssertion, 8 | ValidationContext } from './types'; 9 | import { ValidationError } from './lib/errors'; 10 | import { isUnsafeVarNames } from './lib/protection'; 11 | import { validate } from './validator'; 12 | 13 | 14 | 15 | function pickMapper(value: any, ty: TypeAssertion) { 16 | switch (ty.kind) { 17 | case 'object': 18 | { 19 | const ret = Array.isArray(value) ? [] : {}; 20 | 21 | const dataMembers = new Set(); 22 | if (! Array.isArray(value)) { 23 | for (const m in value) { 24 | if (Object.prototype.hasOwnProperty.call(value, m)) { 25 | dataMembers.add(m); 26 | } 27 | } 28 | } 29 | 30 | for (const x of ty.members) { 31 | if (Object.hasOwnProperty.call(value, x[0])) { 32 | dataMembers.delete(x[0]); 33 | ret[x[0]] = value[x[0]]; 34 | } 35 | } 36 | if (ty.additionalProps && 0 < ty.additionalProps.length) { 37 | function* getAdditionalMembers() { 38 | for (const m of dataMembers.values()) { 39 | yield m; 40 | } 41 | if (Array.isArray(value)) { 42 | for (let i = 0; i < value.length; i++) { 43 | yield String(i); 44 | } 45 | } 46 | } 47 | for (const m of getAdditionalMembers()) { 48 | ret[m] = value[m]; 49 | } 50 | } 51 | return ret; 52 | } 53 | default: 54 | return value; 55 | } 56 | } 57 | 58 | 59 | export function pickRoot(data: T, ty: TypeAssertion, ctx: ValidationContext): T { 60 | switch (ty.kind) { 61 | case 'never': 62 | throw new ValidationError(`Type unmatched: ${(ty as any).kind}`, ty, ctx); 63 | case 'any': 64 | // FALL_THRU 65 | case 'unknown': 66 | // FALL_THRU 67 | case 'primitive': 68 | // FALL_THRU 69 | case 'primitive-value': 70 | // FALL_THRU 71 | case 'repeated': 72 | // FALL_THRU 73 | case 'sequence': 74 | // FALL_THRU 75 | case 'one-of': 76 | // FALL_THRU 77 | case 'enum': 78 | // FALL_THRU 79 | case 'object': 80 | { 81 | const r = validate(data, ty, ctx); 82 | if (r) { 83 | return r.value; 84 | } else { 85 | throw new ValidationError('Validation failed.', ty, ctx); 86 | } 87 | } 88 | case 'spread': case 'optional': case 'symlink': case 'operator': 89 | throw new ValidationError(`Unexpected type assertion: ${(ty as any).kind}`, ty, ctx); 90 | default: 91 | throw new ValidationError(`Unknown type assertion: ${(ty as any).kind}`, ty, ctx); 92 | } 93 | } 94 | 95 | 96 | export function pick(data: T, ty: TypeAssertion, ctx?: Partial): RecursivePartial { 97 | const ctx2: ValidationContext = { 98 | ...{errors: [], typeStack: []}, 99 | ...(ctx || {}), 100 | mapper: pickMapper, 101 | }; 102 | try { 103 | return pickRoot(data, ty, ctx2); 104 | } finally { 105 | if (ctx) { 106 | ctx.errors = ctx2.errors; 107 | } 108 | } 109 | } 110 | 111 | 112 | function merge(data: any, needle: any) { 113 | if (data === null || data === void 0) { 114 | return needle; 115 | } 116 | switch (typeof data) { 117 | case 'object': 118 | if (Array.isArray(data)) { 119 | return [...needle]; 120 | } else { 121 | const r: any = {...data}; 122 | for (const k in needle) { 123 | if (Object.prototype.hasOwnProperty.call(needle, k)) { 124 | if (isUnsafeVarNames(r, k)) { 125 | continue; 126 | } 127 | r[k] = merge(r[k], needle[k]); 128 | } 129 | } 130 | return r; 131 | } 132 | default: 133 | return needle; 134 | } 135 | } 136 | 137 | 138 | export function patch(data: T, needle: any, ty: TypeAssertion, ctx?: Partial): T { 139 | const ctx2: ValidationContext = { 140 | ...{errors: [], typeStack: []}, 141 | ...(ctx || {}), 142 | }; 143 | const validated = pick(needle, ty, ctx2); 144 | return merge(data, validated); 145 | } 146 | -------------------------------------------------------------------------------- /src/serializer.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 Shellyl_N and Authors 2 | // license: ISC 3 | // https://github.com/shellyln 4 | 5 | 6 | import { TypeAssertion, 7 | ObjectAssertion, 8 | SerializedSchemaInfo, 9 | TypeAssertionSetValue, 10 | TypeAssertionMap } from './types'; 11 | import { escapeString } from './lib/escape'; 12 | import { resolveSchema } from './lib/resolver'; 13 | 14 | 15 | 16 | export const TynderSchemaVersion = 'tynder/1.0'; 17 | 18 | 19 | function hasMetaInfo(ty: TypeAssertion) { 20 | let hasInfo = false; 21 | 22 | if (ty.messages) { 23 | hasInfo = true; 24 | } 25 | if (ty.message) { 26 | hasInfo = true; 27 | } 28 | if (ty.messageId) { 29 | hasInfo = true; 30 | } 31 | 32 | switch (ty.kind) { 33 | case 'repeated': 34 | if (typeof ty.min === 'number') { 35 | hasInfo = true; 36 | } 37 | if (typeof ty.max === 'number') { 38 | hasInfo = true; 39 | } 40 | break; 41 | case 'primitive': 42 | if (typeof ty.minValue === 'number') { 43 | hasInfo = true; 44 | } 45 | if (typeof ty.maxValue === 'number') { 46 | hasInfo = true; 47 | } 48 | if (typeof ty.greaterThanValue === 'number') { 49 | hasInfo = true; 50 | } 51 | if (typeof ty.lessThanValue === 'number') { 52 | hasInfo = true; 53 | } 54 | if (typeof ty.minLength === 'number') { 55 | hasInfo = true; 56 | } 57 | if (typeof ty.maxLength === 'number') { 58 | hasInfo = true; 59 | } 60 | if (ty.pattern) { 61 | hasInfo = true; 62 | } 63 | break; 64 | } 65 | 66 | return hasInfo; 67 | } 68 | 69 | 70 | function serializeInner(ty: TypeAssertion, nestLevel: number): TypeAssertion { 71 | if (0 < nestLevel && ty.typeName && !hasMetaInfo(ty)) { 72 | switch (ty.kind) { 73 | case 'optional': 74 | // nothing to do. 75 | break; 76 | default: 77 | return ({ 78 | ...{ 79 | kind: 'symlink', 80 | symlinkTargetName: ty.typeName as string, // NOTE: type inference failed if the switch statement is exists. 81 | typeName: ty.typeName, 82 | }, 83 | ...(ty.name ? {name: ty.name} : {}), 84 | ...(ty.docComment ? {docComment: ty.docComment} : {}), 85 | }); 86 | } 87 | } 88 | 89 | const ret: TypeAssertion = {...ty}; 90 | switch (ret.kind) { 91 | case 'never': case 'any': case 'unknown': case 'symlink': case 'operator': 92 | break; 93 | case 'primitive-value': 94 | if (typeof ret.value === 'bigint') { 95 | ret.value = String(ret.value); 96 | ret.primitiveName = 'bigint'; 97 | } 98 | break; 99 | case 'primitive': 100 | if (ret.pattern) { 101 | ret.pattern = `/${ret.pattern.source}/${ret.pattern.flags}` as any; 102 | } 103 | break; 104 | case 'repeated': 105 | ret.repeated = serializeInner(ret.repeated, nestLevel + 1); 106 | break; 107 | case 'spread': 108 | ret.spread = serializeInner(ret.spread, nestLevel + 1); 109 | break; 110 | case 'sequence': 111 | ret.sequence = ret.sequence.map(x => serializeInner(x, nestLevel + 1)); 112 | break; 113 | case 'one-of': 114 | ret.oneOf = ret.oneOf.map(x => serializeInner(x, nestLevel + 1)); 115 | break; 116 | case 'optional': 117 | ret.optional = serializeInner(ret.optional, nestLevel + 1); 118 | break; 119 | case 'enum': 120 | ret.values = ret.values.slice().map(x => x[2] === null || x[2] === void 0 ? x.slice(0, 2) : x) as any; 121 | break; 122 | case 'object': 123 | ret.members = ret.members 124 | .map(x => [x[0], serializeInner(x[1], nestLevel + 1), ...x.slice(2)]) as any; 125 | if (ret.additionalProps) { 126 | ret.additionalProps = ret.additionalProps 127 | .map(x => [x[0].map( 128 | p => typeof p === 'string' ? 129 | p : `/${p.source}/${p.flags}`), 130 | serializeInner(x[1], nestLevel + 1), ...x.slice(2)]) as any; 131 | } 132 | if (ret.baseTypes) { 133 | // NOTE: convert 'baseTypes' to 'symlink'. 134 | ret.baseTypes = ret.baseTypes.map(x => serializeInner(x, nestLevel + 1)) as ObjectAssertion[]; 135 | } 136 | break; 137 | default: 138 | throw new Error(`Unknown type assertion: ${(ret as any).kind}`); 139 | } 140 | 141 | return ret; 142 | } 143 | 144 | 145 | export function serializeToObject(schema: TypeAssertionMap): SerializedSchemaInfo { 146 | const ret: SerializedSchemaInfo = { 147 | version: TynderSchemaVersion, 148 | ns: {}, 149 | }; 150 | const current = {}; 151 | 152 | for (const ty of schema.entries()) { 153 | current[ty[0]] = serializeInner(ty[1].ty, 0); 154 | } 155 | 156 | ret.ns['.'] = current; 157 | 158 | return ret; 159 | } 160 | 161 | 162 | export function serialize(schema: TypeAssertionMap, asTs?: boolean): string { 163 | const ret = serializeToObject(schema); 164 | 165 | if (asTs) { 166 | return ( 167 | `\n// tslint:disable: object-literal-key-quotes\n` + 168 | `const schema = ${JSON.stringify(ret, null, 2)};\nexport default schema;\n\n` + 169 | `export const enum Schema {\n${Object.keys(ret.ns['.']).filter(x => { 170 | return (! 171 | (/^[0-9]/.test(x) || 172 | /[\u0000-\u001f\u007f]/.test(x) || 173 | /\s/.test(x) || 174 | /[@#$%^&+-=:;.,?!'"`/|{}()<>[\]\*\\]/.test(x)) 175 | ); 176 | }).map(x => ` ${x} = '${x}',\n`).join('')}` + 177 | `}\n// tslint:enable: object-literal-key-quotes\n` 178 | ); 179 | } else { 180 | return JSON.stringify(ret, null, 2); 181 | } 182 | } 183 | 184 | 185 | function deserializeRegExp(pat: string, errMsg: string) { 186 | const m = (/^\/(.*)\/([gimsuy]*)$/s).exec(pat); 187 | if (m) { 188 | return new RegExp(m[1], m[2]); 189 | } else { 190 | throw new Error(errMsg); 191 | } 192 | } 193 | 194 | 195 | function deserializeInner(ty: TypeAssertion) { 196 | const ret: TypeAssertion = {...ty}; 197 | switch (ret.kind) { 198 | case 'never': case 'any': case 'unknown': 199 | case 'enum': case 'symlink': case 'operator': 200 | // NOTE: 'symlink' and 'operator' will resolved by calling 'resolveSymbols()' in 'deserialize()'. 201 | break; 202 | case 'primitive-value': 203 | if (ret.primitiveName === 'bigint') { 204 | delete ret.primitiveName; 205 | ret.value = BigInt(ret.value); 206 | } 207 | break; 208 | case 'primitive': 209 | if (ret.pattern) { 210 | ret.pattern = deserializeRegExp( 211 | ret.pattern as any, 212 | `Unknown pattern match assertion: ${ret.pattern as any}`); 213 | } 214 | break; 215 | case 'repeated': 216 | ret.repeated = deserializeInner(ret.repeated); 217 | break; 218 | case 'spread': 219 | ret.spread = deserializeInner(ret.spread); 220 | break; 221 | case 'sequence': 222 | ret.sequence = ret.sequence.map(x => deserializeInner(x)); 223 | break; 224 | case 'one-of': 225 | ret.oneOf = ret.oneOf.map(x => deserializeInner(x)); 226 | break; 227 | case 'optional': 228 | ret.optional = deserializeInner(ret.optional); 229 | break; 230 | case 'object': 231 | ret.members = ret.members 232 | .map(x => [x[0], deserializeInner(x[1]), ...x.slice(2)]) as any; 233 | if (ret.additionalProps) { 234 | ret.additionalProps = ret.additionalProps 235 | .map(x => [x[0].map( 236 | p => String(p).startsWith('/') ? 237 | deserializeRegExp(p as any, `Unknown additional props: ${p}`) : p), 238 | deserializeInner(x[1]), ...x.slice(2)]) as any; 239 | } 240 | // NOTE: keep 'baseTypes' as 'symlink'. 241 | break; 242 | default: 243 | throw new Error(`Unknown type assertion: ${(ret as any).kind}`); 244 | } 245 | return ret; 246 | } 247 | 248 | 249 | export function deserializeFromObject(obj: any) { 250 | if (obj.version !== TynderSchemaVersion) { 251 | throw new Error(`Unknown schema version: ${obj.version}`); 252 | } 253 | 254 | const schema: TypeAssertionMap = new Map(); 255 | const current = obj.ns['.']; 256 | 257 | for (const k in current) { 258 | if (! Object.prototype.hasOwnProperty.call(current, k)) { 259 | continue; 260 | } 261 | schema.set(k, { 262 | ty: deserializeInner(current[k]), 263 | exported: false, 264 | isDeclare: false, 265 | resolved: false, 266 | }); 267 | } 268 | 269 | return resolveSchema(schema, {isDeserialization: true}); 270 | } 271 | 272 | 273 | export function deserialize(text: string) { 274 | const parsed = JSON.parse(text); 275 | return deserializeFromObject(parsed); 276 | } 277 | -------------------------------------------------------------------------------- /src/stereotypes/noop.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 Shellyl_N and Authors 2 | // license: ISC 3 | // https://github.com/shellyln 4 | 5 | 6 | import { Stereotype } from '../types'; 7 | 8 | 9 | 10 | export const noopStereotype: Stereotype = { 11 | tryParse: (value: any) => { 12 | return ({ value }); 13 | }, 14 | evaluateFormula: (valueOrFormula: any) => { 15 | return valueOrFormula; 16 | }, 17 | compare: (a: any, b: any) => { 18 | // NOTE: You should pass assertion value (schema value) into 'a'. 19 | const tyA = typeof a; 20 | const tyB = typeof b; 21 | if (tyA !== tyB) { 22 | return NaN; 23 | } 24 | switch (tyA) { 25 | case 'number': 26 | if (Number.isNaN(a) && Number.isNaN(b)) { 27 | return 0; 28 | } else { 29 | return a - b; 30 | } 31 | default: 32 | if (a === b) { 33 | return 0; 34 | } else if (a > b) { 35 | return 1; 36 | } else if (a < b) { 37 | return -1; 38 | } else { 39 | return NaN; 40 | } 41 | } 42 | }, 43 | doCast: false, 44 | }; 45 | -------------------------------------------------------------------------------- /src/types.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 Shellyl_N and Authors 2 | // license: ISC 3 | // https://github.com/shellyln 4 | 5 | 6 | import { PrimitiveTypeAssertionConstraints, 7 | RepeatedAssertionConstraints, 8 | ErrorMessages, 9 | TypeAssertion } from './types/tynder-schema-types'; 10 | 11 | export * from './types/tynder-schema-types'; 12 | 13 | 14 | 15 | // https://stackoverflow.com/questions/41980195/recursive-partialt-in-typescript 16 | export type RecursivePartial = { 17 | [P in keyof T]?: 18 | T[P] extends (infer U)[] ? RecursivePartial[] : 19 | T[P] extends object ? RecursivePartial : 20 | T[P]; 21 | }; 22 | 23 | 24 | export type TypeAssertionErrorMessageConstraints = 25 | Partial & 27 | {pattern: string}>; 28 | 29 | 30 | export interface TypeAssertionErrorMessage { 31 | code: string; 32 | message: string; 33 | dataPath: string; 34 | constraints: TypeAssertionErrorMessageConstraints; 35 | value?: any; // Only number, bigint, string, boolean, undefined, null 36 | } 37 | 38 | 39 | 40 | export interface Stereotype { 41 | tryParse: (value: any) => {value: any} | null; // for data 42 | evaluateFormula: (valueOrFormula: any) => any; // for PrimitiveValue and decorator comparison values 43 | compare: (a: any, b: any) => number; 44 | doCast: boolean; 45 | } 46 | 47 | 48 | export type CustomConstraint = (data: any, args: any) => boolean; 49 | 50 | 51 | export interface CustomConstraintInfo { 52 | kinds?: Array; // If undefined, all the kinds are allowed. 53 | check: CustomConstraint; 54 | } 55 | 56 | 57 | export interface ValidationContext { 58 | checkAll?: boolean; 59 | noAdditionalProps?: boolean; 60 | errorMessages?: ErrorMessages; 61 | 62 | // maxDepth: number; 63 | // depth: number; 64 | mapper?: (value: any, ty: TypeAssertion) => any; 65 | 66 | // === returned values === 67 | errors: TypeAssertionErrorMessage[]; 68 | 69 | // === internal use === 70 | typeStack: Array< // For error reporting (keyword substitutions) 71 | TypeAssertion | 72 | [TypeAssertion, 73 | number | string | undefined] // [1]: data index 74 | >; // NOTE: DO NOT reassign! 75 | // Push or pop items instead of reassign. 76 | 77 | recordTypeFieldValidated?: boolean; 78 | 79 | // === additional infos === 80 | schema?: TypeAssertionMap; // To resolve 'symlink' assertion, 81 | // the context need to have a schema instance. 82 | 83 | stereotypes?: Map; 84 | customConstraints?: Map; 85 | } 86 | 87 | 88 | export interface TypeAssertionSetValue { 89 | ty: TypeAssertion; 90 | exported: boolean; 91 | isDeclare: boolean; // for non-pass-thru declare statements 92 | resolved: boolean; 93 | } 94 | 95 | 96 | export type TypeAssertionMap = Map; 97 | 98 | 99 | export interface SymbolResolverOperators { 100 | [propName: string]: (...args: Array) => TypeAssertion; 101 | } 102 | 103 | 104 | export interface ResolveSymbolOptions { 105 | isDeserialization?: boolean; 106 | } 107 | 108 | export interface SymbolResolverContext extends ResolveSymbolOptions { 109 | nestLevel: number; 110 | symlinkStack: string[]; // For detecting recursive type 111 | operators?: SymbolResolverOperators; // TODO: Add it to resolve backref in type operator's operands 112 | } 113 | 114 | 115 | export interface CodegenContext { 116 | nestLevel: number; 117 | schema?: TypeAssertionMap; // To resolve 'symlink' assertion, the context need to have a schema instance. 118 | } 119 | -------------------------------------------------------------------------------- /src/types/json-schema-types.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 Shellyl_N and Authors 2 | // license: ISC 3 | // https://github.com/shellyln 4 | 5 | 6 | 7 | export interface JsonSchemaObjectPropertyAssertion { 8 | [propName: string]: JsonSchemaAssertion; 9 | } 10 | 11 | export interface JsonSchemaAssertionBase { 12 | description?: string; 13 | } 14 | 15 | export interface JsonSchemaNullAssertion extends JsonSchemaAssertionBase { 16 | type: 'null'; 17 | } 18 | 19 | export interface JsonSchemaAnyAssertion extends JsonSchemaAssertionBase { 20 | type: Array<'null' | 'number' | 'integer' | 'string' | 'boolean' | 'array' | 'object'>; 21 | } 22 | 23 | export type JsonSchemaUnknownAssertion = JsonSchemaAnyAssertion; 24 | 25 | interface JsonSchemaNumberAssertionConstraints { 26 | multipleOf?: number; 27 | maximum?: number; 28 | exclusiveMaximum?: number; 29 | minimum?: number; 30 | exclusiveMinimum?: number; 31 | } 32 | 33 | export interface JsonSchemaNumberAssertion extends JsonSchemaAssertionBase, JsonSchemaNumberAssertionConstraints { 34 | type: 'number' | 'integer' | Array<'number' | 'integer' | 'null'>; 35 | } 36 | 37 | export interface JsonSchemaBigIntAssertion extends JsonSchemaAssertionBase, JsonSchemaNumberAssertionConstraints { 38 | type: Array<'integer' | 'string'>; 39 | } 40 | 41 | export interface JsonSchemaNumberValueAssertion extends JsonSchemaAssertionBase { 42 | type: 'number'; 43 | enum: number[]; 44 | } 45 | 46 | export interface JsonSchemaBigIntNumberValueAssertion extends JsonSchemaAssertionBase { 47 | type: Array<'integer' | 'string'>; 48 | enum: Array; 49 | } 50 | 51 | export interface JsonSchemaStringAssertion extends JsonSchemaAssertionBase { 52 | type: 'string' | Array<'string' | 'null'>; 53 | maxLength?: number; 54 | minLength?: number; 55 | pattern?: string; 56 | } 57 | 58 | export interface JsonSchemaStringValueAssertion extends JsonSchemaAssertionBase { 59 | type: 'string'; 60 | enum: string[]; 61 | } 62 | 63 | export interface JsonSchemaBooleanAssertion extends JsonSchemaAssertionBase { 64 | type: 'boolean' | Array<'boolean' | 'null'>; 65 | } 66 | 67 | export interface JsonSchemaBooleanValueAssertion extends JsonSchemaAssertionBase { 68 | type: 'boolean'; 69 | enum: boolean[]; 70 | } 71 | 72 | export interface JsonSchemaArrayAssertion extends JsonSchemaAssertionBase { 73 | type: 'array' | Array<'array' | 'null'>; 74 | items: JsonSchemaAssertion; 75 | maxItems?: number; 76 | minItems?: number; 77 | uniqueItems?: boolean; 78 | maxContains?: number; 79 | minContains?: number; 80 | } 81 | 82 | export interface JsonSchemaObjectAssertion extends JsonSchemaAssertionBase { 83 | type: 'object' | Array<'object' | 'null'>; 84 | properties?: JsonSchemaObjectPropertyAssertion; 85 | maxProperties?: number; 86 | minProperties?: number; 87 | required?: string[]; 88 | dependentRequired?: {}; // TODO: 89 | additionalProperties?: boolean; 90 | patternProperties?: JsonSchemaObjectPropertyAssertion; 91 | anyOf?: JsonSchemaAssertion[]; 92 | oneOf?: JsonSchemaAssertion[]; 93 | allOf?: JsonSchemaAssertion[]; 94 | } 95 | 96 | export interface JsonSchemaTsEnumAssertion extends JsonSchemaAssertionBase { 97 | type: Array<'string' | 'number'>; 98 | enum: Array; 99 | } 100 | 101 | export interface JsonSchemaAnyOfAssertion extends JsonSchemaAssertionBase { 102 | anyOf: JsonSchemaAssertion[]; 103 | } 104 | 105 | export interface JsonSchemaOneOfAssertion extends JsonSchemaAssertionBase { 106 | oneOf: JsonSchemaAssertion[]; 107 | } 108 | 109 | export interface JsonSchemaAllOfAssertion extends JsonSchemaAssertionBase { 110 | allOf: JsonSchemaAssertion[]; 111 | } 112 | 113 | export interface JsonSchemaNotAssertion extends JsonSchemaAssertionBase { 114 | not: JsonSchemaAssertion; 115 | } 116 | 117 | export interface JsonSchemaRefAssertion extends JsonSchemaAssertionBase { 118 | $ref: string; 119 | } 120 | 121 | export type JsonSchemaAssertion = 122 | JsonSchemaNullAssertion | 123 | JsonSchemaAnyAssertion | 124 | JsonSchemaUnknownAssertion | 125 | JsonSchemaNumberAssertion | 126 | JsonSchemaBigIntAssertion | 127 | JsonSchemaNumberValueAssertion | 128 | JsonSchemaBigIntNumberValueAssertion | 129 | JsonSchemaStringAssertion | 130 | JsonSchemaStringValueAssertion | 131 | JsonSchemaBooleanAssertion | 132 | JsonSchemaBooleanValueAssertion | 133 | JsonSchemaArrayAssertion | 134 | JsonSchemaObjectAssertion | 135 | JsonSchemaTsEnumAssertion | 136 | JsonSchemaAnyOfAssertion | 137 | JsonSchemaOneOfAssertion | 138 | JsonSchemaAllOfAssertion | 139 | JsonSchemaRefAssertion; 140 | 141 | export interface JsonSchemaRootAssertion extends Partial { 142 | $schema?: string; 143 | $id?: string; 144 | title?: string; 145 | definitions?: JsonSchemaObjectPropertyAssertion; 146 | } 147 | -------------------------------------------------------------------------------- /src/types/tynder-schema-types.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 Shellyl_N and Authors 2 | // license: ISC 3 | // https://github.com/shellyln 4 | 5 | 6 | export type PrimitiveValueTypes = number | bigint | string | boolean | null | undefined; // TODO: Function 7 | export type PrimitiveValueTypeNames = 'number' | 'integer' | 'bigint' | 'string' | 'boolean' | 'null' | 'undefined'; // TODO: Function, DateStr, DateTimeStr 8 | export type OptionalPrimitiveValueTypeNames = 'number?' | 'integer?' | 'bigint?' | 'string?' | 'boolean?' | 'null?' | 'undefined?'; // TODO: Function?, DateStr?, DateTimeStr? 9 | export type PlaceholderTypeNames = 'never' | 'any' | 'unknown'; 10 | export type OptionalPlaceholderTypeNames = 'never?' | 'any?' | 'unknown?'; 11 | 12 | 13 | 14 | export enum ErrorTypes { 15 | InvalidDefinition = 1, 16 | Required, // (all) 17 | TypeUnmatched, // Never/Unknown/Primitive/Object 18 | AdditionalPropUnmatched, // Additional prop 19 | RepeatQtyUnmatched, // Repeated/Spread 20 | SequenceUnmatched, // Sequence 21 | ValueRangeUnmatched, // Primitive: minValue, maxValue, greaterThanValue, lessThanValue 22 | ValuePatternUnmatched, // Primitive: pattern 23 | ValueLengthUnmatched, // Primitive: minLength, maxLength 24 | ValueUnmatched, // PrimitiveValue 25 | } 26 | 27 | 28 | export type ErrorMessages = Partial<{ 29 | invalidDefinition: string, 30 | required: string, 31 | typeUnmatched: string, 32 | additionalPropUnmatched: string, 33 | repeatQtyUnmatched: string, 34 | sequenceUnmatched: string, 35 | valueRangeUnmatched: string, 36 | valuePatternUnmatched: string, 37 | valueLengthUnmatched: string, 38 | valueUnmatched: string, 39 | }>; 40 | 41 | 42 | export interface TypeAssertionBase { 43 | messageId?: string; 44 | message?: string; // Only one of 'message' or 'messages' can be set. 45 | messages?: ErrorMessages; // Only one of 'message' or 'messages' can be set. 46 | name?: string; // Member name or 'typeName' below. For error reporting and codegen. 47 | typeName?: string; // Named user defined 'type' or 'interface' name. For error reporting and codegen. 48 | originalTypeName?: string; // To keep right hand side type name of `type Y = X;`. 49 | stereotype?: string; // `stereotype` decorator value. 50 | customConstraints?: string[]; // 51 | customConstraintsArgs?: { // 52 | [constraintName: string]: any; 53 | }; 54 | forceCast?: boolean; // `forceCast` decorator value. 55 | isRecordTypeField?: boolean; // true if `recordType` decorator is set. 56 | meta?: any; // `meta` decorator value; user defined custom properties (meta informations). 57 | docComment?: string; // Doc comment. 58 | passThruCodeBlock?: string; // Store a pass-thru code block (e.g. import statement). use it with kind===never 59 | noOutput?: boolean; // If true, skip code generation. 60 | } 61 | 62 | 63 | export interface NeverTypeAssertion extends TypeAssertionBase { 64 | kind: 'never'; 65 | } 66 | 67 | 68 | export interface AnyTypeAssertion extends TypeAssertionBase { 69 | kind: 'any'; 70 | } 71 | 72 | 73 | export interface UnknownTypeAssertion extends TypeAssertionBase { 74 | kind: 'unknown'; 75 | } 76 | 77 | 78 | export interface PrimitiveTypeAssertionConstraints { 79 | minValue?: number | string | null; // TODO: bigint 80 | maxValue?: number | string | null; // TODO: bigint 81 | greaterThanValue?: number | string | null; 82 | lessThanValue?: number | string | null; 83 | minLength?: number | null; 84 | maxLength?: number | null; 85 | pattern?: RegExp | null; 86 | } 87 | 88 | 89 | export interface PrimitiveTypeAssertion extends TypeAssertionBase, PrimitiveTypeAssertionConstraints { 90 | kind: 'primitive'; 91 | primitiveName: PrimitiveValueTypeNames; 92 | } 93 | 94 | 95 | export interface PrimitiveValueTypeAssertion extends TypeAssertionBase { 96 | kind: 'primitive-value'; 97 | value: PrimitiveValueTypes; 98 | primitiveName?: 'bigint'; // for deserializer hinting 99 | } 100 | 101 | 102 | export interface RepeatedAssertionConstraints { 103 | min: number | null; 104 | max: number | null; 105 | } 106 | 107 | 108 | export interface RepeatedAssertion extends TypeAssertionBase, RepeatedAssertionConstraints { 109 | kind: 'repeated'; 110 | repeated: TypeAssertion; 111 | } 112 | 113 | 114 | export interface SpreadAssertion extends TypeAssertionBase, RepeatedAssertionConstraints { 115 | kind: 'spread'; 116 | spread: TypeAssertion; 117 | } 118 | 119 | 120 | export interface SequenceAssertion extends TypeAssertionBase { 121 | kind: 'sequence'; 122 | sequence: TypeAssertion[]; 123 | } 124 | 125 | 126 | export interface OneOfAssertion extends TypeAssertionBase { 127 | kind: 'one-of'; 128 | oneOf: TypeAssertion[]; 129 | } 130 | 131 | 132 | export interface OptionalAssertion extends TypeAssertionBase { 133 | kind: 'optional'; 134 | optional: TypeAssertion; 135 | } 136 | 137 | 138 | export interface EnumAssertion extends TypeAssertionBase { 139 | kind: 'enum'; 140 | values: Array<[ 141 | string, // enum key 142 | number | string, // enum value 143 | string?, // doc comment 144 | ]>; 145 | isConst?: boolean; // If true, it is `const enum` 146 | } 147 | 148 | 149 | export type ObjectAssertionMember = [ 150 | string, // name 151 | TypeAssertion, // type 152 | ] | [ 153 | string, // name 154 | TypeAssertion, // type 155 | boolean, // If true, defined by ancestor types 156 | ] | [ 157 | string, // name 158 | TypeAssertion, // type 159 | boolean, // If true, defined by ancestor types 160 | string, // doc comment 161 | ]; 162 | 163 | 164 | 165 | export type AdditionalPropsKey = Array<'string' | 'number' | RegExp>; 166 | 167 | 168 | export type AdditionalPropsMember = [ 169 | AdditionalPropsKey, // name 170 | TypeAssertion, // type 171 | ] | [ 172 | AdditionalPropsKey, // name 173 | TypeAssertion, // type 174 | boolean, // If true, defined by ancestor types 175 | ] | [ 176 | AdditionalPropsKey, // name 177 | TypeAssertion, // type 178 | boolean, // If true, defined by ancestor types 179 | string, // doc comment 180 | ]; 181 | 182 | 183 | export interface ObjectAssertion extends TypeAssertionBase { 184 | kind: 'object'; 185 | members: ObjectAssertionMember[]; 186 | additionalProps?: AdditionalPropsMember[]; 187 | baseTypes?: Array; 188 | } 189 | 190 | 191 | export interface AssertionSymlink extends TypeAssertionBase { 192 | kind: 'symlink'; 193 | symlinkTargetName: string; 194 | memberTree?: string[]; 195 | } 196 | 197 | 198 | // TODO: Add it to resolve backref in type operator's operands 199 | export interface AssertionOperator extends TypeAssertionBase { 200 | kind: 'operator'; 201 | operator: string; 202 | operands: Array; 203 | } 204 | 205 | 206 | export type TypeAssertion = 207 | NeverTypeAssertion | 208 | AnyTypeAssertion | 209 | UnknownTypeAssertion | 210 | PrimitiveTypeAssertion | 211 | PrimitiveValueTypeAssertion | 212 | RepeatedAssertion | 213 | SpreadAssertion | 214 | SequenceAssertion | 215 | OneOfAssertion | 216 | OptionalAssertion | 217 | EnumAssertion | 218 | ObjectAssertion | 219 | AssertionSymlink | 220 | AssertionOperator; 221 | 222 | 223 | export interface SerializedSchemaInfo { 224 | version: string; 225 | ns: { 226 | [namespaceName: string]: { 227 | [typeName: string]: TypeAssertion; 228 | } 229 | }; 230 | } 231 | -------------------------------------------------------------------------------- /tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "moduleResolution": "node", 4 | "module": "es2015", 5 | "target": "es2015", 6 | "lib": ["dom", "es6", "es2015", "es2016", "es2017", "scripthost"], 7 | "sourceMap": true, 8 | // "declaration": true, 9 | // "declarationDir": "./declarations", 10 | "alwaysStrict": true, 11 | "noEmitOnError": true, 12 | "noImplicitAny": true, 13 | "suppressImplicitAnyIndexErrors": true, 14 | "noImplicitReturns": true, 15 | "noImplicitThis": true, 16 | "strictNullChecks": true, 17 | // "noUnusedParameters": true, 18 | // "noUnusedLocals": true, 19 | // "noFallthroughCasesInSwitch": true, 20 | "experimentalDecorators": true, 21 | "emitDecoratorMetadata": true, 22 | "baseUrl": ".", 23 | "paths": { 24 | }, 25 | // "traceResolution": true, 26 | "types": ["node"], 27 | "jsx": "react", 28 | "jsxFactory": "FooBar.dom" 29 | }, 30 | "include": [ 31 | "src/**/*" 32 | ], 33 | "exclude": [ 34 | "bin", 35 | "modules", 36 | "node_modules", 37 | "spec", 38 | "src/_spec/**/*" 39 | ] 40 | } 41 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "moduleResolution": "node", 4 | "module": "es2015", 5 | "target": "es2015", 6 | "lib": ["dom", "es6", "es2015", "es2016", "es2017", "scripthost"], 7 | "sourceMap": true, 8 | // "declaration": true, 9 | // "declarationDir": "./declarations", 10 | "alwaysStrict": true, 11 | "noEmitOnError": true, 12 | "noImplicitAny": true, 13 | "suppressImplicitAnyIndexErrors": true, 14 | "noImplicitReturns": true, 15 | "noImplicitThis": true, 16 | "strictNullChecks": true, 17 | // "noUnusedParameters": true, 18 | // "noUnusedLocals": true, 19 | // "noFallthroughCasesInSwitch": true, 20 | "experimentalDecorators": true, 21 | "emitDecoratorMetadata": true, 22 | "baseUrl": ".", 23 | "paths": { 24 | }, 25 | // "traceResolution": true, 26 | "types": ["node", "jasmine"], 27 | "jsx": "react", 28 | "jsxFactory": "FooBar.dom" 29 | }, 30 | "include": [ 31 | "src/**/*" 32 | ], 33 | "exclude": [ 34 | "bin", 35 | "modules", 36 | "node_modules", 37 | "spec" 38 | ] 39 | } 40 | -------------------------------------------------------------------------------- /tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "moduleResolution": "node", 4 | "module": "commonjs", 5 | "target": "es2015", 6 | "lib": ["dom", "es6", "es2015", "es2016", "es2017", "scripthost"], 7 | "sourceMap": true, 8 | // "declaration": true, 9 | // "declarationDir": "./declarations", 10 | "alwaysStrict": true, 11 | "noEmitOnError": true, 12 | "noImplicitAny": true, 13 | "suppressImplicitAnyIndexErrors": true, 14 | "noImplicitReturns": true, 15 | "noImplicitThis": true, 16 | "strictNullChecks": true, 17 | // "noUnusedParameters": true, 18 | // "noUnusedLocals": true, 19 | // "noFallthroughCasesInSwitch": true, 20 | "experimentalDecorators": true, 21 | "emitDecoratorMetadata": true, 22 | "baseUrl": ".", 23 | "paths": { 24 | }, 25 | // "traceResolution": true, 26 | "types": ["node", "jasmine"], 27 | "jsx": "react", 28 | "jsxFactory": "FooBar.dom" 29 | }, 30 | "include": [ 31 | "src/_spec/index.ts", 32 | "src/_spec/**/*.spec.ts" 33 | ], 34 | "exclude": [ 35 | "bin", 36 | "modules", 37 | "node_modules", 38 | "spec", 39 | "src/**/*!(.spec.ts)" 40 | ] 41 | } 42 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["tslint:latest"], 3 | "rules": { 4 | "array-type": false, 5 | "no-reference": false, 6 | "no-consecutive-blank-lines": false, 7 | "no-console": false, 8 | "only-arrow-functions": false, 9 | "interface-name": false, 10 | "object-literal-sort-keys": false, 11 | "trailing-comma": false, 12 | "one-variable-per-declaration": false, 13 | "ordered-imports": false, 14 | "import-spacing": false, 15 | "max-classes-per-file": false, 16 | "max-line-length": [true, 240], 17 | "prefer-object-spread": false, 18 | "no-empty": false, 19 | "curly": false, 20 | "member-ordering": false, 21 | "no-bitwise": false, 22 | "one-line": false, 23 | "no-empty-interface": false, 24 | "arrow-parens": false, 25 | "prefer-conditional-expression": false, 26 | "prefer-for-of": false, 27 | "space-within-parens": false, 28 | "quotemark": false, 29 | "no-submodule-imports": false 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | var webpack = require('webpack'); 3 | 4 | 5 | 6 | module.exports = function (env) { 7 | return [{ 8 | target: "node", 9 | entry: { 10 | spec: [ 11 | path.resolve(__dirname, 'src/cli.ts') 12 | ] 13 | }, 14 | output: { 15 | library: 'tynder', 16 | 17 | libraryTarget: 'commonjs2', 18 | filename: 'tynder.js', 19 | path: path.resolve(__dirname, 'bin.cli'), 20 | devtoolModuleFilenameTemplate: '../[resource-path]', 21 | // devtoolModuleFilenameTemplate: void 0 22 | }, 23 | module: { 24 | rules: [{ 25 | test: /\.tsx?$/, 26 | use: [ 27 | 'babel-loader', 28 | 'ts-loader?' + JSON.stringify({ 29 | configFile: 'tsconfig.spec.json' 30 | }), 31 | ], 32 | exclude: /node_modules[\/\\](?!tynder|liyad|fruitsconfits).*$/ 33 | }, { 34 | test: /\.jsx?$/, 35 | use: ['babel-loader'], 36 | exclude: /node_modules[\/\\](?!tynder|liyad|fruitsconfits).*$/ 37 | }, { 38 | test: /\.m?js/, 39 | resolve: { 40 | fullySpecified: false, 41 | }, 42 | }, { 43 | enforce: 'pre', 44 | test: /\.[tj]sx?$/, 45 | use: { 46 | loader: 'source-map-loader', 47 | options: { 48 | } 49 | }, 50 | exclude: /node_modules[\/\\](?!tynder|liyad|fruitsconfits).*$/ 51 | }] 52 | }, 53 | plugins: [ 54 | new webpack.BannerPlugin({ banner: "#!/usr/bin/env node", raw: true }) 55 | ], 56 | resolve: { 57 | extensions: ['.tsx', '.ts', '.jsx', '.js'] 58 | }, 59 | devtool: 'source-map' 60 | }, 61 | 62 | ]} 63 | -------------------------------------------------------------------------------- /webpack.dist.config.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | var webpack = require('webpack'); 3 | 4 | 5 | const babelOptions = { 6 | loader: 'babel-loader', 7 | options: { 8 | 'sourceMaps': true, 9 | 'presets': [ 10 | ['@babel/preset-env', { 11 | 'targets': { 12 | 'chrome': 68 13 | } 14 | }] 15 | ], 16 | 'ignore': [], 17 | } 18 | }; 19 | 20 | 21 | module.exports = function (env) { 22 | return [{ 23 | target: "web", 24 | entry: { 25 | tynder: [ 26 | path.resolve(__dirname, 'src/index.ts') 27 | ], 28 | "tynder-rt": [ 29 | path.resolve(__dirname, 'src/index-rt.ts') 30 | ] 31 | }, 32 | node: { 33 | // fs: false, 34 | // console: false, 35 | // process: false, 36 | global: false, 37 | __filename: false, 38 | __dirname: false, 39 | // Buffer: false, 40 | // setImmediate: false, 41 | }, 42 | output: { 43 | library: 'tynder', 44 | 45 | libraryTarget: 'umd', 46 | globalObject: 'this', 47 | filename: process.env.NODE_ENV === 'production' ? '[name].min.js' : '[name].js', 48 | path: path.resolve(__dirname, 'dist'), 49 | devtoolModuleFilenameTemplate: void 0 50 | }, 51 | module: { 52 | rules: [{ 53 | test: /\.tsx?$/, 54 | use: [ 55 | babelOptions, 56 | 'ts-loader?' + JSON.stringify({ 57 | configFile: 'tsconfig.json' 58 | }), 59 | ], 60 | exclude: /node_modules[\/\\](?!tynder|liyad|fruitsconfits).*$/ 61 | }, { 62 | test: /\.jsx?$/, 63 | use: [ 64 | babelOptions, 65 | ], 66 | exclude: /node_modules[\/\\](?!tynder|liyad|fruitsconfits).*$/ 67 | }, { 68 | test: /\.m?js/, 69 | resolve: { 70 | fullySpecified: false, 71 | }, 72 | }, { 73 | enforce: 'pre', 74 | test: /\.[tj]sx?$/, 75 | use: { 76 | loader: 'source-map-loader', 77 | options: { 78 | } 79 | }, 80 | exclude: /node_modules[\/\\](?!tynder|liyad|fruitsconfits).*$/ 81 | }] 82 | }, 83 | plugins: [], 84 | resolve: { 85 | extensions: ['.tsx', '.ts', '.jsx', '.js'] 86 | }, 87 | devtool: 'source-map' 88 | }, 89 | 90 | ]} 91 | -------------------------------------------------------------------------------- /webpack.spec.config.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | var webpack = require('webpack'); 3 | 4 | 5 | 6 | module.exports = function (env) { 7 | return [{ 8 | target: "node", 9 | entry: { 10 | spec: [ 11 | path.resolve(__dirname, 'src/_spec/index.ts') 12 | ] 13 | }, 14 | output: { 15 | library: 'JasmineSpecsRunnerApp', 16 | 17 | libraryTarget: 'commonjs2', 18 | filename: 'index.spec.js', 19 | path: path.resolve(__dirname, 'bin.test'), 20 | devtoolModuleFilenameTemplate: '../[resource-path]', 21 | // devtoolModuleFilenameTemplate: void 0 22 | }, 23 | module: { 24 | rules: [{ 25 | test: /\.tsx?$/, 26 | use: [ 27 | 'babel-loader', 28 | 'ts-loader?' + JSON.stringify({ 29 | configFile: 'tsconfig.spec.json' 30 | }), 31 | ], 32 | exclude: /node_modules[\/\\](?!tynder|liyad|fruitsconfits).*$/ 33 | }, { 34 | test: /\.jsx?$/, 35 | use: ['babel-loader'], 36 | exclude: /node_modules[\/\\](?!tynder|liyad|fruitsconfits).*$/ 37 | }, { 38 | test: /\.m?js/, 39 | resolve: { 40 | fullySpecified: false, 41 | }, 42 | }, { 43 | enforce: 'pre', 44 | test: /\.[tj]sx?$/, 45 | use: { 46 | loader: 'source-map-loader', 47 | options: { 48 | } 49 | }, 50 | exclude: /node_modules[\/\\](?!tynder|liyad|fruitsconfits).*$/ 51 | }] 52 | }, 53 | plugins: [], 54 | resolve: { 55 | extensions: ['.tsx', '.ts', '.jsx', '.js'] 56 | }, 57 | devtool: 'source-map' 58 | }, 59 | 60 | ]} 61 | --------------------------------------------------------------------------------