├── .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 | [](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 | [](https://travis-ci.com/paroi-tech/ladc)
4 | [](https://david-dm.org/paroi-tech/ladc)
5 |
6 |
7 |
8 | [](https://www.npmjs.com/package/ladc)
9 | 
10 | [](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 | [](https://travis-ci.com/paroi-tech/ladc)
4 | [](https://www.npmjs.com/package/@ladc/mysql-adapter)
5 | 
6 | [](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 | [](https://travis-ci.com/paroi-tech/ladc)
7 | [](https://www.npmjs.com/package/@ladc/mysql2-adapter)
8 | 
9 | [](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 | [](https://travis-ci.com/paroi-tech/ladc)
7 | [](https://www.npmjs.com/package/@ladc/pg-adapter)
8 | 
9 | [](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 | [](https://travis-ci.com/paroi-tech/ladc)
7 | [](https://www.npmjs.com/package/@ladc/sql-bricks-modifier)
8 | 
9 | [](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 | [](https://travis-ci.com/paroi-tech/ladc)
7 | [](https://www.npmjs.com/package/@ladc/sqlite3-adapter)
8 | 
9 | [](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 | }
--------------------------------------------------------------------------------