@architect/inventory 3.0.0-RC.7 → 3.0.0-RC.8

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/changelog.md CHANGED
@@ -37,6 +37,7 @@
37
37
  - Breaking change: renamed `lambda.handlerFunction` to `lambda.handlerMethod`
38
38
  - Breaking change: prioritize `mod.ts|js` handlers in Deno Lambdas
39
39
  - Breaking change: removed `toml` support
40
+ - Breaking change: removed `get.macros` method; as `@macros` are now automatically mapped to the Architect plugins, you can simply use `get.plugins` instead
40
41
  - Performance improvements to building `inv.shared` + `inv.views`
41
42
  - Improved memory footprint of Inventory object by preserving references in `lambdaSrcDirs`, `lambdasBySrcDir`
42
43
  - Added `pragma` property to all Lambdas to aid in reference preservation
@@ -48,6 +49,8 @@ Update CI
48
49
  ### Fixed
49
50
 
50
51
  - Added file path validation because `aws-sdk` blows up on !ascii paths; fixes #1292, thanks @GustMartins!
52
+ - Fixed env var validation from preference files
53
+ - Fixed error bubbling when reading a preferences file with issues
51
54
 
52
55
  ---
53
56
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@architect/inventory",
3
- "version": "3.0.0-RC.7",
3
+ "version": "3.0.0-RC.8",
4
4
  "description": "Architect project resource enumeration utility",
5
5
  "main": "src/index.js",
