├── .npmignore ├── docs ├── CNAME ├── content │ ├── docs │ │ ├── _index.md │ │ ├── core │ │ │ ├── _index.md │ │ │ ├── buildable-resource.md │ │ │ └── build-configuration.md │ │ ├── decorators │ │ │ ├── _index.md │ │ │ ├── resource.md │ │ │ ├── ignore.md │ │ │ ├── alias.md │ │ │ ├── transform.md │ │ │ └── list-of.md │ │ └── begin.md │ ├── search.md │ └── contributors.md ├── static │ ├── tapi-logo.png │ └── fonts │ │ ├── Roboto-Black.ttf │ │ ├── Roboto-Regular.ttf │ │ └── FiraCode-Regular.ttf ├── archetypes │ └── default.md ├── .gitignore ├── README.md ├── assets │ └── css │ │ └── extended │ │ └── custom.css ├── config.yml └── layouts │ └── partials │ └── footer.html ├── .github ├── FUNDING.yml ├── workflows │ ├── testing-only.yml │ ├── release-npm.yml │ └── update-site.yml └── ISSUE_TEMPLATE │ └── bug_report.md ├── src ├── types │ ├── ObjectReference.d.ts │ └── Delegates.d.ts ├── helpers │ ├── functions.ts │ ├── Describer.ts │ └── Dot.ts ├── contracts │ ├── ResourceFactory.ts │ ├── JSONConvertible.ts │ └── BuildableResource.ts ├── index.ts ├── functions.ts ├── extensions.ts ├── decorators.ts └── core │ ├── BuildConfiguration.ts │ └── Builder.ts ├── .gitmodules ├── .eslintignore ├── tsconfig.build.json ├── tsconfig.json ├── tests ├── Describer.test.ts ├── Promises.test.ts ├── Decorators.test.ts ├── ComplexBuilds.test.ts ├── Builder.test.ts └── Inheritance.test.ts ├── jest.config.cjs ├── scripts └── thankyou.cjs ├── .eslintrc.json ├── CONTRIBUTING.md ├── package.json ├── rollup.config.cjs ├── README.md ├── .gitignore ├── CODE_OF_CONDUCT.md └── LICENSE /.npmignore: -------------------------------------------------------------------------------- 1 | !dist/ -------------------------------------------------------------------------------- /docs/CNAME: -------------------------------------------------------------------------------- 1 | tapi.js.org 2 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | #💖 2 | 3 | ko_fi: sinisimattia 4 | -------------------------------------------------------------------------------- /docs/content/docs/_index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Documentation 3 | ShowBreadCrumbs: false 4 | --- -------------------------------------------------------------------------------- /docs/static/tapi-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sinisimattia/tapi/HEAD/docs/static/tapi-logo.png -------------------------------------------------------------------------------- /docs/archetypes/default.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "{{ replace .Name "-" " " | title }}" 3 | draft: true 4 | --- 5 | 6 | -------------------------------------------------------------------------------- /src/types/ObjectReference.d.ts: -------------------------------------------------------------------------------- 1 | declare type ObjectReference = { 2 | path: string; 3 | value: any; 4 | } 5 | -------------------------------------------------------------------------------- /docs/content/search.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Search" 3 | layout: "search" 4 | placeholder: "type anything to search" 5 | --- -------------------------------------------------------------------------------- /docs/static/fonts/Roboto-Black.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sinisimattia/tapi/HEAD/docs/static/fonts/Roboto-Black.ttf -------------------------------------------------------------------------------- /docs/static/fonts/Roboto-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sinisimattia/tapi/HEAD/docs/static/fonts/Roboto-Regular.ttf -------------------------------------------------------------------------------- /docs/static/fonts/FiraCode-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sinisimattia/tapi/HEAD/docs/static/fonts/FiraCode-Regular.ttf -------------------------------------------------------------------------------- /docs/content/docs/core/_index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Core ⚙️ 3 | summary: Explore the main components of tapi... a bit more in depth. 4 | --- -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "docs/themes/PaperMod"] 2 | path = docs/themes/PaperMod 3 | url = https://github.com/adityatelange/hugo-PaperMod.git 4 | -------------------------------------------------------------------------------- /src/types/Delegates.d.ts: -------------------------------------------------------------------------------- 1 | declare type Action = (value: any) => any; 2 | 3 | declare type ValueTransformer = { 4 | in: Action, 5 | out: Action 6 | } 7 | -------------------------------------------------------------------------------- /docs/content/docs/decorators/_index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Decorators ✨ 3 | summary: A complete list of all available decorators to customize how your object gets built. 4 | --- -------------------------------------------------------------------------------- /docs/content/contributors.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Contributors 💝 3 | ShowBreadCrumbs: false 4 | --- 5 | 6 | ![Contributors Profile Wall](https://contrib.rocks/image?repo=sinisimattia/tapi) -------------------------------------------------------------------------------- /src/helpers/functions.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @param source Any source 3 | * @returns An exact copy of the given source 4 | */ 5 | import cloneDeep from 'deepclone' 6 | export function deepCopy(source: T): T { 7 | return cloneDeep(source) as T 8 | }; 9 | -------------------------------------------------------------------------------- /docs/.gitignore: -------------------------------------------------------------------------------- 1 | # Generated files by hugo 2 | /public/ 3 | /resources/_gen/ 4 | /assets/jsconfig.json 5 | hugo_stats.json 6 | 7 | # Executable may be added to repository 8 | hugo.exe 9 | hugo.darwin 10 | hugo.linux 11 | 12 | # Temporary lock file while building 13 | /.hugo_build.lock -------------------------------------------------------------------------------- /src/contracts/ResourceFactory.ts: -------------------------------------------------------------------------------- 1 | import BuildableResource from "@/contracts/BuildableResource" 2 | 3 | /** 4 | * A factory used to construct from generic types derived from {@link BuildableResource}. 5 | */ 6 | export default interface ResourceFactory { 7 | new(): Type; 8 | };;;;;;;;;;; 9 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | # don't ever lint node_modules 2 | node_modules 3 | # don't lint build output (make sure it's set to your correct build folder name) 4 | dist 5 | # don't lint nyc coverage output 6 | coverage 7 | # Ignore config files 8 | .eslintrc.js 9 | *config.js* 10 | *config.cjs* 11 | 12 | # Ignore extra directories 13 | scripts -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | // Core 2 | import Builder from '@/core/Builder' 3 | import BuildConfiguration from '@/core/BuildConfiguration' 4 | import BuildableResource from "@/contracts/BuildableResource" 5 | 6 | // Decorators 7 | import * as Properties from '@/decorators' 8 | 9 | export { 10 | Builder, 11 | BuildConfiguration, 12 | BuildableResource, 13 | Properties, 14 | } 15 | -------------------------------------------------------------------------------- /tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "diagnostics": true, 5 | "declaration": true, 6 | "declarationMap": true, 7 | "sourceMap": true, 8 | "rootDir": "src", 9 | "removeComments": true, 10 | "skipLibCheck": false, 11 | }, 12 | "include": [ 13 | "src" 14 | ], 15 | "exclude": [ 16 | "node_modules", 17 | "tests" 18 | ] 19 | } -------------------------------------------------------------------------------- /docs/content/docs/decorators/resource.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "@Resource" 3 | --- 4 | 5 | This is what checks if your class satisfies the requirements needed to be buildable. It is completely optional but highly recommended. 6 | 7 | ```typescript 8 | import { BuildableResource, Properties } from "tapi.js"; 9 | 10 | @Properties.Resource 11 | class TestClass extends BuildableResource { 12 | // ... 13 | } 14 | ``` 15 | 16 | -------------------------------------------------------------------------------- /docs/content/docs/decorators/ignore.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "@Ignore" 3 | --- 4 | 5 | Not all of your class properties need to be mappable, some can be ignored. This is where this decorator comes in handy! 6 | 7 | ```typescript 8 | import { BuildableResource, Properties } from "tapi.js"; 9 | 10 | @Properties.Resource 11 | class TestClass extends BuildableResource { 12 | @Properties.Ignore 13 | public thingToBeIgnored: string; 14 | } 15 | ``` 16 | 17 | -------------------------------------------------------------------------------- /src/functions.ts: -------------------------------------------------------------------------------- 1 | import BuildConfiguration from "@/core/BuildConfiguration" 2 | import Builder from "@/core/Builder" 3 | import ResourceFactory from "@/contracts/ResourceFactory" 4 | import BuildableResource from "@/contracts/BuildableResource" 5 | 6 | export function build>( 7 | json: any, 8 | ctor: ResourceFactory, 9 | buildConfig: BuildConfiguration 10 | ): T { 11 | return new Builder(ctor, buildConfig).fromJSON(json) 12 | } 13 | -------------------------------------------------------------------------------- /docs/content/docs/decorators/alias.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "@Alias" 3 | --- 4 | 5 | Sometimes the front-end and back-end don't agree on what a property should be called, that's no problem thanks to this decorator! 6 | 7 | ```typescript 8 | import { BuildableResource, Properties } from "tapi.js"; 9 | 10 | @Properties.Resource 11 | class TestClass extends BuildableResource { 12 | @Properties.Alias("incomingPropertyName") 13 | public myProperty: string; 14 | } 15 | ``` 16 | 17 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "esModuleInterop": true, 4 | "strict": true, 5 | "allowJs": true, 6 | "noImplicitAny": false, 7 | "experimentalDecorators": true, 8 | "forceConsistentCasingInFileNames": true, 9 | "baseUrl": "src", 10 | "paths": { 11 | "@/*": [ 12 | "./*" 13 | ] 14 | } 15 | }, 16 | "include": [ 17 | "src", 18 | "tests" 19 | ], 20 | "exclude": [ 21 | "node_modules", 22 | "dist" 23 | ] 24 | } -------------------------------------------------------------------------------- /src/helpers/Describer.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * An object describer helper. 3 | */ 4 | export default class Describer { 5 | /** 6 | * Retrieve a list of all params of a class instance. 7 | * 8 | * @param instance The class to analyze. 9 | * @returns A list of the class's properties. 10 | */ 11 | public static getParameters(instance: any): Array { 12 | return Object.getOwnPropertyNames(instance).filter(propertyName => !(instance[propertyName] instanceof Function)) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /tests/Describer.test.ts: -------------------------------------------------------------------------------- 1 | import Describer from '@/helpers/Describer'; 2 | 3 | class TestClass { 4 | public param1 = "value1"; 5 | public param2 = "value2"; 6 | public param3 = "value3"; 7 | 8 | public doSomething() {} 9 | } 10 | 11 | describe('Object describer', () => { 12 | test('properly gets property names', () => { 13 | const properties = Describer.getParameters(new TestClass()); 14 | 15 | expect(properties.length).toBe(3); 16 | expect(properties.includes('param1')).toBe(true); 17 | }) 18 | }) -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # tapi's website 2 | 3 | This folder is used to contain tapi's site, built with [Hugo](https://gohugo.io) (static site generator). 4 | 5 | To get started working on it you must: 6 | - [Install Hugo](https://gohugo.io/installation/) 7 | - Open a new terminal session 8 | - Change directory to `docs` (if you haven't already): `cd docs` 9 | - Update/Fetch git submodules: `git submodule update` 10 | - Run Hugo's development server: `hugo server` 11 | - Follow the link in the terminal to see the locally hosted website -------------------------------------------------------------------------------- /src/contracts/JSONConvertible.ts: -------------------------------------------------------------------------------- 1 | export default interface JSONConvertible { 2 | /** 3 | * Convert from a generic JSON object to a typed one. 4 | * 5 | * @param source The object to transform. 6 | * @returns A typed object populated according to the rules you defined while configuring. 7 | */ 8 | fromJSON(source: any); 9 | 10 | /** 11 | * Convert the typed object to a JSON one by applying all the rules backwards. 12 | * @param args Additional arguments 13 | */ 14 | toJSON(...args: any[]): any; 15 | } 16 | -------------------------------------------------------------------------------- /docs/content/docs/decorators/transform.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "@Transform" 3 | --- 4 | 5 | Sometimes, assigning the value as it arrives is not enough. What if we want to apply a transformation before it gets assigned? Simple! 6 | 7 | ```typescript 8 | import { BuildableResource, Properties } from "tapi.js"; 9 | 10 | @Properties.Resource 11 | class TestClass extends BuildableResource { 12 | @Properties.Transform(incomingValue => { 13 | return incomingValue.toUpperCase(); 14 | }) 15 | public needsToBeUppercase: string; 16 | } 17 | ``` 18 | -------------------------------------------------------------------------------- /jest.config.cjs: -------------------------------------------------------------------------------- 1 | const { pathsToModuleNameMapper } = require('ts-jest'); 2 | const { compilerOptions } = require('./tsconfig.json'); 3 | 4 | module.exports = { 5 | // Core 6 | roots: [ 7 | '', 8 | ], 9 | preset: 'ts-jest', 10 | testEnvironment: 'node', 11 | modulePaths: [compilerOptions.baseUrl], 12 | moduleNameMapper: pathsToModuleNameMapper(compilerOptions.paths, {prefix: '/src/'}), 13 | // Coverage 14 | collectCoverage: true, 15 | coverageDirectory: 'coverage', 16 | coverageProvider: 'v8', 17 | // Mocking 18 | clearMocks: true, 19 | }; 20 | -------------------------------------------------------------------------------- /docs/assets/css/extended/custom.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'Roboto'; 3 | src: url('/fonts/Roboto-Regular.ttf') format('truetype'); 4 | font-style: normal; 5 | } 6 | 7 | @font-face { 8 | font-family: 'Roboto'; 9 | src: url('/fonts/Roboto-Black.ttf') format('truetype'); 10 | font-style: bold; 11 | font-weight: 700; 12 | } 13 | 14 | @font-face { 15 | font-family: 'Fira Code'; 16 | src: url('/fonts/FiraCode-Regular.ttf') format('truetype'); 17 | font-style: normal; 18 | } 19 | 20 | 21 | body { 22 | font-family: 'Roboto', sans-serif; 23 | } 24 | 25 | code { 26 | font-family: 'Fira Code', monospace; 27 | } -------------------------------------------------------------------------------- /docs/content/docs/decorators/list-of.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "@ListOf" 3 | --- 4 | 5 | If the incoming object has a list that needs to be converted to a typed one we need to indicate the class of the individual items. 6 | 7 | > **Remember**: The class of the list items must be a [`BuildableResource`](/docs/core/buildable-resource). 8 | 9 | ```typescript 10 | import { BuildableResource, Properties } from "tapi.js"; 11 | import Post from "path/to/classes/Post"; 12 | 13 | @Properties.Resource 14 | class TestClass extends BuildableResource { 15 | @Properties.ListOf(Post) 16 | public listOfPosts: Post[]; 17 | } 18 | ``` 19 | 20 | -------------------------------------------------------------------------------- /scripts/thankyou.cjs: -------------------------------------------------------------------------------- 1 | const p = require('../package.json') 2 | 3 | const colored = (message, color = 32) => { 4 | return "\x1b[" + color + "m" + message + "\x1b[0m" 5 | } 6 | 7 | 8 | const message = ` 9 | __ Thanks for using _ 10 | / /_____ _____ (_) (_)____ 11 | / __/ __ \`/ __ \\/ / / / ___/ 12 | / /_/ /_/ / /_/ / / / (__ ) 13 | \\__/\\__,_/ .___/_(_)_/ /____/ 14 | /_/ /____/ ${colored("v") + p.version} 15 | ` 16 | 17 | const info = ` • • • • • • • • • • 18 | 19 | Info & docs: ${colored(p.homepage, 34)} 20 | License: ${colored(p.license, 35)} 21 | 22 | By ${colored(p.author.name, 36)} and contributors 23 | ` 24 | 25 | console.log(colored(message, 33)) 26 | console.log(info) -------------------------------------------------------------------------------- /docs/content/docs/core/buildable-resource.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: BuildableResource 3 | --- 4 | 5 | In order to make a class buildable from a JSON we have to inherit some common behaviors to make is usable to the `Builder`. 6 | 7 | # How to create a *buildable* class 8 | 9 | To do this all we need is to extend the `BuildableResource` abstract class and to create **a constructor without arguments**, this will be needed in order to create the instance. 10 | 11 | ```typescript 12 | import { BuildableResource } from 'tapi.js'; 13 | 14 | class TestClass extends BuildableResource { 15 | // All your other stuff here 16 | } 17 | ``` 18 | 19 | Forgetting to include the constructor will result in a compilation error due to the [`ResourceFactory`](/docs/core/resource-factory) contract. -------------------------------------------------------------------------------- /.github/workflows/testing-only.yml: -------------------------------------------------------------------------------- 1 | name: Testing your update 2 | 3 | on: 4 | pull_request: 5 | branches: [main] 6 | 7 | jobs: 8 | test: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: Checking out 12 | uses: actions/checkout@v2 13 | - name: Node.js 14 | uses: actions/setup-node@v2 15 | with: 16 | node-version: 14 17 | - name: Cache dependencies 18 | uses: actions/cache@v2 19 | with: 20 | path: ~/.npm 21 | key: npm-${{ hashFiles('npm-shrinkwrap.json') }} 22 | restore-keys: npm- 23 | - name: Installing directly from source-tree 24 | run: npm ci 25 | - name: Running unit tests 26 | run: npm t 27 | - name: Is the code clean? 28 | run: npm run lint 29 | -------------------------------------------------------------------------------- /docs/content/docs/core/build-configuration.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Build Configuration 3 | --- 4 | 5 | This is what specifies how to build an object, it can be configured by hand or [with decorators](/docs/decorators) (*suggested*). 6 | 7 | All the available configuration methods are the same as the decorators mentioned above, the only difference is how you set them. 8 | 9 | > Please use this **only** if you can't enable TypeScript's decorators. 10 | 11 | ```typescript 12 | import { Builder, BuildConfiguration } from 'tapi.js'; 13 | 14 | const buildConfig = new BuildConfiguration() 15 | .ignore("param2") 16 | .transform('toBeTransformed', value => "transformed", value => "transformed again") 17 | .alias("_param_1", "param1"); 18 | // etc... 19 | 20 | const builtObject = new Builder(TestClass, buildConfig).fromJSON(json); 21 | ``` 22 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "parser": "@typescript-eslint/parser", 4 | "plugins": [ 5 | "@typescript-eslint" 6 | ], 7 | "extends": [ 8 | "eslint:recommended", 9 | "plugin:@typescript-eslint/recommended" 10 | ], 11 | "rules": { 12 | "@typescript-eslint/no-explicit-any": "off", 13 | "@typescript-eslint/no-inferrable-types": "off", 14 | "no-prototype-builtins": "off", 15 | "@typescript-eslint/no-empty-interface": "off", 16 | "prefer-const": "warn", 17 | "@typescript-eslint/ban-types": "off", 18 | "@typescript-eslint/explicit-module-boundary-types": "off", 19 | "@typescript-eslint/no-empty-function": "off", 20 | "brace-style": ["warn", "1tbs"], 21 | "no-trailing-spaces": "warn", 22 | "eol-last": "warn", 23 | "comma-dangle": ["warn", "always"], 24 | "indent": ["error", "tab"], 25 | "no-alert": ["error"], 26 | "@typescript-eslint/no-extra-semi": "off", 27 | "semi": ["error", "never"] 28 | }, 29 | "ignorePatterns": [ 30 | "tests/**/*" 31 | ] 32 | } 33 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help tapi.js improve 4 | title: '' 5 | labels: bug 6 | assignees: sinisimattia 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | ```typescript 15 | class YourClass { 16 | // Show us your class or only the interested parts. More info the better 17 | } 18 | ``` 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | How should de built object look? What is returned instead? 23 | 24 | **Screenshots** 25 | If applicable, add screenshots to help explain your problem. 26 | 27 | **Environment (please complete the following information):** 28 | - tapi version [eg.: v0.4.1] 29 | - node version [eg.: v14.x] 30 | 31 | **Typescript config file(s):** 32 | ```json 33 | { 34 | "compilerOptions": {} 35 | // etc.... 36 | } 37 | ``` 38 | 39 | **Additional context** 40 | Add any other context about the problem here. 41 | -------------------------------------------------------------------------------- /.github/workflows/release-npm.yml: -------------------------------------------------------------------------------- 1 | name: NPM Release 2 | 3 | on: 4 | push: 5 | tags: 6 | - '*' 7 | 8 | jobs: 9 | publish-npm: 10 | name: Build, test & publish! 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Checkout 14 | uses: actions/checkout@v2 15 | - name: Node.js Setup 16 | uses: actions/setup-node@v2 17 | with: 18 | node-version: 14 19 | registry-url: https://registry.npmjs.org/ 20 | - name: Cache dependencies 21 | uses: actions/cache@v2 22 | with: 23 | path: ~/.npm 24 | key: npm-${{ hashFiles('npm-shrinkwrap.json') }} 25 | restore-keys: npm- 26 | - name: Installing directly from source-tree 27 | run: npm ci 28 | - name: Running unit tests 29 | run: npm test 30 | - name: Building the package 31 | run: npm run build 32 | - name: Publishing! 33 | run: npm publish --access public 34 | env: 35 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 36 | 37 | -------------------------------------------------------------------------------- /tests/Promises.test.ts: -------------------------------------------------------------------------------- 1 | import BuildableResource from '@/contracts/BuildableResource'; 2 | import { Resource, Alias, Transform, Ignore} from '@/decorators'; 3 | import '@/extensions'; 4 | 5 | @Resource 6 | class TestClass extends BuildableResource { 7 | @Alias("_param_1") 8 | public param1: string = "unassigned"; 9 | 10 | @Ignore 11 | public param2: string = "still private" 12 | 13 | @Transform((value) => { 14 | return "transformed"; 15 | }) 16 | public toBeTransformed = "not transformed"; 17 | 18 | constructor() { 19 | super(); 20 | } 21 | } 22 | 23 | const buildPromise = new Promise((resolve, reject) => { 24 | const json = { 25 | _param_1: "ok", 26 | param2: "this should not be reassigned", 27 | extraParam: "not ok! abort. ABORT!", 28 | toBeTransformed: "something, not important. you shouldn't even see me" 29 | }; 30 | 31 | resolve(json); 32 | }).as(TestClass); 33 | 34 | describe("Promise building", () => { 35 | test("properly builds", async () => { 36 | const result = await buildPromise; 37 | expect(result).toBeInstanceOf(TestClass); 38 | }) 39 | }) -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contribution guidelines 2 | 3 | Here's a short guide for contributing to the project. 4 | 5 | ## For pull requests 6 | ### Be clean 7 | Make sure to always clean your code before submitting, 8 | you can do it automatically with `npm run clean`, or by hand 9 | by running `npm run lint` and then checking manually. 10 | 11 | If the clean command throws errors you won't be able to merge. 12 | 13 | ### Write unit tests 14 | If introducing a new feature, be sure to include thorough unit 15 | tests, so that the intended behavior is as clear as possible. 16 | A better approach would be to write the test BEFORE starting 17 | the actual implementation. 18 | 19 | ### Ask for code reviews 20 | Please include me as a reviewer. That's it. 21 | 22 | ## For issues and pull requests 23 | 24 | ### Use the correct tags 25 | Yeah... the tags help us organize issues and PRs so... 26 | 27 | ### Proper references 28 | Be sure to properly include references to issues and pull requests 29 | in comments, descriptions and commits whenever possible. 30 | 31 | ___ 32 | 33 | Thanks for taking an interest and helping us! Have a nice rest of the day 34 | -------------------------------------------------------------------------------- /docs/config.yml: -------------------------------------------------------------------------------- 1 | baseURL: 'https://tapi.js.org/' 2 | languageCode: 'en-us' 3 | title: 'tapi.js' 4 | theme: 'PaperMod' 5 | 6 | enableInlineShortcodes: true 7 | enableRobotsTXT: true 8 | buildDrafts: false 9 | buildFuture: false 10 | buildExpired: false 11 | enableEmoji: false 12 | 13 | params: 14 | assets: 15 | favicon: /tapi-logo.png 16 | defaultTheme: light 17 | disableThemeToggle: false 18 | ShowCodeCopyButtons: true 19 | ShowToc: false 20 | ShowBreadCrumbs: true 21 | ShowPostNavLinks: false 22 | cover: 23 | responsiveImages: false 24 | profileMode: 25 | enabled: true 26 | title: Convert raw JSON into typed objects 27 | subtitle: "This is a tiny JS/TS package for customizable object mapping to strongly-typed classes." 28 | imageUrl: "/tapi-logo.png" # optional 29 | imageWidth: 200 # custom size 30 | imageHeight: 200 # custom size 31 | buttons: 32 | - name: Get Started 33 | url: "/docs" 34 | socialIcons: 35 | - name: GitHub 36 | url: https://github.com/sinisimattia/tapi/ 37 | - name: npm 38 | url: https://npm.im/tapi.js/ 39 | 40 | menu: 41 | main: 42 | - name: Documentation 43 | url: docs 44 | weight: 1 45 | - name: Search 46 | url: search/ 47 | weight: 10 48 | - name: Contribute 49 | url: https://github.com/sinisimattia/tapi/ 50 | weight: 100 51 | 52 | outputs: 53 | home: 54 | - HTML 55 | - JSON # necessary for search functionality 56 | -------------------------------------------------------------------------------- /src/helpers/Dot.ts: -------------------------------------------------------------------------------- 1 | export default class Dot { 2 | /** 3 | * Get a value in a nested object with dot notation. 4 | * 5 | * @param path The path of the field you want written in "dot notation" 6 | * @param object The object from which to extract the field from 7 | * @param separator The character that splits the different levels of the object, defaults to '.' 8 | * @returns The value of the selected field 9 | */ 10 | static get(path: string, object: any, separator: string = '.'): any { 11 | return path 12 | .split(separator) 13 | .reduce((level, key) => { 14 | if (level) return level[key] 15 | }, object) 16 | } 17 | 18 | /** 19 | * Assigns a value to an object using dot-notation 20 | * 21 | * Kudos to [this article](https://www.tutorialspoint.com/safely-setting-object-properties-with-dot-notation-strings-in-javascript). 22 | * 23 | * @param path The path to which to assign the value 24 | * @param object The object to modify 25 | * @param value The value to assign 26 | * @param separator The dot-notation separator 27 | */ 28 | static assign(path: string, object: any, value: any, separator: string = '.'): void { 29 | const levels = path.split(separator) 30 | if (levels.length === 0) throw new Error("Please provide a non-empty path.") 31 | else if (levels.length === 1) object[levels[0]] = value 32 | else { 33 | if (object[levels[0]]) 34 | return Dot.assign(levels.slice(1).join(separator), object[levels[0]], value) 35 | else { 36 | object[levels[0]] = {} 37 | return Dot.assign(levels.slice(1).join(separator), object[levels[0]], value) 38 | } 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /tests/Decorators.test.ts: -------------------------------------------------------------------------------- 1 | import { Resource, Alias, Transform, Ignore } from '@/decorators'; 2 | import BuildableResource from '@/contracts/BuildableResource'; 3 | 4 | @Resource 5 | class TestClass extends BuildableResource { 6 | @Alias("_param_1") 7 | public param1: string = "unassigned"; 8 | 9 | @Ignore 10 | public param2: string = "still private" 11 | 12 | @Transform((value) => { 13 | return "transformed"; 14 | }) 15 | public toBeTransformed = "not transformed"; 16 | 17 | public list: Array = [] 18 | 19 | constructor() { 20 | super(); 21 | } 22 | 23 | public getParam2(): string { 24 | return this.param2; 25 | } 26 | } 27 | 28 | describe('Decorated class new TestClass()', () => { 29 | test('properly sets ignore directives', () => { 30 | const json = { 31 | param1: "ok", 32 | param2: "this should not be reassigned", 33 | extraParam: "not ok! abort. ABORT!" 34 | } 35 | 36 | const instance = new TestClass().fromJSON(json); 37 | 38 | expect(instance.param1).toBe("ok"); 39 | expect(instance.hasOwnProperty("extraParam")).toBe(false); 40 | expect(instance.getParam2()).toBe("still private"); 41 | }) 42 | 43 | test('properly sets transformers', () => { 44 | const json = { 45 | param1: "ok", 46 | param2: "this should not be reassigned", 47 | toBeTransformed: "something" 48 | } 49 | 50 | const instance = new TestClass().fromJSON(json); 51 | 52 | expect(instance.toBeTransformed).toBe("transformed"); 53 | }) 54 | 55 | test('properly sets aliases', () => { 56 | const json = { 57 | _param_1: "ok" 58 | } 59 | 60 | const instance = new TestClass().fromJSON(json); 61 | 62 | expect(instance.param1).toBe("ok"); 63 | }) 64 | }) -------------------------------------------------------------------------------- /src/extensions.ts: -------------------------------------------------------------------------------- 1 | import Builder from '@/core/Builder' 2 | import ResourceFactory from '@/contracts/ResourceFactory' 3 | import BuildableResource from '@/contracts/BuildableResource' 4 | 5 | declare global { 6 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 7 | export interface Promise { 8 | /** 9 | * Converts the incoming data into a typed object and returns a promise with the built object. 10 | * 11 | * @param classToBuild The class the object needs to be converted to. 12 | * @return The typed object. 13 | */ 14 | as(classToBuild: ResourceFactory): Promise; 15 | /** 16 | * Converts an inner section of the incoming data into a typed object and returns a promise with the built object. 17 | * 18 | * @param classToBuild The class the object needs to be converted to. 19 | * @param from The inner property to convert. 20 | * @return The typed object. 21 | */ 22 | as(classToBuild: ResourceFactory, from: string): Promise; 23 | } 24 | } 25 | 26 | Promise.prototype.as = function (classToBuild: ResourceFactory, from: string = ''): Promise { 27 | return this.then((input: any) => { 28 | return new Promise((resolve, reject) => { 29 | let result 30 | try { 31 | if (from != '') { 32 | if (input.hasOwnProperty(from)) { 33 | input = input[from] 34 | } else { 35 | throw new Error(`Incoming object does not contain a field called "${input}".`) 36 | } 37 | } 38 | 39 | result = new Builder(classToBuild).fromJSON(input) 40 | resolve(result) 41 | } catch (err) { 42 | reject(err) 43 | } 44 | }) 45 | }) 46 | } 47 | -------------------------------------------------------------------------------- /src/contracts/BuildableResource.ts: -------------------------------------------------------------------------------- 1 | import BuildConfiguration from "@/core/BuildConfiguration" 2 | import Builder from "@/core/Builder" 3 | import JSONConvertible from "@/contracts/JSONConvertible" 4 | import { deepCopy, } from "@/helpers/functions" 5 | 6 | /** 7 | * Defines an object that can be automatically built from JSON data. 8 | * 9 | * Use this as the base class for your resources (ie. Users, Articles, Posts, ect.) 10 | */ 11 | export default abstract class BuildableResource = any> implements JSONConvertible { 12 | /** 13 | * The specified {@link BuildConfiguration} for this class. 14 | */ 15 | private resourceBuildConfiguration?: BuildConfiguration 16 | 17 | /** 18 | * The base constructor with no arguments. Your class NEEDS to have it. 19 | */ 20 | constructor() {} 21 | 22 | public get buildConfig(): BuildConfiguration { 23 | if (!Object.getOwnPropertyDescriptor(this, 'resourceBuildConfiguration') && this.resourceBuildConfiguration) { 24 | this.resourceBuildConfiguration = deepCopy(this.resourceBuildConfiguration) 25 | 26 | } else if (!this.resourceBuildConfiguration) { 27 | this.resourceBuildConfiguration = new BuildConfiguration 28 | } 29 | 30 | return this.resourceBuildConfiguration 31 | } 32 | 33 | /** 34 | * Public getter for this class' current builder. 35 | */ 36 | public get build(): Builder { 37 | const c = (this as unknown as Type).constructor.prototype.constructor 38 | return new Builder(c, this.buildConfig) 39 | } 40 | 41 | public fromJSON(source: any): this { 42 | return this.build.fromJSON(source) 43 | } 44 | 45 | public toJSON(): any { 46 | return this.build.toJSON(this) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tapi.js", 3 | "type": "module", 4 | "version": "0.5.1", 5 | "description": "Consume JSONs as TypeScript objects.", 6 | "keywords": [ 7 | "API", 8 | "converter", 9 | "resolver", 10 | "mapper", 11 | "automapper", 12 | "TypeScript" 13 | ], 14 | "author": { 15 | "name": "Mattia Sinisi", 16 | "email": "mattia@snisni.it", 17 | "url": "https://mattia.codes" 18 | }, 19 | "license": "GPL-3.0-only", 20 | "bugs": { 21 | "url": "https://github.com/sinisimattia/tapi/issues" 22 | }, 23 | "homepage": "https://tapi.js.org", 24 | "main": "dist/cjs/index.js", 25 | "module": "dist/es/index.js", 26 | "types": "dist/types.d.ts", 27 | "scripts": { 28 | "build": "rollup -c --silent", 29 | "test": "jest", 30 | "lint": "eslint .", 31 | "clean": "eslint . --fix", 32 | "prepare": "npm run build", 33 | "postinstall": "node scripts/thankyou.cjs" 34 | }, 35 | "repository": { 36 | "type": "git", 37 | "url": "git+https://github.com/sinisimattia/tapi.git" 38 | }, 39 | "directories": { 40 | "bin": "dist", 41 | "test": "tests", 42 | "lib": "dist" 43 | }, 44 | "files": [ 45 | "dist/**/*", 46 | "scripts", 47 | "tests", 48 | "types" 49 | ], 50 | "devDependencies": { 51 | "@rollup/plugin-alias": "^4.0.2", 52 | "@rollup/plugin-commonjs": "^24.0.0", 53 | "@rollup/plugin-json": "^6.0.0", 54 | "@rollup/plugin-node-resolve": "^15.0.1", 55 | "@rollup/plugin-typescript": "^10.0.1", 56 | "@types/jest": "^29.2.4", 57 | "@typescript-eslint/eslint-plugin": "^5.45.0", 58 | "@typescript-eslint/parser": "^5.45.0", 59 | "eslint": "^8.29.0", 60 | "jest": "^29.3.1", 61 | "rollup": "^3.8.0", 62 | "rollup-plugin-delete": "^2.0.0", 63 | "rollup-plugin-dts": "^5.3.0", 64 | "ts-jest": "^29.0.3", 65 | "typescript": "^4.9.3" 66 | }, 67 | "dependencies": { 68 | "deepclone": "^1.0.2", 69 | "postcss-cli": "^10.1.0" 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /rollup.config.cjs: -------------------------------------------------------------------------------- 1 | // Plugins 2 | const ts = require('@rollup/plugin-typescript') 3 | const dts = require('rollup-plugin-dts').default 4 | const cjs = require('@rollup/plugin-commonjs') 5 | const json = require('@rollup/plugin-json') 6 | const resolve = require('@rollup/plugin-node-resolve') 7 | const alias = require('@rollup/plugin-alias') 8 | const del = require('rollup-plugin-delete') 9 | 10 | // Constants 11 | const pkg = require('./package.json') 12 | const tsConfigBase = require('./tsconfig.json') 13 | const SRC_DIR = tsConfigBase.compilerOptions.baseUrl 14 | const LIB_DIR = pkg.directories.lib 15 | 16 | function createBuildConfig(outSubDir, outFormat) { 17 | const OUT_DIR = LIB_DIR + '/' + outSubDir 18 | 19 | return { 20 | input: [ 21 | SRC_DIR + '/index.ts', 22 | SRC_DIR + '/functions.ts', 23 | SRC_DIR + '/extensions.ts', 24 | SRC_DIR + '/decorators.ts', 25 | ], 26 | output:[ 27 | { 28 | dir: OUT_DIR, 29 | format: outFormat 30 | }, 31 | ], 32 | plugins: [ 33 | alias({ 34 | customResolver: resolve({ 35 | extensions: [ 36 | // JavaScript 37 | '.js', '.cjs', '.mjs', 38 | // TypeScript 39 | '.ts', '.d.ts', 40 | // Other 41 | '.json', '.node', 42 | ], 43 | }), 44 | }), 45 | cjs(), 46 | json(), 47 | ts({ 48 | tsconfig: './tsconfig.build.json', 49 | outDir: OUT_DIR, 50 | declarationDir: OUT_DIR + '/types', 51 | }), 52 | ], 53 | } 54 | } 55 | 56 | module.exports = [ 57 | createBuildConfig('cjs', 'cjs'), 58 | createBuildConfig('es', 'es'), 59 | { 60 | input: LIB_DIR + '/es/types/index.d.ts', 61 | output: [{ file: LIB_DIR + '/types.d.ts', format: 'es' }], 62 | plugins: [ 63 | dts({ 64 | tsconfig: './tsconfig.build.json', 65 | compilerOptions: { 66 | baseUrl: 'src', 67 | sourceMap: true, 68 | declarationMap: true, 69 | }, 70 | }), 71 | del({ 72 | targets: [ 73 | LIB_DIR + '/es/types', 74 | LIB_DIR + '/cjs/types', 75 | ], 76 | hook: 'buildEnd', 77 | runOnce: true, 78 | }), 79 | ], 80 | }, 81 | ] 82 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 |
 3 |    __              _     _     
 4 |   / /_____ _____  (_)   (_)____
 5 |  / __/ __ `/ __ \/ /   / / ___/
 6 | / /_/ /_/ / /_/ / /   / (__  ) 
 7 | \__/\__,_/ .___/_(_)_/ /____/  
 8 |         /_/       /___/        
 9 | 	
10 |

Consume APIs as typed objects!

11 |
12 | 13 |
14 | 15 | ![License](https://badgen.net/github/license/sinisimattia/tapi) 16 | ![TS](https://badgen.net/npm/types/tapi.js) 17 | ![Dependencies](https://badgen.net/bundlephobia/dependency-count/tapi.js) 18 | ![Stars](https://badgen.net/github/stars/sinisimattia/tapi) 19 | 20 | [![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/B0B561G42) 21 | 22 |
23 | 24 | 25 | ## Imagine doing this... 26 | 27 | ```javascript 28 | http 29 | .get('/some-url-that-returns-an-object') // Get data 📡 30 | .as(YourClass) // Map it onto your class ✨ 31 | .then((builtObject) => { 32 | builtObject.doSomething(); // Use it directly 🎉 33 | }) 34 | ``` 35 | 36 | Or even this... 37 | 38 | ```javascript 39 | const builtObject = await http.get('/some-url-that-returns-an-object').as(YourClass) 40 | builtObject.doSomething(); 41 | ``` 42 | 43 | ## How? 44 | 45 | Introducing **tapi**, a tiny TypeScript package used to define how to interpret any given API response as a typed object. 46 | 47 | It **automatically** converts JSON objects into TypeScript class instances, however you can still configure it however you want with *Aliases*, *Transformers*, *Mappers* and more. 48 | 49 | ___ 50 | 51 |
52 | 53 | ### **[🚀 Get started 🚀](https://tapi.js.org)** 54 | 55 |
56 | 57 | ___ 58 | 59 | ## More info 60 | 61 | 📦 [Check it out on **npm**](https://npm.im/tapi.js) 62 | 63 | 🦕 [Check it out on **deno**](https://deno.land/x/tapi) 64 | 65 | 😱 [Check it out on **Bundlephobia**](https://bundlephobia.com/package/tapi.js@latest) 66 | 67 | ☁ [Use directly from the browser](https://cdn.jsdelivr.net/npm/tapi.js/) 68 | 69 | 📚 [Additional info on the file on **libraries.io**](https://libraries.io/npm/tapi.js) 70 | 71 | ## Big thanks to anyone who contributes! 72 | 73 | **You** could be here! Just follow the [**contributor's guide**](CONTRIBUTING.md). 74 | 75 | 👇 76 | 77 | ![Contributors](https://contrib.rocks/image?repo=sinisimattia/tapi) 78 | ___ 79 | 80 | Thanks for using **tapi**! 😊 I hope it's been useful to you in some way. 81 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | *.lcov 24 | 25 | # nyc test coverage 26 | .nyc_output 27 | 28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # Bower dependency directory (https://bower.io/) 32 | bower_components 33 | 34 | # node-waf configuration 35 | .lock-wscript 36 | 37 | # Compiled binary addons (https://nodejs.org/api/addons.html) 38 | build/Release 39 | 40 | # Dependency directories 41 | node_modules/ 42 | jspm_packages/ 43 | 44 | # TypeScript v1 declaration files 45 | typings/ 46 | 47 | # TypeScript cache 48 | *.tsbuildinfo 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Microbundle cache 57 | .rpt2_cache/ 58 | .rts2_cache_cjs/ 59 | .rts2_cache_es/ 60 | .rts2_cache_umd/ 61 | 62 | # Optional REPL history 63 | .node_repl_history 64 | 65 | # Output of 'npm pack' 66 | *.tgz 67 | 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | 71 | # dotenv environment variables file 72 | .env 73 | .env.test 74 | 75 | # Rollup cache 76 | .rollup.cache 77 | 78 | # parcel-bundler cache (https://parceljs.org/) 79 | .cache 80 | 81 | # Next.js build output 82 | .next 83 | 84 | # Nuxt.js build / generate output 85 | .nuxt 86 | dist 87 | 88 | # Gatsby files 89 | .cache/ 90 | # Comment in the public line in if your project uses Gatsby and *not* Next.js 91 | # https://nextjs.org/blog/next-9-1#public-directory-support 92 | # public 93 | 94 | # vuepress build output 95 | .vuepress/dist 96 | 97 | # Serverless directories 98 | .serverless/ 99 | 100 | # FuseBox cache 101 | .fusebox/ 102 | 103 | # DynamoDB Local files 104 | .dynamodb/ 105 | 106 | # TernJS port file 107 | .tern-port 108 | 109 | # Jekyll 110 | Gemfile.lock 111 | _site 112 | .sass-cache 113 | 114 | # Editors 115 | .idea 116 | .vscode 117 | .vs 118 | -------------------------------------------------------------------------------- /src/decorators.ts: -------------------------------------------------------------------------------- 1 | import BuildableResource from "@/contracts/BuildableResource" 2 | import ResourceFactory from "@/contracts/ResourceFactory" 3 | 4 | class ResourceDecorator { 5 | /** 6 | * Define a class as a resource and check for its validity. 7 | */ 8 | public Resource(constructor: Function): any { 9 | const target = constructor.prototype 10 | 11 | if (!(target instanceof BuildableResource)) { 12 | throw new Error("Target class has to extend BuildableResource in order to use this decorator.") 13 | } 14 | 15 | if(!target.constructor) { 16 | throw new Error("Target class must have a constructor with no arguments.") 17 | } 18 | } 19 | 20 | /** 21 | * Adds an alias. 22 | * @param alias The alias to add. 23 | */ 24 | public Alias(alias: string): any { 25 | return (target: BuildableResource, name: PropertyKey): any => { 26 | target.buildConfig?.alias(alias, name.toString()) 27 | } 28 | } 29 | 30 | /** 31 | * Adds a transformer. 32 | * @param transformerIn The transformer for incoming properties. 33 | * @param transformerOut The transformer for outgoing properties. 34 | */ 35 | public Transform(transformerIn: Action, transformerOut?: Action): any { 36 | return function (target: BuildableResource, name: string) { 37 | target.buildConfig?.transform(name, transformerIn, transformerOut) 38 | } 39 | } 40 | 41 | /** 42 | * Adds an required directive. 43 | */ 44 | public Required(target: BuildableResource, name: PropertyKey) { 45 | target.buildConfig?.require(name.toString()) 46 | } 47 | 48 | /** 49 | * Adds an ignore directive. 50 | */ 51 | public Ignore(target: BuildableResource, name: PropertyKey) { 52 | target.buildConfig?.ignore(name.toString()) 53 | } 54 | 55 | //NOTE we can use the Resource decorator for the same purpose 56 | /** 57 | * Define a list of {@link BuildableResource}(s). 58 | * @param resource The list items' class. 59 | */ 60 | public ListOf(resource: ResourceFactory) { 61 | return function (target: BuildableResource, name: string) { 62 | target.buildConfig?.listType(name, new resource()) 63 | } 64 | } 65 | } 66 | 67 | const resourceDecorator = new ResourceDecorator() 68 | 69 | export const Resource = resourceDecorator.Resource 70 | export const Alias = resourceDecorator.Alias 71 | export const Transform = resourceDecorator.Transform 72 | export const Ignore = resourceDecorator.Ignore 73 | export const Required = resourceDecorator.Required 74 | export const ListOf = resourceDecorator.ListOf 75 | -------------------------------------------------------------------------------- /.github/workflows/update-site.yml: -------------------------------------------------------------------------------- 1 | # Sample workflow for building and deploying a Hugo site to GitHub Pages 2 | name: Deploy site to GitHub Pages 3 | 4 | on: 5 | workflow_dispatch: 6 | release: 7 | types: [created] 8 | push: 9 | tags: 10 | - '*' 11 | 12 | # Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages 13 | permissions: 14 | contents: read 15 | pages: write 16 | id-token: write 17 | 18 | # Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. 19 | # However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. 20 | concurrency: 21 | group: "pages" 22 | cancel-in-progress: false 23 | 24 | # Default to bash 25 | defaults: 26 | run: 27 | shell: bash 28 | 29 | jobs: 30 | # Build job 31 | build: 32 | runs-on: ubuntu-latest 33 | env: 34 | HUGO_VERSION: 0.114.0 35 | steps: 36 | - name: Install Hugo CLI 37 | run: | 38 | wget -O ${{ runner.temp }}/hugo.deb https://github.com/gohugoio/hugo/releases/download/v${HUGO_VERSION}/hugo_extended_${HUGO_VERSION}_linux-amd64.deb \ 39 | && sudo dpkg -i ${{ runner.temp }}/hugo.deb 40 | - name: Install Dart Sass 41 | run: sudo snap install dart-sass 42 | - name: Checkout 43 | uses: actions/checkout@v3 44 | with: 45 | submodules: recursive 46 | fetch-depth: 0 47 | - name: Setup Pages 48 | id: pages 49 | uses: actions/configure-pages@v3 50 | - name: Install Node.js dependencies 51 | working-directory: ./docs 52 | run: "[[ -f package-lock.json || -f npm-shrinkwrap.json ]] && npm ci || true" 53 | - name: Build with Hugo 54 | env: 55 | # For maximum backward compatibility with Hugo modules 56 | HUGO_ENVIRONMENT: production 57 | HUGO_ENV: production 58 | working-directory: ./docs 59 | run: | 60 | hugo \ 61 | --gc \ 62 | --minify \ 63 | --baseURL "${{ steps.pages.outputs.base_url }}/" 64 | - name: Upload artifact 65 | uses: actions/upload-pages-artifact@v1 66 | with: 67 | path: ./docs/public 68 | 69 | # Deployment job 70 | deploy: 71 | environment: 72 | name: github-pages 73 | url: ${{ steps.deployment.outputs.page_url }} 74 | runs-on: ubuntu-latest 75 | needs: build 76 | steps: 77 | - name: Deploy to GitHub Pages 78 | id: deployment 79 | uses: actions/deploy-pages@v2 -------------------------------------------------------------------------------- /tests/ComplexBuilds.test.ts: -------------------------------------------------------------------------------- 1 | import { Resource, Alias, Transform, Ignore, ListOf } from '@/decorators'; 2 | import BuildableResource from '@/contracts/BuildableResource'; 3 | 4 | @Resource 5 | class AnotherClass extends BuildableResource { 6 | @Alias("_param_1") 7 | public param1: string = "unassigned"; 8 | 9 | @Ignore 10 | public param2: string = "still private" 11 | 12 | @Transform((value) => { 13 | return "transformed"; 14 | }) 15 | public toBeTransformed = "not transformed"; 16 | 17 | constructor() { 18 | super(); 19 | } 20 | } 21 | 22 | @Resource 23 | class TestClass extends BuildableResource { 24 | @Alias("inner") 25 | public innerObject = new AnotherClass(); 26 | 27 | @Alias("listOfThings") 28 | @ListOf(AnotherClass) 29 | public list: AnotherClass[] = []; 30 | 31 | public listOfPrimitives: Array = []; 32 | 33 | @Alias("buried.deep.within.objects") 34 | public buriedProperty = "not assigned"; 35 | 36 | constructor() { 37 | super(); 38 | } 39 | } 40 | 41 | const json = { 42 | inner: { 43 | _param_1: "ok", 44 | param2: "this should not be reassigned", 45 | extraParam: "not ok! abort. ABORT!", 46 | toBeTransformed: "something, not important. you shouldn't even see me" 47 | }, 48 | listOfThings: [ 49 | { 50 | _param_1: "ok", 51 | param2: "this should not be reassigned", 52 | extraParam: "not ok! abort. ABORT!", 53 | toBeTransformed: "something, not important. you shouldn't even see me" 54 | } 55 | ], 56 | listOfPrimitives: [true, 2, "three"], 57 | buried: {deep: {within: {objects: "assigned"}}} 58 | } 59 | 60 | const instance = new TestClass().fromJSON(json); 61 | 62 | describe('Decorated class builder', () => { 63 | test('properly builds recursive resources', () => { 64 | const innerObject = instance.innerObject; 65 | 66 | expect(innerObject).toBeInstanceOf(AnotherClass); 67 | expect(innerObject.param1).toBe("ok"); 68 | expect(innerObject.hasOwnProperty("extraParam")).toBe(false); 69 | expect(innerObject.param2).toBe("still private"); 70 | }) 71 | 72 | test('properly builds listed resources', () => { 73 | const list = instance.list; 74 | 75 | expect(list.length).toBeGreaterThan(0); 76 | expect(list[0]).toBeInstanceOf(AnotherClass); 77 | expect(list[0].param1).toBe("ok"); 78 | expect(list[0].param2).toBe("still private"); 79 | expect(list[0].toBeTransformed).toBe("transformed"); 80 | }) 81 | 82 | test('properly builds listed primitives', () => { 83 | const list = instance.listOfPrimitives; 84 | 85 | expect(list.length).toBeGreaterThan(0); 86 | expect(list[0]).toBe(true); 87 | expect(list[1]).toBe(2); 88 | expect(list[2]).toBe("three"); 89 | }) 90 | 91 | test('handles dot-notation paths', () => { 92 | expect(instance.buriedProperty).toBe("assigned"); 93 | }) 94 | }) -------------------------------------------------------------------------------- /src/core/BuildConfiguration.ts: -------------------------------------------------------------------------------- 1 | export default class BuildConfiguration { 2 | /** 3 | * List of required parameters 4 | */ 5 | public required: Set = new Set 6 | 7 | /** 8 | * List of parameters that need to be ignored. 9 | */ 10 | readonly ignores: Set = new Set 11 | 12 | /** 13 | * Collection of transformers. Each transformer is a callback function associated to a property, 14 | * this gets called just before the property is assigned to the base object. 15 | */ 16 | readonly transformers: {[localPath: string]: ValueTransformer} = {} 17 | 18 | /** 19 | * Collection of aliases. Each alias represents an alternative name for the property 20 | * in the incoming object. 21 | */ 22 | readonly aliases: {[localPath: string]: string} = {} 23 | 24 | /** 25 | * Collection of list element constructors. This is used to define how to construct 26 | * the individual items of an incoming list. 27 | */ 28 | readonly listElementConstructors: {[localPath: string]: ResultType} = {} 29 | 30 | /** 31 | * Add a required directive for one or more paths. 32 | * 33 | * @param paths The object paths to set as required when instantiating. 34 | */ 35 | public require(...paths: string[]): this { 36 | paths.forEach(path => this.required.add(path)) 37 | return this 38 | } 39 | 40 | /** 41 | * Add an ignore directive for one or more paths. 42 | * 43 | * @param paths The object paths to ignore when instantiating. 44 | */ 45 | public ignore(...paths: string[]): this { 46 | paths.forEach(path => this.ignores.add(path)) 47 | return this 48 | } 49 | 50 | /** 51 | * Add a transform directive for a given path. 52 | * 53 | * @param localPath The property of the typed object that needs to be transformed. 54 | * @param transformer The {@link ValueTransformer} used. 55 | */ 56 | public transform(localPath: string, transformerIn: Action, transformerOut?: Action): this { 57 | const defaultTransformer: Action = (value) => value 58 | 59 | this.transformers[localPath] = { 60 | in: transformerIn, 61 | out: transformerOut ?? defaultTransformer, 62 | } 63 | 64 | return this 65 | } 66 | 67 | /** 68 | * Add an alias for a given property. 69 | * 70 | * @param foreignPath The path of the incoming object. 71 | * @param localPath The path of the typed object's property. 72 | */ 73 | public alias(foreignPath: string, localPath: string): this { 74 | this.aliases[localPath] = foreignPath 75 | return this 76 | } 77 | 78 | /** 79 | * Add a list item type to let the builder know how to construct 80 | * the elements of a list. 81 | * 82 | * @param localPath The path of the typed object's property. 83 | * @param builtObject The list element as a typed object. 84 | */ 85 | public listType(localPath: string, builtObject: ResultType): this { 86 | this.listElementConstructors[localPath] = builtObject 87 | return this 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /tests/Builder.test.ts: -------------------------------------------------------------------------------- 1 | import BuildConfiguration from '@/core/BuildConfiguration'; 2 | import Builder from '@/core/Builder'; 3 | import BuildableResource from '@/contracts/BuildableResource'; 4 | 5 | class TestClass extends BuildableResource { 6 | public param1: string = "unassigned"; 7 | public param2: string = "still private" 8 | public toBeTransformed = "not transformed"; 9 | public list: Array = [] 10 | 11 | public getParam2(): string { 12 | return this.param2; 13 | } 14 | } 15 | 16 | const buildConfig = new BuildConfiguration() 17 | .ignore("param2") 18 | .transform('toBeTransformed', value => "transformed", value => "transformed again") 19 | .alias("_param_1", "param1"); 20 | 21 | const strictBuildConfig = new BuildConfiguration() 22 | .ignore("param2") 23 | .transform('toBeTransformed', value => "transformed", value => "transformed again") 24 | .alias("_param_1", "param1") 25 | .require('param1', 'param2', 'toBeTransformed', 'list'); 26 | 27 | describe('Typed object builder', () => { 28 | test('ignores extra parameters', () => { 29 | const json = { 30 | param1: "ok", 31 | param2: "this should not be reassigned", 32 | extraParam: "not ok! abort. ABORT!" 33 | } 34 | 35 | const builder = new Builder(TestClass, buildConfig) 36 | 37 | const instance = builder.fromJSON(json); 38 | 39 | expect(instance instanceof TestClass).toBe(true); 40 | 41 | expect(instance.param1).toBe("ok"); 42 | expect(instance.hasOwnProperty("extraParam")).toBe(false); 43 | expect(instance.getParam2()).toBe("still private"); 44 | }) 45 | 46 | test('enforces strict assignment', () => { 47 | const json = { 48 | param1: "ok", 49 | extraParam: "not ok! abort. ABORT!" 50 | } 51 | 52 | const builder = new Builder(TestClass, strictBuildConfig) 53 | 54 | expect(() => { 55 | builder.fromJSON(json); 56 | }).toThrowError(); 57 | }) 58 | 59 | test('supports custom mappings', () => { 60 | const json = { 61 | param1: "ok", 62 | param2: "this should not be reassigned", 63 | toBeTransformed: "something" 64 | } 65 | 66 | const builder = new Builder(TestClass, buildConfig) 67 | 68 | const instance = builder.fromJSON(json); 69 | 70 | expect(instance.toBeTransformed).toBe("transformed"); 71 | }) 72 | 73 | test('supports aliases', () => { 74 | const json = { 75 | _param_1: "ok" 76 | } 77 | 78 | const builder = new Builder(TestClass, buildConfig) 79 | 80 | const instance = builder.fromJSON(json); 81 | 82 | expect(instance.param1).toBe("ok"); 83 | }) 84 | 85 | test('can transform in reverse', () => { 86 | const json = { 87 | param1: "ok", 88 | param2: "this should not be reassigned", 89 | toBeTransformed: "something" 90 | } 91 | 92 | const builder = new Builder(TestClass, buildConfig) 93 | 94 | const builtObject = builder.fromJSON(json); 95 | const instance = builder.toJSON(builtObject); 96 | 97 | expect(instance.toBeTransformed).toBe("transformed again"); 98 | expect(instance["_param_1"]).toBe("ok"); 99 | }) 100 | }) -------------------------------------------------------------------------------- /tests/Inheritance.test.ts: -------------------------------------------------------------------------------- 1 | import { Resource, Transform } from "@/decorators"; 2 | import { BuildableResource, Builder } from "@/index"; 3 | 4 | @Resource 5 | class ParentClass extends BuildableResource { 6 | @Transform((value) => { 7 | return value.toUpperCase(); 8 | }) 9 | public name: string = ''; 10 | } 11 | 12 | class ChildClass extends ParentClass { 13 | public email: string = ''; 14 | 15 | public isChildClass = () => true; 16 | } 17 | 18 | class ChildClassOverrideTransform extends ParentClass { 19 | @Transform(() => 'transformed') 20 | public name: string = ''; 21 | 22 | public isChildClass = () => true; 23 | } 24 | 25 | class ChildClassWithOtherTransform extends ParentClass { 26 | @Transform((value) => +value) 27 | public id: number = 0; 28 | 29 | public isChildClass = () => true; 30 | } 31 | 32 | class DeepClassWithWeirdTransform extends ChildClassWithOtherTransform { 33 | @Transform((value) => +value + 50) 34 | public id: number = 0; 35 | 36 | public isChildClass = () => true; 37 | } 38 | 39 | describe('It uses the child class', () => { 40 | test('when using the builder.', () => { 41 | const instance = new Builder(ChildClass).fromJSON({ 42 | name: 'This is my name' 43 | }); 44 | 45 | expect(instance instanceof ChildClass).toBeTruthy(); 46 | 47 | /* 48 | This is because TS also considers this 49 | to be an instance of ParentClass. It's 50 | actually intended behavior in TS, so 51 | I've added this additional test to make 52 | sure it instantiated the child. 53 | */ 54 | expect(instance.isChildClass()).toBeTruthy(); 55 | }); 56 | 57 | test('when using the decorators.', () => { 58 | const instance = new ChildClass().fromJSON({ 59 | name: 'This is my name' 60 | }); 61 | 62 | expect(instance instanceof ChildClass).toBeTruthy(); 63 | 64 | /* 65 | This is because TS also considers this 66 | to be an instance of ParentClass. It's 67 | actually intended behavior in TS, so 68 | I've added this additional test to make 69 | sure it instantiated the child. 70 | */ 71 | expect(instance.isChildClass()).toBeTruthy(); 72 | 73 | expect(instance.name).toStrictEqual('THIS IS MY NAME'); 74 | }); 75 | }); 76 | 77 | describe('It compose with the parent class', () => { 78 | test('when overriding parent class decorator.', () => { 79 | const instanceWithOverride = new ChildClassOverrideTransform().fromJSON({ 80 | name: 'This is my name' 81 | }); 82 | 83 | const instanceWithoutOverride = new ChildClass().fromJSON({ 84 | name: 'This is my name' 85 | }); 86 | 87 | expect(instanceWithOverride.name).toStrictEqual('transformed'); 88 | expect(instanceWithoutOverride.name).toStrictEqual('THIS IS MY NAME'); 89 | }) 90 | 91 | test('when composing parent class decorator and child class decorator.', () => { 92 | const instance = new ChildClassWithOtherTransform().fromJSON({ 93 | id: '42', 94 | name: 'This is my name' 95 | }); 96 | 97 | expect(instance.name).toStrictEqual('THIS IS MY NAME'); 98 | expect(instance.id).toStrictEqual(42); 99 | }) 100 | 101 | test('when extending another subclass and overriding decorator.', () => { 102 | const instance = new DeepClassWithWeirdTransform().fromJSON({ 103 | id: '42', 104 | name: 'This is my name' 105 | }); 106 | 107 | expect(instance.name).toStrictEqual('THIS IS MY NAME'); 108 | expect(instance.id).toStrictEqual(92); 109 | }) 110 | }) 111 | -------------------------------------------------------------------------------- /docs/content/docs/begin.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Usage (from start to finish) 🚀 3 | summary: Complete guide to using tapi, from install to usage. 4 | weight: 1 # pins this post at the top 5 | ShowToc: true 6 | ShowBreadCrumbs: true 7 | --- 8 | 9 | # The situation 10 | 11 | Let's imagine that you have a class, we'll call it `TestClass`. 12 | 13 | ```typescript 14 | class TestClass { 15 | public param1: string; 16 | public param2: string; 17 | public list: string[]; 18 | public thing: AnotherClass; 19 | public thingToBeIgnored: string = "Leave me here!" 20 | } 21 | ``` 22 | 23 | Now we want to instantiate that class from a generic JSON response. 24 | 25 | ```json 26 | { 27 | "_param_1": "something", 28 | "param2": "something else", 29 | "items": ["item 1", "item 2", "item 3"], 30 | "something": { 31 | "inner1": "inner thing", 32 | "inner2": "another inner thing" 33 | }, 34 | "thingToBeIgnored": "some useless data" 35 | } 36 | ``` 37 | 38 | How do we go about it? Mapping manually is too cumbersome... that's where **tapi.js** comes to help! 39 | 40 | # Introducing: the **Typed** API consumer (or just tapi) 41 | 42 | **tapi** is a tiny TypeScript package used to define how to interpret any given API response as a typed object. 43 | 44 | ## Install 📦 45 | ```bash 46 | npm i -S tapi.js 47 | ``` 48 | 49 | ## Usage 🚀 50 | 51 | First off you need to implement the `BuildableResource` interface and define the builder the class will use. 52 | 53 | ### With decorators ✨ 54 | 55 | ```typescript 56 | // TestClass.ts 57 | import { BuildableResource, Properties } from 'tapi.js'; 58 | 59 | @Properties.Resource 60 | class TestClass extends BuildableResource { 61 | @Properties.Alias('_param_1') 62 | public param: string = 'unassigned'; 63 | 64 | @Properties.Ignore 65 | public toBeIgnored: string = 'still private' 66 | 67 | @Properties.Transform((value: string) => { 68 | return value.toUppercase(); 69 | }) 70 | public toBeTransformed = 'not transformed'; 71 | 72 | @Properties.ListOf 73 | public list: Array = [] 74 | 75 | // Define a constructor with no arguments. 76 | constructor() { 77 | super(); 78 | } 79 | } 80 | ``` 81 | 82 | Then it's as simple as writing: 83 | ```typescript 84 | const instance: TestClass = new TestClass().fromJSON(json); 85 | ``` 86 | 87 | ### ... or with explicit builder 👷‍♂️ 88 | 89 | ```typescript 90 | // TestClass.ts 91 | import { BuildableResource, BuildConfiguration } from 'tapi.js'; 92 | 93 | class TestClass extends BuildableResource { 94 | public param: string = 'unassigned'; 95 | public toBeIgnored: string = 'still private' 96 | public toBeTransformed = 'not transformed'; 97 | public list: Array = [] 98 | 99 | constructor() { 100 | super(); 101 | } 102 | } 103 | 104 | // example.ts 105 | const testClassBuildConfig = new BuildConfiguration() 106 | .ignore(['toBeIgnored']) 107 | .transform('toBeTransformed', (value) => { 108 | return 'transformed'; 109 | }) 110 | .alias('_param_1', 'param'); 111 | 112 | const builtObject = new Builder(TestClass, testClassBuildConfig).fromJSON(json); 113 | ``` 114 | 115 | ### ... and also with Promises 🤞 116 | 117 | The conversion tool can also be used with promises, to demonstrate this we'll be using a simple Axios request. 118 | 119 | ```typescript 120 | import axios from 'axios' // 👈 Of course, you can use whatever library you want 121 | 122 | import { BuildableResource, ... } from 'tapi.js' 123 | 124 | import 'tapi.js/extensions' // 👈 Use this line to import all the extended functionalities of core types 125 | 126 | // Let's create a simple class... 127 | class TestClass extends BuildableResource { 128 | // You know the drill by now... 129 | } 130 | 131 | // Then make a request and get a promise... 132 | axios.get('/some-url-that-returns-an-object') 133 | // Now let's build the object with its defined builder! 🎉 134 | .as(TestClass) 135 | // Aaaaand we can use the typed object to do whatever we want. 136 | .then((builtObject) => { 137 | builtObject.doSomething(); 138 | }) 139 | ``` 140 | 141 | If you want to select only a specific field of the response object (for example: *data*) you can define it as a string. 142 | 143 | ```typescript 144 | axios.get('/some-url-that-returns-an-object') 145 | // Now let's build the object with its defined builder! 🎉 146 | .as(TestClass, 'data') 147 | // Aaaaand we can use the typed object to do whatever we want. 148 | .then((builtObject) => { 149 | builtObject.doSomething(); 150 | }) 151 | ``` 152 | 153 | ___ 154 | 155 | Thanks for using **tapi**! 😊 I hope it's been useful to you in some way. 156 | -------------------------------------------------------------------------------- /src/core/Builder.ts: -------------------------------------------------------------------------------- 1 | import Describer from "@/helpers/Describer" 2 | import BuildableResource from "@/contracts/BuildableResource" 3 | import ResourceFactory from "@/contracts/ResourceFactory" 4 | import JSONConvertible from "@/contracts/JSONConvertible" 5 | import { deepCopy, } from "@/helpers/functions" 6 | import BuildConfiguration from "@/core/BuildConfiguration" 7 | import Dot from "@/helpers/Dot" 8 | 9 | /** 10 | * This is used to define how a class needs to be constructed from an object. 11 | */ 12 | export default class Builder> implements JSONConvertible { 13 | /** 14 | * The built object to populate. 15 | */ 16 | private baseObject: ResultType 17 | 18 | /** 19 | * A {@link BuildConfiguration} containing all the instructions 20 | * on how the object should be built. 21 | */ 22 | readonly buildConfig: BuildConfiguration 23 | 24 | /** 25 | * Instantiates the given class thanks to a {@link ResourceFactory}. 26 | * 27 | * @param ctor The class that needs to be instantiated. 28 | * @param buildConfig A {@link BuildConfiguration} instance (it overrides the one set in the object) 29 | */ 30 | constructor(ctor: ResourceFactory, buildConfig: BuildConfiguration|null = null) { 31 | this.baseObject = new ctor() 32 | this.buildConfig = buildConfig ?? this.baseObject.buildConfig ?? new BuildConfiguration 33 | } 34 | 35 | private getForeignObjectReference(localPath: string, foreignObject: any = undefined): ObjectReference { 36 | const foreignPath = this.buildConfig.aliases[localPath] ?? localPath 37 | const foreignValue = Dot.get(foreignPath, foreignObject) 38 | 39 | let result: ObjectReference 40 | 41 | if (foreignObject == undefined) { 42 | result = {path: foreignPath, value: null,} 43 | } else { 44 | result = {path: foreignValue != null ? foreignPath : localPath, value: foreignValue,} 45 | } 46 | 47 | result.value = Dot.get(result.path, foreignObject) 48 | 49 | return result 50 | } 51 | 52 | public fromJSON(source: any): ResultType { 53 | const target = this.baseObject 54 | const params = Describer.getParameters(target) 55 | 56 | params.forEach(param => { 57 | if (this.buildConfig.ignores.has(param) || !params.includes(param)) { 58 | return 59 | } 60 | 61 | const foreignObject = this.getForeignObjectReference(param, source) 62 | 63 | if (!source || !foreignObject.value) { 64 | if (this.buildConfig.required.has(param)) { 65 | throw new Error("Invalid input object, missing required parameter: " + param) 66 | } else return 67 | } 68 | 69 | if (target[param] instanceof BuildableResource) { 70 | target[param] = target[param].build.fromJSON(foreignObject.value) 71 | } else if (Array.isArray(target[param])) { 72 | const list: any[] = foreignObject.value 73 | 74 | target[param] = list.map((item: any, index: number) => { 75 | const listClassElement = this.buildConfig.listElementConstructors[param] 76 | if(listClassElement) { 77 | const listElementClassBuilder = listClassElement.build 78 | return listElementClassBuilder.fromJSON(item) 79 | } else if (!this.buildConfig.required.has(param) && foreignObject.value) { 80 | return foreignObject.value[index] 81 | } 82 | }) 83 | } else { 84 | if (this.buildConfig.transformers[param]){ 85 | target[param] = this.buildConfig.transformers[param].in(foreignObject.value) 86 | } else { 87 | target[param] = foreignObject.value 88 | } 89 | } 90 | }) 91 | 92 | return deepCopy(target) as ResultType 93 | } 94 | 95 | public toJSON(source: ResultType): any { 96 | const params = Describer.getParameters(source) 97 | const result = {} 98 | 99 | params.forEach(param => { 100 | const foreignObject = this.getForeignObjectReference(param) 101 | let valueToAssign: any = undefined 102 | 103 | if (this.buildConfig.ignores.has(param)) { 104 | return 105 | } 106 | 107 | if (source[param] instanceof BuildableResource) { 108 | valueToAssign = source[param].build.toJSON(source[param]) 109 | } else if (Array.isArray(source[param])) { 110 | const list: any[] = source[param] 111 | 112 | valueToAssign = list.map((item: any, index: number) => { 113 | const listClassElement = this.buildConfig.listElementConstructors[param] 114 | if(listClassElement) { 115 | const listElementClassBuilder = listClassElement.build 116 | return listElementClassBuilder.toJSON(item) 117 | } else if (source.hasOwnProperty(param)) { 118 | const x = Dot.get(foreignObject.path, result) 119 | return this.buildConfig.transformers[param] ? this.buildConfig.transformers[param].out(x[index]) : x[index] 120 | } 121 | }) 122 | } else { 123 | valueToAssign = this.buildConfig.transformers[param] ? this.buildConfig.transformers[param].out(source[param]) : source[param] 124 | } 125 | 126 | Dot.assign(foreignObject.path, result, valueToAssign) 127 | }) 128 | 129 | delete result["resourceBuildConfiguration"] // FIXME Magic number 130 | 131 | return result 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /docs/layouts/partials/footer.html: -------------------------------------------------------------------------------- 1 | {{- if not (.Param "hideFooter") }} 2 |
3 | {{- if site.Copyright }} 4 | {{ site.Copyright | markdownify }} 5 | {{- else }} 6 | © {{ now.Year }} {{ site.Title }} 7 | {{- end }} 8 | 9 | by 10 | Mattia Sinisi & 11 | contributors 12 | 13 |
14 | {{- end }} 15 | 16 | {{- if (not site.Params.disableScrollToTop) }} 17 | 18 | 19 | 20 | 21 | 22 | {{- end }} 23 | 24 | {{- partial "extend_footer.html" . }} 25 | 26 | 55 | 56 | {{- if (not site.Params.disableScrollToTop) }} 57 | 70 | {{- end }} 71 | 72 | {{- if (not site.Params.disableThemeToggle) }} 73 | 85 | {{- end }} 86 | 87 | {{- if (and (eq .Kind "page") (ne .Layout "archives") (ne .Layout "search") (.Param "ShowCodeCopyButtons")) }} 88 | 135 | {{- end }} -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our 6 | community a harassment-free experience for everyone, regardless of age, body 7 | size, visible or invisible disability, ethnicity, sex characteristics, gender 8 | identity and expression, level of experience, education, socio-economic status, 9 | nationality, personal appearance, race, religion, or sexual identity 10 | and orientation. 11 | 12 | We pledge to act and interact in ways that contribute to an open, welcoming, 13 | diverse, inclusive, and healthy community. 14 | 15 | ## Our Standards 16 | 17 | Examples of behavior that contributes to a positive environment for our 18 | community include: 19 | 20 | * Demonstrating empathy and kindness toward other people 21 | * Being respectful of differing opinions, viewpoints, and experiences 22 | * Giving and gracefully accepting constructive feedback 23 | * Accepting responsibility and apologizing to those affected by our mistakes, 24 | and learning from the experience 25 | * Focusing on what is best not just for us as individuals, but for the 26 | overall community 27 | 28 | Examples of unacceptable behavior include: 29 | 30 | * The use of sexualized language or imagery, and sexual attention or 31 | advances of any kind 32 | * Trolling, insulting or derogatory comments, and personal or political attacks 33 | * Public or private harassment 34 | * Publishing others' private information, such as a physical or email 35 | address, without their explicit permission 36 | * Other conduct which could reasonably be considered inappropriate in a 37 | professional setting 38 | 39 | ## Enforcement Responsibilities 40 | 41 | Community leaders are responsible for clarifying and enforcing our standards of 42 | acceptable behavior and will take appropriate and fair corrective action in 43 | response to any behavior that they deem inappropriate, threatening, offensive, 44 | or harmful. 45 | 46 | Community leaders have the right and responsibility to remove, edit, or reject 47 | comments, commits, code, wiki edits, issues, and other contributions that are 48 | not aligned to this Code of Conduct, and will communicate reasons for moderation 49 | decisions when appropriate. 50 | 51 | ## Scope 52 | 53 | This Code of Conduct applies within all community spaces, and also applies when 54 | an individual is officially representing the community in public spaces. 55 | Examples of representing our community include using an official e-mail address, 56 | posting via an official social media account, or acting as an appointed 57 | representative at an online or offline event. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported to the community leaders responsible for enforcement at 63 | mattia@snisni.it. 64 | All complaints will be reviewed and investigated promptly and fairly. 65 | 66 | All community leaders are obligated to respect the privacy and security of the 67 | reporter of any incident. 68 | 69 | ## Enforcement Guidelines 70 | 71 | Community leaders will follow these Community Impact Guidelines in determining 72 | the consequences for any action they deem in violation of this Code of Conduct: 73 | 74 | ### 1. Correction 75 | 76 | **Community Impact**: Use of inappropriate language or other behavior deemed 77 | unprofessional or unwelcome in the community. 78 | 79 | **Consequence**: A private, written warning from community leaders, providing 80 | clarity around the nature of the violation and an explanation of why the 81 | behavior was inappropriate. A public apology may be requested. 82 | 83 | ### 2. Warning 84 | 85 | **Community Impact**: A violation through a single incident or series 86 | of actions. 87 | 88 | **Consequence**: A warning with consequences for continued behavior. No 89 | interaction with the people involved, including unsolicited interaction with 90 | those enforcing the Code of Conduct, for a specified period of time. This 91 | includes avoiding interactions in community spaces as well as external channels 92 | like social media. Violating these terms may lead to a temporary or 93 | permanent ban. 94 | 95 | ### 3. Temporary Ban 96 | 97 | **Community Impact**: A serious violation of community standards, including 98 | sustained inappropriate behavior. 99 | 100 | **Consequence**: A temporary ban from any sort of interaction or public 101 | communication with the community for a specified period of time. No public or 102 | private interaction with the people involved, including unsolicited interaction 103 | with those enforcing the Code of Conduct, is allowed during this period. 104 | Violating these terms may lead to a permanent ban. 105 | 106 | ### 4. Permanent Ban 107 | 108 | **Community Impact**: Demonstrating a pattern of violation of community 109 | standards, including sustained inappropriate behavior, harassment of an 110 | individual, or aggression toward or disparagement of classes of individuals. 111 | 112 | **Consequence**: A permanent ban from any sort of public interaction within 113 | the community. 114 | 115 | ## Attribution 116 | 117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 118 | version 2.0, available at 119 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. 120 | 121 | Community Impact Guidelines were inspired by [Mozilla's code of conduct 122 | enforcement ladder](https://github.com/mozilla/diversity). 123 | 124 | [homepage]: https://www.contributor-covenant.org 125 | 126 | For answers to common questions about this code of conduct, see the FAQ at 127 | https://www.contributor-covenant.org/faq. Translations are available at 128 | https://www.contributor-covenant.org/translations. 129 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | 635 | Copyright (C) 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | Copyright (C) 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . 675 | --------------------------------------------------------------------------------