├── .travis.yml ├── .vscode └── settings.json ├── LICENSE ├── README.md ├── eslint-rules.js ├── ladc ├── .eslintignore ├── .eslintrc.js ├── .gitignore ├── .npmignore ├── LICENSE ├── README.md ├── build │ └── make-bundle.js ├── package-lock.json ├── package.json ├── src │ ├── adapter-definitions.d.ts │ ├── createPool.ts │ ├── exported-definitions.d.ts │ ├── factories │ │ ├── Cursor.ts │ │ ├── ExecResult.ts │ │ ├── MainConnection.ts │ │ ├── PreparedStatement.ts │ │ └── TransactionConnection.ts │ ├── helpers.ts │ └── index.ts ├── tsconfig.json └── tslint.json ├── mysql-adapter ├── .eslintignore ├── .eslintrc.js ├── .gitignore ├── .npmignore ├── LICENSE ├── README.md ├── build │ └── make-bundle.js ├── package-lock.json ├── package.json ├── src │ ├── AConnection.ts │ ├── exported-definitions.d.ts │ ├── index.ts │ └── promisifyMysql.ts └── tsconfig.json ├── mysql2-adapter ├── .eslintignore ├── .eslintrc.js ├── .gitignore ├── .npmignore ├── LICENSE ├── README.md ├── build │ └── make-bundle.js ├── package-lock.json ├── package.json ├── src │ ├── AConnection.ts │ ├── exported-definitions.d.ts │ └── index.ts └── tsconfig.json ├── pg-adapter ├── .gitignore ├── .npmignore ├── LICENSE ├── README.md ├── build │ └── make-bundle.js ├── package-lock.json ├── package.json ├── src │ ├── AConnection.ts │ ├── exported-definitions.d.ts │ ├── index.ts │ └── promisifyCursor.ts ├── tsconfig.json └── tslint.json ├── sql-bricks-modifier ├── .gitignore ├── .npmignore ├── LICENSE ├── README.md ├── build │ └── make-bundle.js ├── package-lock.json ├── package.json ├── src │ ├── exported-definitions.d.ts │ └── index.ts ├── tsconfig.json └── tslint.json └── sqlite3-adapter ├── .gitignore ├── .npmignore ├── LICENSE ├── README.md ├── build └── make-bundle.js ├── package-lock.json ├── package.json ├── src ├── AConnection.ts ├── exported-definitions.d.ts ├── index.ts └── promisifySqlite3.ts ├── tsconfig.json └── tslint.json /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - 10 4 | - 12 5 | - 14 6 | cache: 7 | directories: 8 | - "node_modules" 9 | env: 10 | - SUB_PROJECT=ladc 11 | - SUB_PROJECT=mysql-adapter 12 | - SUB_PROJECT=mysql2-adapter 13 | - SUB_PROJECT=pg-adapter 14 | - SUB_PROJECT=sqlite3-adapter 15 | - SUB_PROJECT=sql-bricks-modifier 16 | script: cd $SUB_PROJECT && npm i && npm run prepublishOnly 17 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.codeActionsOnSave": { 3 | "source.fixAll": true, 4 | "source.organizeImports": true 5 | }, 6 | "editor.formatOnSave": true, 7 | "editor.insertSpaces": true, 8 | "editor.rulers": [ 9 | 120 10 | ], 11 | "editor.tabSize": 2, 12 | "editor.wordWrapColumn": 132, 13 | "editor.wordWrap": "wordWrapColumn", 14 | "eslint.workingDirectories": [ 15 | { 16 | "pattern": "./ladc" 17 | }, 18 | { 19 | "pattern": "./*-adapter" 20 | }, 21 | { 22 | "pattern": "./*-modifier" 23 | } 24 | ], 25 | "files.encoding": "utf8", 26 | "files.trimTrailingWhitespace": true, 27 | "search.useIgnoreFiles": true, 28 | "tslint.configFile": "tslint.json", 29 | "typescript.locale": "en", 30 | "typescript.preferences.importModuleSpecifier": "relative", 31 | "typescript.preferences.quoteStyle": "double", 32 | "typescript.tsdk": "./ladc/ladc/node_modules/typescript/lib" 33 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Creative Commons Legal Code 2 | 3 | CC0 1.0 Universal 4 | 5 | CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE 6 | LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN 7 | ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS 8 | INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES 9 | REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS 10 | PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM 11 | THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED 12 | HEREUNDER. 13 | 14 | Statement of Purpose 15 | 16 | The laws of most jurisdictions throughout the world automatically confer 17 | exclusive Copyright and Related Rights (defined below) upon the creator 18 | and subsequent owner(s) (each and all, an "owner") of an original work of 19 | authorship and/or a database (each, a "Work"). 20 | 21 | Certain owners wish to permanently relinquish those rights to a Work for 22 | the purpose of contributing to a commons of creative, cultural and 23 | scientific works ("Commons") that the public can reliably and without fear 24 | of later claims of infringement build upon, modify, incorporate in other 25 | works, reuse and redistribute as freely as possible in any form whatsoever 26 | and for any purposes, including without limitation commercial purposes. 27 | These owners may contribute to the Commons to promote the ideal of a free 28 | culture and the further production of creative, cultural and scientific 29 | works, or to gain reputation or greater distribution for their Work in 30 | part through the use and efforts of others. 31 | 32 | For these and/or other purposes and motivations, and without any 33 | expectation of additional consideration or compensation, the person 34 | associating CC0 with a Work (the "Affirmer"), to the extent that he or she 35 | is an owner of Copyright and Related Rights in the Work, voluntarily 36 | elects to apply CC0 to the Work and publicly distribute the Work under its 37 | terms, with knowledge of his or her Copyright and Related Rights in the 38 | Work and the meaning and intended legal effect of CC0 on those rights. 39 | 40 | 1. Copyright and Related Rights. A Work made available under CC0 may be 41 | protected by copyright and related or neighboring rights ("Copyright and 42 | Related Rights"). Copyright and Related Rights include, but are not 43 | limited to, the following: 44 | 45 | i. the right to reproduce, adapt, distribute, perform, display, 46 | communicate, and translate a Work; 47 | ii. moral rights retained by the original author(s) and/or performer(s); 48 | iii. publicity and privacy rights pertaining to a person's image or 49 | likeness depicted in a Work; 50 | iv. rights protecting against unfair competition in regards to a Work, 51 | subject to the limitations in paragraph 4(a), below; 52 | v. rights protecting the extraction, dissemination, use and reuse of data 53 | in a Work; 54 | vi. database rights (such as those arising under Directive 96/9/EC of the 55 | European Parliament and of the Council of 11 March 1996 on the legal 56 | protection of databases, and under any national implementation 57 | thereof, including any amended or successor version of such 58 | directive); and 59 | vii. other similar, equivalent or corresponding rights throughout the 60 | world based on applicable law or treaty, and any national 61 | implementations thereof. 62 | 63 | 2. Waiver. To the greatest extent permitted by, but not in contravention 64 | of, applicable law, Affirmer hereby overtly, fully, permanently, 65 | irrevocably and unconditionally waives, abandons, and surrenders all of 66 | Affirmer's Copyright and Related Rights and associated claims and causes 67 | of action, whether now known or unknown (including existing as well as 68 | future claims and causes of action), in the Work (i) in all territories 69 | worldwide, (ii) for the maximum duration provided by applicable law or 70 | treaty (including future time extensions), (iii) in any current or future 71 | medium and for any number of copies, and (iv) for any purpose whatsoever, 72 | including without limitation commercial, advertising or promotional 73 | purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each 74 | member of the public at large and to the detriment of Affirmer's heirs and 75 | successors, fully intending that such Waiver shall not be subject to 76 | revocation, rescission, cancellation, termination, or any other legal or 77 | equitable action to disrupt the quiet enjoyment of the Work by the public 78 | as contemplated by Affirmer's express Statement of Purpose. 79 | 80 | 3. Public License Fallback. Should any part of the Waiver for any reason 81 | be judged legally invalid or ineffective under applicable law, then the 82 | Waiver shall be preserved to the maximum extent permitted taking into 83 | account Affirmer's express Statement of Purpose. In addition, to the 84 | extent the Waiver is so judged Affirmer hereby grants to each affected 85 | person a royalty-free, non transferable, non sublicensable, non exclusive, 86 | irrevocable and unconditional license to exercise Affirmer's Copyright and 87 | Related Rights in the Work (i) in all territories worldwide, (ii) for the 88 | maximum duration provided by applicable law or treaty (including future 89 | time extensions), (iii) in any current or future medium and for any number 90 | of copies, and (iv) for any purpose whatsoever, including without 91 | limitation commercial, advertising or promotional purposes (the 92 | "License"). The License shall be deemed effective as of the date CC0 was 93 | applied by Affirmer to the Work. Should any part of the License for any 94 | reason be judged legally invalid or ineffective under applicable law, such 95 | partial invalidity or ineffectiveness shall not invalidate the remainder 96 | of the License, and in such case Affirmer hereby affirms that he or she 97 | will not (i) exercise any of his or her remaining Copyright and Related 98 | Rights in the Work or (ii) assert any associated claims and causes of 99 | action with respect to the Work, in either case contrary to Affirmer's 100 | express Statement of Purpose. 101 | 102 | 4. Limitations and Disclaimers. 103 | 104 | a. No trademark or patent rights held by Affirmer are waived, abandoned, 105 | surrendered, licensed or otherwise affected by this document. 106 | b. Affirmer offers the Work as-is and makes no representations or 107 | warranties of any kind concerning the Work, express, implied, 108 | statutory or otherwise, including without limitation warranties of 109 | title, merchantability, fitness for a particular purpose, non 110 | infringement, or the absence of latent or other defects, accuracy, or 111 | the present or absence of errors, whether or not discoverable, all to 112 | the greatest extent permissible under applicable law. 113 | c. Affirmer disclaims responsibility for clearing rights of other persons 114 | that may apply to the Work or any use thereof, including without 115 | limitation any person's Copyright and Related Rights in the Work. 116 | Further, Affirmer disclaims responsibility for obtaining any necessary 117 | consents, permissions or other rights required for any use of the 118 | Work. 119 | d. Affirmer understands and acknowledges that Creative Commons is not a 120 | party to this document and has no duty or obligation with respect to 121 | this CC0 or use of the Work. 122 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Monorepo for LADC 2 | 3 | [![Build Status](https://travis-ci.com/paroi-tech/ladc.svg?branch=master)](https://travis-ci.com/paroi-tech/ladc) 4 | 5 | LADC is a common API on top of relational database (SQL) connectors. It can connect to Postgresql, MariaDB / MySQL, SQLite. The API is inspired from PDO and JDBC. It’s named LADC for “a Layer Above Database Connectors”. 6 | 7 | ## Projects 8 | 9 | - [ladc](https://github.com/paroi-tech/ladc/tree/master/ladc); 10 | - [@ladc/pg-adapter](https://github.com/paroi-tech/ladc/tree/master/pg-adapter) for **Postgresql**, using the _pg_ connector; 11 | - [@ladc/mysql-adapter](https://github.com/paroi-tech/ladc/tree/master/mysql-adapter) for **MariaDB** and **MySQL**, using the _mysql_ connector; 12 | - [@ladc/mysql2-adapter](https://github.com/paroi-tech/ladc/tree/master/mysql2-adapter) for **MariaDB** and **MySQL**, using the _mysql2_ connector; 13 | - [@ladc/sqlite3-adapter](https://github.com/paroi-tech/ladc/tree/master/sqlite3-adapter) for **SQLite**, using the _sqlite3_ connector. 14 | -------------------------------------------------------------------------------- /eslint-rules.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "rules": { 3 | "@typescript-eslint/adjacent-overload-signatures": "warn", 4 | "@typescript-eslint/array-type": [ 5 | "warn", 6 | { 7 | "default": "array" 8 | } 9 | ], 10 | "@typescript-eslint/ban-types": [ 11 | "warn", 12 | { 13 | "types": { 14 | "object": false, 15 | "Function": false, 16 | "Object": { "message": "Prefer `object`?" }, 17 | "Boolean": { "message": "Prefer `boolean`?" }, 18 | "Number": { "message": "Prefer `number`?" }, 19 | "String": { "message": "Prefer `string`?" }, 20 | "Symbol": { "message": "Prefer `symbol`?" } 21 | } 22 | } 23 | ], 24 | // "@typescript-eslint/class-name-casing": "warn", 25 | "@typescript-eslint/consistent-type-assertions": "warn", 26 | "@typescript-eslint/dot-notation": "off", 27 | "@typescript-eslint/explicit-member-accessibility": [ 28 | "warn", 29 | { 30 | "accessibility": "no-public" 31 | } 32 | ], 33 | "@typescript-eslint/explicit-module-boundary-types": "off", 34 | "@typescript-eslint/indent": [ 35 | "warn", 36 | 2 37 | ], 38 | "@typescript-eslint/interface-name-prefix": "off", 39 | "@typescript-eslint/member-delimiter-style": [ 40 | "warn", 41 | { 42 | "multiline": { 43 | "delimiter": "none", 44 | "requireLast": true 45 | }, 46 | "singleline": { 47 | "delimiter": "semi", 48 | "requireLast": false 49 | } 50 | } 51 | ], 52 | "@typescript-eslint/no-empty-function": "warn", 53 | "@typescript-eslint/no-empty-interface": "warn", 54 | "@typescript-eslint/no-explicit-any": "off", 55 | "@typescript-eslint/no-non-null-assertion": "off", 56 | "@typescript-eslint/no-misused-new": "warn", 57 | "@typescript-eslint/no-misused-promises": "off", 58 | "@typescript-eslint/no-namespace": "warn", 59 | "@typescript-eslint/no-parameter-properties": "off", 60 | "@typescript-eslint/no-this-alias": "off", 61 | "@typescript-eslint/no-unnecessary-type-assertion": "off", 62 | "@typescript-eslint/no-unsafe-assignment": "off", 63 | "@typescript-eslint/no-unsafe-call": "off", 64 | "@typescript-eslint/no-unsafe-member-access": "off", 65 | "@typescript-eslint/no-unused-expressions": "warn", 66 | "@typescript-eslint/no-unused-vars": ["warn", { "ignoreRestSiblings": true }], 67 | "@typescript-eslint/no-unsafe-return": "off", 68 | "@typescript-eslint/no-use-before-define": "off", 69 | "@typescript-eslint/no-var-requires": "off", 70 | "@typescript-eslint/prefer-for-of": "warn", 71 | "@typescript-eslint/prefer-function-type": "warn", 72 | "@typescript-eslint/prefer-namespace-keyword": "warn", 73 | "@typescript-eslint/quotes": [ 74 | "warn", 75 | "double" 76 | ], 77 | "@typescript-eslint/restrict-template-expressions": "off", 78 | "@typescript-eslint/semi": [ 79 | "warn", 80 | "never" 81 | ], 82 | "@typescript-eslint/triple-slash-reference": [ 83 | "warn", 84 | { 85 | "path": "always", 86 | "types": "prefer-import", 87 | "lib": "always" 88 | } 89 | ], 90 | "@typescript-eslint/unbound-method": "off", 91 | "@typescript-eslint/unified-signatures": "off", 92 | "arrow-parens": [ 93 | "off", 94 | "always" 95 | ], 96 | "camelcase": "warn", 97 | "comma-dangle": "off", 98 | "complexity": "off", 99 | "constructor-super": "warn", 100 | "curly": "off", 101 | "eol-last": "off", 102 | "eqeqeq": [ 103 | "warn", 104 | "smart" 105 | ], 106 | "guard-for-in": "warn", 107 | "id-blacklist": "warn", 108 | "id-match": "warn", 109 | // "import/order": "warn", 110 | // "jsdoc/check-alignment": "warn", 111 | // "jsdoc/check-indentation": "warn", 112 | // "jsdoc/newline-after-description": "warn", 113 | "max-classes-per-file": "off", 114 | "max-len": "off", 115 | "new-parens": "warn", 116 | "no-bitwise": "warn", 117 | "no-caller": "warn", 118 | "no-case-declarations": "off", 119 | "no-cond-assign": "off", 120 | "no-console": "warn", 121 | "no-constant-condition": "off", 122 | "no-debugger": "warn", 123 | "no-empty": "warn", 124 | "no-eval": "warn", 125 | "no-fallthrough": "off", 126 | "no-invalid-this": "off", 127 | "no-multiple-empty-lines": "off", 128 | "no-new-wrappers": "warn", 129 | "no-prototype-builtins": "off", 130 | "no-shadow": [ 131 | "off", 132 | { 133 | "hoist": "all" 134 | } 135 | ], 136 | "no-throw-literal": "warn", 137 | "no-trailing-spaces": "warn", 138 | "no-undef-init": "warn", 139 | "no-underscore-dangle": "off", 140 | "no-unsafe-finally": "warn", 141 | "no-unused-labels": "warn", 142 | "no-var": "warn", 143 | "object-shorthand": "off", 144 | "one-var": [ 145 | "warn", 146 | "never" 147 | ], 148 | "prefer-arrow/prefer-arrow-functions": "off", 149 | "prefer-const": [ 150 | "error", { "destructuring": "all" } 151 | ], 152 | "quote-props": "off", 153 | "radix": "off", 154 | "space-before-function-paren": "off", 155 | "spaced-comment": [ 156 | "warn", 157 | "always", 158 | { 159 | "markers": [ 160 | "/" 161 | ] 162 | } 163 | ], 164 | "use-isnan": "warn", 165 | "valid-typeof": "off" 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /ladc/.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .* 3 | *.js 4 | /*.d.ts 5 | build -------------------------------------------------------------------------------- /ladc/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "env": { 3 | "es6": true, 4 | "node": true 5 | }, 6 | "plugins": [ 7 | "@typescript-eslint" 8 | ], 9 | "parser": "@typescript-eslint/parser", 10 | "parserOptions": { 11 | "ecmaVersion": 2018, 12 | "project": "./tsconfig.json", 13 | "sourceType": "module" 14 | }, 15 | "extends": [ 16 | "eslint:recommended", 17 | "plugin:@typescript-eslint/recommended", 18 | "plugin:@typescript-eslint/recommended-requiring-type-checking", 19 | "../eslint-rules" 20 | ], 21 | } 22 | -------------------------------------------------------------------------------- /ladc/.gitignore: -------------------------------------------------------------------------------- 1 | .npmrc 2 | node_modules 3 | /build/compiled 4 | /ladc.* -------------------------------------------------------------------------------- /ladc/.npmignore: -------------------------------------------------------------------------------- 1 | /build/compiled -------------------------------------------------------------------------------- /ladc/LICENSE: -------------------------------------------------------------------------------- 1 | CC0 1.0 Universal 2 | 3 | Statement of Purpose 4 | 5 | The laws of most jurisdictions throughout the world automatically confer 6 | exclusive Copyright and Related Rights (defined below) upon the creator and 7 | subsequent owner(s) (each and all, an "owner") of an original work of 8 | authorship and/or a database (each, a "Work"). 9 | 10 | Certain owners wish to permanently relinquish those rights to a Work for the 11 | purpose of contributing to a commons of creative, cultural and scientific 12 | works ("Commons") that the public can reliably and without fear of later 13 | claims of infringement build upon, modify, incorporate in other works, reuse 14 | and redistribute as freely as possible in any form whatsoever and for any 15 | purposes, including without limitation commercial purposes. These owners may 16 | contribute to the Commons to promote the ideal of a free culture and the 17 | further production of creative, cultural and scientific works, or to gain 18 | reputation or greater distribution for their Work in part through the use and 19 | efforts of others. 20 | 21 | For these and/or other purposes and motivations, and without any expectation 22 | of additional consideration or compensation, the person associating CC0 with a 23 | Work (the "Affirmer"), to the extent that he or she is an owner of Copyright 24 | and Related Rights in the Work, voluntarily elects to apply CC0 to the Work 25 | and publicly distribute the Work under its terms, with knowledge of his or her 26 | Copyright and Related Rights in the Work and the meaning and intended legal 27 | effect of CC0 on those rights. 28 | 29 | 1. Copyright and Related Rights. A Work made available under CC0 may be 30 | protected by copyright and related or neighboring rights ("Copyright and 31 | Related Rights"). Copyright and Related Rights include, but are not limited 32 | to, the following: 33 | 34 | i. the right to reproduce, adapt, distribute, perform, display, communicate, 35 | and translate a Work; 36 | 37 | ii. moral rights retained by the original author(s) and/or performer(s); 38 | 39 | iii. publicity and privacy rights pertaining to a person's image or likeness 40 | depicted in a Work; 41 | 42 | iv. rights protecting against unfair competition in regards to a Work, 43 | subject to the limitations in paragraph 4(a), below; 44 | 45 | v. rights protecting the extraction, dissemination, use and reuse of data in 46 | a Work; 47 | 48 | vi. database rights (such as those arising under Directive 96/9/EC of the 49 | European Parliament and of the Council of 11 March 1996 on the legal 50 | protection of databases, and under any national implementation thereof, 51 | including any amended or successor version of such directive); and 52 | 53 | vii. other similar, equivalent or corresponding rights throughout the world 54 | based on applicable law or treaty, and any national implementations thereof. 55 | 56 | 2. Waiver. To the greatest extent permitted by, but not in contravention of, 57 | applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and 58 | unconditionally waives, abandons, and surrenders all of Affirmer's Copyright 59 | and Related Rights and associated claims and causes of action, whether now 60 | known or unknown (including existing as well as future claims and causes of 61 | action), in the Work (i) in all territories worldwide, (ii) for the maximum 62 | duration provided by applicable law or treaty (including future time 63 | extensions), (iii) in any current or future medium and for any number of 64 | copies, and (iv) for any purpose whatsoever, including without limitation 65 | commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes 66 | the Waiver for the benefit of each member of the public at large and to the 67 | detriment of Affirmer's heirs and successors, fully intending that such Waiver 68 | shall not be subject to revocation, rescission, cancellation, termination, or 69 | any other legal or equitable action to disrupt the quiet enjoyment of the Work 70 | by the public as contemplated by Affirmer's express Statement of Purpose. 71 | 72 | 3. Public License Fallback. Should any part of the Waiver for any reason be 73 | judged legally invalid or ineffective under applicable law, then the Waiver 74 | shall be preserved to the maximum extent permitted taking into account 75 | Affirmer's express Statement of Purpose. In addition, to the extent the Waiver 76 | is so judged Affirmer hereby grants to each affected person a royalty-free, 77 | non transferable, non sublicensable, non exclusive, irrevocable and 78 | unconditional license to exercise Affirmer's Copyright and Related Rights in 79 | the Work (i) in all territories worldwide, (ii) for the maximum duration 80 | provided by applicable law or treaty (including future time extensions), (iii) 81 | in any current or future medium and for any number of copies, and (iv) for any 82 | purpose whatsoever, including without limitation commercial, advertising or 83 | promotional purposes (the "License"). The License shall be deemed effective as 84 | of the date CC0 was applied by Affirmer to the Work. Should any part of the 85 | License for any reason be judged legally invalid or ineffective under 86 | applicable law, such partial invalidity or ineffectiveness shall not 87 | invalidate the remainder of the License, and in such case Affirmer hereby 88 | affirms that he or she will not (i) exercise any of his or her remaining 89 | Copyright and Related Rights in the Work or (ii) assert any associated claims 90 | and causes of action with respect to the Work, in either case contrary to 91 | Affirmer's express Statement of Purpose. 92 | 93 | 4. Limitations and Disclaimers. 94 | 95 | a. No trademark or patent rights held by Affirmer are waived, abandoned, 96 | surrendered, licensed or otherwise affected by this document. 97 | 98 | b. Affirmer offers the Work as-is and makes no representations or warranties 99 | of any kind concerning the Work, express, implied, statutory or otherwise, 100 | including without limitation warranties of title, merchantability, fitness 101 | for a particular purpose, non infringement, or the absence of latent or 102 | other defects, accuracy, or the present or absence of errors, whether or not 103 | discoverable, all to the greatest extent permissible under applicable law. 104 | 105 | c. Affirmer disclaims responsibility for clearing rights of other persons 106 | that may apply to the Work or any use thereof, including without limitation 107 | any person's Copyright and Related Rights in the Work. Further, Affirmer 108 | disclaims responsibility for obtaining any necessary consents, permissions 109 | or other rights required for any use of the Work. 110 | 111 | d. Affirmer understands and acknowledges that Creative Commons is not a 112 | party to this document and has no duty or obligation with respect to this 113 | CC0 or use of the Work. 114 | 115 | For more information, please see 116 | 117 | -------------------------------------------------------------------------------- /ladc/README.md: -------------------------------------------------------------------------------- 1 | # LADC 2 | 3 | [![Build Status](https://travis-ci.com/paroi-tech/ladc.svg?branch=master)](https://travis-ci.com/paroi-tech/ladc) 4 | [![Dependencies Status](https://david-dm.org/paroi-tech/ladc/status.svg)](https://david-dm.org/paroi-tech/ladc) 5 | 6 | 7 | 8 | [![npm](https://img.shields.io/npm/dm/ladc)](https://www.npmjs.com/package/ladc) 9 | ![Type definitions](https://img.shields.io/npm/types/ladc) 10 | [![GitHub](https://img.shields.io/github/license/paroi-tech/ladc)](https://github.com/paroi-tech/ladc) 11 | 12 | LADC is a common API on top of relational database (SQL) connectors. It can connect to Postgresql, MariaDB, MySQL, SQLite. The API is inspired from PDO and JDBC. It’s named LADC for “a Layer Above Database Connectors”. 13 | 14 | ## A sample of code 15 | 16 | Here is an example of code that uses a LADC connection: 17 | 18 | ```ts 19 | async function showMeSomeCode(cn) { 20 | const result = await cn.exec( 21 | "insert into test (message) values ('Hello, World!')" 22 | ); 23 | const newId = result.getInsertedIdAsString(); 24 | 25 | const row = await cn.singleRow( 26 | "select message, ts from test where test_id = $1", 27 | [newId] 28 | ); 29 | 30 | console.log(`Inserted row ${newId}:`, row); 31 | } 32 | ``` 33 | 34 | ## Tutorial: Getting Started with LADC 35 | 36 | LADC works with Node 8 or above. Add it to a project: 37 | 38 | ```sh 39 | npm install ladc 40 | ``` 41 | 42 | Then you'll need an adapter for the DBMS of your choice. Here are the available adapters: 43 | 44 | - [@ladc/pg-adapter](https://github.com/paroi-tech/ladc/tree/master/pg-adapter) for **Postgresql**, using the _pg_ connector; 45 | - [@ladc/mysql2-adapter](https://github.com/paroi-tech/ladc/tree/master/mysql2-adapter) for **MariaDB** and **MySQL**, using the _mysql2_ connector; 46 | - [@ladc/sqlite3-adapter](https://github.com/paroi-tech/ladc/tree/master/sqlite3-adapter) for **SQLite**, using the _sqlite3_ connector. 47 | 48 | Let's pick the SQLite's one: 49 | 50 | ```sh 51 | npm install @ladc/sqlite3-adapter 52 | ``` 53 | 54 | The code below show how to connect to a SQLite database: 55 | 56 | ```ts 57 | import ladc from "ladc"; 58 | import sqlite3Adapter from "@ladc/sqlite3-adapter"; 59 | 60 | function createConnection() { 61 | return ladc({ 62 | adapter: sqlite3Adapter({ fileName: `${__dirname}/testdb.sqlite` }), 63 | initConnection: async (cn) => { 64 | await cn.exec("PRAGMA foreign_keys = ON"); 65 | }, 66 | }); 67 | } 68 | ``` 69 | 70 | The SQLite driver will create an empty database in a new `testdb.sqlite` file if it does not exist. We can then create a `test` table: 71 | 72 | ```ts 73 | async function createSchema(cn) { 74 | await cn.script(` 75 | create table if not exists test ( 76 | test_id integer not null primary key autoincrement, 77 | message varchar(250) not null, 78 | ts timestamp not null default current_timestamp 79 | ); 80 | `); 81 | } 82 | ``` 83 | 84 | Finally, here is how to execute all this stuff. 85 | 86 | ```ts 87 | async function main() { 88 | const cn = createConnection(); 89 | try { 90 | await createSchema(cn); 91 | await showMeSomeCode(cn); 92 | } finally { 93 | await cn.close(); 94 | } 95 | } 96 | 97 | main().catch(console.log); 98 | ``` 99 | 100 | ## The Particular Case of Transactions in Asynchronous Programming 101 | 102 | In asynchronous programming, it is common to open once a connection to a database. But we shouldn’t use a common connection for transactions, because other queries from other callbacks could be unintentionally executed in the transaction. 103 | 104 | LADC provides **a pool of connections**. Each transaction takes an exclusive underlying connection. When the transaction is committed or rolled back, the underlying connection is released into the pool. In addition, the mechanism is optimized so that, if no operation has taken place simultaneously outside the transaction, then the transaction will have simply used the main underlying connection without opening a second one. 105 | 106 | Here is an example of code with a transaction: 107 | 108 | ```ts 109 | async function transactionExample(cn) { 110 | // Get an exclusive underlying connection in 'tx' 111 | const tx = await cn.beginTransaction(); 112 | try { 113 | // Use 'tx' to execute some queries 114 | const result = await tx.exec("insert into test (message) values ($1)", [ 115 | "Message 1 of the transaction", 116 | ]); 117 | const newId = result.getInsertedId(); 118 | await tx.exec("insert into test (message) values ($1)", [ 119 | `Message 2 related to ${newId}`, 120 | ]); 121 | // A commit releases the underlying connection 122 | await tx.commit(); 123 | } finally { 124 | if (tx.inTransaction) { 125 | // A rollback releases the underlying connection, too 126 | await tx.rollback(); 127 | } 128 | } 129 | } 130 | ``` 131 | 132 | ## Prepared Statements 133 | 134 | Drivers for Node.js allow to start several prepared statements on the same connection. But the way to proceed is very different from a DBMS to another. The LADC API provides a common way to use prepared statements: 135 | 136 | ```ts 137 | async function exampleWithPreparedStatement(cn, dialect) { 138 | const messages = ["Hello, World!", "Hi there!", "Hi!"]; 139 | const ps = await cn.prepare(`insert into test (message) values ($1)`); 140 | for (const message of messages) await ps.exec([message]); 141 | await ps.close(); 142 | } 143 | ``` 144 | 145 | ## Cursors 146 | 147 | A LADC cursor implements the interfaces `AsyncIterable` and `AsyncIterator`. Here is how to use a cursor with Node.js 10 and above: 148 | 149 | ```ts 150 | async function exampleWithCursor(cn) { 151 | const cursor = await cn.cursor("select test_id, message from test"); 152 | for await (let row of cursor) console.log(row); 153 | } 154 | ``` 155 | 156 | Notice: 157 | 158 | - There is a limitation of one cursor by underlying connection; 159 | - The MySQL connector doesn't provide cursors. 160 | 161 | ## How to Integrate a Query Builder 162 | 163 | LADC will integrate well with the query builder [SQL Bricks](https://csnw.github.io/sql-bricks/), using the package [@ladc/sql-bricks-modifier](https://github.com/paroi-tech/ladc/sql-bricks-modifier). 164 | 165 | Add the dependencies: 166 | 167 | ```sh 168 | npm install sql-bricks @ladc/sql-bricks-modifier 169 | ``` 170 | 171 | And create the modified connection: 172 | 173 | ```ts 174 | import sqlBricksModifier from "@ladc/sql-bricks-modifier" 175 | import ladc from "ladc" 176 | 177 | const cn = ladc({ 178 | adapter: /* … adapter to your DBMS here … */, 179 | modifier: sqlBricksModifier() 180 | }) // TypeScript users, append here: as SBMainConnection 181 | ``` 182 | 183 | Then, use it: 184 | 185 | ```ts 186 | import { select, like } from "sql-bricks"; 187 | 188 | async function exampleWithSqlBricks(cn) { 189 | const q = select("test_id, message") 190 | .from("test") 191 | .where(like("message", "Hi%")); 192 | const rows = await cn.all(q); 193 | console.log(rows); 194 | } 195 | ``` 196 | 197 | ## Log Errors 198 | 199 | Because LADC uses a pool of underlying connections, errors can occur independently of any query. By default, independant errors are logged with `console.error(message)`. But it is possible to log them where you want: 200 | 201 | ```ts 202 | import ladc from "ladc"; 203 | 204 | const cn = ladc({ 205 | // … 206 | logError: (err) => { 207 | /* Do something with the error. */ 208 | }, 209 | }); 210 | ``` 211 | 212 | ## The Complete API 213 | 214 | ### Members of a `MainConnection` 215 | 216 | Common methods between `MainConnection` and `TransactionConnection`: 217 | 218 | - `cn.prepare(sql)` returns a promise of a `PreparedStatement`; 219 | - `cn.exec(sql, params?)` executes the query and returns a promise of an `ExecResult`; 220 | - `cn.all(sql, params?)` executes the select query and returns a promise of an array of rows; 221 | - `cn.singleRow(sql, params?)` fetches with `cn.all` and returns the single row; 222 | - `cn.singleValue(sql, params?)` fetches with `cn.all` and returns the single value of the single row; 223 | - `cn.cursor(sql, params?)` opens a cursor and returns a promise of a `AsyncIterableIterator`. 224 | 225 | Members that are specific to a `MainConnection`: 226 | 227 | - `cn.beginTransaction()` starts a transaction and returns a promise of a `TransactionConnection`; 228 | - `cn.script(sql)` executes a multi-line script; 229 | - `cn.close()` closes the LADC connection, this includes closing the pool of underlying connections. 230 | 231 | ### Members of an `ExecResult` 232 | 233 | - `result.affectedRows` is a readonly number; 234 | - `result.getInsertedId()` returns the inserted identifier; 235 | - `result.getInsertedIdAsNumber()` returns the inserted identifier as a `number`; 236 | - `result.getInsertedIdAsString()` returns the inserted identifier as a `string`. 237 | 238 | ### Members of a `PreparedStatement` 239 | 240 | - `ps.bind(params)` binds values to the specified parameters; 241 | - `ps.bind(indexOrKey, value)` binds a value to the specified parameter; 242 | - `ps.unbind()` unbinds all the bound values; 243 | - `ps.unbind(indexOrKey)` unbinds the value from the specified parameter; 244 | - `ps.exec(params?)` executes the query and returns a promise of an `ExecResult`; 245 | - `ps.all(params?)` executes the select query and returns a promise of an array of rows; 246 | - `ps.singleRow(params?)` fetches with `ps.all` and returns the single row; 247 | - `ps.singleValue(params?)` fetches with `ps.all` and returns the single value of the single row; 248 | - `ps.cursor(params?)` opens a cursor and returns a promise of a `AsyncIterableIterator`; 249 | - `ps.close()` closes the prepared statement. 250 | 251 | ### Members of a `TransactionConnection` 252 | 253 | Common methods between `MainConnection` and `TransactionConnection`: 254 | 255 | - `tx.prepare(sql)` returns a promise of a `PreparedStatement`; 256 | - `tx.exec(sql, params?)` executes the query and returns a promise of an `ExecResult`; 257 | - `tx.all(sql, params?)` executes the select query and returns a promise of an array of rows; 258 | - `tx.singleRow(sql, params?)` fetches with `tx.all` and returns the single row; 259 | - `tx.singleValue(sql, params?)` fetches with `tx.all` and returns the single value of the single row; 260 | - `tx.cursor(sql, params?)` opens a cursor and returns a promise of a `AsyncIterableIterator`. 261 | 262 | Members that are specific to a `TransactionConnection`: 263 | 264 | - `tx.inTransaction` is a readonly boolean; 265 | - `tx.rollback()` rollbacks the transaction, then releases the underlying connection to the pool; 266 | - `tx.commit()` commits the transaction, then releases the underlying connection to the pool. 267 | 268 | ## Contribute 269 | 270 | With VS Code, our recommanded plugin is: 271 | 272 | - **TSLint** from Microsoft (`ms-vscode.vscode-typescript-tslint-plugin`) 273 | -------------------------------------------------------------------------------- /ladc/build/make-bundle.js: -------------------------------------------------------------------------------- 1 | const { promisify } = require("util") 2 | const fs = require("fs") 3 | const path = require("path") 4 | const rollup = require("rollup") 5 | const { minify } = require("terser") 6 | 7 | const readFile = promisify(fs.readFile) 8 | const writeFile = promisify(fs.writeFile) 9 | 10 | const bundleName = "ladc" 11 | const srcPath = path.join(__dirname, "..", "src") 12 | const compiledPath = path.join(__dirname, "compiled") 13 | const distNpmPath = path.join(__dirname, "..") 14 | 15 | async function build() { 16 | const bundle = await rollup.rollup({ 17 | input: path.join(compiledPath, "index.js") 18 | }) 19 | const { output } = await bundle.generate({ 20 | format: "commonjs", 21 | exports: "named", 22 | sourcemap: false, 23 | }) 24 | 25 | const code = output[0].code 26 | 27 | if (!code) 28 | throw new Error("Missing code") 29 | 30 | // const minified = await minify(output[0].code, { sourceMap: false }) 31 | // if (minified.error) 32 | // throw minified.error 33 | // if (!minified.code) 34 | // throw new Error("Missing code") 35 | 36 | await writeFile(path.join(distNpmPath, `${bundleName}.js`), code) 37 | await writeFile(path.join(distNpmPath, `${bundleName}.d.ts`), await makeDefinitionsCode()) 38 | } 39 | 40 | async function makeDefinitionsCode() { 41 | const defs = [ 42 | "// -- Usage definitions --", 43 | removeLocalImportsExports((await readFile(path.join(srcPath, "exported-definitions.d.ts"), "utf-8")).trim()), 44 | "// -- Adapter definitions --", 45 | removeLocalImportsExports((await readFile(path.join(srcPath, "adapter-definitions.d.ts"), "utf-8")).trim()), 46 | "// -- Entry point definition --", 47 | removeSemicolons( 48 | removeLocalImportsExports((await readFile(path.join(compiledPath, "index.d.ts"), "utf-8")).trim()), 49 | ) 50 | ] 51 | return defs.join("\n\n") 52 | } 53 | 54 | function removeLocalImportsExports(code) { 55 | const localImportExport = /^\s*(import|export) .* from "\.\/.*"\s*;?\s*$/ 56 | return code.split("\n").filter(line => { 57 | return !localImportExport.test(line) 58 | }).join("\n").trim() 59 | } 60 | 61 | function removeSemicolons(code) { 62 | return code.replace(/;/g, "") 63 | } 64 | 65 | build().catch(err => console.log(err.message, err.stack)) 66 | -------------------------------------------------------------------------------- /ladc/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ladc", 3 | "version": "0.22.14", 4 | "description": "LADC is a common API on top of relational database (SQL) connectors. It connects to Postgresql, MariaDB, MySQL, SQLite.", 5 | "main": "ladc.js", 6 | "types": "ladc.d.ts", 7 | "devDependencies": { 8 | "@types/node": "^12.12.55", 9 | "npm-run-all": "^4.1.5", 10 | "rimraf": "^3.0.2", 11 | "rollup": "^2.26.10", 12 | "terser": "^5.3.0", 13 | "typescript": "^4.0.2", 14 | "eslint": "~7.7.0", 15 | "@typescript-eslint/eslint-plugin": "~3.10.1", 16 | "@typescript-eslint/parser": "~3.10.1" 17 | }, 18 | "scripts": { 19 | "build": "run-s clear tsc make-bundle", 20 | "tsc": "tsc", 21 | "tsc:watch": "tsc --watch", 22 | "make-bundle": "node build/make-bundle", 23 | "clear": "rimraf build/compiled/*", 24 | "lint": "eslint . --ext .ts --max-warnings=0", 25 | "prepublishOnly": "npm run lint && npm run build" 26 | }, 27 | "repository": { 28 | "type": "git", 29 | "url": "git+https://github.com/paroi-tech/ladc.git" 30 | }, 31 | "homepage": "https://github.com/paroi-tech/ladc/tree/master/ladc", 32 | "author": { 33 | "name": "Paleo" 34 | }, 35 | "license": "CC0-1.0", 36 | "keywords": [ 37 | "sqlite", 38 | "postgresql", 39 | "mariadb", 40 | "mysql" 41 | ] 42 | } -------------------------------------------------------------------------------- /ladc/src/adapter-definitions.d.ts: -------------------------------------------------------------------------------- 1 | import { ResultRow, SqlParameters } from "./exported-definitions" 2 | 3 | export interface LadcAdapter { 4 | createConnection: (createOptions?: ACreateConnectionOptions) => Promise 5 | capabilities: AdapterCapabilities 6 | hooks?: AdapterHooks 7 | } 8 | 9 | export interface AdapterHooks { 10 | beginTransaction?(cn: AConnection): Promise 11 | commit?(cn: AConnection): Promise 12 | rollback?(cn: AConnection): Promise 13 | } 14 | 15 | export interface ACreateConnectionOptions { 16 | enableScript?: boolean 17 | } 18 | 19 | export interface AdapterCapabilities { 20 | namedParameters?: boolean 21 | preparedStatements?: boolean 22 | cursors?: boolean 23 | script?: boolean | "onASeparateConnection" 24 | } 25 | 26 | export interface AConnection { 27 | prepare(sql: string): Promise> 28 | exec(sql: string, params?: SqlParameters): Promise 29 | all(sql: string, params?: SqlParameters): Promise 30 | cursor(sql: string, params?: SqlParameters): Promise> 31 | script(sql: string): Promise 32 | close(): Promise 33 | } 34 | 35 | export interface AExecResult { 36 | readonly affectedRows: number 37 | /** 38 | * NB: This method can return `undefined` if there is no value. 39 | */ 40 | getInsertedId(options?: unknown): unknown 41 | } 42 | 43 | export interface APreparedStatement { 44 | exec(params?: SqlParameters): Promise 45 | all(params?: SqlParameters): Promise 46 | cursor(params?: SqlParameters): Promise> 47 | close(): Promise 48 | } -------------------------------------------------------------------------------- /ladc/src/createPool.ts: -------------------------------------------------------------------------------- 1 | import { AConnection } from "./adapter-definitions" 2 | import { DebugEvent, LadcOptions } from "./exported-definitions" 3 | 4 | export interface Pool { 5 | /** 6 | * @param exclusive Default value is `false`. 7 | */ 8 | grab(exclusive?: boolean): Promise 9 | release(db: AConnection): void 10 | abandon(db: AConnection): void 11 | close(): Promise 12 | } 13 | 14 | interface PoolItem { 15 | db: AConnection 16 | releaseTime: number 17 | } 18 | 19 | export default function createPool(provider: () => Promise, options: LadcOptions): Pool { 20 | const poolOptions = options.poolOptions ?? {} 21 | const connectionTtl = poolOptions.connectionTtl ?? 60 22 | const logMonitoring = poolOptions.logMonitoring ?? (() => { 23 | // Nothing to do. 24 | }) 25 | const keepOneConnection = !!poolOptions.keepOneConnection 26 | // eslint-disable-next-line no-console 27 | const logError = options.logError ?? (err => console.error(err)) 28 | const logDebug = options.logDebug 29 | 30 | if (logDebug) 31 | provider = debugWrapProvider(provider, logDebug) 32 | 33 | let closed = false 34 | let available: PoolItem[] = [] 35 | let nonExclusiveDb: AConnection | undefined 36 | let nonExclusiveCount = 0 37 | let cleaning: any | undefined 38 | 39 | let counter = 0 40 | const identifiers = new WeakMap() 41 | 42 | return { 43 | grab: async (exclusive = false) => { 44 | if (closed) 45 | throw new Error("Invalid call to \"grab\", the pool is closed") 46 | 47 | if (!exclusive && nonExclusiveDb) { 48 | ++nonExclusiveCount 49 | return nonExclusiveDb 50 | } 51 | 52 | const item = available.pop() 53 | let db: AConnection 54 | if (item) 55 | db = item.db 56 | else { 57 | db = await provider() 58 | const id = ++counter 59 | if (logDebug) 60 | db = debugWrapAsyncMethods(db, id, logDebug) 61 | identifiers.set(db, id) 62 | logMonitoring({ event: "open", cn: db, id: identifiers.get(db) }) 63 | } 64 | 65 | if (!exclusive) { 66 | ++nonExclusiveCount 67 | nonExclusiveDb = db 68 | } 69 | 70 | logMonitoring({ event: "grab", cn: db, id: identifiers.get(db) || -123 }) 71 | return db 72 | }, 73 | release: (db: AConnection) => { 74 | logMonitoring({ event: "release", cn: db, id: identifiers.get(db) }) 75 | if (db === nonExclusiveDb) { 76 | if (--nonExclusiveCount === 0) { 77 | nonExclusiveDb = undefined 78 | available.push({ db, releaseTime: Date.now() }) 79 | } 80 | } else 81 | available.push({ db, releaseTime: Date.now() }) 82 | if (closed) 83 | cleanOldConnections(true) 84 | else 85 | startCleaning() 86 | }, 87 | abandon: (db: AConnection) => { 88 | logMonitoring({ event: "abandon", cn: db, id: identifiers.get(db) }) 89 | if (db === nonExclusiveDb) { 90 | --nonExclusiveCount 91 | nonExclusiveDb = undefined 92 | } 93 | db.close().catch(err => logError(err)) 94 | }, 95 | close: async () => { 96 | if (closed) 97 | throw new Error("Invalid call to \"close\", the pool is already closed") 98 | closed = true 99 | const closeAll = available.map(item => { 100 | logMonitoring({ event: "close", cn: item.db, id: identifiers.get(item.db) }) 101 | return item.db.close() 102 | }) 103 | available = [] 104 | await Promise.all(closeAll) 105 | if (cleaning) 106 | clearInterval(cleaning) 107 | } 108 | } 109 | 110 | function startCleaning() { 111 | if (cleaning) 112 | return 113 | let stopCount = 0 114 | cleaning = setInterval(() => { 115 | cleanOldConnections() 116 | if (available.length > 0) 117 | stopCount = 0 118 | else if (++stopCount >= 10) { 119 | clearInterval(cleaning) 120 | cleaning = undefined 121 | } 122 | }, 1000) 123 | cleaning.unref() 124 | } 125 | 126 | function cleanOldConnections(force = false) { 127 | const olderThanTime = Date.now() - 1000 * connectionTtl 128 | let index: number 129 | const lastIndex = available.length - 1 130 | for (index = 0; index <= lastIndex; ++index) { 131 | if (!force) { 132 | const tooOld = available[index].releaseTime > olderThanTime 133 | const keepThisOne = keepOneConnection && !nonExclusiveDb && index === lastIndex 134 | if (tooOld || keepThisOne) 135 | break 136 | } 137 | const db = available[index].db 138 | logMonitoring({ event: "close", cn: db, id: identifiers.get(db) }) 139 | db.close().catch(err => logError(err)) 140 | } 141 | if (index > 0) 142 | available = available.slice(index) 143 | } 144 | 145 | function debugWrapProvider(provider: () => Promise, logDebug: (debug: DebugEvent) => void): () => Promise { 146 | return async () => { 147 | try { 148 | const result = await provider() 149 | logDebug({ result }) 150 | return result 151 | } catch (error) { 152 | logDebug({ error }) 153 | throw error 154 | } 155 | } 156 | } 157 | 158 | function debugWrapAsyncMethods(db: AConnection, idInPool: number, logDebug: (debug: DebugEvent) => void): AConnection { 159 | const inTransactions = new WeakSet() 160 | const wrap: any = {} 161 | for (const name of Object.keys(db)) { 162 | wrap[name] = async (...args: any[]) => { 163 | if (name === "exec" && args.length === 1 && args[0] === "begin") 164 | inTransactions.add(wrap) 165 | try { 166 | const result = await (db as any)[name](...args) 167 | logDebug({ 168 | callingContext: { 169 | connection: db, 170 | method: name, 171 | args, 172 | inTransaction: inTransactions.has(wrap), 173 | idInPool, 174 | }, 175 | result 176 | }) 177 | return result 178 | } catch (error) { 179 | logDebug({ 180 | callingContext: { 181 | connection: db, 182 | method: name, 183 | args, 184 | inTransaction: inTransactions.has(wrap), 185 | idInPool 186 | }, 187 | error 188 | }) 189 | throw error 190 | } 191 | } 192 | } 193 | return wrap 194 | } 195 | } 196 | -------------------------------------------------------------------------------- /ladc/src/exported-definitions.d.ts: -------------------------------------------------------------------------------- 1 | import { AConnection, LadcAdapter } from "./adapter-definitions" 2 | 3 | export interface LadcModifier { 4 | /** 5 | * This callback will be executed for each new `MainConnection` object. It returns the same or another object that will be used as the `MainConnection`. 6 | */ 7 | modifyConnection?(cn: C): C 8 | /** 9 | * This callback will be executed for each new `PreparedStatement` object. It returns the same or another object that will be used as the `PreparedStatement`. 10 | */ 11 | modifyPreparedStatement?(ps: PreparedStatement): PreparedStatement 12 | } 13 | 14 | export interface LadcOptions { 15 | adapter: LadcAdapter 16 | modifier?: LadcModifier 17 | /** 18 | * This callback will be executed for each new `MainConnection` when it has a new underlying connection created by the pool. It is a right place to update the underlying connection with `PRAGMA` orders. 19 | */ 20 | initConnection?(cn: AConnection): void | Promise 21 | /** 22 | * The configuration of the connection pool. 23 | */ 24 | poolOptions?: PoolOptions 25 | /** 26 | * By default, unhandled errors will be logged with `console.error`. 27 | */ 28 | logError?(error: unknown): void 29 | /** 30 | * Activate development mode. 31 | */ 32 | logDebug?(ev: DebugEvent): void 33 | } 34 | 35 | export interface DebugEventContext { 36 | connection: AConnection 37 | method: string 38 | args: any[] 39 | inTransaction: boolean 40 | idInPool: number 41 | } 42 | 43 | export interface DebugEvent { 44 | callingContext?: DebugEventContext 45 | /** 46 | * Set when an error occured 47 | */ 48 | error?: any 49 | /** 50 | * Maybe defined only when there is no error 51 | */ 52 | result?: any 53 | } 54 | 55 | export interface PoolMonitoring { 56 | event: "open" | "close" | "grab" | "release" | "abandon" 57 | cn: AConnection 58 | id?: number 59 | } 60 | 61 | export interface PoolOptions { 62 | /** 63 | * In seconds. Default value is: 60. 64 | */ 65 | connectionTtl?: number 66 | logMonitoring?(monitoring: PoolMonitoring): void 67 | keepOneConnection?: boolean 68 | } 69 | 70 | export type SqlParameters = any[] | { [key: string]: any } 71 | 72 | export interface ResultRow { 73 | [columnName: string]: unknown 74 | } 75 | 76 | export interface Connection { 77 | prepare(sql: string): Promise> 78 | exec(sql: string, params?: SqlParameters): Promise 79 | 80 | all(sql: string, params?: SqlParameters): Promise 81 | singleRow(sql: string, params?: SqlParameters): Promise 82 | singleValue(sql: string, params?: SqlParameters): Promise 83 | cursor(sql: string, params?: SqlParameters): Promise> 84 | 85 | script(sql: string): Promise 86 | } 87 | 88 | export interface MainConnection extends Connection { 89 | beginTransaction(): Promise 90 | close(): Promise 91 | } 92 | 93 | export interface TransactionConnection extends Connection { 94 | readonly inTransaction: boolean 95 | commit(): Promise 96 | rollback(): Promise 97 | } 98 | 99 | export interface ExecResult { 100 | /** 101 | * When the ID is `undefined`, an exception is thrown. 102 | * @param options (optional) is specific for the underlying adapter. 103 | */ 104 | getInsertedId(options?: unknown): unknown 105 | /** 106 | * When the ID is `undefined`, an exception is thrown. 107 | * @param options (optional) is specific for the underlying adapter. 108 | */ 109 | getInsertedIdAsString(options?: unknown): string 110 | /** 111 | * When the ID is `undefined`, an exception is thrown. 112 | * @param options (optional) is specific for the underlying adapter. 113 | */ 114 | getInsertedIdAsNumber(options?: unknown): number 115 | readonly affectedRows: number 116 | } 117 | 118 | export interface PreparedStatement { 119 | bind(params: SqlParameters): void 120 | bind(indexOrKey: number | string, value: unknown): void 121 | unbind(): void 122 | unbind(indexOrKey: number | string): void 123 | 124 | exec(params?: SqlParameters): Promise 125 | 126 | all(params?: SqlParameters): Promise 127 | singleRow(params?: SqlParameters): Promise 128 | singleValue(params?: SqlParameters): Promise 129 | cursor(params?: SqlParameters): Promise> 130 | 131 | close(): Promise 132 | } -------------------------------------------------------------------------------- /ladc/src/factories/Cursor.ts: -------------------------------------------------------------------------------- 1 | import { SqlParameters } from "../exported-definitions" 2 | import { formatError } from "../helpers" 3 | import { Context } from "./MainConnection" 4 | 5 | export class CursorProvider { 6 | private items = new Set() 7 | 8 | constructor(private context: Context) { 9 | } 10 | 11 | async open(sql: string, params?: SqlParameters): Promise> { 12 | this.context.check.parameters(params) 13 | const { pool } = this.context 14 | const cn = await pool.grab() 15 | try { 16 | const inst = new CursorItem( 17 | { 18 | context: this.context, 19 | end: (item: CursorItem) => { 20 | this.items.delete(item) 21 | pool.release(cn) 22 | } 23 | }, 24 | await cn.cursor(sql, params) 25 | ) 26 | this.items.add(inst) 27 | return inst.cursor 28 | } catch (err) { 29 | throw formatError(err) 30 | } 31 | } 32 | 33 | async closeAll() { 34 | await Promise.all(Array.from(this.items).map(item => item.close())) 35 | } 36 | } 37 | 38 | interface CursorItemContext { 39 | context: Context 40 | end: (item: CursorItem) => void 41 | } 42 | 43 | export class CursorItem { 44 | cursor: AsyncIterableIterator 45 | 46 | constructor(itemContext: CursorItemContext, ac: AsyncIterableIterator) { 47 | this.cursor = this.toCursor(itemContext, ac) 48 | } 49 | 50 | async close(): Promise { 51 | if (this.cursor.return) 52 | await this.cursor.return() 53 | } 54 | 55 | private toCursor(itemContext: CursorItemContext, ac: AsyncIterableIterator | undefined) { 56 | const obj: AsyncIterableIterator = { 57 | [Symbol.asyncIterator]: () => obj, 58 | next: async () => { 59 | if (!ac) 60 | return { done: true, value: undefined } 61 | const result = await ac.next() 62 | if (result.done) { 63 | ac = undefined 64 | itemContext.end(this) 65 | } 66 | return result 67 | }, 68 | return: async () => { 69 | if (!ac) 70 | return { done: true, value: undefined } 71 | itemContext.end(this) 72 | const returnCb = ac.return 73 | ac = undefined 74 | if (returnCb) 75 | return await returnCb() 76 | return { done: true, value: undefined } 77 | }, 78 | throw: async err => { 79 | if (!ac) 80 | throw err 81 | itemContext.end(this) 82 | const throwCb = ac.return 83 | ac = undefined 84 | if (throwCb) 85 | await throwCb(err) 86 | throw err 87 | } 88 | } 89 | return obj 90 | } 91 | } 92 | 93 | // function makeInMemoryACursor(rows?: any[]): AsyncIterableIterator { 94 | // let currentIndex = -1 95 | // const obj: AsyncIterableIterator = { 96 | // [Symbol.asyncIterator]: () => obj, 97 | // next: async () => { 98 | // if (!rows) 99 | // return { done: true, value: undefined } 100 | // const value = rows[++currentIndex] 101 | // if (!value) 102 | // rows = undefined 103 | // return { done: !rows, value } 104 | // }, 105 | // return: async () => { 106 | // rows = undefined 107 | // return { done: true, value: undefined } 108 | // }, 109 | // throw: async err => { 110 | // rows = undefined 111 | // throw err 112 | // } 113 | // } 114 | // return obj 115 | // } -------------------------------------------------------------------------------- /ladc/src/factories/ExecResult.ts: -------------------------------------------------------------------------------- 1 | import { AExecResult } from "../adapter-definitions" 2 | import { ExecResult } from "../exported-definitions" 3 | 4 | export function toExecResult(result: AExecResult): ExecResult { 5 | const obj: ExecResult = { 6 | get affectedRows() { 7 | return result.affectedRows 8 | }, 9 | getInsertedId(options?: unknown) { 10 | const id = result.getInsertedId(options) 11 | if (id === undefined) 12 | throw new Error("Missing inserted ID") 13 | return id 14 | }, 15 | getInsertedIdAsString(options?: unknown): string { 16 | const val = obj.getInsertedId(options) 17 | switch (typeof val) { 18 | case "string": 19 | return val as string 20 | case "number": 21 | return (val as number).toString() 22 | default: 23 | throw new Error(`Unexpected inserted ID type: ${typeof val}`) 24 | } 25 | }, 26 | getInsertedIdAsNumber(options?: unknown): number { 27 | const val = obj.getInsertedId(options) 28 | switch (typeof val) { 29 | case "string": 30 | return parseInt(val as string, 10) 31 | case "number": 32 | return val as number 33 | default: 34 | throw new Error(`Unexpected inserted ID type: ${typeof val}`) 35 | } 36 | } 37 | } 38 | return obj 39 | } 40 | -------------------------------------------------------------------------------- /ladc/src/factories/MainConnection.ts: -------------------------------------------------------------------------------- 1 | import { AConnection, ACreateConnectionOptions, AdapterCapabilities, AdapterHooks } from "../adapter-definitions" 2 | import { Pool } from "../createPool" 3 | import { LadcOptions, MainConnection, SqlParameters } from "../exported-definitions" 4 | import { formatError, toSingleRow, toSingleValue } from "../helpers" 5 | import { CursorProvider } from "./Cursor" 6 | import { toExecResult } from "./ExecResult" 7 | import { PsProvider } from "./PreparedStatement" 8 | import { TxProvider } from "./TransactionConnection" 9 | 10 | export interface Context { 11 | pool: Pool 12 | provider: (createOptions?: ACreateConnectionOptions) => Promise 13 | options: LadcOptions 14 | capabilities: AdapterCapabilities 15 | hooks: AdapterHooks 16 | check: { 17 | [K in keyof AdapterCapabilities]-?: () => void 18 | } & { 19 | parameters(params: SqlParameters | undefined): void 20 | } 21 | } 22 | 23 | export default function makeMainConnection(context: Context): MainConnection { 24 | const { pool } = context 25 | const psProvider = new PsProvider({ 26 | context, 27 | canCreateCursor: () => true 28 | }) 29 | const txProvider = new TxProvider(context) 30 | const cursorProvider = new CursorProvider(context) 31 | 32 | let closed = false 33 | 34 | let obj: MainConnection = { 35 | async prepare(sql: string) { 36 | context.check.preparedStatements() 37 | if (closed) 38 | throw new Error("Invalid call to 'prepare', the connection is closed") 39 | return await psProvider.prepare(sql) 40 | }, 41 | async exec(sql: string, params?: SqlParameters) { 42 | context.check.parameters(params) 43 | if (closed) 44 | throw new Error("Invalid call to 'exec', the connection is closed") 45 | const cn = await pool.grab() 46 | try { 47 | const res = await cn.exec(sql, params) 48 | return toExecResult(res) 49 | } finally { 50 | pool.release(cn) 51 | } 52 | }, 53 | async all(sql: string, params?: SqlParameters) { 54 | context.check.parameters(params) 55 | if (closed) 56 | throw new Error("Invalid call to 'all', the connection is closed") 57 | const cn = await pool.grab() 58 | try { 59 | return await cn.all(sql, params) 60 | } catch (err) { 61 | throw formatError(err) 62 | } finally { 63 | pool.release(cn) 64 | } 65 | }, 66 | async singleRow(sql: string, params?: SqlParameters) { 67 | return toSingleRow(await this.all(sql, params)) 68 | }, 69 | async singleValue(sql: string, params?: SqlParameters) { 70 | return toSingleValue(await this.singleRow(sql, params)) 71 | }, 72 | async cursor(sql: string, params?: SqlParameters) { 73 | context.check.cursors() 74 | context.check.parameters(params) 75 | if (closed) 76 | throw new Error("Invalid call to 'cursor', the connection is closed") 77 | return await cursorProvider.open(sql, params) 78 | }, 79 | async script(sql: string) { 80 | context.check.script() 81 | if (closed) 82 | throw new Error("Invalid call to 'script', the connection is closed") 83 | if (context.capabilities.script === "onASeparateConnection") { 84 | const cn = await context.provider({ enableScript: true }) 85 | try { 86 | return await cn.script(sql) 87 | } catch (err) { 88 | throw formatError(err) 89 | } finally { 90 | await cn.close() 91 | } 92 | } else { 93 | const cn = await pool.grab() 94 | try { 95 | return await cn.script(sql) 96 | } catch (err) { 97 | throw formatError(err) 98 | } finally { 99 | pool.release(cn) 100 | } 101 | } 102 | }, 103 | 104 | beginTransaction: async () => { 105 | if (closed) 106 | throw new Error("Invalid call to 'beginTransaction', the connection is closed") 107 | return await txProvider.create() 108 | }, 109 | close: async () => { 110 | if (closed) 111 | throw new Error("Invalid call to 'close', the connection is already closed") 112 | closed = true 113 | await Promise.all([ 114 | psProvider.closeAll(), 115 | cursorProvider.closeAll(), 116 | txProvider.closeAll() 117 | ]) 118 | await pool.close() 119 | } 120 | } 121 | 122 | if (context.options.modifier && context.options.modifier.modifyConnection) 123 | obj = context.options.modifier.modifyConnection(obj) 124 | 125 | return obj 126 | } 127 | -------------------------------------------------------------------------------- /ladc/src/factories/PreparedStatement.ts: -------------------------------------------------------------------------------- 1 | import { AConnection, APreparedStatement } from "../adapter-definitions" 2 | import { PreparedStatement, SqlParameters } from "../exported-definitions" 3 | import { formatError, toSingleRow, toSingleValue } from "../helpers" 4 | import { CursorItem } from "./Cursor" 5 | import { toExecResult } from "./ExecResult" 6 | import { Context } from "./MainConnection" 7 | 8 | export interface PsProviderContext { 9 | exclusiveCn?: AConnection 10 | context: Context 11 | canCreateCursor(): boolean 12 | } 13 | 14 | export class PsProvider { 15 | private items = new Set() 16 | 17 | constructor(private ppContext: PsProviderContext) { 18 | } 19 | 20 | async prepare(sql: string): Promise> { 21 | const item = await PsItem.create(await this.createPsContext(), sql) 22 | this.items.add(item) 23 | return item.ps 24 | } 25 | 26 | async closeAll() { 27 | await Promise.all(Array.from(this.items).map(item => item.ps.close())) 28 | } 29 | 30 | hasCursor() { 31 | for (const item of this.items) { 32 | if (item.hasCursor()) 33 | return true 34 | } 35 | return false 36 | } 37 | 38 | private async createPsContext(): Promise { 39 | const { exclusiveCn } = this.ppContext 40 | if (exclusiveCn) { 41 | return { 42 | context: this.ppContext.context, 43 | cn: exclusiveCn, 44 | end: (item: PsItem) => { 45 | this.items.delete(item) 46 | }, 47 | canCreateCursor: () => this.ppContext.canCreateCursor() 48 | } 49 | } 50 | const { pool } = this.ppContext.context 51 | const cn = await pool.grab() 52 | return { 53 | context: this.ppContext.context, 54 | cn, 55 | end: (item: PsItem) => { 56 | this.items.delete(item) 57 | pool.release(cn) 58 | }, 59 | canCreateCursor: () => this.ppContext.canCreateCursor() 60 | } 61 | } 62 | } 63 | 64 | interface PsItemContext { 65 | context: Context 66 | cn: AConnection 67 | end: (item: PsItem) => void 68 | canCreateCursor(): boolean 69 | } 70 | 71 | class PsItem { 72 | static async create(psContext: PsItemContext, sql: string): Promise { 73 | const aps = await psContext.cn.prepare(sql) 74 | return new PsItem(psContext, aps) 75 | } 76 | 77 | ps: PreparedStatement 78 | private cursorItem?: CursorItem 79 | private boundParams?: SqlParameters 80 | 81 | constructor(itemContext: PsItemContext, aps: APreparedStatement) { 82 | this.ps = this.toPs(itemContext, aps) 83 | } 84 | 85 | hasCursor() { 86 | return !!this.cursorItem 87 | } 88 | 89 | private toPs(itemContext: PsItemContext, aps: APreparedStatement | undefined) { 90 | let obj: PreparedStatement = { 91 | exec: async (params?: SqlParameters) => { 92 | itemContext.context.check.parameters(params) 93 | check("exec") 94 | try { 95 | return toExecResult(await aps!.exec(mergeParameters(this.boundParams, params))) 96 | } catch (err) { 97 | throw formatError(err) 98 | } 99 | }, 100 | all: async (params?: SqlParameters) => { 101 | itemContext.context.check.parameters(params) 102 | check("all") 103 | try { 104 | return await aps!.all(mergeParameters(this.boundParams, params)) 105 | } catch (err) { 106 | throw formatError(err) 107 | } 108 | }, 109 | singleRow: async (params?: SqlParameters) => toSingleRow(await obj.all(params)), 110 | singleValue: async (params?: SqlParameters) => toSingleValue(await obj.singleRow(params)), 111 | cursor: async (params?: SqlParameters) => { 112 | itemContext.context.check.parameters(params) 113 | check("cursor") 114 | if (!itemContext.context.capabilities.cursors) 115 | throw new Error("Cursors are not available with this adapter") 116 | if (!itemContext.canCreateCursor()) 117 | throw new Error("Only one cursor is allowed by underlying transaction") 118 | try { 119 | const aCursor = await aps!.cursor(mergeParameters(this.boundParams, params)) 120 | this.cursorItem = new CursorItem({ 121 | context: itemContext.context, 122 | end: () => { 123 | this.cursorItem = undefined 124 | } 125 | }, aCursor) 126 | return this.cursorItem.cursor 127 | } catch (err) { 128 | throw formatError(err) 129 | } 130 | }, 131 | bind: async (paramsOrIndexOrKey: SqlParameters | number | string, value?: any) => { 132 | check("bind") 133 | if (typeof paramsOrIndexOrKey === "object") { 134 | itemContext.context.check.parameters(paramsOrIndexOrKey) 135 | this.boundParams = mergeParameters(this.boundParams, paramsOrIndexOrKey) 136 | } else { 137 | if (typeof paramsOrIndexOrKey === "string") { 138 | itemContext.context.check.namedParameters() 139 | } 140 | (this.boundParams as any)[paramsOrIndexOrKey] = value 141 | } 142 | return Promise.resolve() 143 | }, 144 | unbind: async (indexOrKey?: number | string) => { 145 | check("unbind") 146 | if (indexOrKey === undefined) 147 | this.boundParams = undefined 148 | else { 149 | if (typeof indexOrKey === "string") { 150 | itemContext.context.check.namedParameters() 151 | } 152 | (this.boundParams as any)[indexOrKey] = undefined 153 | } 154 | return Promise.resolve() 155 | }, 156 | close: async () => { 157 | if (!aps) 158 | throw new Error("Prepared statement is already closed") 159 | const copy = aps 160 | aps = undefined 161 | if (this.cursorItem) 162 | await this.cursorItem.close() 163 | try { 164 | await copy.close() 165 | } catch (err) { 166 | throw formatError(err) 167 | } 168 | itemContext.end(this) 169 | } 170 | } 171 | 172 | if (itemContext.context.options.modifier && itemContext.context.options.modifier.modifyPreparedStatement) 173 | obj = itemContext.context.options.modifier.modifyPreparedStatement(obj) 174 | 175 | return obj 176 | 177 | function check(method: string) { 178 | if (!aps) 179 | throw new Error(`Invalid call to '${method}', the prepared statement is closed`) 180 | } 181 | } 182 | } 183 | 184 | function mergeParameters( 185 | params1: SqlParameters | undefined, 186 | params2: SqlParameters | undefined 187 | ): SqlParameters | undefined { 188 | if (!params1) 189 | return params2 190 | if (!params2) 191 | return params1 192 | const isArr = Array.isArray(params1) 193 | if (isArr !== Array.isArray(params2)) 194 | throw new Error("Cannot merge named parameters with positioned parameters") 195 | if (isArr) { 196 | const result = [...(params1 as any[])] 197 | const p2 = params2 as any[] 198 | p2.forEach((val, index) => result[index] = val) 199 | return result 200 | } 201 | return { 202 | ...params1, 203 | ...params2 204 | } 205 | } -------------------------------------------------------------------------------- /ladc/src/factories/TransactionConnection.ts: -------------------------------------------------------------------------------- 1 | import { AConnection } from "../adapter-definitions" 2 | import { SqlParameters, TransactionConnection } from "../exported-definitions" 3 | import { formatError, toSingleRow, toSingleValue } from "../helpers" 4 | import { CursorItem } from "./Cursor" 5 | import { toExecResult } from "./ExecResult" 6 | import { Context } from "./MainConnection" 7 | import { PsProvider } from "./PreparedStatement" 8 | 9 | export class TxProvider { 10 | private items = new Set() 11 | 12 | constructor(private context: Context) { 13 | } 14 | 15 | async create(): Promise { 16 | const item = await TxItem.create({ 17 | context: this.context, 18 | end: (item: TxItem) => { 19 | this.items.delete(item) 20 | } 21 | }) 22 | this.items.add(item) 23 | return item.tx 24 | } 25 | 26 | async closeAll() { 27 | await Promise.all(Array.from(this.items).map(item => item.tx.rollback())) 28 | } 29 | } 30 | 31 | interface TxItemContext { 32 | context: Context 33 | end: (item: TxItem) => void 34 | } 35 | 36 | class TxItem { 37 | static async create(txContext: TxItemContext): Promise { 38 | const acn: AConnection = await txContext.context.pool.grab(true) 39 | try { 40 | if (txContext.context.hooks.beginTransaction) 41 | await txContext.context.hooks.beginTransaction(acn) 42 | else 43 | await acn.exec("begin") 44 | return new TxItem(txContext, acn) 45 | } catch (err) { 46 | throw formatError(err) 47 | } 48 | } 49 | 50 | tx: TransactionConnection 51 | private psProvider?: PsProvider 52 | private cursorItem?: CursorItem 53 | 54 | constructor(itemContext: TxItemContext, acn: AConnection) { 55 | this.tx = this.toTx(itemContext, acn) 56 | } 57 | 58 | hasCursor() { 59 | return !!this.cursorItem 60 | } 61 | 62 | private canCreateCursor(): boolean { 63 | return !this.cursorItem && (!this.psProvider || !this.psProvider.hasCursor()) 64 | } 65 | 66 | private async closeDependencies() { 67 | const promises: Promise[] = [] 68 | if (this.cursorItem) 69 | promises.push(this.cursorItem.close()) 70 | if (this.psProvider) 71 | promises.push(this.psProvider.closeAll()) 72 | await Promise.all(promises) 73 | } 74 | 75 | private toTx(itemContext: TxItemContext, acn: AConnection | undefined): TransactionConnection { 76 | const endOfTransaction = async (method: "commit" | "rollback", item: TxItem) => { 77 | if (!acn) 78 | throw new Error(`Invalid call to '${method}', not in a transaction`) 79 | const copy = acn 80 | acn = undefined 81 | itemContext.end(item) 82 | try { 83 | await item.closeDependencies() 84 | const hook = itemContext.context.hooks[method] 85 | try { 86 | if (hook) 87 | await hook(copy) 88 | else 89 | await copy.exec(method) 90 | } catch (err) { 91 | throw formatError(err) 92 | } 93 | itemContext.context.pool.release(copy) 94 | } catch (err) { 95 | itemContext.context.pool.abandon(copy) 96 | throw err 97 | } 98 | } 99 | 100 | let obj: TransactionConnection = { 101 | prepare: async (sql: string) => { 102 | itemContext.context.check.preparedStatements() 103 | if (!acn) 104 | throw new Error("Invalid call to 'prepare', the connection is closed") 105 | if (!this.psProvider) { 106 | this.psProvider = new PsProvider({ 107 | context: itemContext.context, 108 | exclusiveCn: acn, 109 | canCreateCursor: () => this.canCreateCursor() 110 | }) 111 | } 112 | return await this.psProvider.prepare(sql) 113 | }, 114 | async exec(sql: string, params?: SqlParameters) { 115 | itemContext.context.check.parameters(params) 116 | if (!acn) 117 | throw new Error("Invalid call to 'exec', not in a transaction") 118 | try { 119 | return toExecResult(await acn.exec(sql, params)) 120 | } catch (err) { 121 | throw formatError(err) 122 | } 123 | }, 124 | async all(sql: string, params?: SqlParameters) { 125 | itemContext.context.check.parameters(params) 126 | if (!acn) 127 | throw new Error("Invalid call to 'all', not in a transaction") 128 | try { 129 | return acn.all(sql, params) 130 | } catch (err) { 131 | throw formatError(err) 132 | } 133 | }, 134 | singleRow: async (sql: string, params?: SqlParameters) => toSingleRow(await obj.all(sql, params)), 135 | singleValue: async (sql: string, params?: SqlParameters) => toSingleValue(await obj.singleRow(sql, params)), 136 | cursor: async (sql: string, params?: SqlParameters) => { 137 | itemContext.context.check.cursors() 138 | itemContext.context.check.parameters(params) 139 | if (!acn) 140 | throw new Error("Invalid call to 'cursor', not in a transaction") 141 | if (!this.canCreateCursor()) 142 | throw new Error("Only one cursor is allowed by underlying transaction") 143 | try { 144 | this.cursorItem = new CursorItem({ 145 | context: itemContext.context, 146 | end: () => { 147 | this.cursorItem = undefined 148 | } 149 | }, await acn.cursor(sql, params)) 150 | return this.cursorItem.cursor 151 | } catch (err) { 152 | throw formatError(err) 153 | } 154 | }, 155 | async script(sql: string) { 156 | itemContext.context.check.script() 157 | if (itemContext.context.capabilities.script === "onASeparateConnection") 158 | throw new Error("Scripts are available only on the main connection with this adapter.") 159 | if (!acn) 160 | throw new Error("Invalid call to 'script', not in a transaction") 161 | try { 162 | return acn.script(sql) 163 | } catch (err) { 164 | throw formatError(err) 165 | } 166 | }, 167 | get inTransaction() { 168 | return !!acn 169 | }, 170 | commit: () => endOfTransaction("commit", this), 171 | rollback: () => endOfTransaction("rollback", this) 172 | } 173 | 174 | if (itemContext.context.options.modifier && itemContext.context.options.modifier.modifyConnection) 175 | obj = itemContext.context.options.modifier.modifyConnection(obj) 176 | 177 | return obj 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /ladc/src/helpers.ts: -------------------------------------------------------------------------------- 1 | export function toSingleRow(rows: any[]) { 2 | if (rows.length !== 1) { 3 | if (rows.length === 0) 4 | return 5 | throw new Error(`Cannot fetch one row, row count: ${rows.length}`) 6 | } 7 | return rows[0] 8 | } 9 | 10 | export function toSingleValue(row: any) { 11 | if (row === undefined) 12 | return 13 | const columns = Object.keys(row) 14 | if (columns.length !== 1) 15 | throw new Error(`Cannot fetch one value, column count: ${columns.length}`) 16 | return row[columns[0]] 17 | } 18 | 19 | export function formatError(err: any) { 20 | if (err instanceof Error) 21 | return err 22 | return new Error(err) 23 | } -------------------------------------------------------------------------------- /ladc/src/index.ts: -------------------------------------------------------------------------------- 1 | import { ACreateConnectionOptions, AdapterCapabilities } from "./adapter-definitions" 2 | import createPool from "./createPool" 3 | import { LadcOptions, MainConnection, SqlParameters } from "./exported-definitions" 4 | import makeMainConnection, { Context } from "./factories/MainConnection" 5 | 6 | export default function ladc(options: LadcOptions): MainConnection { 7 | const provider = async (createOptions?: ACreateConnectionOptions) => { 8 | const cn = await options.adapter.createConnection(createOptions) 9 | if (options.initConnection) 10 | await options.initConnection(cn) 11 | return cn 12 | } 13 | return makeMainConnection({ 14 | options, 15 | pool: createPool(provider, options), 16 | provider, 17 | capabilities: options.adapter.capabilities, 18 | hooks: options.adapter.hooks ?? {}, 19 | check: makeCheckers(options.adapter.capabilities) 20 | }) 21 | } 22 | 23 | export type { AConnection, ACreateConnectionOptions, AdapterCapabilities, AdapterHooks, AExecResult, APreparedStatement, LadcAdapter } from "./adapter-definitions" 24 | export type { Connection, DebugEvent, DebugEventContext, ExecResult, LadcModifier, LadcOptions, MainConnection, PoolMonitoring, PoolOptions, PreparedStatement, ResultRow, SqlParameters, TransactionConnection } from "./exported-definitions" 25 | 26 | function makeCheckers(capabilities: AdapterCapabilities): Context["check"] { 27 | return { 28 | cursors() { 29 | if (!capabilities.cursors) 30 | throw new Error("Cursors are not available with this adapter.") 31 | }, 32 | namedParameters() { 33 | if (!capabilities.namedParameters) 34 | throw new Error("Named parameters are not available with this adapter.") 35 | }, 36 | preparedStatements() { 37 | if (!capabilities.preparedStatements) 38 | throw new Error("Prepared statements are not available with this adapter.") 39 | }, 40 | script() { 41 | if (!capabilities.script) 42 | throw new Error("Scripts are not available with this adapter.") 43 | }, 44 | parameters(params: SqlParameters | undefined) { 45 | if (params && !Array.isArray(params) && !capabilities.namedParameters) 46 | throw new Error("Named parameters are not available with this adapter.") 47 | } 48 | } 49 | } -------------------------------------------------------------------------------- /ladc/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2018", 4 | "moduleResolution": "node", 5 | "strict": true, 6 | "outDir": "build/compiled", 7 | "declaration": true, 8 | "lib": [ 9 | "es2018" 10 | ] 11 | }, 12 | "include": [ 13 | "src" 14 | ] 15 | } -------------------------------------------------------------------------------- /ladc/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "defaultSeverity": "error", 3 | "extends": [ 4 | "tslint:recommended" 5 | ], 6 | "linterOptions": { 7 | "exclude": [ 8 | "node_modules/**" 9 | ] 10 | }, 11 | "rules": { 12 | "arrow-parens": false, 13 | "curly": false, 14 | "eofline": false, 15 | "indent": [ 16 | true, 17 | "spaces", 18 | 2 19 | ], 20 | "interface-name": false, 21 | "max-classes-per-file": false, 22 | "max-line-length": false, 23 | "member-access": [ 24 | true, 25 | "no-public" 26 | ], 27 | "no-conditional-assignment": false, 28 | "no-consecutive-blank-lines": false, 29 | "no-empty": [ 30 | true, 31 | "allow-empty-catch", 32 | "allow-empty-functions" 33 | ], 34 | "no-shadowed-variable": false, 35 | "no-string-literal": false, 36 | "no-var-requires": false, 37 | "ordered-imports": true, 38 | "object-literal-sort-keys": false, 39 | "object-literal-key-quotes": false, 40 | "only-arrow-functions": false, 41 | "prefer-const": true, 42 | "quotemark": [ 43 | true, 44 | "double" 45 | ], 46 | "semicolon": [ 47 | true, 48 | "never" 49 | ], 50 | "space-before-function-paren": [ 51 | true, 52 | { 53 | "anonymous": "always", 54 | "named": "never", 55 | "asyncArrow": "always" 56 | } 57 | ], 58 | "trailing-comma": [ 59 | true, 60 | { 61 | "singleline": "never" 62 | } 63 | ], 64 | "variable-name": [ 65 | true, 66 | "allow-leading-underscore", 67 | "allow-pascal-case" 68 | ] 69 | } 70 | } -------------------------------------------------------------------------------- /mysql-adapter/.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .* 3 | *.js 4 | /*.d.ts 5 | build -------------------------------------------------------------------------------- /mysql-adapter/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "env": { 3 | "es6": true, 4 | "node": true 5 | }, 6 | "plugins": [ 7 | "@typescript-eslint" 8 | ], 9 | "parser": "@typescript-eslint/parser", 10 | "parserOptions": { 11 | "ecmaVersion": 2018, 12 | "project": "./tsconfig.json", 13 | "sourceType": "module" 14 | }, 15 | "extends": [ 16 | "eslint:recommended", 17 | "plugin:@typescript-eslint/recommended", 18 | "plugin:@typescript-eslint/recommended-requiring-type-checking", 19 | "../eslint-rules" 20 | ], 21 | } 22 | -------------------------------------------------------------------------------- /mysql-adapter/.gitignore: -------------------------------------------------------------------------------- 1 | .npmrc 2 | node_modules 3 | /build/compiled 4 | /ladc-mysql-adapter* -------------------------------------------------------------------------------- /mysql-adapter/.npmignore: -------------------------------------------------------------------------------- 1 | /build/compiled -------------------------------------------------------------------------------- /mysql-adapter/LICENSE: -------------------------------------------------------------------------------- 1 | CC0 1.0 Universal 2 | 3 | Statement of Purpose 4 | 5 | The laws of most jurisdictions throughout the world automatically confer 6 | exclusive Copyright and Related Rights (defined below) upon the creator and 7 | subsequent owner(s) (each and all, an "owner") of an original work of 8 | authorship and/or a database (each, a "Work"). 9 | 10 | Certain owners wish to permanently relinquish those rights to a Work for the 11 | purpose of contributing to a commons of creative, cultural and scientific 12 | works ("Commons") that the public can reliably and without fear of later 13 | claims of infringement build upon, modify, incorporate in other works, reuse 14 | and redistribute as freely as possible in any form whatsoever and for any 15 | purposes, including without limitation commercial purposes. These owners may 16 | contribute to the Commons to promote the ideal of a free culture and the 17 | further production of creative, cultural and scientific works, or to gain 18 | reputation or greater distribution for their Work in part through the use and 19 | efforts of others. 20 | 21 | For these and/or other purposes and motivations, and without any expectation 22 | of additional consideration or compensation, the person associating CC0 with a 23 | Work (the "Affirmer"), to the extent that he or she is an owner of Copyright 24 | and Related Rights in the Work, voluntarily elects to apply CC0 to the Work 25 | and publicly distribute the Work under its terms, with knowledge of his or her 26 | Copyright and Related Rights in the Work and the meaning and intended legal 27 | effect of CC0 on those rights. 28 | 29 | 1. Copyright and Related Rights. A Work made available under CC0 may be 30 | protected by copyright and related or neighboring rights ("Copyright and 31 | Related Rights"). Copyright and Related Rights include, but are not limited 32 | to, the following: 33 | 34 | i. the right to reproduce, adapt, distribute, perform, display, communicate, 35 | and translate a Work; 36 | 37 | ii. moral rights retained by the original author(s) and/or performer(s); 38 | 39 | iii. publicity and privacy rights pertaining to a person's image or likeness 40 | depicted in a Work; 41 | 42 | iv. rights protecting against unfair competition in regards to a Work, 43 | subject to the limitations in paragraph 4(a), below; 44 | 45 | v. rights protecting the extraction, dissemination, use and reuse of data in 46 | a Work; 47 | 48 | vi. database rights (such as those arising under Directive 96/9/EC of the 49 | European Parliament and of the Council of 11 March 1996 on the legal 50 | protection of databases, and under any national implementation thereof, 51 | including any amended or successor version of such directive); and 52 | 53 | vii. other similar, equivalent or corresponding rights throughout the world 54 | based on applicable law or treaty, and any national implementations thereof. 55 | 56 | 2. Waiver. To the greatest extent permitted by, but not in contravention of, 57 | applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and 58 | unconditionally waives, abandons, and surrenders all of Affirmer's Copyright 59 | and Related Rights and associated claims and causes of action, whether now 60 | known or unknown (including existing as well as future claims and causes of 61 | action), in the Work (i) in all territories worldwide, (ii) for the maximum 62 | duration provided by applicable law or treaty (including future time 63 | extensions), (iii) in any current or future medium and for any number of 64 | copies, and (iv) for any purpose whatsoever, including without limitation 65 | commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes 66 | the Waiver for the benefit of each member of the public at large and to the 67 | detriment of Affirmer's heirs and successors, fully intending that such Waiver 68 | shall not be subject to revocation, rescission, cancellation, termination, or 69 | any other legal or equitable action to disrupt the quiet enjoyment of the Work 70 | by the public as contemplated by Affirmer's express Statement of Purpose. 71 | 72 | 3. Public License Fallback. Should any part of the Waiver for any reason be 73 | judged legally invalid or ineffective under applicable law, then the Waiver 74 | shall be preserved to the maximum extent permitted taking into account 75 | Affirmer's express Statement of Purpose. In addition, to the extent the Waiver 76 | is so judged Affirmer hereby grants to each affected person a royalty-free, 77 | non transferable, non sublicensable, non exclusive, irrevocable and 78 | unconditional license to exercise Affirmer's Copyright and Related Rights in 79 | the Work (i) in all territories worldwide, (ii) for the maximum duration 80 | provided by applicable law or treaty (including future time extensions), (iii) 81 | in any current or future medium and for any number of copies, and (iv) for any 82 | purpose whatsoever, including without limitation commercial, advertising or 83 | promotional purposes (the "License"). The License shall be deemed effective as 84 | of the date CC0 was applied by Affirmer to the Work. Should any part of the 85 | License for any reason be judged legally invalid or ineffective under 86 | applicable law, such partial invalidity or ineffectiveness shall not 87 | invalidate the remainder of the License, and in such case Affirmer hereby 88 | affirms that he or she will not (i) exercise any of his or her remaining 89 | Copyright and Related Rights in the Work or (ii) assert any associated claims 90 | and causes of action with respect to the Work, in either case contrary to 91 | Affirmer's express Statement of Purpose. 92 | 93 | 4. Limitations and Disclaimers. 94 | 95 | a. No trademark or patent rights held by Affirmer are waived, abandoned, 96 | surrendered, licensed or otherwise affected by this document. 97 | 98 | b. Affirmer offers the Work as-is and makes no representations or warranties 99 | of any kind concerning the Work, express, implied, statutory or otherwise, 100 | including without limitation warranties of title, merchantability, fitness 101 | for a particular purpose, non infringement, or the absence of latent or 102 | other defects, accuracy, or the present or absence of errors, whether or not 103 | discoverable, all to the greatest extent permissible under applicable law. 104 | 105 | c. Affirmer disclaims responsibility for clearing rights of other persons 106 | that may apply to the Work or any use thereof, including without limitation 107 | any person's Copyright and Related Rights in the Work. Further, Affirmer 108 | disclaims responsibility for obtaining any necessary consents, permissions 109 | or other rights required for any use of the Work. 110 | 111 | d. Affirmer understands and acknowledges that Creative Commons is not a 112 | party to this document and has no duty or obligation with respect to this 113 | CC0 or use of the Work. 114 | 115 | For more information, please see 116 | 117 | -------------------------------------------------------------------------------- /mysql-adapter/README.md: -------------------------------------------------------------------------------- 1 | # @ladc/mysql-adapter 2 | 3 | [![Build Status](https://travis-ci.com/paroi-tech/ladc.svg?branch=master)](https://travis-ci.com/paroi-tech/ladc) 4 | [![npm](https://img.shields.io/npm/dm/@ladc/mysql-adapter)](https://www.npmjs.com/package/@ladc/mysql-adapter) 5 | ![Type definitions](https://img.shields.io/npm/types/@ladc/mysql-adapter) 6 | [![GitHub](https://img.shields.io/github/license/paroi-tech/ladc)](https://github.com/paroi-tech/ladc) 7 | 8 | [LADC](https://github.com/paroi-tech/ladc/tree/master/ladc) is a common API on top of relational database (SQL) connectors. It can connect to Postgresql, MariaDB / MySQL, SQLite. The API is inspired from PDO and JDBC. It’s named LADC for “a Layer Above Database Connectors”. 9 | 10 | This package is a plugin for LADC. It is an adapter for MySQL and MariaDB, using the connector [mysql](https://www.npmjs.com/package/mysql). 11 | 12 | ## Install 13 | 14 | ``` 15 | npm install @ladc/mysql-adapter ladc 16 | ``` 17 | 18 | ## Use a MySQL connection with LADC 19 | 20 | How to create a connection: 21 | 22 | ```js 23 | import ladc from "ladc"; 24 | import mysqlAdapter from "@ladc/mysql-adapter"; 25 | 26 | const cn = ladc({ 27 | adapter: mysqlAdapter({ 28 | mysqlConfig: { 29 | host: "-my-server-", 30 | database: "-my-database-", 31 | user: "-my-user-", 32 | password: "-my-password-", 33 | }, 34 | }), 35 | }); 36 | ``` 37 | 38 | # Use a MySQL connection with LADC and SQL Bricks 39 | 40 | Add the dependencies for SQL Bricks: 41 | 42 | ```sh 43 | npm install sql-bricks @ladc/sql-bricks-modifier 44 | ``` 45 | 46 | In your code, MySQL requires to set a specific `placeholder` option in SQL Bricks: 47 | 48 | ```js 49 | import ladc from "ladc"; 50 | import mysqlAdapter from "@ladc/mysql-adapter"; 51 | import sqlBricksModifier from "@ladc/sql-bricks-modifier"; 52 | 53 | const cn = ladc({ 54 | adapter: mysqlAdapter({ 55 | mysqlConfig: { 56 | host: "-my-server-", 57 | database: "-my-database-", 58 | user: "-my-user-", 59 | password: "-my-password-", 60 | }, 61 | }), 62 | modifier: sqlBricksModifier({ 63 | toParamsOptions: { placeholder: "?" }, // ← Specific to MySQL 64 | }), 65 | }); 66 | ``` 67 | 68 | Now, use it: 69 | 70 | ```js 71 | import { select } from "sql-bricks"; 72 | 73 | async function test(cn) { 74 | const q = select("col1, col2").from("table1"); 75 | const rows = await cn.all(q); 76 | console.log(rows); 77 | } 78 | ``` 79 | 80 | ## Contribute 81 | 82 | With VS Code, our recommanded plugin is: 83 | 84 | - **TSLint** from Microsoft (`ms-vscode.vscode-typescript-tslint-plugin`) 85 | -------------------------------------------------------------------------------- /mysql-adapter/build/make-bundle.js: -------------------------------------------------------------------------------- 1 | const { promisify } = require("util") 2 | const fs = require("fs") 3 | const path = require("path") 4 | const { rollup } = require("rollup") 5 | const { minify } = require("terser") 6 | 7 | const readFile = promisify(fs.readFile) 8 | const writeFile = promisify(fs.writeFile) 9 | 10 | const bundleName = "ladc-mysql-adapter" 11 | const srcPath = path.join(__dirname, "..", "src") 12 | const compiledPath = path.join(__dirname, "compiled") 13 | const distNpmPath = path.join(__dirname, "..") 14 | 15 | async function build() { 16 | const bundle = await rollup({ 17 | input: path.join(compiledPath, "index.js"), 18 | 19 | }) 20 | const { output } = await bundle.generate({ 21 | format: "commonjs", 22 | exports: "named", 23 | sourcemap: false, 24 | }) 25 | 26 | if (!output[0].code) 27 | throw new Error("Missing code") 28 | 29 | // const minified = await minify(output[0].code, { sourceMap: false }) 30 | // if (minified.error) 31 | // throw minified.error 32 | // if (!minified.code) 33 | // throw new Error("Missing code") 34 | 35 | await writeFile(path.join(distNpmPath, `${bundleName}.js`), output[0].code) 36 | await writeFile(path.join(distNpmPath, `${bundleName}.d.ts`), await makeDefinitionsCode()) 37 | } 38 | 39 | async function makeDefinitionsCode() { 40 | const defs = [ 41 | "// -- Usage definitions --", 42 | removeLocalImportsExports((await readFile(path.join(srcPath, "exported-definitions.d.ts"), "utf-8")).trim()), 43 | "// -- Entry point definition --", 44 | removeSemicolons( 45 | removeLocalImportsExports((await readFile(path.join(compiledPath, "index.d.ts"), "utf-8")).trim()), 46 | ) 47 | ] 48 | return defs.join("\n\n") 49 | } 50 | 51 | function removeLocalImportsExports(code) { 52 | const localImportExport = /^\s*(import|export) .* from "\.\/.*"\s*;?\s*$/ 53 | return code.split("\n").filter(line => { 54 | return !localImportExport.test(line) 55 | }).join("\n").trim() 56 | } 57 | 58 | function removeSemicolons(code) { 59 | return code.replace(/;/g, "") 60 | } 61 | 62 | build().catch(err => console.log(err.message, err.stack)) 63 | -------------------------------------------------------------------------------- /mysql-adapter/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@ladc/mysql-adapter", 3 | "version": "0.22.15", 4 | "description": "Connect to MySQL and MariaDB with the LADC API ('mysql' connector).", 5 | "main": "ladc-mysql-adapter.js", 6 | "types": "ladc-mysql-adapter.d.ts", 7 | "dependencies": { 8 | "mysql": "^2.18.1" 9 | }, 10 | "peerDependencies": { 11 | "ladc": "^0.22.13" 12 | }, 13 | "devDependencies": { 14 | "@types/mysql": "^2.15.15", 15 | "@types/node": "^12.12.55", 16 | "@typescript-eslint/eslint-plugin": "~3.10.1", 17 | "@typescript-eslint/parser": "~3.10.1", 18 | "eslint": "~7.7.0", 19 | "ladc": "^0.22.13", 20 | "npm-run-all": "^4.1.5", 21 | "rimraf": "^3.0.2", 22 | "rollup": "^2.26.0", 23 | "terser": "^5.3.0", 24 | "typescript": "^4.0.2" 25 | }, 26 | "scripts": { 27 | "build": "run-s clear tsc make-bundle", 28 | "tsc": "tsc", 29 | "tsc:watch": "tsc --watch", 30 | "make-bundle": "node build/make-bundle", 31 | "clear": "rimraf build/compiled/*", 32 | "lint": "eslint . --ext .ts --max-warnings=0", 33 | "prepublishOnly": "npm run lint && npm run build" 34 | }, 35 | "repository": { 36 | "type": "git", 37 | "url": "git+https://github.com/paroi-tech/ladc.git" 38 | }, 39 | "homepage": "https://github.com/paroi-tech/ladc/tree/master/mysql-adapter", 40 | "author": { 41 | "name": "Paleo" 42 | }, 43 | "license": "CC0-1.0", 44 | "keywords": [ 45 | "mysql", 46 | "mariadb", 47 | "connection", 48 | "ladc" 49 | ] 50 | } 51 | -------------------------------------------------------------------------------- /mysql-adapter/src/AConnection.ts: -------------------------------------------------------------------------------- 1 | import { AConnection, ACreateConnectionOptions, AExecResult, APreparedStatement } from "ladc" 2 | import { createPromisifiedConnection, PromisifiedConnection, UnderlyingExecResult } from "./promisifyMysql" 3 | 4 | export function createMysqlConnection( 5 | mysqlConfig: any, 6 | createOptions?: ACreateConnectionOptions 7 | ): Promise { 8 | if (createOptions && createOptions.enableScript) { 9 | mysqlConfig = { 10 | ...mysqlConfig, 11 | multipleStatements: true 12 | } 13 | } 14 | return createPromisifiedConnection(mysqlConfig) 15 | } 16 | 17 | export function toAConnection(mc: PromisifiedConnection): AConnection { // , options: LadcMysqlOptions 18 | return { 19 | prepare: async (sql: string) => Promise.resolve(makeAPreparedStatement(mc, sql)), 20 | exec: async (sql: string, params?: unknown[]) => { 21 | const result = await mc.exec(sql, params) 22 | return toAExecResult(result) 23 | }, 24 | all: async (sql: string, params?: unknown[]) => await mc.query(sql, params), 25 | cursor: (sql: string, params?: unknown[]) => createACursor({ mc, sql, params }), 26 | script: async (sql: string) => { 27 | await mc.query(sql) 28 | }, 29 | close: async () => { 30 | await mc.end() 31 | } 32 | } 33 | } 34 | 35 | function toAExecResult(result: UnderlyingExecResult): AExecResult { 36 | if (result.affectedRows === undefined) 37 | throw new Error("Missing affected rows") 38 | return { 39 | affectedRows: result.affectedRows, 40 | getInsertedId: () => result.insertId 41 | } 42 | } 43 | 44 | function makeAPreparedStatement(mc: PromisifiedConnection, sql: string): APreparedStatement { 45 | const obj: APreparedStatement = { 46 | exec: async (params?: unknown[]) => { 47 | const result = await mc.exec(sql, params) 48 | return toAExecResult(result) 49 | }, 50 | all: async (params?: unknown[]) => await mc.query(sql, params), 51 | cursor: async (params?: unknown[]) => createACursor({ mc, sql, params }), 52 | close: async () => { 53 | // Nothing to do. 54 | } 55 | } 56 | return obj 57 | } 58 | 59 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 60 | function createACursor({ mc, sql, params, statement }: { 61 | mc: PromisifiedConnection 62 | sql: string 63 | params: unknown[] | undefined 64 | statement?: any 65 | }): Promise> { 66 | throw new Error("Not implemented") 67 | // return new Promise((resolve, reject) => { 68 | // const stream = mc.query(sql, params) 69 | // stream.on("error", reject) 70 | // stream.on("result", (row: unknown[]) => { 71 | // mc.pause() 72 | // mc.resume() 73 | // }) 74 | // stream.on("end", () => { 75 | // }) 76 | 77 | // const obj: AsyncIterableIterator = { 78 | // [Symbol.asyncIterator]: () => obj, 79 | // next: async () => { 80 | // mc.resume() 81 | 82 | // if (!rows) 83 | // return { done: true, value: undefined } 84 | // const value = rows[++currentIndex] 85 | // if (!value) 86 | // rows = undefined 87 | // return { done: !rows, value } 88 | // }, 89 | // return: async () => { 90 | // rows = undefined 91 | // return { done: true, value: undefined } 92 | // }, 93 | // throw: async err => { 94 | // rows = undefined 95 | // throw err 96 | // } 97 | // } 98 | // return obj 99 | // }) 100 | } 101 | -------------------------------------------------------------------------------- /mysql-adapter/src/exported-definitions.d.ts: -------------------------------------------------------------------------------- 1 | export interface LadcMysqlOptions { 2 | mysqlConfig: any 3 | } -------------------------------------------------------------------------------- /mysql-adapter/src/index.ts: -------------------------------------------------------------------------------- 1 | import { AConnection, ACreateConnectionOptions, LadcAdapter } from "ladc" 2 | import { createMysqlConnection, toAConnection } from "./AConnection" 3 | import { LadcMysqlOptions } from "./exported-definitions" 4 | import { PromisifiedConnection } from "./promisifyMysql" 5 | 6 | const connections = new WeakMap() 7 | 8 | export default function mysqlAdapter(options: LadcMysqlOptions): LadcAdapter { 9 | return { 10 | createConnection: async (createOptions?: ACreateConnectionOptions) => { 11 | const mc = await createMysqlConnection(options.mysqlConfig, createOptions) 12 | const cn = toAConnection(mc) 13 | connections.set(cn, mc) 14 | return cn 15 | }, 16 | capabilities: { 17 | cursors: false, 18 | namedParameters: false, 19 | preparedStatements: true, 20 | script: "onASeparateConnection" 21 | }, 22 | hooks: { 23 | async beginTransaction(cn: AConnection): Promise { 24 | const mc = connections.get(cn) 25 | if (mc) 26 | await mc.beginTransaction() 27 | else 28 | await cn.exec("start transaction") 29 | }, 30 | async commit(cn: AConnection): Promise { 31 | const mc = connections.get(cn) 32 | if (mc) 33 | await mc.commit() 34 | else 35 | await cn.exec("commit") 36 | }, 37 | async rollback(cn: AConnection): Promise { 38 | const mc = connections.get(cn) 39 | if (mc) 40 | await mc.rollback() 41 | else 42 | await cn.exec("rollback") 43 | }, 44 | } 45 | } 46 | } -------------------------------------------------------------------------------- /mysql-adapter/src/promisifyMysql.ts: -------------------------------------------------------------------------------- 1 | import { SqlParameters } from "ladc" 2 | import { Connection as UnderlyingConnection, createConnection } from "mysql" 3 | 4 | export interface PromisifiedConnection { 5 | query(sql: string, params?: SqlParameters): Promise 6 | exec(sql: string, params?: SqlParameters): Promise 7 | beginTransaction(): Promise 8 | commit(): Promise 9 | rollback(): Promise 10 | end(): Promise 11 | } 12 | 13 | export interface UnderlyingExecResult { 14 | readonly insertId?: number 15 | readonly affectedRows?: number 16 | readonly changedRows?: number 17 | } 18 | 19 | export async function createPromisifiedConnection(options: any): Promise { 20 | const cn = createConnection(options) 21 | if (options.init) 22 | await options.init(cn) 23 | return promisifyDatabase(cn) 24 | } 25 | 26 | function promisifyDatabase(cn: UnderlyingConnection): PromisifiedConnection { 27 | return { 28 | query: (sql: string, params = []) => { 29 | return new Promise((resolve, reject) => { 30 | cn.query(sql, params, (err, result) => { 31 | if (err) 32 | reject(err) 33 | else 34 | resolve(result) 35 | }) 36 | }) 37 | }, 38 | exec: (sql: string, params = []) => { 39 | return new Promise((resolve, reject) => { 40 | cn.query(sql, params, (err, result) => { 41 | if (err) 42 | reject(err) 43 | else { 44 | resolve(result) 45 | } 46 | }) 47 | }) 48 | }, 49 | beginTransaction: () => { 50 | return new Promise((resolve, reject) => { 51 | cn.beginTransaction((err: any) => { 52 | if (err) 53 | reject(err) 54 | else 55 | resolve() 56 | }) 57 | }) 58 | }, 59 | commit: () => { 60 | return new Promise((resolve, reject) => { 61 | cn.commit((err: any) => { 62 | if (err) 63 | reject(err) 64 | else 65 | resolve() 66 | }) 67 | }) 68 | }, 69 | rollback: () => { 70 | return new Promise((resolve, reject) => { 71 | cn.rollback((err: any) => { 72 | if (err) 73 | reject(err) 74 | else 75 | resolve() 76 | }) 77 | }) 78 | }, 79 | end: () => { 80 | return new Promise((resolve, reject) => { 81 | cn.end((err: any) => { 82 | if (err) 83 | reject(err) 84 | else 85 | resolve() 86 | }) 87 | }) 88 | }, 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /mysql-adapter/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2018", 4 | "moduleResolution": "node", 5 | "strict": true, 6 | "outDir": "build/compiled", 7 | "declaration": true, 8 | "lib": [ 9 | "es2018" 10 | ] 11 | }, 12 | "include": [ 13 | "src" 14 | ] 15 | } -------------------------------------------------------------------------------- /mysql2-adapter/.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .* 3 | *.js 4 | /*.d.ts 5 | build -------------------------------------------------------------------------------- /mysql2-adapter/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "env": { 3 | "es6": true, 4 | "node": true 5 | }, 6 | "plugins": [ 7 | "@typescript-eslint" 8 | ], 9 | "parser": "@typescript-eslint/parser", 10 | "parserOptions": { 11 | "ecmaVersion": 2018, 12 | "project": "./tsconfig.json", 13 | "sourceType": "module" 14 | }, 15 | "extends": [ 16 | "eslint:recommended", 17 | "plugin:@typescript-eslint/recommended", 18 | "plugin:@typescript-eslint/recommended-requiring-type-checking", 19 | "../eslint-rules" 20 | ], 21 | } 22 | -------------------------------------------------------------------------------- /mysql2-adapter/.gitignore: -------------------------------------------------------------------------------- 1 | .npmrc 2 | node_modules 3 | /build/compiled 4 | /ladc-mysql2-adapter.d.ts 5 | /ladc-mysql2-adapter.min.js -------------------------------------------------------------------------------- /mysql2-adapter/.npmignore: -------------------------------------------------------------------------------- 1 | /build/compiled -------------------------------------------------------------------------------- /mysql2-adapter/LICENSE: -------------------------------------------------------------------------------- 1 | CC0 1.0 Universal 2 | 3 | Statement of Purpose 4 | 5 | The laws of most jurisdictions throughout the world automatically confer 6 | exclusive Copyright and Related Rights (defined below) upon the creator and 7 | subsequent owner(s) (each and all, an "owner") of an original work of 8 | authorship and/or a database (each, a "Work"). 9 | 10 | Certain owners wish to permanently relinquish those rights to a Work for the 11 | purpose of contributing to a commons of creative, cultural and scientific 12 | works ("Commons") that the public can reliably and without fear of later 13 | claims of infringement build upon, modify, incorporate in other works, reuse 14 | and redistribute as freely as possible in any form whatsoever and for any 15 | purposes, including without limitation commercial purposes. These owners may 16 | contribute to the Commons to promote the ideal of a free culture and the 17 | further production of creative, cultural and scientific works, or to gain 18 | reputation or greater distribution for their Work in part through the use and 19 | efforts of others. 20 | 21 | For these and/or other purposes and motivations, and without any expectation 22 | of additional consideration or compensation, the person associating CC0 with a 23 | Work (the "Affirmer"), to the extent that he or she is an owner of Copyright 24 | and Related Rights in the Work, voluntarily elects to apply CC0 to the Work 25 | and publicly distribute the Work under its terms, with knowledge of his or her 26 | Copyright and Related Rights in the Work and the meaning and intended legal 27 | effect of CC0 on those rights. 28 | 29 | 1. Copyright and Related Rights. A Work made available under CC0 may be 30 | protected by copyright and related or neighboring rights ("Copyright and 31 | Related Rights"). Copyright and Related Rights include, but are not limited 32 | to, the following: 33 | 34 | i. the right to reproduce, adapt, distribute, perform, display, communicate, 35 | and translate a Work; 36 | 37 | ii. moral rights retained by the original author(s) and/or performer(s); 38 | 39 | iii. publicity and privacy rights pertaining to a person's image or likeness 40 | depicted in a Work; 41 | 42 | iv. rights protecting against unfair competition in regards to a Work, 43 | subject to the limitations in paragraph 4(a), below; 44 | 45 | v. rights protecting the extraction, dissemination, use and reuse of data in 46 | a Work; 47 | 48 | vi. database rights (such as those arising under Directive 96/9/EC of the 49 | European Parliament and of the Council of 11 March 1996 on the legal 50 | protection of databases, and under any national implementation thereof, 51 | including any amended or successor version of such directive); and 52 | 53 | vii. other similar, equivalent or corresponding rights throughout the world 54 | based on applicable law or treaty, and any national implementations thereof. 55 | 56 | 2. Waiver. To the greatest extent permitted by, but not in contravention of, 57 | applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and 58 | unconditionally waives, abandons, and surrenders all of Affirmer's Copyright 59 | and Related Rights and associated claims and causes of action, whether now 60 | known or unknown (including existing as well as future claims and causes of 61 | action), in the Work (i) in all territories worldwide, (ii) for the maximum 62 | duration provided by applicable law or treaty (including future time 63 | extensions), (iii) in any current or future medium and for any number of 64 | copies, and (iv) for any purpose whatsoever, including without limitation 65 | commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes 66 | the Waiver for the benefit of each member of the public at large and to the 67 | detriment of Affirmer's heirs and successors, fully intending that such Waiver 68 | shall not be subject to revocation, rescission, cancellation, termination, or 69 | any other legal or equitable action to disrupt the quiet enjoyment of the Work 70 | by the public as contemplated by Affirmer's express Statement of Purpose. 71 | 72 | 3. Public License Fallback. Should any part of the Waiver for any reason be 73 | judged legally invalid or ineffective under applicable law, then the Waiver 74 | shall be preserved to the maximum extent permitted taking into account 75 | Affirmer's express Statement of Purpose. In addition, to the extent the Waiver 76 | is so judged Affirmer hereby grants to each affected person a royalty-free, 77 | non transferable, non sublicensable, non exclusive, irrevocable and 78 | unconditional license to exercise Affirmer's Copyright and Related Rights in 79 | the Work (i) in all territories worldwide, (ii) for the maximum duration 80 | provided by applicable law or treaty (including future time extensions), (iii) 81 | in any current or future medium and for any number of copies, and (iv) for any 82 | purpose whatsoever, including without limitation commercial, advertising or 83 | promotional purposes (the "License"). The License shall be deemed effective as 84 | of the date CC0 was applied by Affirmer to the Work. Should any part of the 85 | License for any reason be judged legally invalid or ineffective under 86 | applicable law, such partial invalidity or ineffectiveness shall not 87 | invalidate the remainder of the License, and in such case Affirmer hereby 88 | affirms that he or she will not (i) exercise any of his or her remaining 89 | Copyright and Related Rights in the Work or (ii) assert any associated claims 90 | and causes of action with respect to the Work, in either case contrary to 91 | Affirmer's express Statement of Purpose. 92 | 93 | 4. Limitations and Disclaimers. 94 | 95 | a. No trademark or patent rights held by Affirmer are waived, abandoned, 96 | surrendered, licensed or otherwise affected by this document. 97 | 98 | b. Affirmer offers the Work as-is and makes no representations or warranties 99 | of any kind concerning the Work, express, implied, statutory or otherwise, 100 | including without limitation warranties of title, merchantability, fitness 101 | for a particular purpose, non infringement, or the absence of latent or 102 | other defects, accuracy, or the present or absence of errors, whether or not 103 | discoverable, all to the greatest extent permissible under applicable law. 104 | 105 | c. Affirmer disclaims responsibility for clearing rights of other persons 106 | that may apply to the Work or any use thereof, including without limitation 107 | any person's Copyright and Related Rights in the Work. Further, Affirmer 108 | disclaims responsibility for obtaining any necessary consents, permissions 109 | or other rights required for any use of the Work. 110 | 111 | d. Affirmer understands and acknowledges that Creative Commons is not a 112 | party to this document and has no duty or obligation with respect to this 113 | CC0 or use of the Work. 114 | 115 | For more information, please see 116 | 117 | -------------------------------------------------------------------------------- /mysql2-adapter/README.md: -------------------------------------------------------------------------------- 1 | # @ladc/mysql2-adapter 2 | 3 | 5 | 6 | [![Build Status](https://travis-ci.com/paroi-tech/ladc.svg?branch=master)](https://travis-ci.com/paroi-tech/ladc) 7 | [![npm](https://img.shields.io/npm/dm/@ladc/mysql2-adapter)](https://www.npmjs.com/package/@ladc/mysql2-adapter) 8 | ![Type definitions](https://img.shields.io/npm/types/@ladc/mysql2-adapter) 9 | [![GitHub](https://img.shields.io/github/license/paroi-tech/ladc)](https://github.com/paroi-tech/ladc) 10 | 11 | [LADC](https://github.com/paroi-tech/ladc/tree/master/ladc) is a common API on top of relational database (SQL) connectors. It can connect to Postgresql, MariaDB / MySQL, SQLite. The API is inspired from PDO and JDBC. It’s named LADC for “a Layer Above Database Connectors”. 12 | 13 | This package is a plugin for LADC. It is an adapter for MySQL and MariaDB, using the connector [mysql2](https://github.com/sidorares/node-mysql2). 14 | 15 | ## Install 16 | 17 | ``` 18 | npm install @ladc/mysql2-adapter ladc 19 | ``` 20 | 21 | ## Use a MySQL connection with LADC 22 | 23 | How to create a connection: 24 | 25 | ```js 26 | import ladc from "ladc"; 27 | import mysql2Adapter from "@ladc/mysql2-adapter"; 28 | 29 | const cn = ladc({ 30 | adapter: mysql2Adapter({ 31 | mysql2Config: { 32 | host: "-my-server-", 33 | database: "-my-database-", 34 | user: "-my-user-", 35 | password: "-my-password-", 36 | }, 37 | }), 38 | }); 39 | ``` 40 | 41 | # Use a MySQL connection with LADC and SQL Bricks 42 | 43 | Add the dependencies for SQL Bricks: 44 | 45 | ```sh 46 | npm install sql-bricks @ladc/sql-bricks-modifier 47 | ``` 48 | 49 | In your code, MySQL requires to set a specific `placeholder` option in SQL Bricks: 50 | 51 | ```js 52 | import ladc from "ladc"; 53 | import mysql2Adapter from "@ladc/mysql2-adapter"; 54 | import sqlBricksModifier from "@ladc/sql-bricks-modifier"; 55 | 56 | const cn = ladc({ 57 | adapter: mysql2Adapter({ 58 | mysql2Config: { 59 | host: "-my-server-", 60 | database: "-my-database-", 61 | user: "-my-user-", 62 | password: "-my-password-", 63 | }, 64 | }), 65 | modifier: sqlBricksModifier({ 66 | toParamsOptions: { placeholder: "?" }, // ← Specific to MySQL 67 | }), 68 | }); 69 | ``` 70 | 71 | Now, use it: 72 | 73 | ```js 74 | import { select } from "sql-bricks"; 75 | 76 | async function test(cn) { 77 | const q = select("col1, col2").from("table1"); 78 | const rows = await cn.all(q); 79 | console.log(rows); 80 | } 81 | ``` 82 | 83 | ## Contribute 84 | 85 | With VS Code, our recommanded plugin is: 86 | 87 | - **TSLint** from Microsoft (`ms-vscode.vscode-typescript-tslint-plugin`) 88 | -------------------------------------------------------------------------------- /mysql2-adapter/build/make-bundle.js: -------------------------------------------------------------------------------- 1 | const { promisify } = require("util") 2 | const fs = require("fs") 3 | const path = require("path") 4 | const { rollup } = require("rollup") 5 | const { minify } = require("terser") 6 | 7 | const readFile = promisify(fs.readFile) 8 | const writeFile = promisify(fs.writeFile) 9 | 10 | const bundleName = "ladc-mysql2-adapter" 11 | const srcPath = path.join(__dirname, "..", "src") 12 | const compiledPath = path.join(__dirname, "compiled") 13 | const distNpmPath = path.join(__dirname, "..") 14 | 15 | async function build() { 16 | const bundle = await rollup({ 17 | input: path.join(compiledPath, "index.js"), 18 | 19 | }) 20 | const { output } = await bundle.generate({ 21 | format: "commonjs", 22 | exports: "named", 23 | sourcemap: false, 24 | }) 25 | 26 | const code = output[0].code 27 | if (!code) 28 | throw new Error("Missing code") 29 | 30 | // const minified = await minify(output[0].code, { sourceMap: false }) 31 | // if (minified.error) 32 | // throw minified.error 33 | // if (!minified.code) 34 | // throw new Error("Missing code") 35 | 36 | await writeFile(path.join(distNpmPath, `${bundleName}.min.js`), code) 37 | await writeFile(path.join(distNpmPath, `${bundleName}.d.ts`), await makeDefinitionsCode()) 38 | } 39 | 40 | async function makeDefinitionsCode() { 41 | const defs = [ 42 | "// -- Usage definitions --", 43 | removeLocalImportsExports((await readFile(path.join(srcPath, "exported-definitions.d.ts"), "utf-8")).trim()), 44 | "// -- Entry point definition --", 45 | removeSemicolons( 46 | removeLocalImportsExports((await readFile(path.join(compiledPath, "index.d.ts"), "utf-8")).trim()), 47 | ) 48 | ] 49 | return defs.join("\n\n") 50 | } 51 | 52 | function removeLocalImportsExports(code) { 53 | const localImportExport = /^\s*(import|export) .* from "\.\/.*"\s*;?\s*$/ 54 | return code.split("\n").filter(line => { 55 | return !localImportExport.test(line) 56 | }).join("\n").trim() 57 | } 58 | 59 | function removeSemicolons(code) { 60 | return code.replace(/;/g, "") 61 | } 62 | 63 | build().catch(err => console.log(err.message, err.stack)) 64 | -------------------------------------------------------------------------------- /mysql2-adapter/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@ladc/mysql2-adapter", 3 | "version": "0.22.14", 4 | "description": "Connect to MySQL and MariaDB with the LADC API ('mysql2' connector).", 5 | "main": "ladc-mysql2-adapter.min.js", 6 | "types": "ladc-mysql2-adapter.d.ts", 7 | "dependencies": { 8 | "mysql2": "^2.1.0" 9 | }, 10 | "peerDependencies": { 11 | "ladc": "^0.22.13" 12 | }, 13 | "devDependencies": { 14 | "@types/node": "^12.12.55", 15 | "@typescript-eslint/eslint-plugin": "~3.10.1", 16 | "@typescript-eslint/parser": "~3.10.1", 17 | "eslint": "~7.7.0", 18 | "ladc": "^0.22.13", 19 | "npm-run-all": "^4.1.5", 20 | "rimraf": "^3.0.2", 21 | "rollup": "^2.26.0", 22 | "terser": "^5.3.0", 23 | "typescript": "^4.0.2" 24 | }, 25 | "scripts": { 26 | "build": "run-s clear tsc make-bundle", 27 | "tsc": "tsc", 28 | "tsc:watch": "tsc --watch", 29 | "make-bundle": "node build/make-bundle", 30 | "clear": "rimraf build/compiled/*", 31 | "lint": "eslint . --ext .ts --max-warnings=0", 32 | "prepublishOnly": "npm run lint && npm run build" 33 | }, 34 | "repository": { 35 | "type": "git", 36 | "url": "git+https://github.com/paroi-tech/ladc.git" 37 | }, 38 | "homepage": "https://github.com/paroi-tech/ladc/tree/master/mysql2-adapter", 39 | "author": { 40 | "name": "Paleo" 41 | }, 42 | "license": "CC0-1.0", 43 | "keywords": [ 44 | "mysql", 45 | "mariadb", 46 | "connection", 47 | "ladc" 48 | ] 49 | } 50 | -------------------------------------------------------------------------------- /mysql2-adapter/src/AConnection.ts: -------------------------------------------------------------------------------- 1 | import { AConnection, ACreateConnectionOptions, AExecResult, APreparedStatement } from "ladc" 2 | const { createConnection } = require("mysql2/promise") 3 | 4 | export async function createMysqlConnection( 5 | mysql2Config: any, 6 | createOptions?: ACreateConnectionOptions 7 | ): Promise { 8 | if (createOptions && createOptions.enableScript) { 9 | mysql2Config = { 10 | ...mysql2Config, 11 | multipleStatements: true 12 | } 13 | } 14 | return await createConnection(mysql2Config) 15 | } 16 | 17 | export function toAConnection(mc: any): AConnection { // , options: LadcMysql2Options 18 | return { 19 | prepare: async (sql: string) => makeAPreparedStatement(mc, sql), 20 | exec: async (sql: string, params?: unknown[]) => { 21 | const [result] = await mc.execute(sql, params) 22 | return toAExecResult(result) 23 | }, 24 | all: async (sql: string, params?: unknown[]) => toRows(await mc.query(sql, params)), 25 | cursor: (sql: string, params?: unknown[]) => createACursor({ mc, sql, params }), 26 | script: async (sql: string) => { 27 | await mc.query(sql) 28 | }, 29 | close: async () => { 30 | await mc.end() 31 | } 32 | } 33 | } 34 | 35 | function toRows([rows]: any): any[] { 36 | // console.log(">> result", result) 37 | return rows 38 | } 39 | 40 | function toAExecResult(result: any): AExecResult { 41 | return { 42 | affectedRows: result.affectedRows, 43 | getInsertedId: () => result.insertId 44 | } 45 | } 46 | 47 | async function makeAPreparedStatement(mc: any, sql: string): Promise> { 48 | const statement = await mc.prepare(sql) 49 | const obj: APreparedStatement = { 50 | exec: async (params?: unknown[]) => { 51 | const [result] = await statement.execute(params) 52 | return toAExecResult(result) 53 | }, 54 | all: async (params?: unknown[]) => toRows(await statement.execute(params)), 55 | cursor: (params?: unknown[]) => createACursor({ mc, sql, params, statement }), 56 | close: async () => { 57 | statement.close() 58 | return Promise.resolve() 59 | } 60 | } 61 | return obj 62 | } 63 | 64 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 65 | function createACursor({ mc, sql, params, statement }: { 66 | mc: any 67 | sql: string 68 | params: unknown[] | undefined 69 | statement?: any 70 | }): Promise> { 71 | throw new Error("Not implemented") 72 | // return new Promise((resolve, reject) => { 73 | // const stream = mc.query(sql, params) 74 | // stream.on("error", reject) 75 | // stream.on("result", (row: unknown[]) => { 76 | // mc.pause() 77 | // mc.resume() 78 | // }) 79 | // stream.on("end", () => { 80 | // }) 81 | 82 | // const obj: AsyncIterableIterator = { 83 | // [Symbol.asyncIterator]: () => obj, 84 | // next: async () => { 85 | // mc.resume() 86 | 87 | // if (!rows) 88 | // return { done: true, value: undefined } 89 | // const value = rows[++currentIndex] 90 | // if (!value) 91 | // rows = undefined 92 | // return { done: !rows, value } 93 | // }, 94 | // return: async () => { 95 | // rows = undefined 96 | // return { done: true, value: undefined } 97 | // }, 98 | // throw: async err => { 99 | // rows = undefined 100 | // throw err 101 | // } 102 | // } 103 | // return obj 104 | // }) 105 | } 106 | -------------------------------------------------------------------------------- /mysql2-adapter/src/exported-definitions.d.ts: -------------------------------------------------------------------------------- 1 | export interface LadcMysql2Options { 2 | mysql2Config: any 3 | } -------------------------------------------------------------------------------- /mysql2-adapter/src/index.ts: -------------------------------------------------------------------------------- 1 | import { AConnection, ACreateConnectionOptions, LadcAdapter } from "ladc" 2 | import { createMysqlConnection, toAConnection } from "./AConnection" 3 | import { LadcMysql2Options } from "./exported-definitions" 4 | 5 | const connections = new WeakMap() 6 | 7 | export default function mysql2Adapter(options: LadcMysql2Options): LadcAdapter { 8 | return { 9 | createConnection: async (createOptions?: ACreateConnectionOptions) => { 10 | const mc = await createMysqlConnection(options.mysql2Config, createOptions) 11 | const cn = toAConnection(mc) 12 | connections.set(cn, mc) 13 | return cn 14 | }, 15 | capabilities: { 16 | cursors: false, 17 | namedParameters: false, 18 | preparedStatements: true, 19 | script: "onASeparateConnection" 20 | }, 21 | hooks: { 22 | async beginTransaction(cn: AConnection): Promise { 23 | const mc = connections.get(cn) 24 | if (mc) { 25 | await mc.beginTransaction() 26 | await mc.end() 27 | } else 28 | await cn.exec("start transaction") 29 | }, 30 | async commit(cn: AConnection): Promise { 31 | const mc = connections.get(cn) 32 | if (mc) { 33 | await mc.commit() 34 | await mc.end() 35 | } else 36 | await cn.exec("commit") 37 | }, 38 | async rollback(cn: AConnection): Promise { 39 | const mc = connections.get(cn) 40 | if (mc) { 41 | await mc.rollback() 42 | await mc.end() 43 | } else 44 | await cn.exec("rollback") 45 | }, 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /mysql2-adapter/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2018", 4 | "moduleResolution": "node", 5 | "strict": true, 6 | "outDir": "build/compiled", 7 | "declaration": true, 8 | "lib": [ 9 | "es2018" 10 | ] 11 | }, 12 | "include": [ 13 | "src" 14 | ] 15 | } -------------------------------------------------------------------------------- /pg-adapter/.gitignore: -------------------------------------------------------------------------------- 1 | .npmrc 2 | node_modules 3 | /build/compiled 4 | /ladc-pg-adapter.d.ts 5 | /ladc-pg-adapter.min.js -------------------------------------------------------------------------------- /pg-adapter/.npmignore: -------------------------------------------------------------------------------- 1 | /build/compiled -------------------------------------------------------------------------------- /pg-adapter/LICENSE: -------------------------------------------------------------------------------- 1 | CC0 1.0 Universal 2 | 3 | Statement of Purpose 4 | 5 | The laws of most jurisdictions throughout the world automatically confer 6 | exclusive Copyright and Related Rights (defined below) upon the creator and 7 | subsequent owner(s) (each and all, an "owner") of an original work of 8 | authorship and/or a database (each, a "Work"). 9 | 10 | Certain owners wish to permanently relinquish those rights to a Work for the 11 | purpose of contributing to a commons of creative, cultural and scientific 12 | works ("Commons") that the public can reliably and without fear of later 13 | claims of infringement build upon, modify, incorporate in other works, reuse 14 | and redistribute as freely as possible in any form whatsoever and for any 15 | purposes, including without limitation commercial purposes. These owners may 16 | contribute to the Commons to promote the ideal of a free culture and the 17 | further production of creative, cultural and scientific works, or to gain 18 | reputation or greater distribution for their Work in part through the use and 19 | efforts of others. 20 | 21 | For these and/or other purposes and motivations, and without any expectation 22 | of additional consideration or compensation, the person associating CC0 with a 23 | Work (the "Affirmer"), to the extent that he or she is an owner of Copyright 24 | and Related Rights in the Work, voluntarily elects to apply CC0 to the Work 25 | and publicly distribute the Work under its terms, with knowledge of his or her 26 | Copyright and Related Rights in the Work and the meaning and intended legal 27 | effect of CC0 on those rights. 28 | 29 | 1. Copyright and Related Rights. A Work made available under CC0 may be 30 | protected by copyright and related or neighboring rights ("Copyright and 31 | Related Rights"). Copyright and Related Rights include, but are not limited 32 | to, the following: 33 | 34 | i. the right to reproduce, adapt, distribute, perform, display, communicate, 35 | and translate a Work; 36 | 37 | ii. moral rights retained by the original author(s) and/or performer(s); 38 | 39 | iii. publicity and privacy rights pertaining to a person's image or likeness 40 | depicted in a Work; 41 | 42 | iv. rights protecting against unfair competition in regards to a Work, 43 | subject to the limitations in paragraph 4(a), below; 44 | 45 | v. rights protecting the extraction, dissemination, use and reuse of data in 46 | a Work; 47 | 48 | vi. database rights (such as those arising under Directive 96/9/EC of the 49 | European Parliament and of the Council of 11 March 1996 on the legal 50 | protection of databases, and under any national implementation thereof, 51 | including any amended or successor version of such directive); and 52 | 53 | vii. other similar, equivalent or corresponding rights throughout the world 54 | based on applicable law or treaty, and any national implementations thereof. 55 | 56 | 2. Waiver. To the greatest extent permitted by, but not in contravention of, 57 | applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and 58 | unconditionally waives, abandons, and surrenders all of Affirmer's Copyright 59 | and Related Rights and associated claims and causes of action, whether now 60 | known or unknown (including existing as well as future claims and causes of 61 | action), in the Work (i) in all territories worldwide, (ii) for the maximum 62 | duration provided by applicable law or treaty (including future time 63 | extensions), (iii) in any current or future medium and for any number of 64 | copies, and (iv) for any purpose whatsoever, including without limitation 65 | commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes 66 | the Waiver for the benefit of each member of the public at large and to the 67 | detriment of Affirmer's heirs and successors, fully intending that such Waiver 68 | shall not be subject to revocation, rescission, cancellation, termination, or 69 | any other legal or equitable action to disrupt the quiet enjoyment of the Work 70 | by the public as contemplated by Affirmer's express Statement of Purpose. 71 | 72 | 3. Public License Fallback. Should any part of the Waiver for any reason be 73 | judged legally invalid or ineffective under applicable law, then the Waiver 74 | shall be preserved to the maximum extent permitted taking into account 75 | Affirmer's express Statement of Purpose. In addition, to the extent the Waiver 76 | is so judged Affirmer hereby grants to each affected person a royalty-free, 77 | non transferable, non sublicensable, non exclusive, irrevocable and 78 | unconditional license to exercise Affirmer's Copyright and Related Rights in 79 | the Work (i) in all territories worldwide, (ii) for the maximum duration 80 | provided by applicable law or treaty (including future time extensions), (iii) 81 | in any current or future medium and for any number of copies, and (iv) for any 82 | purpose whatsoever, including without limitation commercial, advertising or 83 | promotional purposes (the "License"). The License shall be deemed effective as 84 | of the date CC0 was applied by Affirmer to the Work. Should any part of the 85 | License for any reason be judged legally invalid or ineffective under 86 | applicable law, such partial invalidity or ineffectiveness shall not 87 | invalidate the remainder of the License, and in such case Affirmer hereby 88 | affirms that he or she will not (i) exercise any of his or her remaining 89 | Copyright and Related Rights in the Work or (ii) assert any associated claims 90 | and causes of action with respect to the Work, in either case contrary to 91 | Affirmer's express Statement of Purpose. 92 | 93 | 4. Limitations and Disclaimers. 94 | 95 | a. No trademark or patent rights held by Affirmer are waived, abandoned, 96 | surrendered, licensed or otherwise affected by this document. 97 | 98 | b. Affirmer offers the Work as-is and makes no representations or warranties 99 | of any kind concerning the Work, express, implied, statutory or otherwise, 100 | including without limitation warranties of title, merchantability, fitness 101 | for a particular purpose, non infringement, or the absence of latent or 102 | other defects, accuracy, or the present or absence of errors, whether or not 103 | discoverable, all to the greatest extent permissible under applicable law. 104 | 105 | c. Affirmer disclaims responsibility for clearing rights of other persons 106 | that may apply to the Work or any use thereof, including without limitation 107 | any person's Copyright and Related Rights in the Work. Further, Affirmer 108 | disclaims responsibility for obtaining any necessary consents, permissions 109 | or other rights required for any use of the Work. 110 | 111 | d. Affirmer understands and acknowledges that Creative Commons is not a 112 | party to this document and has no duty or obligation with respect to this 113 | CC0 or use of the Work. 114 | 115 | For more information, please see 116 | 117 | -------------------------------------------------------------------------------- /pg-adapter/README.md: -------------------------------------------------------------------------------- 1 | # @ladc/pg-adapter 2 | 3 | 5 | 6 | [![Build Status](https://travis-ci.com/paroi-tech/ladc.svg?branch=master)](https://travis-ci.com/paroi-tech/ladc) 7 | [![npm](https://img.shields.io/npm/dm/@ladc/pg-adapter)](https://www.npmjs.com/package/@ladc/pg-adapter) 8 | ![Type definitions](https://img.shields.io/npm/types/@ladc/pg-adapter) 9 | [![GitHub](https://img.shields.io/github/license/paroi-tech/ladc)](https://github.com/paroi-tech/ladc) 10 | 11 | [LADC](https://github.com/paroi-tech/ladc/tree/master/ladc) is a common API on top of relational database (SQL) connectors. It can connect to Postgresql, MariaDB / MySQL, SQLite. The API is inspired from PDO and JDBC. It’s named LADC for “a Layer Above Database Connectors”. 12 | 13 | This package is a plugin for LADC. It is an adapter for Postgresql, using the connector [pg](https://github.com/brianc/node-postgres). 14 | 15 | ## Install 16 | 17 | ```sh 18 | npm install @ladc/pg-adapter ladc 19 | ``` 20 | 21 | ## Usage 22 | 23 | How to create a connection: 24 | 25 | ```ts 26 | import ladc from "ladc"; 27 | import pgAdapter from "@ladc/pg-adapter"; 28 | 29 | const cn = ladc({ 30 | adapter: pgAdapter({ 31 | pgConfig: { 32 | host: "-my-server-", 33 | database: "-my-database-", 34 | user: "-my-user-", 35 | password: "-my-password-", 36 | }, 37 | }), 38 | }); 39 | ``` 40 | 41 | ## How to Retrieve the Last Inserted Identifier with Postgresql 42 | 43 | Postgresql has a gotcha regarding autoincremented identifiers. The insert query must end with a non-standard returning statement. Then, the name of the autoincremented column is required to obtain its last inserted value. 44 | 45 | The LADC adapter for Postgresql provides the `autoincMapping` option. It allows to provide a mapping of autoincremented column name for each table that has one. Here is an example: 46 | 47 | ```js 48 | const autoincMapping = { 49 | test: "test_id", 50 | }; 51 | 52 | const cn = ladc({ 53 | adapter: pgAdapter({ 54 | pgConfig: { 55 | /* credentials */ 56 | }, 57 | autoincMapping, 58 | }), 59 | }); 60 | 61 | // The adapter will append 'returning test_id' 62 | const result = await cn.exec( 63 | "insert into test (message) values ('Hello, World!')" 64 | ); 65 | 66 | // Returns the value of 'test_id' 67 | const newId = result.getInsertedId(); 68 | ``` 69 | 70 | Or, it is still possible to manually write the `returning` statement then to get it: 71 | 72 | ```js 73 | const result = await cn.exec( 74 | "insert into test(message) values ('Hi there!') returning test_id" // Postgres only 75 | ); 76 | const newId = result.getInsertedId("test_id"); 77 | ``` 78 | 79 | ## Contribute 80 | 81 | With VS Code, our recommanded plugin is: 82 | 83 | - **TSLint** from Microsoft (`ms-vscode.vscode-typescript-tslint-plugin`) 84 | -------------------------------------------------------------------------------- /pg-adapter/build/make-bundle.js: -------------------------------------------------------------------------------- 1 | const { promisify } = require("util") 2 | const fs = require("fs") 3 | const path = require("path") 4 | const rollup = require("rollup") 5 | const terser = require("terser") 6 | 7 | const readFile = promisify(fs.readFile) 8 | const writeFile = promisify(fs.writeFile) 9 | 10 | const bundleName = "ladc-pg-adapter" 11 | const srcPath = path.join(__dirname, "..", "src") 12 | const compiledPath = path.join(__dirname, "compiled") 13 | const distNpmPath = path.join(__dirname, "..") 14 | 15 | async function build() { 16 | const bundle = await rollup.rollup({ 17 | input: path.join(compiledPath, "index.js") 18 | }) 19 | const { output } = await bundle.generate({ 20 | format: "commonjs", 21 | exports: "named", 22 | sourcemap: false, 23 | }) 24 | 25 | const minified = terser.minify({ 26 | bundle: output[0].code 27 | }) 28 | if (minified.error) 29 | throw minified.error 30 | 31 | await writeFile(path.join(distNpmPath, `${bundleName}.min.js`), minified.code) 32 | await writeFile(path.join(distNpmPath, `${bundleName}.d.ts`), await makeDefinitionsCode()) 33 | } 34 | 35 | async function makeDefinitionsCode() { 36 | const defs = [ 37 | "// -- Usage definitions --", 38 | removeLocalImportsExports((await readFile(path.join(srcPath, "exported-definitions.d.ts"), "utf-8")).trim()), 39 | "// -- Entry point definition --", 40 | removeSemicolons( 41 | removeLocalImportsExports((await readFile(path.join(compiledPath, "index.d.ts"), "utf-8")).trim()), 42 | ) 43 | ] 44 | return defs.join("\n\n") 45 | } 46 | 47 | function removeLocalImportsExports(code) { 48 | const localImportExport = /^\s*(import|export) .* from "\.\/.*"\s*;?\s*$/ 49 | return code.split("\n").filter(line => { 50 | return !localImportExport.test(line) 51 | }).join("\n").trim() 52 | } 53 | 54 | function removeSemicolons(code) { 55 | return code.replace(/;/g, "") 56 | } 57 | 58 | build().catch(err => console.log(err.message, err.stack)) 59 | -------------------------------------------------------------------------------- /pg-adapter/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@ladc/pg-adapter", 3 | "version": "0.22.7", 4 | "description": "Connect to Postgresql with the LADC API.", 5 | "main": "ladc-pg-adapter.min.js", 6 | "types": "ladc-pg-adapter.d.ts", 7 | "dependencies": { 8 | "@types/pg": "^7.14.3", 9 | "pg": "^8.2.1", 10 | "pg-cursor": "^2.2.1" 11 | }, 12 | "peerDependencies": { 13 | "ladc": "0.22" 14 | }, 15 | "devDependencies": { 16 | "@types/node": "^12.12.44", 17 | "ladc": "^0.22.7", 18 | "npm-run-all": "4", 19 | "rimraf": "^3.0.2", 20 | "rollup": "^2.15.0", 21 | "terser": "^4.7.0", 22 | "tslint": "^6.1.2", 23 | "typescript": "^3.9.5" 24 | }, 25 | "scripts": { 26 | "build": "run-s clear tsc make-bundle", 27 | "tsc": "tsc", 28 | "tsc:watch": "tsc --watch", 29 | "make-bundle": "node build/make-bundle", 30 | "clear": "rimraf build/compiled/*", 31 | "lint": "tslint -p tsconfig.json -t verbose", 32 | "prepublishOnly": "npm run lint && npm run build" 33 | }, 34 | "engines": { 35 | "node": ">=8" 36 | }, 37 | "repository": { 38 | "type": "git", 39 | "url": "git+https://github.com/paroi-tech/ladc.git" 40 | }, 41 | "homepage": "https://github.com/paroi-tech/ladc/tree/master/pg-adapter", 42 | "author": { 43 | "name": "Paleo" 44 | }, 45 | "license": "CC0-1.0", 46 | "keywords": [ 47 | "postgresql", 48 | "connection", 49 | "ladc" 50 | ] 51 | } -------------------------------------------------------------------------------- /pg-adapter/src/AConnection.ts: -------------------------------------------------------------------------------- 1 | import { AConnection, AExecResult, APreparedStatement } from "ladc" 2 | import { Client, ClientConfig, QueryResult } from "pg" 3 | const Cursor = require("pg-cursor") 4 | import { LadcPgOptions } from "./exported-definitions" 5 | import { promisifyCursor } from "./promisifyCursor" 6 | 7 | export async function createPgConnection(config: string | ClientConfig): Promise { 8 | const client = new Client(config) 9 | client.connect() 10 | return client 11 | } 12 | 13 | const insertRegexp = /^\s*insert\s+into\s+([^\s\(]+)\s*(?:\([^)]+\))?\s*values\s*\([\s\S]*\)\s*$/i 14 | 15 | function addReturningToInsert(sql: string, options: LadcPgOptions) { 16 | const matches = insertRegexp.exec(sql) 17 | if (!matches) 18 | return { sql } 19 | const insertTable = matches[1] 20 | const idColumnName = options.autoincMapping && options.autoincMapping[insertTable] 21 | if (idColumnName) 22 | sql = `${sql} returning ${idColumnName}` 23 | return { sql, insertTable, idColumnName } 24 | } 25 | 26 | export function toAConnection(client: Client, options: LadcPgOptions): AConnection { 27 | return { 28 | prepare: async (sql: string) => makeAPreparedStatement(options, client, sql), 29 | exec: async (sql: string, params?: unknown[]) => { 30 | const { sql: text, insertTable, idColumnName } = addReturningToInsert(sql, options) 31 | return toAExecResult( 32 | await client.query({ 33 | text, 34 | values: params 35 | }), 36 | insertTable, 37 | idColumnName 38 | ) 39 | }, 40 | all: async (sql: string, params?: unknown[]) => toRows(await client.query({ 41 | text: sql, 42 | values: params 43 | })), 44 | cursor: async (sql: string, params?: unknown[]) => createACursor({ client, sql, params }), 45 | script: async (sql: string) => { 46 | await client.query(sql) 47 | }, 48 | close: async () => { 49 | await client.end() 50 | } 51 | } 52 | } 53 | 54 | function toRows(result: QueryResult): any[] { 55 | return result.rows 56 | } 57 | 58 | function toAExecResult(result: QueryResult, insertTable?: string, optIdCol?: string): AExecResult { 59 | return { 60 | affectedRows: result.rowCount, 61 | getInsertedId: (options?: { columnName?: string }) => { 62 | if (result.rows.length !== 1) { 63 | if (result.rows.length === 0) 64 | throw new Error(`Cannot get the inserted ID, please append 'returning your_column_id' to your query`) 65 | throw new Error(`Cannot get the inserted ID, there must be one result row (${result.rows.length})`) 66 | } 67 | const idColumnName = options && options.columnName 68 | const row = result.rows[0] 69 | let col: string | undefined 70 | if (idColumnName) { 71 | if (!row.hasOwnProperty(idColumnName)) 72 | throw new Error(`Cannot get the inserted ID '${idColumnName}', available columns are: ${Object.keys(row).join(", ")}`) 73 | col = idColumnName 74 | } else if (optIdCol) { 75 | if (!row.hasOwnProperty(optIdCol)) 76 | throw new Error(`Cannot get the inserted ID '${optIdCol}', available columns are: ${Object.keys(row).join(", ")}`) 77 | col = optIdCol 78 | } else if (insertTable) { 79 | if (row.hasOwnProperty("id")) 80 | col = "id" 81 | else { 82 | let composed = `${insertTable.toLowerCase()}_id` 83 | if (row.hasOwnProperty(composed)) 84 | col = composed 85 | else { 86 | composed = composed.toUpperCase() 87 | if (row.hasOwnProperty(composed)) 88 | col = composed 89 | } 90 | } 91 | } 92 | if (!col) 93 | throw new Error(`Cannot get the inserted ID, available columns are: ${Object.keys(row).join(", ")}`) 94 | return row[col] 95 | } 96 | } 97 | } 98 | 99 | let psSequence = 0 100 | 101 | async function makeAPreparedStatement( 102 | options: LadcPgOptions, 103 | client: Client, 104 | sql: string 105 | ): Promise> { 106 | const psName = `ladc-ps-${++psSequence}` 107 | const obj: APreparedStatement = { 108 | exec: async (params?: unknown[]) => { 109 | const { sql: text, insertTable, idColumnName } = addReturningToInsert(sql, options) 110 | return toAExecResult(await client.query({ 111 | name: psName, 112 | text, 113 | values: params 114 | }), insertTable, idColumnName) 115 | }, 116 | all: async (params?: unknown[]) => { 117 | return toRows(await client.query({ 118 | name: psName, 119 | text: sql, 120 | values: params 121 | })) 122 | }, 123 | cursor: async (params?: unknown[]) => createACursor({ client, sql, params, psName }), 124 | close: async () => { } 125 | } 126 | return obj 127 | } 128 | 129 | async function createACursor({ client, sql, params, psName }: { 130 | client: Client 131 | sql: string 132 | params?: unknown[] 133 | psName?: string 134 | }): Promise> { 135 | const cursorObj = new Cursor(sql, params) 136 | if (psName) 137 | cursorObj.name = psName 138 | const innerCursor = promisifyCursor(client.query(cursorObj)) 139 | 140 | let done = false 141 | const closeCursor = async () => { 142 | done = true 143 | await innerCursor.close() 144 | } 145 | const obj: AsyncIterableIterator = { 146 | [Symbol.asyncIterator]: () => obj, 147 | next: async () => { 148 | if (done) 149 | return { done, value: undefined } 150 | const value = await innerCursor.read(1) 151 | if (!value) 152 | await closeCursor() 153 | return { done, value } 154 | }, 155 | return: async () => { 156 | if (!done) 157 | await closeCursor() 158 | return { done, value: undefined } 159 | }, 160 | throw: async err => { 161 | if (!done) 162 | await closeCursor() 163 | throw err 164 | } 165 | } 166 | return obj 167 | } 168 | -------------------------------------------------------------------------------- /pg-adapter/src/exported-definitions.d.ts: -------------------------------------------------------------------------------- 1 | import { ClientConfig } from "pg" 2 | 3 | export interface LadcPgOptions { 4 | pgConfig: string | ClientConfig, 5 | autoincMapping?: { [tableName: string]: string | undefined } 6 | } -------------------------------------------------------------------------------- /pg-adapter/src/index.ts: -------------------------------------------------------------------------------- 1 | import { LadcAdapter } from "ladc" 2 | import { createPgConnection, toAConnection } from "./AConnection" 3 | import { LadcPgOptions } from "./exported-definitions" 4 | 5 | export default function pgAdapter(options: LadcPgOptions): LadcAdapter { 6 | return { 7 | createConnection: async () => { 8 | const db = await createPgConnection(options.pgConfig) 9 | return toAConnection(db, options) 10 | }, 11 | capabilities: { 12 | cursors: true, 13 | namedParameters: false, 14 | preparedStatements: true, 15 | script: true 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /pg-adapter/src/promisifyCursor.ts: -------------------------------------------------------------------------------- 1 | export interface PromisifiedCursor { 2 | read(rowCount: number): Promise 3 | close(): Promise 4 | } 5 | 6 | export function promisifyCursor(innerCursor: any): PromisifiedCursor { 7 | return { 8 | read(rowCount) { 9 | return new Promise((resolve, reject) => { 10 | innerCursor.read(rowCount, (err: any, rows: unknown[]) => { 11 | if (err) 12 | reject(err) 13 | else 14 | resolve(rows) 15 | }) 16 | }) 17 | }, 18 | close() { 19 | return new Promise((resolve, reject) => { 20 | innerCursor.close((err: any) => { 21 | if (err) 22 | reject(err) 23 | else 24 | resolve() 25 | }) 26 | }) 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /pg-adapter/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2018", 4 | "moduleResolution": "node", 5 | "strict": true, 6 | "outDir": "build/compiled", 7 | "declaration": true, 8 | "lib": [ 9 | "es2018" 10 | ] 11 | }, 12 | "include": [ 13 | "src" 14 | ] 15 | } -------------------------------------------------------------------------------- /pg-adapter/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "defaultSeverity": "error", 3 | "extends": [ 4 | "tslint:recommended" 5 | ], 6 | "linterOptions": { 7 | "exclude": [ 8 | "node_modules/**" 9 | ] 10 | }, 11 | "rules": { 12 | "arrow-parens": false, 13 | "curly": false, 14 | "eofline": false, 15 | "indent": [ 16 | true, 17 | "spaces", 18 | 2 19 | ], 20 | "interface-name": false, 21 | "max-classes-per-file": false, 22 | "max-line-length": false, 23 | "member-access": [ 24 | true, 25 | "no-public" 26 | ], 27 | "no-conditional-assignment": false, 28 | "no-consecutive-blank-lines": false, 29 | "no-empty": [ 30 | true, 31 | "allow-empty-catch", 32 | "allow-empty-functions" 33 | ], 34 | "no-shadowed-variable": false, 35 | "no-string-literal": false, 36 | "no-var-requires": false, 37 | "ordered-imports": true, 38 | "object-literal-sort-keys": false, 39 | "object-literal-key-quotes": false, 40 | "only-arrow-functions": false, 41 | "prefer-const": true, 42 | "quotemark": [ 43 | true, 44 | "double" 45 | ], 46 | "semicolon": [ 47 | true, 48 | "never" 49 | ], 50 | "space-before-function-paren": [ 51 | true, 52 | { 53 | "anonymous": "always", 54 | "named": "never", 55 | "asyncArrow": "always" 56 | } 57 | ], 58 | "trailing-comma": [ 59 | true, 60 | { 61 | "singleline": "never" 62 | } 63 | ], 64 | "variable-name": [ 65 | true, 66 | "allow-leading-underscore", 67 | "allow-pascal-case" 68 | ] 69 | } 70 | } -------------------------------------------------------------------------------- /sql-bricks-modifier/.gitignore: -------------------------------------------------------------------------------- 1 | .npmrc 2 | node_modules 3 | /build/compiled 4 | /ladc-sql-bricks-modifier.d.ts 5 | /ladc-sql-bricks-modifier.min.js -------------------------------------------------------------------------------- /sql-bricks-modifier/.npmignore: -------------------------------------------------------------------------------- 1 | /build/compiled -------------------------------------------------------------------------------- /sql-bricks-modifier/LICENSE: -------------------------------------------------------------------------------- 1 | CC0 1.0 Universal 2 | 3 | Statement of Purpose 4 | 5 | The laws of most jurisdictions throughout the world automatically confer 6 | exclusive Copyright and Related Rights (defined below) upon the creator and 7 | subsequent owner(s) (each and all, an "owner") of an original work of 8 | authorship and/or a database (each, a "Work"). 9 | 10 | Certain owners wish to permanently relinquish those rights to a Work for the 11 | purpose of contributing to a commons of creative, cultural and scientific 12 | works ("Commons") that the public can reliably and without fear of later 13 | claims of infringement build upon, modify, incorporate in other works, reuse 14 | and redistribute as freely as possible in any form whatsoever and for any 15 | purposes, including without limitation commercial purposes. These owners may 16 | contribute to the Commons to promote the ideal of a free culture and the 17 | further production of creative, cultural and scientific works, or to gain 18 | reputation or greater distribution for their Work in part through the use and 19 | efforts of others. 20 | 21 | For these and/or other purposes and motivations, and without any expectation 22 | of additional consideration or compensation, the person associating CC0 with a 23 | Work (the "Affirmer"), to the extent that he or she is an owner of Copyright 24 | and Related Rights in the Work, voluntarily elects to apply CC0 to the Work 25 | and publicly distribute the Work under its terms, with knowledge of his or her 26 | Copyright and Related Rights in the Work and the meaning and intended legal 27 | effect of CC0 on those rights. 28 | 29 | 1. Copyright and Related Rights. A Work made available under CC0 may be 30 | protected by copyright and related or neighboring rights ("Copyright and 31 | Related Rights"). Copyright and Related Rights include, but are not limited 32 | to, the following: 33 | 34 | i. the right to reproduce, adapt, distribute, perform, display, communicate, 35 | and translate a Work; 36 | 37 | ii. moral rights retained by the original author(s) and/or performer(s); 38 | 39 | iii. publicity and privacy rights pertaining to a person's image or likeness 40 | depicted in a Work; 41 | 42 | iv. rights protecting against unfair competition in regards to a Work, 43 | subject to the limitations in paragraph 4(a), below; 44 | 45 | v. rights protecting the extraction, dissemination, use and reuse of data in 46 | a Work; 47 | 48 | vi. database rights (such as those arising under Directive 96/9/EC of the 49 | European Parliament and of the Council of 11 March 1996 on the legal 50 | protection of databases, and under any national implementation thereof, 51 | including any amended or successor version of such directive); and 52 | 53 | vii. other similar, equivalent or corresponding rights throughout the world 54 | based on applicable law or treaty, and any national implementations thereof. 55 | 56 | 2. Waiver. To the greatest extent permitted by, but not in contravention of, 57 | applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and 58 | unconditionally waives, abandons, and surrenders all of Affirmer's Copyright 59 | and Related Rights and associated claims and causes of action, whether now 60 | known or unknown (including existing as well as future claims and causes of 61 | action), in the Work (i) in all territories worldwide, (ii) for the maximum 62 | duration provided by applicable law or treaty (including future time 63 | extensions), (iii) in any current or future medium and for any number of 64 | copies, and (iv) for any purpose whatsoever, including without limitation 65 | commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes 66 | the Waiver for the benefit of each member of the public at large and to the 67 | detriment of Affirmer's heirs and successors, fully intending that such Waiver 68 | shall not be subject to revocation, rescission, cancellation, termination, or 69 | any other legal or equitable action to disrupt the quiet enjoyment of the Work 70 | by the public as contemplated by Affirmer's express Statement of Purpose. 71 | 72 | 3. Public License Fallback. Should any part of the Waiver for any reason be 73 | judged legally invalid or ineffective under applicable law, then the Waiver 74 | shall be preserved to the maximum extent permitted taking into account 75 | Affirmer's express Statement of Purpose. In addition, to the extent the Waiver 76 | is so judged Affirmer hereby grants to each affected person a royalty-free, 77 | non transferable, non sublicensable, non exclusive, irrevocable and 78 | unconditional license to exercise Affirmer's Copyright and Related Rights in 79 | the Work (i) in all territories worldwide, (ii) for the maximum duration 80 | provided by applicable law or treaty (including future time extensions), (iii) 81 | in any current or future medium and for any number of copies, and (iv) for any 82 | purpose whatsoever, including without limitation commercial, advertising or 83 | promotional purposes (the "License"). The License shall be deemed effective as 84 | of the date CC0 was applied by Affirmer to the Work. Should any part of the 85 | License for any reason be judged legally invalid or ineffective under 86 | applicable law, such partial invalidity or ineffectiveness shall not 87 | invalidate the remainder of the License, and in such case Affirmer hereby 88 | affirms that he or she will not (i) exercise any of his or her remaining 89 | Copyright and Related Rights in the Work or (ii) assert any associated claims 90 | and causes of action with respect to the Work, in either case contrary to 91 | Affirmer's express Statement of Purpose. 92 | 93 | 4. Limitations and Disclaimers. 94 | 95 | a. No trademark or patent rights held by Affirmer are waived, abandoned, 96 | surrendered, licensed or otherwise affected by this document. 97 | 98 | b. Affirmer offers the Work as-is and makes no representations or warranties 99 | of any kind concerning the Work, express, implied, statutory or otherwise, 100 | including without limitation warranties of title, merchantability, fitness 101 | for a particular purpose, non infringement, or the absence of latent or 102 | other defects, accuracy, or the present or absence of errors, whether or not 103 | discoverable, all to the greatest extent permissible under applicable law. 104 | 105 | c. Affirmer disclaims responsibility for clearing rights of other persons 106 | that may apply to the Work or any use thereof, including without limitation 107 | any person's Copyright and Related Rights in the Work. Further, Affirmer 108 | disclaims responsibility for obtaining any necessary consents, permissions 109 | or other rights required for any use of the Work. 110 | 111 | d. Affirmer understands and acknowledges that Creative Commons is not a 112 | party to this document and has no duty or obligation with respect to this 113 | CC0 or use of the Work. 114 | 115 | For more information, please see 116 | 117 | -------------------------------------------------------------------------------- /sql-bricks-modifier/README.md: -------------------------------------------------------------------------------- 1 | # @ladc/sql-bricks-modifier 2 | 3 | 5 | 6 | [![Build Status](https://travis-ci.com/paroi-tech/ladc.svg?branch=master)](https://travis-ci.com/paroi-tech/ladc) 7 | [![npm](https://img.shields.io/npm/dm/@ladc/sql-bricks-modifier)](https://www.npmjs.com/package/@ladc/sql-bricks-modifier) 8 | ![Type definitions](https://img.shields.io/npm/types/@ladc/sql-bricks-modifier) 9 | [![GitHub](https://img.shields.io/github/license/paroi-tech/ladc)](https://github.com/paroi-tech/ladc) 10 | 11 | [LADC](https://github.com/paroi-tech/ladc/tree/master/ladc) is a common API on top of relational database (SQL) connectors. It can connect to Postgresql, MariaDB / MySQL, SQLite. The API is inspired from PDO and JDBC. It’s named LADC for “a Layer Above Database Connectors”. 12 | 13 | This package is a plugin for LADC. It integrates the query builder [SQL Bricks](https://github.com/CSNW/sql-bricks) to LADC. 14 | 15 | ## Additional API 16 | 17 | This package overloads the following methods of the LADC objects `MainConnection` and `TransactionConnection`: 18 | 19 | - `prepare(sqlBricksQuery)` 20 | - `exec(sqlBricksQuery)` 21 | - `all(sqlBricksSelectStatement)` 22 | - `singleRow(sqlBricksSelectStatement)` 23 | - `singleValue(sqlBricksSelectStatement)` 24 | - `cursor(sqlBricksSelectStatement)` 25 | 26 | ## How to use SQL Bricks with LADC (example with SQLite) 27 | 28 | Install with LADC and a connector (here is an example with SQLite): 29 | 30 | ``` 31 | npm install ladc @ladc/sqlite3-adapter sql-bricks @ladc/sql-bricks-modifier 32 | ``` 33 | 34 | Here is how to create a connection: 35 | 36 | ```js 37 | import ladc from "ladc"; 38 | import sqlite3Adapter from "@ladc/sqlite3-adapter"; 39 | import sqlBricksModifier from "@ladc/sql-bricks-modifier"; 40 | 41 | const cn = ladc({ 42 | adapter: sqlite3Adapter({ fileName: `${__dirname}/mydb.sqlite` }), 43 | modifier: sqlBricksModifier(), 44 | }); 45 | ``` 46 | 47 | Then, use it: 48 | 49 | ```js 50 | import { select } from "sql-bricks"; 51 | 52 | async function test(cn) { 53 | const q = select("col1, col2").from("table1"); 54 | const rows = await cn.all(q); 55 | console.log(rows); 56 | } 57 | ``` 58 | 59 | ## How to use SQL Bricks with LADC: The case of MySQL 60 | 61 | MySQL requires a specific `placeholder` option for SQL Bricks: 62 | 63 | ```js 64 | import ladc from "ladc"; 65 | import mysql2Adapter from "@ladc/mysql2-adapter"; 66 | import sqlBricksModifier from "@ladc/sql-bricks-modifier"; 67 | 68 | const cn = ladc({ 69 | adapter: mysql2Adapter({ 70 | mysql2Config: { 71 | host: "-my-server-", 72 | database: "-my-database-", 73 | user: "-my-user-", 74 | password: "-my-password-", 75 | }, 76 | }), 77 | modifier: sqlBricksModifier({ 78 | toParamsOptions: { placeholder: "?" }, // ← Specific to MySQL 79 | }), 80 | }); 81 | ``` 82 | 83 | Now, the API can be used as usual. 84 | 85 | ## Contribute 86 | 87 | With VS Code, our recommanded plugin is: 88 | 89 | - **TSLint** from Microsoft (`ms-vscode.vscode-typescript-tslint-plugin`) 90 | -------------------------------------------------------------------------------- /sql-bricks-modifier/build/make-bundle.js: -------------------------------------------------------------------------------- 1 | const { promisify } = require("util") 2 | const fs = require("fs") 3 | const path = require("path") 4 | const rollup = require("rollup") 5 | const terser = require("terser") 6 | 7 | const readFile = promisify(fs.readFile) 8 | const writeFile = promisify(fs.writeFile) 9 | 10 | const bundleName = "ladc-sql-bricks-modifier" 11 | const srcPath = path.join(__dirname, "..", "src") 12 | const compiledPath = path.join(__dirname, "compiled") 13 | const distNpmPath = path.join(__dirname, "..") 14 | 15 | async function build() { 16 | const bundle = await rollup.rollup({ 17 | input: path.join(compiledPath, "index.js") 18 | }) 19 | const { output } = await bundle.generate({ 20 | format: "commonjs", 21 | exports: "named", 22 | sourcemap: false, 23 | }) 24 | 25 | const minified = terser.minify({ 26 | bundle: output[0].code 27 | }) 28 | if (minified.error) 29 | throw minified.error 30 | 31 | await writeFile(path.join(distNpmPath, `${bundleName}.min.js`), minified.code) 32 | await writeFile(path.join(distNpmPath, `${bundleName}.d.ts`), await makeDefinitionsCode()) 33 | } 34 | 35 | async function makeDefinitionsCode() { 36 | const defs = [ 37 | "// -- Usage definitions --", 38 | removeLocalImportsExports((await readFile(path.join(srcPath, "exported-definitions.d.ts"), "utf-8")).trim()), 39 | "// -- Entry point definition --", 40 | removeSemicolons( 41 | removeLocalImportsExports((await readFile(path.join(compiledPath, "index.d.ts"), "utf-8")).trim()), 42 | ) 43 | ] 44 | return defs.join("\n\n") 45 | } 46 | 47 | function removeLocalImportsExports(code) { 48 | const localImportExport = /^\s*(import|export) .* from "\.\/.*"\s*;?\s*$/ 49 | return code.split("\n").filter(line => { 50 | return !localImportExport.test(line) 51 | }).join("\n").trim() 52 | } 53 | 54 | function removeSemicolons(code) { 55 | return code.replace(/;/g, "") 56 | } 57 | 58 | build().catch(err => console.log(err.message, err.stack)) 59 | -------------------------------------------------------------------------------- /sql-bricks-modifier/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@ladc/sql-bricks-modifier", 3 | "version": "0.22.5", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "@babel/code-frame": { 8 | "version": "7.10.1", 9 | "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.1.tgz", 10 | "integrity": "sha512-IGhtTmpjGbYzcEDOw7DcQtbQSXcG9ftmAXtWTu9V936vDye4xjjekktFAtgZsWpzTj/X01jocB46mTywm/4SZw==", 11 | "dev": true, 12 | "requires": { 13 | "@babel/highlight": "^7.10.1" 14 | } 15 | }, 16 | "@babel/helper-validator-identifier": { 17 | "version": "7.10.1", 18 | "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.1.tgz", 19 | "integrity": "sha512-5vW/JXLALhczRCWP0PnFDMCJAchlBvM7f4uk/jXritBnIa6E1KmqmtrS3yn1LAnxFBypQ3eneLuXjsnfQsgILw==", 20 | "dev": true 21 | }, 22 | "@babel/highlight": { 23 | "version": "7.10.1", 24 | "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.1.tgz", 25 | "integrity": "sha512-8rMof+gVP8mxYZApLF/JgNDAkdKa+aJt3ZYxF8z6+j/hpeXL7iMsKCPHa2jNMHu/qqBwzQF4OHNoYi8dMA/rYg==", 26 | "dev": true, 27 | "requires": { 28 | "@babel/helper-validator-identifier": "^7.10.1", 29 | "chalk": "^2.0.0", 30 | "js-tokens": "^4.0.0" 31 | } 32 | }, 33 | "@types/node": { 34 | "version": "12.12.44", 35 | "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.44.tgz", 36 | "integrity": "sha512-jM6QVv0Sm5d3nW+nUD5jSzPcO6oPqboitSNcwgBay9hifVq/Rauq1PYnROnsmuw45JMBiTnsPAno0bKu2e2xrg==", 37 | "dev": true 38 | }, 39 | "@types/sql-bricks": { 40 | "version": "2.0.1", 41 | "resolved": "https://registry.npmjs.org/@types/sql-bricks/-/sql-bricks-2.0.1.tgz", 42 | "integrity": "sha512-57WN7R1oCqA6sQRDsRwpKi2x2SKfcc10w9KYsmaFxrcnjJ2N3M9rE9DIvqSylHRV16pwuPzF4fRbtIx5TzBzIA==", 43 | "dev": true 44 | }, 45 | "ansi-styles": { 46 | "version": "3.2.1", 47 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", 48 | "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", 49 | "dev": true, 50 | "requires": { 51 | "color-convert": "^1.9.0" 52 | } 53 | }, 54 | "argparse": { 55 | "version": "1.0.10", 56 | "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", 57 | "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", 58 | "dev": true, 59 | "requires": { 60 | "sprintf-js": "~1.0.2" 61 | } 62 | }, 63 | "array-filter": { 64 | "version": "0.0.1", 65 | "resolved": "https://registry.npmjs.org/array-filter/-/array-filter-0.0.1.tgz", 66 | "integrity": "sha1-fajPLiZijtcygDWB/SH2fKzS7uw=", 67 | "dev": true 68 | }, 69 | "array-map": { 70 | "version": "0.0.0", 71 | "resolved": "https://registry.npmjs.org/array-map/-/array-map-0.0.0.tgz", 72 | "integrity": "sha1-iKK6tz0c97zVwbEYoAP2b2ZfpmI=", 73 | "dev": true 74 | }, 75 | "array-reduce": { 76 | "version": "0.0.0", 77 | "resolved": "https://registry.npmjs.org/array-reduce/-/array-reduce-0.0.0.tgz", 78 | "integrity": "sha1-FziZ0//Rx9k4PkR5Ul2+J4yrXys=", 79 | "dev": true 80 | }, 81 | "balanced-match": { 82 | "version": "1.0.0", 83 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", 84 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", 85 | "dev": true 86 | }, 87 | "brace-expansion": { 88 | "version": "1.1.11", 89 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 90 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 91 | "dev": true, 92 | "requires": { 93 | "balanced-match": "^1.0.0", 94 | "concat-map": "0.0.1" 95 | } 96 | }, 97 | "buffer-from": { 98 | "version": "1.1.1", 99 | "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", 100 | "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", 101 | "dev": true 102 | }, 103 | "builtin-modules": { 104 | "version": "1.1.1", 105 | "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", 106 | "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", 107 | "dev": true 108 | }, 109 | "chalk": { 110 | "version": "2.4.1", 111 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", 112 | "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", 113 | "dev": true, 114 | "requires": { 115 | "ansi-styles": "^3.2.1", 116 | "escape-string-regexp": "^1.0.5", 117 | "supports-color": "^5.3.0" 118 | } 119 | }, 120 | "color-convert": { 121 | "version": "1.9.3", 122 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", 123 | "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", 124 | "dev": true, 125 | "requires": { 126 | "color-name": "1.1.3" 127 | } 128 | }, 129 | "color-name": { 130 | "version": "1.1.3", 131 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", 132 | "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", 133 | "dev": true 134 | }, 135 | "commander": { 136 | "version": "2.20.3", 137 | "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", 138 | "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", 139 | "dev": true 140 | }, 141 | "concat-map": { 142 | "version": "0.0.1", 143 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 144 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", 145 | "dev": true 146 | }, 147 | "cross-spawn": { 148 | "version": "6.0.5", 149 | "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", 150 | "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", 151 | "dev": true, 152 | "requires": { 153 | "nice-try": "^1.0.4", 154 | "path-key": "^2.0.1", 155 | "semver": "^5.5.0", 156 | "shebang-command": "^1.2.0", 157 | "which": "^1.2.9" 158 | } 159 | }, 160 | "define-properties": { 161 | "version": "1.1.3", 162 | "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", 163 | "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", 164 | "dev": true, 165 | "requires": { 166 | "object-keys": "^1.0.12" 167 | } 168 | }, 169 | "diff": { 170 | "version": "4.0.2", 171 | "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", 172 | "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", 173 | "dev": true 174 | }, 175 | "error-ex": { 176 | "version": "1.3.2", 177 | "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", 178 | "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", 179 | "dev": true, 180 | "requires": { 181 | "is-arrayish": "^0.2.1" 182 | } 183 | }, 184 | "es-abstract": { 185 | "version": "1.12.0", 186 | "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.12.0.tgz", 187 | "integrity": "sha512-C8Fx/0jFmV5IPoMOFPA9P9G5NtqW+4cOPit3MIuvR2t7Ag2K15EJTpxnHAYTzL+aYQJIESYeXZmDBfOBE1HcpA==", 188 | "dev": true, 189 | "requires": { 190 | "es-to-primitive": "^1.1.1", 191 | "function-bind": "^1.1.1", 192 | "has": "^1.0.1", 193 | "is-callable": "^1.1.3", 194 | "is-regex": "^1.0.4" 195 | } 196 | }, 197 | "es-to-primitive": { 198 | "version": "1.2.0", 199 | "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.0.tgz", 200 | "integrity": "sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==", 201 | "dev": true, 202 | "requires": { 203 | "is-callable": "^1.1.4", 204 | "is-date-object": "^1.0.1", 205 | "is-symbol": "^1.0.2" 206 | } 207 | }, 208 | "escape-string-regexp": { 209 | "version": "1.0.5", 210 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", 211 | "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", 212 | "dev": true 213 | }, 214 | "esprima": { 215 | "version": "4.0.1", 216 | "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", 217 | "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", 218 | "dev": true 219 | }, 220 | "fs.realpath": { 221 | "version": "1.0.0", 222 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 223 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", 224 | "dev": true 225 | }, 226 | "fsevents": { 227 | "version": "2.1.3", 228 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz", 229 | "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==", 230 | "dev": true, 231 | "optional": true 232 | }, 233 | "function-bind": { 234 | "version": "1.1.1", 235 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", 236 | "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", 237 | "dev": true 238 | }, 239 | "glob": { 240 | "version": "7.1.5", 241 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.5.tgz", 242 | "integrity": "sha512-J9dlskqUXK1OeTOYBEn5s8aMukWMwWfs+rPTn/jn50Ux4MNXVhubL1wu/j2t+H4NVI+cXEcCaYellqaPVGXNqQ==", 243 | "dev": true, 244 | "requires": { 245 | "fs.realpath": "^1.0.0", 246 | "inflight": "^1.0.4", 247 | "inherits": "2", 248 | "minimatch": "^3.0.4", 249 | "once": "^1.3.0", 250 | "path-is-absolute": "^1.0.0" 251 | } 252 | }, 253 | "graceful-fs": { 254 | "version": "4.1.15", 255 | "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz", 256 | "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==", 257 | "dev": true 258 | }, 259 | "has": { 260 | "version": "1.0.3", 261 | "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", 262 | "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", 263 | "dev": true, 264 | "requires": { 265 | "function-bind": "^1.1.1" 266 | } 267 | }, 268 | "has-flag": { 269 | "version": "3.0.0", 270 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", 271 | "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", 272 | "dev": true 273 | }, 274 | "has-symbols": { 275 | "version": "1.0.0", 276 | "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz", 277 | "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=", 278 | "dev": true 279 | }, 280 | "hosted-git-info": { 281 | "version": "2.7.1", 282 | "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.7.1.tgz", 283 | "integrity": "sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w==", 284 | "dev": true 285 | }, 286 | "inflight": { 287 | "version": "1.0.6", 288 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 289 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 290 | "dev": true, 291 | "requires": { 292 | "once": "^1.3.0", 293 | "wrappy": "1" 294 | } 295 | }, 296 | "inherits": { 297 | "version": "2.0.4", 298 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 299 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", 300 | "dev": true 301 | }, 302 | "is-arrayish": { 303 | "version": "0.2.1", 304 | "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", 305 | "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", 306 | "dev": true 307 | }, 308 | "is-builtin-module": { 309 | "version": "1.0.0", 310 | "resolved": "http://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz", 311 | "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=", 312 | "dev": true, 313 | "requires": { 314 | "builtin-modules": "^1.0.0" 315 | } 316 | }, 317 | "is-callable": { 318 | "version": "1.1.4", 319 | "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz", 320 | "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==", 321 | "dev": true 322 | }, 323 | "is-date-object": { 324 | "version": "1.0.1", 325 | "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", 326 | "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=", 327 | "dev": true 328 | }, 329 | "is-regex": { 330 | "version": "1.0.4", 331 | "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", 332 | "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", 333 | "dev": true, 334 | "requires": { 335 | "has": "^1.0.1" 336 | } 337 | }, 338 | "is-symbol": { 339 | "version": "1.0.2", 340 | "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz", 341 | "integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==", 342 | "dev": true, 343 | "requires": { 344 | "has-symbols": "^1.0.0" 345 | } 346 | }, 347 | "isexe": { 348 | "version": "2.0.0", 349 | "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", 350 | "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", 351 | "dev": true 352 | }, 353 | "js-tokens": { 354 | "version": "4.0.0", 355 | "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", 356 | "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", 357 | "dev": true 358 | }, 359 | "js-yaml": { 360 | "version": "3.14.0", 361 | "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz", 362 | "integrity": "sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==", 363 | "dev": true, 364 | "requires": { 365 | "argparse": "^1.0.7", 366 | "esprima": "^4.0.0" 367 | } 368 | }, 369 | "json-parse-better-errors": { 370 | "version": "1.0.2", 371 | "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", 372 | "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", 373 | "dev": true 374 | }, 375 | "jsonify": { 376 | "version": "0.0.0", 377 | "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", 378 | "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=", 379 | "dev": true 380 | }, 381 | "ladc": { 382 | "version": "0.22.7", 383 | "resolved": "https://registry.npmjs.org/ladc/-/ladc-0.22.7.tgz", 384 | "integrity": "sha512-S778r6+XfRR1Pwd91d8eCPokWCmHTT24OgP50yFPbYFovYIKiEFMUFebBOOfjAqvPGdAYJuYj0S/UPxxT0Ra2Q==", 385 | "dev": true 386 | }, 387 | "load-json-file": { 388 | "version": "4.0.0", 389 | "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", 390 | "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", 391 | "dev": true, 392 | "requires": { 393 | "graceful-fs": "^4.1.2", 394 | "parse-json": "^4.0.0", 395 | "pify": "^3.0.0", 396 | "strip-bom": "^3.0.0" 397 | } 398 | }, 399 | "memorystream": { 400 | "version": "0.3.1", 401 | "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", 402 | "integrity": "sha1-htcJCzDORV1j+64S3aUaR93K+bI=", 403 | "dev": true 404 | }, 405 | "minimatch": { 406 | "version": "3.0.4", 407 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 408 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", 409 | "dev": true, 410 | "requires": { 411 | "brace-expansion": "^1.1.7" 412 | } 413 | }, 414 | "minimist": { 415 | "version": "1.2.5", 416 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", 417 | "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", 418 | "dev": true 419 | }, 420 | "mkdirp": { 421 | "version": "0.5.5", 422 | "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", 423 | "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", 424 | "dev": true, 425 | "requires": { 426 | "minimist": "^1.2.5" 427 | } 428 | }, 429 | "nice-try": { 430 | "version": "1.0.5", 431 | "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", 432 | "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", 433 | "dev": true 434 | }, 435 | "normalize-package-data": { 436 | "version": "2.4.0", 437 | "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz", 438 | "integrity": "sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw==", 439 | "dev": true, 440 | "requires": { 441 | "hosted-git-info": "^2.1.4", 442 | "is-builtin-module": "^1.0.0", 443 | "semver": "2 || 3 || 4 || 5", 444 | "validate-npm-package-license": "^3.0.1" 445 | } 446 | }, 447 | "npm-run-all": { 448 | "version": "4.1.5", 449 | "resolved": "https://registry.npmjs.org/npm-run-all/-/npm-run-all-4.1.5.tgz", 450 | "integrity": "sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ==", 451 | "dev": true, 452 | "requires": { 453 | "ansi-styles": "^3.2.1", 454 | "chalk": "^2.4.1", 455 | "cross-spawn": "^6.0.5", 456 | "memorystream": "^0.3.1", 457 | "minimatch": "^3.0.4", 458 | "pidtree": "^0.3.0", 459 | "read-pkg": "^3.0.0", 460 | "shell-quote": "^1.6.1", 461 | "string.prototype.padend": "^3.0.0" 462 | } 463 | }, 464 | "object-keys": { 465 | "version": "1.0.12", 466 | "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.12.tgz", 467 | "integrity": "sha512-FTMyFUm2wBcGHnH2eXmz7tC6IwlqQZ6mVZ+6dm6vZ4IQIHjs6FdNsQBuKGPuUUUY6NfJw2PshC08Tn6LzLDOag==", 468 | "dev": true 469 | }, 470 | "once": { 471 | "version": "1.4.0", 472 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 473 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 474 | "dev": true, 475 | "requires": { 476 | "wrappy": "1" 477 | } 478 | }, 479 | "parse-json": { 480 | "version": "4.0.0", 481 | "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", 482 | "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", 483 | "dev": true, 484 | "requires": { 485 | "error-ex": "^1.3.1", 486 | "json-parse-better-errors": "^1.0.1" 487 | } 488 | }, 489 | "path-is-absolute": { 490 | "version": "1.0.1", 491 | "resolved": "http://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 492 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", 493 | "dev": true 494 | }, 495 | "path-key": { 496 | "version": "2.0.1", 497 | "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", 498 | "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", 499 | "dev": true 500 | }, 501 | "path-parse": { 502 | "version": "1.0.6", 503 | "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", 504 | "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", 505 | "dev": true 506 | }, 507 | "path-type": { 508 | "version": "3.0.0", 509 | "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", 510 | "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", 511 | "dev": true, 512 | "requires": { 513 | "pify": "^3.0.0" 514 | } 515 | }, 516 | "pidtree": { 517 | "version": "0.3.0", 518 | "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.3.0.tgz", 519 | "integrity": "sha512-9CT4NFlDcosssyg8KVFltgokyKZIFjoBxw8CTGy+5F38Y1eQWrt8tRayiUOXE+zVKQnYu5BR8JjCtvK3BcnBhg==", 520 | "dev": true 521 | }, 522 | "pify": { 523 | "version": "3.0.0", 524 | "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", 525 | "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", 526 | "dev": true 527 | }, 528 | "read-pkg": { 529 | "version": "3.0.0", 530 | "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", 531 | "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", 532 | "dev": true, 533 | "requires": { 534 | "load-json-file": "^4.0.0", 535 | "normalize-package-data": "^2.3.2", 536 | "path-type": "^3.0.0" 537 | } 538 | }, 539 | "resolve": { 540 | "version": "1.17.0", 541 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", 542 | "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", 543 | "dev": true, 544 | "requires": { 545 | "path-parse": "^1.0.6" 546 | } 547 | }, 548 | "rimraf": { 549 | "version": "3.0.2", 550 | "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", 551 | "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", 552 | "dev": true, 553 | "requires": { 554 | "glob": "^7.1.3" 555 | } 556 | }, 557 | "rollup": { 558 | "version": "2.15.0", 559 | "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.15.0.tgz", 560 | "integrity": "sha512-HAk4kyXiV5sdNDnbKWk5zBPnkX/DAgx09Kbp8rRIRDVsTUVN3vnSowR7ZHkV6/lAiE6c2TQ8HtYb72aCPGW4Jw==", 561 | "dev": true, 562 | "requires": { 563 | "fsevents": "~2.1.2" 564 | } 565 | }, 566 | "semver": { 567 | "version": "5.6.0", 568 | "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz", 569 | "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==", 570 | "dev": true 571 | }, 572 | "shebang-command": { 573 | "version": "1.2.0", 574 | "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", 575 | "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", 576 | "dev": true, 577 | "requires": { 578 | "shebang-regex": "^1.0.0" 579 | } 580 | }, 581 | "shebang-regex": { 582 | "version": "1.0.0", 583 | "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", 584 | "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", 585 | "dev": true 586 | }, 587 | "shell-quote": { 588 | "version": "1.6.1", 589 | "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.6.1.tgz", 590 | "integrity": "sha1-9HgZSczkAmlxJ0MOo7PFR29IF2c=", 591 | "dev": true, 592 | "requires": { 593 | "array-filter": "~0.0.0", 594 | "array-map": "~0.0.0", 595 | "array-reduce": "~0.0.0", 596 | "jsonify": "~0.0.0" 597 | } 598 | }, 599 | "source-map": { 600 | "version": "0.6.1", 601 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", 602 | "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", 603 | "dev": true 604 | }, 605 | "source-map-support": { 606 | "version": "0.5.19", 607 | "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", 608 | "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", 609 | "dev": true, 610 | "requires": { 611 | "buffer-from": "^1.0.0", 612 | "source-map": "^0.6.0" 613 | } 614 | }, 615 | "spdx-correct": { 616 | "version": "3.1.0", 617 | "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz", 618 | "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==", 619 | "dev": true, 620 | "requires": { 621 | "spdx-expression-parse": "^3.0.0", 622 | "spdx-license-ids": "^3.0.0" 623 | } 624 | }, 625 | "spdx-exceptions": { 626 | "version": "2.2.0", 627 | "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz", 628 | "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==", 629 | "dev": true 630 | }, 631 | "spdx-expression-parse": { 632 | "version": "3.0.0", 633 | "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", 634 | "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", 635 | "dev": true, 636 | "requires": { 637 | "spdx-exceptions": "^2.1.0", 638 | "spdx-license-ids": "^3.0.0" 639 | } 640 | }, 641 | "spdx-license-ids": { 642 | "version": "3.0.2", 643 | "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.2.tgz", 644 | "integrity": "sha512-qky9CVt0lVIECkEsYbNILVnPvycuEBkXoMFLRWsREkomQLevYhtRKC+R91a5TOAQ3bCMjikRwhyaRqj1VYatYg==", 645 | "dev": true 646 | }, 647 | "sprintf-js": { 648 | "version": "1.0.3", 649 | "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", 650 | "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", 651 | "dev": true 652 | }, 653 | "sql-bricks": { 654 | "version": "2.0.5", 655 | "resolved": "https://registry.npmjs.org/sql-bricks/-/sql-bricks-2.0.5.tgz", 656 | "integrity": "sha512-Q9qpODBZPMaPb+bjlmFYKMNk/6zpXSv1VNjJmGd9LvoqXwxd8TmE39s79wzrgAfw2lzohr4eNEpWRhHT5UYAfQ==", 657 | "dev": true, 658 | "requires": { 659 | "underscore": "1.4.x" 660 | } 661 | }, 662 | "string.prototype.padend": { 663 | "version": "3.0.0", 664 | "resolved": "https://registry.npmjs.org/string.prototype.padend/-/string.prototype.padend-3.0.0.tgz", 665 | "integrity": "sha1-86rvfBcZ8XDF6rHDK/eA2W4h8vA=", 666 | "dev": true, 667 | "requires": { 668 | "define-properties": "^1.1.2", 669 | "es-abstract": "^1.4.3", 670 | "function-bind": "^1.0.2" 671 | } 672 | }, 673 | "strip-bom": { 674 | "version": "3.0.0", 675 | "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", 676 | "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", 677 | "dev": true 678 | }, 679 | "supports-color": { 680 | "version": "5.5.0", 681 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", 682 | "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", 683 | "dev": true, 684 | "requires": { 685 | "has-flag": "^3.0.0" 686 | } 687 | }, 688 | "terser": { 689 | "version": "4.7.0", 690 | "resolved": "https://registry.npmjs.org/terser/-/terser-4.7.0.tgz", 691 | "integrity": "sha512-Lfb0RiZcjRDXCC3OSHJpEkxJ9Qeqs6mp2v4jf2MHfy8vGERmVDuvjXdd/EnP5Deme5F2yBRBymKmKHCBg2echw==", 692 | "dev": true, 693 | "requires": { 694 | "commander": "^2.20.0", 695 | "source-map": "~0.6.1", 696 | "source-map-support": "~0.5.12" 697 | } 698 | }, 699 | "tslib": { 700 | "version": "1.13.0", 701 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", 702 | "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==", 703 | "dev": true 704 | }, 705 | "tslint": { 706 | "version": "6.1.2", 707 | "resolved": "https://registry.npmjs.org/tslint/-/tslint-6.1.2.tgz", 708 | "integrity": "sha512-UyNrLdK3E0fQG/xWNqAFAC5ugtFyPO4JJR1KyyfQAyzR8W0fTRrC91A8Wej4BntFzcvETdCSDa/4PnNYJQLYiA==", 709 | "dev": true, 710 | "requires": { 711 | "@babel/code-frame": "^7.0.0", 712 | "builtin-modules": "^1.1.1", 713 | "chalk": "^2.3.0", 714 | "commander": "^2.12.1", 715 | "diff": "^4.0.1", 716 | "glob": "^7.1.1", 717 | "js-yaml": "^3.13.1", 718 | "minimatch": "^3.0.4", 719 | "mkdirp": "^0.5.3", 720 | "resolve": "^1.3.2", 721 | "semver": "^5.3.0", 722 | "tslib": "^1.10.0", 723 | "tsutils": "^2.29.0" 724 | } 725 | }, 726 | "tsutils": { 727 | "version": "2.29.0", 728 | "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.29.0.tgz", 729 | "integrity": "sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==", 730 | "dev": true, 731 | "requires": { 732 | "tslib": "^1.8.1" 733 | } 734 | }, 735 | "typescript": { 736 | "version": "3.9.5", 737 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.5.tgz", 738 | "integrity": "sha512-hSAifV3k+i6lEoCJ2k6R2Z/rp/H3+8sdmcn5NrS3/3kE7+RyZXm9aqvxWqjEXHAd8b0pShatpcdMTvEdvAJltQ==", 739 | "dev": true 740 | }, 741 | "underscore": { 742 | "version": "1.4.4", 743 | "resolved": "http://registry.npmjs.org/underscore/-/underscore-1.4.4.tgz", 744 | "integrity": "sha1-YaajIBBiKvoHljvzJSA88SI51gQ=", 745 | "dev": true 746 | }, 747 | "validate-npm-package-license": { 748 | "version": "3.0.4", 749 | "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", 750 | "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", 751 | "dev": true, 752 | "requires": { 753 | "spdx-correct": "^3.0.0", 754 | "spdx-expression-parse": "^3.0.0" 755 | } 756 | }, 757 | "which": { 758 | "version": "1.3.1", 759 | "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", 760 | "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", 761 | "dev": true, 762 | "requires": { 763 | "isexe": "^2.0.0" 764 | } 765 | }, 766 | "wrappy": { 767 | "version": "1.0.2", 768 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 769 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", 770 | "dev": true 771 | } 772 | } 773 | } 774 | -------------------------------------------------------------------------------- /sql-bricks-modifier/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@ladc/sql-bricks-modifier", 3 | "version": "0.22.7", 4 | "description": "Use the query builder 'SQL Bricks' with the LADC API.", 5 | "main": "ladc-sql-bricks-modifier.min.js", 6 | "types": "ladc-sql-bricks-modifier.d.ts", 7 | "peerDependencies": { 8 | "ladc": "0.22", 9 | "sql-bricks": "2" 10 | }, 11 | "devDependencies": { 12 | "@types/node": "^12.12.44", 13 | "@types/sql-bricks": "2", 14 | "ladc": "^0.22.7", 15 | "npm-run-all": "4", 16 | "rimraf": "^3.0.2", 17 | "rollup": "^2.15.0", 18 | "sql-bricks": "^2.0.5", 19 | "terser": "^4.7.0", 20 | "tslint": "^6.1.2", 21 | "typescript": "^3.9.5" 22 | }, 23 | "scripts": { 24 | "build": "run-s clear tsc make-bundle", 25 | "tsc": "tsc", 26 | "tsc:watch": "tsc --watch", 27 | "make-bundle": "node build/make-bundle", 28 | "clear": "rimraf build/compiled/*", 29 | "lint": "tslint -p tsconfig.json -t verbose", 30 | "prepublishOnly": "npm run lint && npm run build" 31 | }, 32 | "engines": { 33 | "node": ">=8" 34 | }, 35 | "repository": { 36 | "type": "git", 37 | "url": "git+https://github.com/paroi-tech/ladc.git" 38 | }, 39 | "homepage": "https://github.com/paroi-tech/ladc/tree/master/sql-bricks-modifier", 40 | "author": { 41 | "name": "Paleo" 42 | }, 43 | "license": "CC0-1.0", 44 | "keywords": [ 45 | "sql", 46 | "query", 47 | "builder", 48 | "ladc" 49 | ] 50 | } -------------------------------------------------------------------------------- /sql-bricks-modifier/src/exported-definitions.d.ts: -------------------------------------------------------------------------------- 1 | import { MainConnection, ExecResult, PreparedStatement, Connection, ResultRow, TransactionConnection } from "ladc" 2 | import { Statement as SqlBricksQuery, SelectStatement as SqlBricksSelect } from "sql-bricks" 3 | 4 | export interface SBModifierOptions { 5 | toParamsOptions?: { 6 | [key: string]: any 7 | placeholder?: string 8 | } 9 | trace?(action: string, sqlBricks: SqlBricksQuery): void 10 | } 11 | 12 | export type SBConnection = Connection & { 13 | prepare(sqlBricks: SqlBricksQuery): Promise> 14 | exec(sqlBricks: SqlBricksQuery): Promise 15 | all(sqlBricks: SqlBricksSelect): Promise 16 | singleRow(sqlBricks: SqlBricksSelect): Promise 17 | singleValue(sqlBricks: SqlBricksSelect): Promise 18 | cursor(sqlBricks: SqlBricksSelect): Promise> 19 | } 20 | 21 | interface SBMainConnectionTx extends MainConnection { 22 | // This method replaces the parent one 23 | beginTransaction(): Promise 24 | } 25 | 26 | export type SBMainConnection = SBMainConnectionTx & SBConnection 27 | export type SBTransactionConnection = TransactionConnection & SBConnection -------------------------------------------------------------------------------- /sql-bricks-modifier/src/index.ts: -------------------------------------------------------------------------------- 1 | import { LadcModifier, MainConnection, TransactionConnection } from "ladc" 2 | import { Statement as SqlBricksQuery } from "sql-bricks" 3 | import { SBModifierOptions } from "./exported-definitions" 4 | 5 | export default function sqlBricksModifier(options: SBModifierOptions = {}): LadcModifier { 6 | return { 7 | modifyConnection: cn => modifyConnection(cn, options) 8 | } 9 | } 10 | 11 | const methodNames = ["prepare", "exec", "all", "singleRow", "singleValue", "cursor"] 12 | 13 | function modifyConnection(parent: TransactionConnection | MainConnection, options: SBModifierOptions) { 14 | const modified = Object.create(parent) 15 | 16 | for (const method of methodNames) { 17 | modified[method] = (sql: string | SqlBricksQuery, params?: any[]) => { 18 | if (typeof sql === "string") 19 | return (parent as any)[method](sql, params) 20 | if (options.trace) 21 | options.trace(method, sql) 22 | const { text, values } = sql.toParams(options.toParamsOptions as any) 23 | return (parent as any)[method](text, values) 24 | } 25 | } 26 | 27 | return modified 28 | } -------------------------------------------------------------------------------- /sql-bricks-modifier/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2018", 4 | "moduleResolution": "node", 5 | "strict": true, 6 | "outDir": "build/compiled", 7 | "declaration": true, 8 | "lib": [ 9 | "es2018" 10 | ] 11 | }, 12 | "include": [ 13 | "src" 14 | ] 15 | } -------------------------------------------------------------------------------- /sql-bricks-modifier/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "defaultSeverity": "error", 3 | "extends": [ 4 | "tslint:recommended" 5 | ], 6 | "linterOptions": { 7 | "exclude": [ 8 | "node_modules/**" 9 | ] 10 | }, 11 | "rules": { 12 | "arrow-parens": false, 13 | "curly": false, 14 | "eofline": false, 15 | "indent": [ 16 | true, 17 | "spaces", 18 | 2 19 | ], 20 | "interface-name": false, 21 | "max-classes-per-file": false, 22 | "max-line-length": false, 23 | "member-access": [ 24 | true, 25 | "no-public" 26 | ], 27 | "no-conditional-assignment": false, 28 | "no-consecutive-blank-lines": false, 29 | "no-empty": [ 30 | true, 31 | "allow-empty-catch", 32 | "allow-empty-functions" 33 | ], 34 | "no-shadowed-variable": false, 35 | "no-string-literal": false, 36 | "no-var-requires": false, 37 | "ordered-imports": true, 38 | "object-literal-sort-keys": false, 39 | "object-literal-key-quotes": false, 40 | "only-arrow-functions": false, 41 | "prefer-const": true, 42 | "quotemark": [ 43 | true, 44 | "double" 45 | ], 46 | "semicolon": [ 47 | true, 48 | "never" 49 | ], 50 | "space-before-function-paren": [ 51 | true, 52 | { 53 | "anonymous": "always", 54 | "named": "never", 55 | "asyncArrow": "always" 56 | } 57 | ], 58 | "trailing-comma": [ 59 | true, 60 | { 61 | "singleline": "never" 62 | } 63 | ], 64 | "variable-name": [ 65 | true, 66 | "allow-leading-underscore", 67 | "allow-pascal-case" 68 | ] 69 | } 70 | } -------------------------------------------------------------------------------- /sqlite3-adapter/.gitignore: -------------------------------------------------------------------------------- 1 | .npmrc 2 | node_modules 3 | /build/compiled 4 | /ladc-sqlite3-adapter.d.ts 5 | /ladc-sqlite3-adapter.min.js -------------------------------------------------------------------------------- /sqlite3-adapter/.npmignore: -------------------------------------------------------------------------------- 1 | /build/compiled -------------------------------------------------------------------------------- /sqlite3-adapter/LICENSE: -------------------------------------------------------------------------------- 1 | CC0 1.0 Universal 2 | 3 | Statement of Purpose 4 | 5 | The laws of most jurisdictions throughout the world automatically confer 6 | exclusive Copyright and Related Rights (defined below) upon the creator and 7 | subsequent owner(s) (each and all, an "owner") of an original work of 8 | authorship and/or a database (each, a "Work"). 9 | 10 | Certain owners wish to permanently relinquish those rights to a Work for the 11 | purpose of contributing to a commons of creative, cultural and scientific 12 | works ("Commons") that the public can reliably and without fear of later 13 | claims of infringement build upon, modify, incorporate in other works, reuse 14 | and redistribute as freely as possible in any form whatsoever and for any 15 | purposes, including without limitation commercial purposes. These owners may 16 | contribute to the Commons to promote the ideal of a free culture and the 17 | further production of creative, cultural and scientific works, or to gain 18 | reputation or greater distribution for their Work in part through the use and 19 | efforts of others. 20 | 21 | For these and/or other purposes and motivations, and without any expectation 22 | of additional consideration or compensation, the person associating CC0 with a 23 | Work (the "Affirmer"), to the extent that he or she is an owner of Copyright 24 | and Related Rights in the Work, voluntarily elects to apply CC0 to the Work 25 | and publicly distribute the Work under its terms, with knowledge of his or her 26 | Copyright and Related Rights in the Work and the meaning and intended legal 27 | effect of CC0 on those rights. 28 | 29 | 1. Copyright and Related Rights. A Work made available under CC0 may be 30 | protected by copyright and related or neighboring rights ("Copyright and 31 | Related Rights"). Copyright and Related Rights include, but are not limited 32 | to, the following: 33 | 34 | i. the right to reproduce, adapt, distribute, perform, display, communicate, 35 | and translate a Work; 36 | 37 | ii. moral rights retained by the original author(s) and/or performer(s); 38 | 39 | iii. publicity and privacy rights pertaining to a person's image or likeness 40 | depicted in a Work; 41 | 42 | iv. rights protecting against unfair competition in regards to a Work, 43 | subject to the limitations in paragraph 4(a), below; 44 | 45 | v. rights protecting the extraction, dissemination, use and reuse of data in 46 | a Work; 47 | 48 | vi. database rights (such as those arising under Directive 96/9/EC of the 49 | European Parliament and of the Council of 11 March 1996 on the legal 50 | protection of databases, and under any national implementation thereof, 51 | including any amended or successor version of such directive); and 52 | 53 | vii. other similar, equivalent or corresponding rights throughout the world 54 | based on applicable law or treaty, and any national implementations thereof. 55 | 56 | 2. Waiver. To the greatest extent permitted by, but not in contravention of, 57 | applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and 58 | unconditionally waives, abandons, and surrenders all of Affirmer's Copyright 59 | and Related Rights and associated claims and causes of action, whether now 60 | known or unknown (including existing as well as future claims and causes of 61 | action), in the Work (i) in all territories worldwide, (ii) for the maximum 62 | duration provided by applicable law or treaty (including future time 63 | extensions), (iii) in any current or future medium and for any number of 64 | copies, and (iv) for any purpose whatsoever, including without limitation 65 | commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes 66 | the Waiver for the benefit of each member of the public at large and to the 67 | detriment of Affirmer's heirs and successors, fully intending that such Waiver 68 | shall not be subject to revocation, rescission, cancellation, termination, or 69 | any other legal or equitable action to disrupt the quiet enjoyment of the Work 70 | by the public as contemplated by Affirmer's express Statement of Purpose. 71 | 72 | 3. Public License Fallback. Should any part of the Waiver for any reason be 73 | judged legally invalid or ineffective under applicable law, then the Waiver 74 | shall be preserved to the maximum extent permitted taking into account 75 | Affirmer's express Statement of Purpose. In addition, to the extent the Waiver 76 | is so judged Affirmer hereby grants to each affected person a royalty-free, 77 | non transferable, non sublicensable, non exclusive, irrevocable and 78 | unconditional license to exercise Affirmer's Copyright and Related Rights in 79 | the Work (i) in all territories worldwide, (ii) for the maximum duration 80 | provided by applicable law or treaty (including future time extensions), (iii) 81 | in any current or future medium and for any number of copies, and (iv) for any 82 | purpose whatsoever, including without limitation commercial, advertising or 83 | promotional purposes (the "License"). The License shall be deemed effective as 84 | of the date CC0 was applied by Affirmer to the Work. Should any part of the 85 | License for any reason be judged legally invalid or ineffective under 86 | applicable law, such partial invalidity or ineffectiveness shall not 87 | invalidate the remainder of the License, and in such case Affirmer hereby 88 | affirms that he or she will not (i) exercise any of his or her remaining 89 | Copyright and Related Rights in the Work or (ii) assert any associated claims 90 | and causes of action with respect to the Work, in either case contrary to 91 | Affirmer's express Statement of Purpose. 92 | 93 | 4. Limitations and Disclaimers. 94 | 95 | a. No trademark or patent rights held by Affirmer are waived, abandoned, 96 | surrendered, licensed or otherwise affected by this document. 97 | 98 | b. Affirmer offers the Work as-is and makes no representations or warranties 99 | of any kind concerning the Work, express, implied, statutory or otherwise, 100 | including without limitation warranties of title, merchantability, fitness 101 | for a particular purpose, non infringement, or the absence of latent or 102 | other defects, accuracy, or the present or absence of errors, whether or not 103 | discoverable, all to the greatest extent permissible under applicable law. 104 | 105 | c. Affirmer disclaims responsibility for clearing rights of other persons 106 | that may apply to the Work or any use thereof, including without limitation 107 | any person's Copyright and Related Rights in the Work. Further, Affirmer 108 | disclaims responsibility for obtaining any necessary consents, permissions 109 | or other rights required for any use of the Work. 110 | 111 | d. Affirmer understands and acknowledges that Creative Commons is not a 112 | party to this document and has no duty or obligation with respect to this 113 | CC0 or use of the Work. 114 | 115 | For more information, please see 116 | 117 | -------------------------------------------------------------------------------- /sqlite3-adapter/README.md: -------------------------------------------------------------------------------- 1 | # @ladc/sqlite3-adapter 2 | 3 | 5 | 6 | [![Build Status](https://travis-ci.com/paroi-tech/ladc.svg?branch=master)](https://travis-ci.com/paroi-tech/ladc) 7 | [![npm](https://img.shields.io/npm/dm/@ladc/sqlite3-adapter)](https://www.npmjs.com/package/@ladc/sqlite3-adapter) 8 | ![Type definitions](https://img.shields.io/npm/types/@ladc/sqlite3-adapter) 9 | [![GitHub](https://img.shields.io/github/license/paroi-tech/ladc)](https://github.com/paroi-tech/ladc) 10 | 11 | [LADC](https://github.com/paroi-tech/ladc/tree/master/ladc) is a common API on top of relational database (SQL) connectors. It can connect to Postgresql, MariaDB / MySQL, SQLite. The API is inspired from PDO and JDBC. It’s named LADC for “a Layer Above Database Connectors”. 12 | 13 | This package is a plugin for LADC. It is an adapter for SQLite, using the connector [sqlite3](https://github.com/mapbox/node-sqlite3) (SQLite). 14 | 15 | ## Install 16 | 17 | ``` 18 | npm install @ladc/sqlite3-adapter ladc 19 | ``` 20 | 21 | ## Usage 22 | 23 | How to create a connection: 24 | 25 | ```ts 26 | import ladc from "ladc"; 27 | import sqlite3Adapter from "@ladc/sqlite3-adapter"; 28 | 29 | const cn = ladc({ 30 | adapter: sqlite3Adapter({ fileName: `${__dirname}/mydb.sqlite` }), 31 | initConnection: async (cn) => { 32 | await cn.exec("PRAGMA foreign_keys = ON"); 33 | }, 34 | }); 35 | ``` 36 | 37 | ## Contribute 38 | 39 | With VS Code, our recommanded plugin is: 40 | 41 | - **TSLint** from Microsoft (`ms-vscode.vscode-typescript-tslint-plugin`) 42 | -------------------------------------------------------------------------------- /sqlite3-adapter/build/make-bundle.js: -------------------------------------------------------------------------------- 1 | const { promisify } = require("util") 2 | const fs = require("fs") 3 | const path = require("path") 4 | const rollup = require("rollup") 5 | const terser = require("terser") 6 | 7 | const readFile = promisify(fs.readFile) 8 | const writeFile = promisify(fs.writeFile) 9 | 10 | const bundleName = "ladc-sqlite3-adapter" 11 | const srcPath = path.join(__dirname, "..", "src") 12 | const compiledPath = path.join(__dirname, "compiled") 13 | const distNpmPath = path.join(__dirname, "..") 14 | 15 | async function build() { 16 | const bundle = await rollup.rollup({ 17 | input: path.join(compiledPath, "index.js") 18 | }) 19 | const { output } = await bundle.generate({ 20 | format: "commonjs", 21 | exports: "named", 22 | sourcemap: false, 23 | }) 24 | 25 | const minified = terser.minify({ 26 | bundle: output[0].code 27 | }) 28 | if (minified.error) 29 | throw minified.error 30 | 31 | await writeFile(path.join(distNpmPath, `${bundleName}.min.js`), minified.code) 32 | await writeFile(path.join(distNpmPath, `${bundleName}.d.ts`), await makeDefinitionsCode()) 33 | } 34 | 35 | async function makeDefinitionsCode() { 36 | const defs = [ 37 | "// -- Usage definitions --", 38 | removeLocalImportsExports((await readFile(path.join(srcPath, "exported-definitions.d.ts"), "utf-8")).trim()), 39 | "// -- Entry point definition --", 40 | removeSemicolons( 41 | removeLocalImportsExports((await readFile(path.join(compiledPath, "index.d.ts"), "utf-8")).trim()), 42 | ) 43 | ] 44 | return defs.join("\n\n") 45 | } 46 | 47 | function removeLocalImportsExports(code) { 48 | const localImportExport = /^\s*(import|export) .* from "\.\/.*"\s*;?\s*$/ 49 | return code.split("\n").filter(line => { 50 | return !localImportExport.test(line) 51 | }).join("\n").trim() 52 | } 53 | 54 | function removeSemicolons(code) { 55 | return code.replace(/;/g, "") 56 | } 57 | 58 | build().catch(err => console.log(err.message, err.stack)) 59 | -------------------------------------------------------------------------------- /sqlite3-adapter/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@ladc/sqlite3-adapter", 3 | "version": "0.22.8", 4 | "description": "Connect to SQLite with the LADC API.", 5 | "main": "ladc-sqlite3-adapter.min.js", 6 | "types": "ladc-sqlite3-adapter.d.ts", 7 | "dependencies": { 8 | "@types/sqlite3": "^3.1.6", 9 | "sqlite3": "^4.2.0" 10 | }, 11 | "peerDependencies": { 12 | "ladc": "0.22" 13 | }, 14 | "devDependencies": { 15 | "ladc": "^0.22.5", 16 | "npm-run-all": "4", 17 | "rimraf": "3", 18 | "rollup": "^2.15.0", 19 | "terser": "^4.6.2", 20 | "tslint": "^6.1.2", 21 | "typescript": "^3.7.4" 22 | }, 23 | "scripts": { 24 | "build": "run-s clear tsc make-bundle", 25 | "tsc": "tsc", 26 | "tsc:watch": "tsc --watch", 27 | "make-bundle": "node build/make-bundle", 28 | "clear": "rimraf build/compiled/*", 29 | "lint": "tslint -p tsconfig.json -t verbose", 30 | "prepublishOnly": "npm run lint && npm run build" 31 | }, 32 | "engines": { 33 | "node": ">=8" 34 | }, 35 | "repository": { 36 | "type": "git", 37 | "url": "git+https://github.com/paroi-tech/ladc.git" 38 | }, 39 | "homepage": "https://github.com/paroi-tech/ladc/tree/master/sqlite3-adapter", 40 | "author": { 41 | "name": "Paleo" 42 | }, 43 | "license": "CC0-1.0", 44 | "keywords": [ 45 | "sqlite", 46 | "connection", 47 | "ladc" 48 | ] 49 | } -------------------------------------------------------------------------------- /sqlite3-adapter/src/AConnection.ts: -------------------------------------------------------------------------------- 1 | import { AConnection, AExecResult, APreparedStatement, SqlParameters } from "ladc" 2 | import { Database, RunResult, Statement } from "./promisifySqlite3" 3 | 4 | export function toAConnection(db: Database): AConnection { 5 | return { 6 | prepare: async (sql: string) => toAPreparedStatement(await db.prepare(sql)), 7 | exec: async (sql: string, params?: SqlParameters) => toAExecResult(await db.run(sql, params)), 8 | all: (sql: string, params?: SqlParameters) => db.all(sql, params), 9 | cursor: (sql: string, params?: SqlParameters) => createACursor(db, sql, params), 10 | script: async (sql: string) => { 11 | await db.exec(sql) 12 | }, 13 | close: async () => { 14 | await db.close() 15 | } 16 | } 17 | } 18 | 19 | function toAExecResult(st: RunResult): AExecResult { 20 | return { 21 | affectedRows: st.changes, 22 | getInsertedId: () => st.lastID 23 | } 24 | } 25 | 26 | function toAPreparedStatement(st: Statement): APreparedStatement { 27 | return { 28 | exec: async (params?: SqlParameters) => toAExecResult(await st.run(params)), 29 | all: (params?: SqlParameters) => st.all(params), 30 | cursor: async (params?: SqlParameters) => statementToACursor(st, params), 31 | close: () => st.finalize() 32 | } 33 | } 34 | 35 | function statementToACursor(st: Statement, params?: SqlParameters): AsyncIterableIterator { 36 | let done = false 37 | const obj: AsyncIterableIterator = { 38 | [Symbol.asyncIterator]: () => obj, 39 | next: async () => { 40 | if (done) 41 | return { done, value: undefined } 42 | const copy = params 43 | params = undefined 44 | const value = await st.get(copy) 45 | if (!value) 46 | done = true 47 | return { done, value } 48 | }, 49 | return: async () => { 50 | done = true 51 | return { done, value: undefined } 52 | }, 53 | throw: async err => { 54 | done = true 55 | throw err 56 | } 57 | } 58 | return obj 59 | } 60 | 61 | async function createACursor(db: Database, sql: string, params?: SqlParameters): Promise> { 62 | const st = await db.prepare(sql, params) 63 | let done = false 64 | const closeCursor = async () => { 65 | done = true 66 | await st.finalize() 67 | } 68 | const obj: AsyncIterableIterator = { 69 | [Symbol.asyncIterator]: () => obj, 70 | next: async () => { 71 | if (done) 72 | return { done, value: undefined } 73 | const value = await st.get() 74 | if (!value) 75 | await closeCursor() 76 | return { done, value } 77 | }, 78 | return: async () => { 79 | if (!done) 80 | await closeCursor() 81 | return { done, value: undefined } 82 | }, 83 | throw: async err => { 84 | if (!done) 85 | await closeCursor() 86 | throw err 87 | } 88 | } 89 | return obj 90 | } 91 | -------------------------------------------------------------------------------- /sqlite3-adapter/src/exported-definitions.d.ts: -------------------------------------------------------------------------------- 1 | import * as sqlite3 from "sqlite3"; 2 | 3 | export interface Sqlite3ConnectionOptions { 4 | fileName: string 5 | mode?: number 6 | verbose?: boolean 7 | init?(db: sqlite3.Database): void | Promise 8 | /** 9 | * Default value is `3` 10 | */ 11 | maxAttempts?: number 12 | logWarning?: (error: string) => void 13 | } -------------------------------------------------------------------------------- /sqlite3-adapter/src/index.ts: -------------------------------------------------------------------------------- 1 | import { LadcAdapter } from "ladc" 2 | import * as sqlite3 from "sqlite3" 3 | import { toAConnection } from "./AConnection" 4 | import { Sqlite3ConnectionOptions } from "./exported-definitions" 5 | import { createSqlite3Connection } from "./promisifySqlite3" 6 | 7 | export default function sqlite3Adapter(options: Sqlite3ConnectionOptions): LadcAdapter { 8 | if (options.verbose) 9 | sqlite3.verbose() 10 | return { 11 | createConnection: async () => { 12 | const db = await createSqlite3Connection(options) 13 | return toAConnection(db) 14 | }, 15 | capabilities: { 16 | cursors: true, 17 | namedParameters: true, 18 | preparedStatements: true, 19 | script: true 20 | } 21 | } 22 | } 23 | 24 | export type { Sqlite3ConnectionOptions } from "./exported-definitions" 25 | 26 | -------------------------------------------------------------------------------- /sqlite3-adapter/src/promisifySqlite3.ts: -------------------------------------------------------------------------------- 1 | import { SqlParameters } from "ladc" 2 | import * as sqlite3 from "sqlite3" 3 | import { Sqlite3ConnectionOptions } from "./exported-definitions" 4 | 5 | export interface Database { 6 | run(sql: string, params?: SqlParameters): Promise 7 | all(sql: string, params?: SqlParameters): Promise 8 | exec(sql: string): Promise 9 | prepare(sql: string, params?: SqlParameters): Promise 10 | close(): Promise 11 | } 12 | 13 | export interface Statement { 14 | bind(...params: any[]): void 15 | run(params?: SqlParameters): Promise 16 | all(params?: SqlParameters): Promise 17 | get(params?: SqlParameters): Promise 18 | finalize(): Promise 19 | } 20 | 21 | export interface RunResult { 22 | readonly lastID: number 23 | readonly changes: number 24 | } 25 | 26 | export async function createSqlite3Connection(options: Sqlite3ConnectionOptions): Promise { 27 | const maxAttempts = options.maxAttempts ?? 3 28 | let curAttempt = 1 29 | let delayAfterError = 50 30 | let firstError: unknown 31 | const db = await new Promise((resolve, reject) => { 32 | let innerDb: sqlite3.Database | undefined 33 | const cb = (err: unknown) => { 34 | if (err) { 35 | if (curAttempt >= maxAttempts) 36 | reject(firstError ?? err) 37 | else { 38 | if (!firstError) 39 | firstError = err 40 | setTimeout( 41 | () => { innerDb = create() }, 42 | delayAfterError 43 | ).unref() 44 | ++curAttempt 45 | delayAfterError *= 2 46 | } 47 | } else 48 | resolve(innerDb) 49 | } 50 | const create = () => { 51 | if (options.mode !== undefined) 52 | return new sqlite3.Database(options.fileName, options.mode, cb) 53 | else 54 | return new sqlite3.Database(options.fileName, cb) 55 | } 56 | innerDb = create() 57 | }) 58 | if (curAttempt > 1 && options.logWarning) 59 | options.logWarning(`SQLite connexion was successfully opened after ${curAttempt} attempts, first error was: ${firstError}`) 60 | if (options.init) 61 | await options.init(db) 62 | return promisifyDatabase(db) 63 | } 64 | 65 | function promisifyDatabase(db: sqlite3.Database): Database { 66 | return { 67 | run: (sql: string, params = []) => { 68 | return new Promise((resolve, reject) => { 69 | db.run(sql, params, function (this: RunResult, err: any) { 70 | if (err) 71 | reject(err) 72 | else 73 | resolve(this) // Here, 'this' is the 'RunResult' 74 | }) 75 | }) 76 | }, 77 | all: (sql: string, params = []) => { 78 | return new Promise((resolve, reject) => { 79 | db.all(sql, params, (err: any, rows: unknown[]) => { 80 | if (err) 81 | reject(err) 82 | else 83 | resolve(rows) 84 | }) 85 | }) 86 | }, 87 | exec: (sql: string) => { 88 | return new Promise((resolve, reject) => { 89 | db.exec(sql, (err: any) => { 90 | if (err) 91 | reject(err) 92 | else 93 | resolve() 94 | }) 95 | }) 96 | }, 97 | prepare: (sql: string, params = []) => { 98 | let st: sqlite3.Statement 99 | return new Promise((resolve, reject) => { 100 | st = db.prepare(sql, params, (err: any) => { 101 | if (err) 102 | reject(err) 103 | else 104 | resolve() 105 | }) 106 | }).then(() => promisifyStatement(st)) 107 | }, 108 | close: () => { 109 | return new Promise((resolve, reject) => { 110 | db.close((err: any) => { 111 | if (err) 112 | reject(err) 113 | else 114 | resolve() 115 | }) 116 | }) 117 | } 118 | } 119 | } 120 | 121 | function promisifyStatement(st: sqlite3.Statement): Statement { 122 | return { 123 | bind: (...params: any[]) => { 124 | return new Promise((resolve, reject) => { 125 | st.bind(...params, function (err: any) { 126 | if (err) 127 | reject(err) 128 | else 129 | resolve() 130 | }) 131 | }) 132 | }, 133 | run: (params = []) => { 134 | return new Promise((resolve, reject) => { 135 | st.run(params, function (this: RunResult, err: any) { 136 | if (err) 137 | reject(err) 138 | else 139 | resolve(this) // Here, 'this' is the 'RunResult' 140 | }) 141 | }) 142 | }, 143 | all: (params = []) => { 144 | return new Promise((resolve, reject) => { 145 | st.all(params, (err: any, rows: unknown[]) => { 146 | if (err) 147 | reject(err) 148 | else 149 | resolve(rows) 150 | }) 151 | }) 152 | }, 153 | get: (params = []) => { 154 | return new Promise((resolve, reject) => { 155 | st.get(params, (err: any, row: unknown[]) => { 156 | if (err) 157 | reject(err) 158 | else 159 | resolve(row) 160 | }) 161 | }) 162 | }, 163 | finalize: () => { 164 | return new Promise((resolve, reject) => { 165 | st.finalize((err: any) => { 166 | if (err) 167 | reject(err) 168 | else 169 | resolve() 170 | }) 171 | }) 172 | } 173 | } 174 | } -------------------------------------------------------------------------------- /sqlite3-adapter/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2018", 4 | "moduleResolution": "node", 5 | "strict": true, 6 | "outDir": "build/compiled", 7 | "declaration": true, 8 | "lib": [ 9 | "es2018" 10 | ] 11 | }, 12 | "include": [ 13 | "src" 14 | ] 15 | } -------------------------------------------------------------------------------- /sqlite3-adapter/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "defaultSeverity": "error", 3 | "extends": [ 4 | "tslint:recommended" 5 | ], 6 | "linterOptions": { 7 | "exclude": [ 8 | "node_modules/**" 9 | ] 10 | }, 11 | "rules": { 12 | "arrow-parens": false, 13 | "curly": false, 14 | "eofline": false, 15 | "indent": [ 16 | true, 17 | "spaces", 18 | 2 19 | ], 20 | "interface-name": false, 21 | "max-classes-per-file": false, 22 | "max-line-length": false, 23 | "member-access": [ 24 | true, 25 | "no-public" 26 | ], 27 | "no-conditional-assignment": false, 28 | "no-consecutive-blank-lines": false, 29 | "no-empty": [ 30 | true, 31 | "allow-empty-catch", 32 | "allow-empty-functions" 33 | ], 34 | "no-shadowed-variable": false, 35 | "no-string-literal": false, 36 | "no-var-requires": false, 37 | "ordered-imports": true, 38 | "object-literal-sort-keys": false, 39 | "object-literal-key-quotes": false, 40 | "only-arrow-functions": false, 41 | "prefer-const": true, 42 | "quotemark": [ 43 | true, 44 | "double" 45 | ], 46 | "semicolon": [ 47 | true, 48 | "never" 49 | ], 50 | "space-before-function-paren": [ 51 | true, 52 | { 53 | "anonymous": "always", 54 | "named": "never", 55 | "asyncArrow": "always" 56 | } 57 | ], 58 | "trailing-comma": [ 59 | true, 60 | { 61 | "singleline": "never" 62 | } 63 | ], 64 | "variable-name": [ 65 | true, 66 | "allow-leading-underscore", 67 | "allow-pascal-case" 68 | ] 69 | } 70 | } --------------------------------------------------------------------------------