6
6
  "scripts": {
@@ -23,21 +23,21 @@
23
23
  "dependencies": {
24
24
  "@architect/asap": "~5.0.0-RC.0",
25
25
  "@architect/parser": "~6.0.0-RC.0",
26
- "@architect/utils": "~3.0.4",
26
+ "@architect/utils": "~3.1.0-RC.0",
27
27
  "lambda-runtimes": "~1.1.1"
28
28
  },
29
29
  "devDependencies": {
30
30
  "@architect/eslint-config": "~2.0.1",
31
31
  "aws-sdk": "2.1001.0",
32
- "aws-sdk-mock": "~5.6.0",
32
+ "aws-sdk-mock": "~5.6.2",
33
33
  "cross-env": "~7.0.3",
34
- "dotenv": "~14.3.2",
35
- "eslint": "~8.7.0",
34
+ "dotenv": "~15.0.0",
35
+ "eslint": "~8.8.0",
36
36
  "mock-fs": "~5.1.2",
37
37
  "mock-require": "~3.0.3",
38
38
  "nyc": "~15.1.0",
39
39
  "tap-spec": "^5.0.0",
40
- "tape": "^5.4.1"
40
+ "tape": "^5.5.0"
41
41
  },
42
42
  "eslintConfig": {
43
43
  "extends": "@architect/eslint-config"
@@ -5,6 +5,7 @@ let { lambdas } = pragmas
5
5
  let nonLambdaSetters = [ 'customLambdas', 'env', 'runtimes' ]
6
6
  let setters = [ ...lambdas, ...nonLambdaSetters ]
7
7
  let pluginMethods = [ 'deploy', 'sandbox' ] // TODO add more!
8
+ let reservedNames = [ '_methods', 'events', 'queues', 'static', 'tables' ]
8
9
 
9
10
  module.exports = function getPluginModules ({ arc, inventory, errors }) {
10
11
  if (!arc?.plugins?.length && !arc?.macros?.length) return null
@@ -33,8 +34,8 @@ module.exports = function getPluginModules ({ arc, inventory, errors }) {
33
34
  : join(cwd, 'src', type + 's', name)
34
35
  }
35
36
 
36
- if (name === '_methods') {
37
- errors.push('Plugin name _methods is reserved, please rename your plugin')
37
+ if (reservedNames.includes(name)) {
38
+ errors.push(`Plugin name ${name} is reserved, please rename your plugin`)
38
39
  continue
39
40
  }
40
41
  if (!validationPatterns.veryLooseName.test(name)) {
@@ -1,4 +1,5 @@
1
1
  let { sep } = require('path')
2
+ let { deepFrozenCopy } = require('@architect/utils')
2
3
  let read = require('../../../read')
3
4
  let getLambda = require('./get-lambda')
4
5
  let getRuntime = require('./get-runtime')
@@ -18,9 +19,10 @@ function populateLambda (type, params) {
18
19
  let plugins = inventory.plugins?._methods?.set?.[type]
19
20
  let pluginLambda = []
20
21
  if (plugins) {
22
+ let invCopy = deepFrozenCopy(inventory)
21
23
  let pluginResults = plugins.flatMap(fn => {
22
24
  try {
23
- var result = fn({ arc, inventory: { inv: inventory } })
25
+ var result = fn({ arc: invCopy._project.arc, inventory: { inv: invCopy } })
24
26
  }
25
27
  catch (err) {
26
28
  err.message = `Setter plugin exception: plugin: ${fn.plugin}, method: set.${type}`
@@ -1,4 +1,5 @@
1
- let { is } = require('../../../lib')
1
+ let { deepFrozenCopy } = require('@architect/utils')
2
+ let { is, validationPatterns: valid } = require('../../../lib')
2
3
  let envs = [ 'testing', 'staging', 'production' ]
3
4
  let str = value => {
4
5
  if (is.object(value) || is.array(value)) return JSON.stringify(value)
@@ -15,15 +16,12 @@ module.exports = function setEnvPlugins (params, project) {
15
16
  production: null,
16
17
  }
17
18
 
18
- // IEEE 1003.1-2001 does not allow lowercase, so consider this a compromise for the Windows folks in the house
19
- let validName = /^[a-zA-Z0-9_]+$/
20
-
21
19
  // inventory._project is not yet built, so provide as much as we can to plugins for now
22
- let inv = { ...inventory, _project: project }
20
+ let inv = deepFrozenCopy({ ...inventory, _project: project })
23
21
  envPlugins.forEach(fn => {
24
22
  let errType = `plugin: ${fn.plugin}, method: set.env`
25
23
  try {
26
- let result = fn({ inventory: { inv } })
24
+ let result = fn({ arc: inv._project.arc, inventory: { inv } })
27
25
  if (!is.object(result) || !Object.keys(result).length) {
28
26
  return errors.push(`Env plugin returned invalid data, must return an Object with one or more keys + values: ${errType}`)
29
27
  }
@@ -32,6 +30,9 @@ module.exports = function setEnvPlugins (params, project) {
32
30
  if (Object.keys(result).some(k => envs.includes(k))) {
33
31
  envs.forEach(e => {
34
32
  if (result[e]) Object.entries(result[e]).forEach(([ k, v ]) => {
33
+ if (!valid.envVar.test(k)) {
34
+ return errors.push(`Env var '${k}' is invalid, must be [a-zA-Z0-9_]`)
35
+ }
35
36
  let errored = false, val = str(v)
36
37
  if (!env[e]) env[e] = { [k]: val }
37
38
  else if (env[e][k] && !errored) {
@@ -45,7 +46,7 @@ module.exports = function setEnvPlugins (params, project) {
45
46
  // Populate all environments based on env var
46
47
  else {
47
48
  Object.entries(result).forEach(([ k, v ]) => {
48
- if (!validName.test(k)) {
49
+ if (!valid.envVar.test(k)) {
49
50
  return errors.push(`Env var '${k}' is invalid, must be [a-zA-Z0-9_]`)
50
51
  }
51
52
  let errored = false, val = str(v)
@@ -1,5 +1,6 @@
1
- let { is, validationPatterns } = require('../../../lib')
2
1
  let { aliases, runtimeList } = require('lambda-runtimes')
2
+ let { deepFrozenCopy } = require('@architect/utils')
3
+ let { is, validationPatterns } = require('../../../lib')
3
4
  let { looserName } = validationPatterns
4
5
  let allRuntimes = runtimeList.concat([ 'deno', ...Object.keys(aliases) ])
5
6
  let validTypes = [ 'transpiled', 'compiled', 'interpreted' ]
@@ -13,12 +14,12 @@ module.exports = function setRuntimePlugins (params, project) {
13
14
  runtimes: [],
14
15
  }
15
16
  // inventory._project is not yet built, so provide as much as we can to plugins for now
16
- let inv = { ...inventory, _project: project }
17
+ let inv = deepFrozenCopy({ ...inventory, _project: project })
17
18
  let build
18
19
  runtimePlugins.forEach(fn => {
19
20
  let errType = `plugin: ${fn.plugin}, method: set.runtimes`
20
21
  try {
21
- var result = fn({ inventory: { inv } })
22
+ var result = fn({ arc: inv._project.arc, inventory: { inv } })
22
23
  }
23
24
  catch (err) {
24
25
  err.message = `Runtime plugin exception: ${errType}`
@@ -7,60 +7,30 @@
7
7
  var fs = require("fs");
8
8
  var path = require("path");
9
9
  var os = require("os");
10
- function log(message) {
11
- console.log(`[dotenv][DEBUG] ${message}`);
12
- }
13
- var NEWLINE = "\n";
14
- var RE_INI_KEY_VAL = /^\s*([\w.-]+)\s*=\s*("[^"]*"|'[^']*'|.*?)(\s+#.*)?$/;
15
- var RE_NEWLINES = /\\n/g;
16
- var NEWLINES_MATCH = /\r\n|\n|\r/;
17
- function parse(src, options) {
18
- const debug = Boolean(options && options.debug);
19
- const multiline = Boolean(options && options.multiline);
10
+ var LINE = /(?:^|^)\s*(?:export\s+)?([\w.-]+)(?:\s*=\s*?|:\s+?)(\s*'(?:\\'|[^'])*'|\s*"(?:\\"|[^"])*"|[^#\r\n]+)?\s*(?:#.*)?(?:$|$)/mg;
11
+ function parse(src) {
20
12
  const obj = {};
21
- const lines = src.toString().split(NEWLINES_MATCH);
22
- for (let idx = 0; idx < lines.length; idx++) {
23
- let line = lines[idx];
24
- const keyValueArr = line.match(RE_INI_KEY_VAL);
25
- if (keyValueArr != null) {
26
- const key = keyValueArr[1];
27
- let val = keyValueArr[2] || "";
28
- let end = val.length - 1;
29
- const isDoubleQuoted = val[0] === '"' && val[end] === '"';
30
- const isSingleQuoted = val[0] === "'" && val[end] === "'";
31
- const isMultilineDoubleQuoted = val[0] === '"' && val[end] !== '"';
32
- const isMultilineSingleQuoted = val[0] === "'" && val[end] !== "'";
33
- if (multiline && (isMultilineDoubleQuoted || isMultilineSingleQuoted)) {
34
- const quoteChar = isMultilineDoubleQuoted ? '"' : "'";
35
- val = val.substring(1);
36
- while (idx++ < lines.length - 1) {
37
- line = lines[idx];
38
- end = line.length - 1;
39
- if (line[end] === quoteChar) {
40
- val += NEWLINE + line.substring(0, end);
41
- break;
42
- }
43
- val += NEWLINE + line;
44
- }
45
- } else if (isSingleQuoted || isDoubleQuoted) {
46
- val = val.substring(1, end);
47
- if (isDoubleQuoted) {
48
- val = val.replace(RE_NEWLINES, NEWLINE);
49
- }
50
- } else {
51
- val = val.trim();
52
- }
53
- obj[key] = val;
54
- } else if (debug) {
55
- const trimmedLine = line.trim();
56
- if (trimmedLine.length && trimmedLine[0] !== "#") {
57
- log(`Failed to match key and value when parsing line ${idx + 1}: ${line}`);
58
- }
13
+ let lines = src.toString();
14
+ lines = lines.replace(/\r\n?/mg, "\n");
15
+ let match;
16
+ while ((match = LINE.exec(lines)) != null) {
17
+ const key = match[1];
18
+ let value = match[2] || "";
19
+ value = value.trim();
20
+ const maybeQuote = value[0];
21
+ value = value.replace(/^(['"])([\s\S]+)\1$/mg, "$2");
22
+ if (maybeQuote === '"') {
23
+ value = value.replace(/\\n/g, "\n");
24
+ value = value.replace(/\\r/g, "\r");
59
25
  }
26
+ obj[key] = value;
60
27
  }
61
28
  return obj;
62
29
  }
63
- function resolveHome(envPath) {
30
+ function _log(message) {
31
+ console.log(`[dotenv][DEBUG] ${message}`);
32
+ }
33
+ function _resolveHome(envPath) {
64
34
  return envPath[0] === "~" ? path.join(os.homedir(), envPath.slice(1)) : envPath;
65
35
  }
66
36
  function config(options) {
@@ -68,17 +38,16 @@ function config(options) {
68
38
  let encoding = "utf8";
69
39
  const debug = Boolean(options && options.debug);
70
40
  const override = Boolean(options && options.override);
71
- const multiline = Boolean(options && options.multiline);
72
41
  if (options) {
73
42
  if (options.path != null) {
74
- dotenvPath = resolveHome(options.path);
43
+ dotenvPath = _resolveHome(options.path);
75
44
  }
76
45
  if (options.encoding != null) {
77
46
  encoding = options.encoding;
78
47
  }
79
48
  }
80
49
  try {
81
- const parsed = DotenvModule.parse(fs.readFileSync(dotenvPath, { encoding }), { debug, multiline });
50
+ const parsed = DotenvModule.parse(fs.readFileSync(dotenvPath, { encoding }));
82
51
  Object.keys(parsed).forEach(function(key) {
83
52
  if (!Object.prototype.hasOwnProperty.call(process.env, key)) {
84
53
  process.env[key] = parsed[key];
@@ -88,9 +57,9 @@ function config(options) {
88
57
  }
89
58
  if (debug) {
90
59
  if (override === true) {
91
- log(`"${key}" is already defined in \`process.env\` and WAS overwritten`);
60
+ _log(`"${key}" is already defined in \`process.env\` and WAS overwritten`);
92
61
  } else {
93
- log(`"${key}" is already defined in \`process.env\` and was NOT overwritten`);
62
+ _log(`"${key}" is already defined in \`process.env\` and was NOT overwritten`);
94
63
  }
95
64
  }
96
65
  }
@@ -98,7 +67,7 @@ function config(options) {
98
67
  return { parsed };
99
68
  } catch (e) {
100
69
  if (debug) {
101
- log(`Failed to load ${dotenvPath} ${e.message}`);
70
+ _log(`Failed to load ${dotenvPath} ${e.message}`);
102
71
  }
103
72
  return { error: e };
104
73
  }
@@ -2,7 +2,7 @@ let { join } = require('path')
2
2
  let { existsSync, readFileSync } = require('fs')
3
3
  let read = require('../../../read')
4
4
  let validate = require('../validate')
5
- let { is } = require('../../../lib')
5
+ let { is, validationPatterns: valid } = require('../../../lib')
6
6
  let { parse } = require('./dotenv')
7
7
  let { homedir } = require('os')
8
8
 
@@ -20,13 +20,11 @@ module.exports = function getPrefs ({ scope, inventory, errors }) {
20
20
  let preferences = {}
21
21
 
22
22
  // Populate Architect preferences
23
- if (prefs.filepath) {
23
+ if (prefs.filepath && prefs.arc) {
24
24
  // Ok, this gets a bit hairy
25
25
  // Arc outputs an object of nested arrays
26
26
  // Basically, construct a pared-down intermediate prefs obj for consumers
27
27
  Object.entries(prefs.arc).forEach(([ key, val ]) => {
28
- // TODO add additional preferences checks and normalization
29
-
30
28
  /* istanbul ignore else: Parser should get this, but jic */
31
29
  if (!preferences[key]) preferences[key] = {}
32
30
  /* istanbul ignore else: Parser should only produce arrays, but jic */
@@ -51,6 +49,9 @@ module.exports = function getPrefs ({ scope, inventory, errors }) {
51
49
  /* istanbul ignore else: Yet another jic */
52
50
  if (preferences.env[e]) {
53
51
  Object.entries(preferences.env[e]).forEach(([ key, val ]) => {
52
+ if (!valid.envVar.test(key)) {
53
+ errors.push(`Env var '${key}' is invalid, must be [a-zA-Z0-9_]`)
54
+ }
54
55
  if (is.array(val)) preferences.env[e][key] = val.join(' ')
55
56
  })
56
57
  }
package/src/lib/index.js CHANGED
@@ -20,6 +20,8 @@ let validationPatterns = {
20
20
  looseName: /^[a-z][a-zA-Z0-9-_]+$/,
21
21
  looserName: /^[a-z][a-zA-Z0-9-._]+$/,
22
22
  veryLooseName: /^[a-zA-Z0-9/\-._]*$/,
23
+ // IEEE 1003.1-2001 does not allow lowercase, so consider this a compromise for the Windows folks in the house
24
+ envVar: /^[a-zA-Z0-9_]+$/,
23
25
  }
24
26
 
25
27
  module.exports = {