@karmaniverous/get-dotenv 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/.env ADDED
@@ -0,0 +1 @@
1
+ APP_SETTING=root_app_setting
package/.env.dev ADDED
@@ -0,0 +1 @@
1
+ ENV_SETTING=root_dev_setting
package/.env.test ADDED
@@ -0,0 +1 @@
1
+ ENV_SETTING=root_test_setting
package/README.md ADDED
@@ -0,0 +1,118 @@
1
+ # get-dotenv
2
+
3
+ Load environment variables with a cascade of environment-aware dotenv files. You can:
4
+
5
+ - Load dotenv files synchronously or asynchronously.
6
+ - Specify the directory containing your dotenv files.
7
+ - Specify the token that identifies dotenv files (e.g. '.env').
8
+ - Specify the token that identifies private vatiables (e.g. 'local').
9
+ - Load variables for a specific environment or none.
10
+ - Exclude public or private variables.
11
+ - Extract variables to an object, to `process.env`, or both.
12
+ - Log the result to the console.
13
+
14
+ The command-line version can pull the environment designator from a number of sources, populate `process.env`, and execute a shell command.
15
+
16
+ # Command Line Interface
17
+
18
+ ```text
19
+ Usage: getdotenv [options]
20
+
21
+ Load environment variables with a cascade of environment-aware
22
+ dotenv files. You can:
23
+
24
+ * Specify the directory containing your dotenv files.
25
+ * Specify the token that identifies dotenv files (e.g. '.env').
26
+ * Specify the token that identifies private vatiables (e.g. '.local').
27
+ * Specify a default environment, override the default with an
28
+ environment variable, and override both with a direct setting.
29
+ * Exclude public or private variables.
30
+ * Execute a shell command after loading variables.
31
+
32
+ Options:
33
+ -p, --path <string> path to dotenv directory (default './')
34
+ -t, --dotenv-token <string> token indicating a dotenv file (default: '.env')
35
+ -i, --private-token <string> token indicating private variables (default: 'local')
36
+ -d, --defaultEnvironment <string> default environment
37
+ -e, --environment <string> designated environment
38
+ -v, --variable <string> environment from variable
39
+ -r, --exclude-private exclude private variables (default: false)
40
+ -u, --exclude-public exclude public variables (default: false)
41
+ -c, --command <string> shell command
42
+ -l, --log log extracted variables (default: false)
43
+ -h, --help display help for command
44
+ ```
45
+
46
+ # API Documentation
47
+
48
+ ```js
49
+ import { foo, PACKAGE_INFO } from '@karmaniverous/npm-package-template`;
50
+ ```
51
+
52
+ ## Functions
53
+
54
+ <dl>
55
+ <dt><a href="#getDotenv">getDotenv([options])</a> ⇒ <code>Object</code></dt>
56
+ <dd><p>Asynchronously process dotenv files of the form .env[.<ENV>][.<PRIVATETOKEN>]</p>
57
+ </dd>
58
+ <dt><a href="#getDotenv">getDotenv([options])</a> ⇒ <code>Object</code></dt>
59
+ <dd><p>Synchronously process dotenv files of the form .env[.<ENV>][.&lt;PRIVATEEXT]</p>
60
+ </dd>
61
+ </dl>
62
+
63
+ ## Typedefs
64
+
65
+ <dl>
66
+ <dt><a href="#OptionsType">OptionsType</a> : <code>Object</code></dt>
67
+ <dd><p>get-dotenv options type</p>
68
+ </dd>
69
+ </dl>
70
+
71
+ <a name="getDotenv"></a>
72
+
73
+ ## getDotenv([options]) ⇒ <code>Object</code>
74
+ Asynchronously process dotenv files of the form .env[.<ENV>][.<PRIVATETOKEN>]
75
+
76
+ **Kind**: global function
77
+ **Returns**: <code>Object</code> - The combined parsed dotenv object.
78
+
79
+ | Param | Type | Description |
80
+ | --- | --- | --- |
81
+ | [options] | [<code>OptionsType</code>](#OptionsType) | options object |
82
+
83
+ <a name="getDotenv"></a>
84
+
85
+ ## getDotenv([options]) ⇒ <code>Object</code>
86
+ Synchronously process dotenv files of the form .env[.<ENV>][.<PRIVATEEXT]
87
+
88
+ **Kind**: global function
89
+ **Returns**: <code>Object</code> - The combined parsed dotenv object.
90
+
91
+ | Param | Type | Description |
92
+ | --- | --- | --- |
93
+ | [options] | [<code>OptionsType</code>](#OptionsType) | options object |
94
+
95
+ <a name="OptionsType"></a>
96
+
97
+ ## OptionsType : <code>Object</code>
98
+ get-dotenv options type
99
+
100
+ **Kind**: global typedef
101
+ **Properties**
102
+
103
+ | Name | Type | Description |
104
+ | --- | --- | --- |
105
+ | [dotenvToken] | <code>string</code> | token indicating a dotenv file (default: '.env') |
106
+ | [env] | <code>string</code> | target environment |
107
+ | [excludePrivate] | <code>bool</code> | exclude private variables (default: false) |
108
+ | [excludePublic] | <code>bool</code> | exclude public variables (default: false) |
109
+ | [loadProcess] | <code>bool</code> | load dotenv to process.env (default: false) |
110
+ | [log] | <code>bool</code> | log result to console (default: false) |
111
+ | [path] | <code>string</code> | path to target directory |
112
+ | [privateToken] | <code>string</code> | token indicating private variables (default: 'local'). |
113
+
114
+
115
+ ---
116
+
117
+ See more great templates and other tools on
118
+ [my GitHub Profile](https://github.com/karmaniverous)!
@@ -0,0 +1,75 @@
1
+ #!/usr/bin/env node
2
+
3
+ // Import package exports.
4
+ import { getDotenvSync } from '@karmaniverous/get-dotenv';
5
+ import { execSync } from 'child_process';
6
+
7
+ // Create CLI.
8
+ import { program } from 'commander';
9
+
10
+ // CLI description.
11
+ program.name('getdotenv');
12
+ program.description(
13
+ `Load environment variables with a cascade of environment-aware
14
+ dotenv files. You can:
15
+
16
+ * Specify the directory containing your dotenv files.
17
+ * Specify the token that identifies dotenv files (e.g. '.env').
18
+ * Specify the token that identifies private vatiables (e.g. '.local').
19
+ * Specify a default environment, override the default with an
20
+ environment variable, and override both with a direct setting.
21
+ * Exclude public or private variables.
22
+ * Execute a shell command after loading variables.`
23
+ );
24
+
25
+ // CLI options.
26
+ program
27
+ .option('-p, --path <string>', "path to dotenv directory (default './')")
28
+ .option(
29
+ '-t, --dotenv-token <string>',
30
+ "token indicating a dotenv file (default: '.env')"
31
+ )
32
+ .option(
33
+ '-i, --private-token <string>',
34
+ "token indicating private variables (default: 'local')"
35
+ )
36
+ .option('-d, --defaultEnvironment <string>', 'default environment')
37
+ .option('-e, --environment <string>', 'designated environment')
38
+ .option('-v, --variable <string>', 'environment from variable')
39
+ .option('-r, --exclude-private', 'exclude private variables (default: false)')
40
+ .option('-u, --exclude-public', 'exclude public variables (default: false)')
41
+ .option('-c, --command <string>', 'shell command')
42
+ .option('-l, --log', 'log extracted variables (default: false)');
43
+
44
+ // Parse CLI options from command line.
45
+ program.parse();
46
+ const {
47
+ command,
48
+ defaultEnvironment,
49
+ dotenvToken,
50
+ environment,
51
+ excludePrivate,
52
+ excludePublic,
53
+ log,
54
+ path,
55
+ privateToken,
56
+ variable,
57
+ } = program.opts();
58
+
59
+ // Get environment.
60
+ const env = environment ?? process.env[variable] ?? defaultEnvironment;
61
+
62
+ // Load dotenvs.
63
+ const dotenv = getDotenvSync({
64
+ dotenvToken,
65
+ env,
66
+ excludePrivate,
67
+ excludePublic,
68
+ loadProcess: true,
69
+ log,
70
+ path,
71
+ privateToken,
72
+ });
73
+
74
+ // Execute shell command.
75
+ if (command) execSync(command, { env: dotenv, stdio: 'inherit' });
@@ -0,0 +1,107 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.getDotenvSync = exports.getDotenv = void 0;
7
+ var _path = _interopRequireDefault(require("path"));
8
+ var _dotenvExpand = require("dotenv-expand");
9
+ var _readDotenv = require("../readDotEnv/readDotenv.js");
10
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
11
+ /**
12
+ * get-dotenv options type
13
+ *
14
+ * @typedef {Object} OptionsType
15
+ *
16
+ * @property {string} [dotenvToken] - token indicating a dotenv file (default: '.env')
17
+ * @property {string} [env] - target environment
18
+ * @property {bool} [excludePrivate] - exclude private variables (default: false)
19
+ * @property {bool} [excludePublic] - exclude public variables (default: false)
20
+ * @property {bool} [loadProcess] - load dotenv to process.env (default: false)
21
+ * @property {bool} [log] - log result to console (default: false)
22
+ * @property {string} [path] - path to target directory
23
+ * @property {string} [privateToken] - token indicating private variables (default: 'local').
24
+ */
25
+
26
+ /**
27
+ * Asynchronously process dotenv files of the form .env[.<ENV>][.<PRIVATETOKEN>]
28
+ *
29
+ * @async
30
+ * @function getDotenv
31
+ *
32
+ * @param {OptionsType} [options] - options object
33
+ *
34
+ * @returns {Object} The combined parsed dotenv object.
35
+ */
36
+ const getDotenv = async function () {
37
+ let {
38
+ dotenvToken = '.env',
39
+ env,
40
+ excludePrivate = false,
41
+ excludePublic = false,
42
+ loadProcess = false,
43
+ log = false,
44
+ path: dotenvPath = './',
45
+ privateToken = 'local'
46
+ } = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
47
+ // Read .env files.
48
+ const dotenv = (0, _dotenvExpand.expand)({
49
+ ...(excludePublic ? {} : {
50
+ ...(await (0, _readDotenv.readDotenv)(_path.default.resolve(dotenvPath, dotenvToken))),
51
+ ...(env ? await (0, _readDotenv.readDotenv)(_path.default.resolve(dotenvPath, `${dotenvToken}.${env}`)) : {})
52
+ }),
53
+ ...(excludePrivate ? {} : {
54
+ ...(await (0, _readDotenv.readDotenv)(_path.default.resolve(dotenvPath, `${dotenvToken}.${privateToken}`))),
55
+ ...(env ? await (0, _readDotenv.readDotenv)(_path.default.resolve(dotenvPath, `${dotenvToken}.${env}.${privateToken}`)) : {})
56
+ })
57
+ });
58
+
59
+ // Log result.
60
+ if (log) console.log(dotenv);
61
+
62
+ // Load process.env.
63
+ if (loadProcess) Object.assign(process.env, dotenv);
64
+ return dotenv;
65
+ };
66
+
67
+ /**
68
+ * Synchronously process dotenv files of the form .env[.<ENV>][.<PRIVATEEXT]
69
+ *
70
+ * @function getDotenv
71
+ *
72
+ * @param {OptionsType} [options] - options object
73
+ *
74
+ * @returns {Object} The combined parsed dotenv object.
75
+ */
76
+ exports.getDotenv = getDotenv;
77
+ const getDotenvSync = function () {
78
+ let {
79
+ dotenvToken = '.env',
80
+ env,
81
+ excludePrivate = false,
82
+ excludePublic = false,
83
+ loadProcess = false,
84
+ log = false,
85
+ path: dotenvPath = './',
86
+ privateToken = 'local'
87
+ } = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
88
+ // Read .env files.
89
+ const dotenv = (0, _dotenvExpand.expand)({
90
+ ...(excludePublic ? {} : {
91
+ ...(0, _readDotenv.readDotenvSync)(_path.default.resolve(dotenvPath, dotenvToken)),
92
+ ...(env ? (0, _readDotenv.readDotenvSync)(_path.default.resolve(dotenvPath, `${dotenvToken}.${env}`)) : {})
93
+ }),
94
+ ...(excludePrivate ? {} : {
95
+ ...(0, _readDotenv.readDotenvSync)(_path.default.resolve(dotenvPath, `${dotenvToken}.${privateToken}`)),
96
+ ...(env ? (0, _readDotenv.readDotenvSync)(_path.default.resolve(dotenvPath, `${dotenvToken}.${env}.${privateToken}`)) : {})
97
+ })
98
+ });
99
+
100
+ // Log result.
101
+ if (log) console.log(dotenv);
102
+
103
+ // Load process.env.
104
+ if (loadProcess) Object.assign(process.env, dotenv);
105
+ return dotenv;
106
+ };
107
+ exports.getDotenvSync = getDotenvSync;
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ Object.defineProperty(exports, "getDotenv", {
7
+ enumerable: true,
8
+ get: function () {
9
+ return _getDotenv.getDotenv;
10
+ }
11
+ });
12
+ Object.defineProperty(exports, "getDotenvSync", {
13
+ enumerable: true,
14
+ get: function () {
15
+ return _getDotenv.getDotenvSync;
16
+ }
17
+ });
18
+ var _getDotenv = require("./getDotenv/getDotenv.js");
@@ -0,0 +1,47 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.readDotenvSync = exports.readDotenv = void 0;
7
+ var _dotenv = require("dotenv");
8
+ var _fsExtra = _interopRequireDefault(require("fs-extra"));
9
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
10
+ /**
11
+ * Asynchronously read a dotenv file & parse it into an object.
12
+ *
13
+ * @async
14
+ * @private
15
+ * @function readDotenv
16
+ *
17
+ * @param {string} path - Any value.
18
+ *
19
+ * @returns {Object} The parsed dotenv object.
20
+ */
21
+ const readDotenv = async path => {
22
+ try {
23
+ return (0, _dotenv.parse)(await _fsExtra.default.readFile(path));
24
+ } catch {
25
+ return {};
26
+ }
27
+ };
28
+
29
+ /**
30
+ * Synchronously reads a dotenv file & parses it into an object.
31
+ *
32
+ * @private
33
+ * @function readDotenvSync
34
+ *
35
+ * @param {string} path - Any value.
36
+ *
37
+ * @returns {Object} The parsed dotenv object.
38
+ */
39
+ exports.readDotenv = readDotenv;
40
+ const readDotenvSync = path => {
41
+ try {
42
+ return (0, _dotenv.parse)(_fsExtra.default.readFileSync(path));
43
+ } catch {
44
+ return {};
45
+ }
46
+ };
47
+ exports.readDotenvSync = readDotenvSync;
@@ -0,0 +1,3 @@
1
+ {
2
+ "type": "commonjs"
3
+ }
@@ -0,0 +1,131 @@
1
+ import path from 'path';
2
+ import { expand } from 'dotenv-expand';
3
+ import { readDotenv, readDotenvSync } from '../readDotEnv/readDotenv.js';
4
+
5
+ /**
6
+ * get-dotenv options type
7
+ *
8
+ * @typedef {Object} OptionsType
9
+ *
10
+ * @property {string} [dotenvToken] - token indicating a dotenv file (default: '.env')
11
+ * @property {string} [env] - target environment
12
+ * @property {bool} [excludePrivate] - exclude private variables (default: false)
13
+ * @property {bool} [excludePublic] - exclude public variables (default: false)
14
+ * @property {bool} [loadProcess] - load dotenv to process.env (default: false)
15
+ * @property {bool} [log] - log result to console (default: false)
16
+ * @property {string} [path] - path to target directory
17
+ * @property {string} [privateToken] - token indicating private variables (default: 'local').
18
+ */
19
+
20
+ /**
21
+ * Asynchronously process dotenv files of the form .env[.<ENV>][.<PRIVATETOKEN>]
22
+ *
23
+ * @async
24
+ * @function getDotenv
25
+ *
26
+ * @param {OptionsType} [options] - options object
27
+ *
28
+ * @returns {Object} The combined parsed dotenv object.
29
+ */
30
+ export const getDotenv = async ({
31
+ dotenvToken = '.env',
32
+ env,
33
+ excludePrivate = false,
34
+ excludePublic = false,
35
+ loadProcess = false,
36
+ log = false,
37
+ path: dotenvPath = './',
38
+ privateToken = 'local',
39
+ } = {}) => {
40
+ // Read .env files.
41
+ const dotenv = expand({
42
+ ...(excludePublic
43
+ ? {}
44
+ : {
45
+ ...(await readDotenv(path.resolve(dotenvPath, dotenvToken))),
46
+ ...(env
47
+ ? await readDotenv(
48
+ path.resolve(dotenvPath, `${dotenvToken}.${env}`)
49
+ )
50
+ : {}),
51
+ }),
52
+ ...(excludePrivate
53
+ ? {}
54
+ : {
55
+ ...(await readDotenv(
56
+ path.resolve(dotenvPath, `${dotenvToken}.${privateToken}`)
57
+ )),
58
+ ...(env
59
+ ? await readDotenv(
60
+ path.resolve(
61
+ dotenvPath,
62
+ `${dotenvToken}.${env}.${privateToken}`
63
+ )
64
+ )
65
+ : {}),
66
+ }),
67
+ });
68
+
69
+ // Log result.
70
+ if (log) console.log(dotenv);
71
+
72
+ // Load process.env.
73
+ if (loadProcess) Object.assign(process.env, dotenv);
74
+
75
+ return dotenv;
76
+ };
77
+
78
+ /**
79
+ * Synchronously process dotenv files of the form .env[.<ENV>][.<PRIVATEEXT]
80
+ *
81
+ * @function getDotenv
82
+ *
83
+ * @param {OptionsType} [options] - options object
84
+ *
85
+ * @returns {Object} The combined parsed dotenv object.
86
+ */
87
+ export const getDotenvSync = ({
88
+ dotenvToken = '.env',
89
+ env,
90
+ excludePrivate = false,
91
+ excludePublic = false,
92
+ loadProcess = false,
93
+ log = false,
94
+ path: dotenvPath = './',
95
+ privateToken = 'local',
96
+ } = {}) => {
97
+ // Read .env files.
98
+ const dotenv = expand({
99
+ ...(excludePublic
100
+ ? {}
101
+ : {
102
+ ...readDotenvSync(path.resolve(dotenvPath, dotenvToken)),
103
+ ...(env
104
+ ? readDotenvSync(path.resolve(dotenvPath, `${dotenvToken}.${env}`))
105
+ : {}),
106
+ }),
107
+ ...(excludePrivate
108
+ ? {}
109
+ : {
110
+ ...readDotenvSync(
111
+ path.resolve(dotenvPath, `${dotenvToken}.${privateToken}`)
112
+ ),
113
+ ...(env
114
+ ? readDotenvSync(
115
+ path.resolve(
116
+ dotenvPath,
117
+ `${dotenvToken}.${env}.${privateToken}`
118
+ )
119
+ )
120
+ : {}),
121
+ }),
122
+ });
123
+
124
+ // Log result.
125
+ if (log) console.log(dotenv);
126
+
127
+ // Load process.env.
128
+ if (loadProcess) Object.assign(process.env, dotenv);
129
+
130
+ return dotenv;
131
+ };
package/lib/index.js ADDED
@@ -0,0 +1 @@
1
+ export { getDotenv, getDotenvSync } from './getDotenv/getDotenv.js';
@@ -0,0 +1,39 @@
1
+ import { parse } from 'dotenv';
2
+ import fs from 'fs-extra';
3
+
4
+ /**
5
+ * Asynchronously read a dotenv file & parse it into an object.
6
+ *
7
+ * @async
8
+ * @private
9
+ * @function readDotenv
10
+ *
11
+ * @param {string} path - Any value.
12
+ *
13
+ * @returns {Object} The parsed dotenv object.
14
+ */
15
+ export const readDotenv = async (path) => {
16
+ try {
17
+ return parse(await fs.readFile(path));
18
+ } catch {
19
+ return {};
20
+ }
21
+ };
22
+
23
+ /**
24
+ * Synchronously reads a dotenv file & parses it into an object.
25
+ *
26
+ * @private
27
+ * @function readDotenvSync
28
+ *
29
+ * @param {string} path - Any value.
30
+ *
31
+ * @returns {Object} The parsed dotenv object.
32
+ */
33
+ export const readDotenvSync = (path) => {
34
+ try {
35
+ return parse(fs.readFileSync(path));
36
+ } catch {
37
+ return {};
38
+ }
39
+ };
package/package.json ADDED
@@ -0,0 +1,91 @@
1
+ {
2
+ "name": "@karmaniverous/get-dotenv",
3
+ "bin": {
4
+ "getdotenv": "bin/getdotenv/index.js"
5
+ },
6
+ "version": "0.0.1",
7
+ "publishConfig": {
8
+ "access": "public"
9
+ },
10
+ "repository": {
11
+ "type": "git",
12
+ "url": "https://github.com/karmaniverous/get-dotenv"
13
+ },
14
+ "author": "Jason G. Williscroft",
15
+ "bugs": {
16
+ "url": "https://github.com/karmaniverous/get-dotenv/issues"
17
+ },
18
+ "description": "Process dotenv files in an arbitrary location & optionally populate environment variables.",
19
+ "homepage": "https://github.com/karmaniverous/get-dotenv#readme",
20
+ "keywords": [
21
+ "chai",
22
+ "docs",
23
+ "es6",
24
+ "javascript",
25
+ "npm",
26
+ "mocha",
27
+ "package",
28
+ "release",
29
+ "testing",
30
+ "template"
31
+ ],
32
+ "license": "BSD-3-Clause",
33
+ "dependencies": {
34
+ "commander": "^9.4.1",
35
+ "dotenv": "^16.0.3",
36
+ "dotenv-expand": "^10.0.0",
37
+ "fs-extra": "^11.1.0"
38
+ },
39
+ "devDependencies": {
40
+ "@babel/cli": "^7.20.7",
41
+ "@babel/core": "^7.20.7",
42
+ "@babel/eslint-parser": "^7.19.1",
43
+ "@babel/plugin-syntax-import-assertions": "^7.20.0",
44
+ "@babel/preset-env": "^7.20.2",
45
+ "@babel/register": "^7.18.9",
46
+ "@types/node": "^18.11.18",
47
+ "chai": "^4.3.7",
48
+ "concat-md": "^0.5.0",
49
+ "dotenv-cli": "^6.0.0",
50
+ "eslint": "^8.31.0",
51
+ "eslint-config-standard": "^17.0.0",
52
+ "eslint-plugin-mocha": "^10.1.0",
53
+ "jsdoc-to-markdown": "^8.0.0",
54
+ "mocha": "^10.2.0",
55
+ "prettier": "^2.8.1",
56
+ "release-it": "^15.6.0"
57
+ },
58
+ "exports": {
59
+ ".": {
60
+ "import": "./lib/index.js",
61
+ "require": "./dist/default/lib/index.js"
62
+ }
63
+ },
64
+ "mocha": {
65
+ "exclude": [
66
+ "./dist/**",
67
+ "./node_modules/**"
68
+ ],
69
+ "require": [
70
+ "@babel/register"
71
+ ],
72
+ "spec": "./**/*.test.!(*.*)"
73
+ },
74
+ "release-it": {
75
+ "github": {
76
+ "release": true
77
+ },
78
+ "npm": {
79
+ "publish": true
80
+ }
81
+ },
82
+ "scripts": {
83
+ "lint": "eslint lib/** bin/**",
84
+ "test": "mocha",
85
+ "build": "babel lib -d dist/default/lib --delete-dir-on-start --config-file ./dist/default/.babelrc",
86
+ "doc": "jsdoc2md -c doc/jsdoc.config.json -f lib/**/*.* -t doc/api-template.hbs > doc/3-api.md && concat-md doc --hide-anchor-links > README.md",
87
+ "package": "npm run lint && npm run test && npm run build && npm run doc",
88
+ "release": "npm run package && dotenv -c -- release-it"
89
+ },
90
+ "type": "module"
91
+ }