@architect/inventory 3.0.0-RC.3 → 3.0.0-RC.7
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 +8 -0
- package/package.json +9 -9
- package/src/config/pragmas/plugins.js +5 -1
- package/src/config/pragmas/populate-lambda/index.js +37 -7
- package/src/config/pragmas/static.js +4 -2
- package/src/config/pragmas/validate/_lib.js +3 -10
- package/src/config/pragmas/views.js +2 -2
- package/src/config/project/index.js +1 -1
- package/src/config/project/plugins/env.js +47 -10
- package/src/config/project/plugins/runtimes.js +42 -32
- package/src/config/project/prefs/dotenv.js +50 -11
- package/src/config/project/validate/index.js +14 -7
- package/src/defaults/index.js +3 -3
- package/src/lib/index.js +8 -0
- package/src/read/index.js +1 -3
- package/src/read/reader.js +1 -1
- package/src/validate/index.js +16 -9
- package/src/validate/paths.js +17 -0
package/changelog.md
CHANGED
|
@@ -36,10 +36,18 @@
|
|
|
36
36
|
- Existing functions can be simply moved to `src/tables-streams/{name}` (or use a custom `src` property)
|
|
37
37
|
- Breaking change: renamed `lambda.handlerFunction` to `lambda.handlerMethod`
|
|
38
38
|
- Breaking change: prioritize `mod.ts|js` handlers in Deno Lambdas
|
|
39
|
+
- Breaking change: removed `toml` support
|
|
39
40
|
- Performance improvements to building `inv.shared` + `inv.views`
|
|
40
41
|
- Improved memory footprint of Inventory object by preserving references in `lambdaSrcDirs`, `lambdasBySrcDir`
|
|
41
42
|
- Added `pragma` property to all Lambdas to aid in reference preservation
|
|
42
43
|
- Tidy up order of enumerated properties in each Lambda
|
|
44
|
+
Update CI
|
|
45
|
+
- Stop publishing to the GitHub Package registry
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
### Fixed
|
|
49
|
+
|
|
50
|
+
- Added file path validation because `aws-sdk` blows up on !ascii paths; fixes #1292, thanks @GustMartins!
|
|
43
51
|
|
|
44
52
|
---
|
|
45
53
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@architect/inventory",
|
|
3
|
-
"version": "3.0.0-RC.
|
|
3
|
+
"version": "3.0.0-RC.7",
|
|
4
4
|
"description": "Architect project resource enumeration utility",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -21,28 +21,28 @@
|
|
|
21
21
|
},
|
|
22
22
|
"license": "Apache-2.0",
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@architect/asap": "~
|
|
25
|
-
"@architect/parser": "~
|
|
24
|
+
"@architect/asap": "~5.0.0-RC.0",
|
|
25
|
+
"@architect/parser": "~6.0.0-RC.0",
|
|
26
26
|
"@architect/utils": "~3.0.4",
|
|
27
27
|
"lambda-runtimes": "~1.1.1"
|
|
28
28
|
},
|
|
29
29
|
"devDependencies": {
|
|
30
30
|
"@architect/eslint-config": "~2.0.1",
|
|
31
|
-
"aws-sdk": "2.
|
|
32
|
-
"aws-sdk-mock": "~5.
|
|
31
|
+
"aws-sdk": "2.1001.0",
|
|
32
|
+
"aws-sdk-mock": "~5.6.0",
|
|
33
33
|
"cross-env": "~7.0.3",
|
|
34
|
-
"dotenv": "~
|
|
35
|
-
"eslint": "~8.
|
|
34
|
+
"dotenv": "~14.3.2",
|
|
35
|
+
"eslint": "~8.7.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.
|
|
40
|
+
"tape": "^5.4.1"
|
|
41
41
|
},
|
|
42
42
|
"eslintConfig": {
|
|
43
43
|
"extends": "@architect/eslint-config"
|
|
44
44
|
},
|
|
45
|
-
"
|
|
45
|
+
"nyc": {
|
|
46
46
|
"check-coverage": true,
|
|
47
47
|
"branches": 100,
|
|
48
48
|
"lines": 100,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
let { join } = require('path')
|
|
2
2
|
let { existsSync } = require('fs')
|
|
3
|
-
let { is, normalizeSrc, pragmas } = require('../../lib')
|
|
3
|
+
let { is, normalizeSrc, pragmas, validationPatterns } = require('../../lib')
|
|
4
4
|
let { lambdas } = pragmas
|
|
5
5
|
let nonLambdaSetters = [ 'customLambdas', 'env', 'runtimes' ]
|
|
6
6
|
let setters = [ ...lambdas, ...nonLambdaSetters ]
|
|
@@ -37,6 +37,10 @@ module.exports = function getPluginModules ({ arc, inventory, errors }) {
|
|
|
37
37
|
errors.push('Plugin name _methods is reserved, please rename your plugin')
|
|
38
38
|
continue
|
|
39
39
|
}
|
|
40
|
+
if (!validationPatterns.veryLooseName.test(name)) {
|
|
41
|
+
errors.push('Plugin names can only contain [a-zA-Z0-9/\\-._]')
|
|
42
|
+
continue
|
|
43
|
+
}
|
|
40
44
|
if (pluginPath) {
|
|
41
45
|
try {
|
|
42
46
|
if (type === 'plugin') {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
let { sep } = require('path')
|
|
1
2
|
let read = require('../../../read')
|
|
2
3
|
let getLambda = require('./get-lambda')
|
|
3
4
|
let getRuntime = require('./get-runtime')
|
|
@@ -9,22 +10,44 @@ let { compiledRuntimes, is } = require('../../../lib')
|
|
|
9
10
|
/**
|
|
10
11
|
* Build out the Lambda tree from the Arc manifest or a passed pragma, and plugins
|
|
11
12
|
*/
|
|
12
|
-
function populateLambda (type,
|
|
13
|
+
function populateLambda (type, params) {
|
|
14
|
+
// Passing a pragma array via params allows special overrides
|
|
15
|
+
// See: @tables populating inv['tables-streams']
|
|
16
|
+
let { arc, inventory, errors, pragma } = params
|
|
17
|
+
|
|
13
18
|
let plugins = inventory.plugins?._methods?.set?.[type]
|
|
14
19
|
let pluginLambda = []
|
|
15
20
|
if (plugins) {
|
|
16
21
|
let pluginResults = plugins.flatMap(fn => {
|
|
17
|
-
|
|
18
|
-
|
|
22
|
+
try {
|
|
23
|
+
var result = fn({ arc, inventory: { inv: inventory } })
|
|
24
|
+
}
|
|
25
|
+
catch (err) {
|
|
26
|
+
err.message = `Setter plugin exception: plugin: ${fn.plugin}, method: set.${type}`
|
|
27
|
+
+ `\n` + err.message
|
|
28
|
+
throw err
|
|
29
|
+
}
|
|
30
|
+
if (!result ||
|
|
31
|
+
(!is.object(result) && !is.array(result)) ||
|
|
32
|
+
(is.array(result) && result.some(r => !is.object(r)))) {
|
|
19
33
|
errors.push(`Setter plugins must return a valid response: plugin: ${fn.plugin}, method: set.${type}`)
|
|
20
34
|
return []
|
|
21
35
|
}
|
|
22
|
-
result
|
|
23
|
-
|
|
36
|
+
if (is.array(result)) {
|
|
37
|
+
result.forEach(item => {
|
|
38
|
+
item.plugin = fn.plugin
|
|
39
|
+
item.type = fn.type
|
|
40
|
+
})
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
result.plugin = fn.plugin
|
|
44
|
+
result.type = fn.type
|
|
45
|
+
}
|
|
24
46
|
return result
|
|
25
47
|
})
|
|
26
48
|
pluginLambda = populate(type, pluginResults, inventory, errors, true) || []
|
|
27
49
|
}
|
|
50
|
+
|
|
28
51
|
let pragmaLambda = populate(type, pragma || arc[type], inventory, errors) || []
|
|
29
52
|
let aggregate = [ ...pluginLambda, ...pragmaLambda ]
|
|
30
53
|
return aggregate.length ? aggregate : null
|
|
@@ -46,6 +69,11 @@ function populate (type, pragma, inventory, errors, plugin) {
|
|
|
46
69
|
if (!result) continue
|
|
47
70
|
|
|
48
71
|
let { name, src, build } = result
|
|
72
|
+
|
|
73
|
+
// Normalize paths, especially since plugin authors may not use path.join
|
|
74
|
+
src = normalize(src)
|
|
75
|
+
if (build) build = normalize(build)
|
|
76
|
+
|
|
49
77
|
// Set up fresh config, then overlay plugin config
|
|
50
78
|
let config = defaultProjectConfig()
|
|
51
79
|
config = { ...config, ...getKnownProps(configProps, result.config) }
|
|
@@ -82,7 +110,7 @@ function populate (type, pragma, inventory, errors, plugin) {
|
|
|
82
110
|
|
|
83
111
|
// Tidy up any irrelevant properties
|
|
84
112
|
if (!compiledRuntimes.includes(config.runtimeConfig?.type)) {
|
|
85
|
-
//
|
|
113
|
+
// Super important! If we don't clean up the build prop, many explosions will explode
|
|
86
114
|
build = undefined
|
|
87
115
|
}
|
|
88
116
|
if (type !== 'http') {
|
|
@@ -111,8 +139,10 @@ function populate (type, pragma, inventory, errors, plugin) {
|
|
|
111
139
|
return lambdas
|
|
112
140
|
}
|
|
113
141
|
|
|
142
|
+
let normalize = path => path.replace(/[\\\/]/g, sep)
|
|
143
|
+
|
|
114
144
|
// Lambda setter plugins can technically return anything, so this ensures everything is tidy
|
|
115
|
-
let lambdaProps = [ 'cron', 'method', 'path', 'plugin', 'rate', 'route', 'table' ]
|
|
145
|
+
let lambdaProps = [ 'cron', 'method', 'path', 'plugin', 'rate', 'route', 'table', 'type' ]
|
|
116
146
|
let configProps = [ ...Object.keys(defaultFunctionConfig()), 'fifo', 'views' ]
|
|
117
147
|
let getKnownProps = (knownProps, raw = {}) => {
|
|
118
148
|
let props = knownProps.flatMap(prop => is.defined(raw[prop]) ? [ [ prop, raw[prop] ] ] : [])
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
let { asapSrc, is } = require('../../lib')
|
|
2
2
|
|
|
3
3
|
module.exports = function configureStatic ({ arc, inventory }) {
|
|
4
|
+
let httpSetters = inventory.plugins?._methods?.set?.http
|
|
5
|
+
|
|
4
6
|
// @static is inferred by @http
|
|
5
|
-
if (!arc.static && !arc.http) return null
|
|
7
|
+
if (!arc.static && !arc.http && !httpSetters) return null
|
|
6
8
|
|
|
7
9
|
let staticPragma = arc.static || []
|
|
8
10
|
let _static = {
|
|
@@ -37,7 +39,7 @@ module.exports = function configureStatic ({ arc, inventory }) {
|
|
|
37
39
|
}
|
|
38
40
|
|
|
39
41
|
// Handy shortcut to ASAP for bare @static
|
|
40
|
-
if (!arc.http) {
|
|
42
|
+
if (!arc.http && !httpSetters) {
|
|
41
43
|
inventory._project.rootHandler = 'arcStaticAssetProxy'
|
|
42
44
|
inventory._project.asapSrc = asapSrc()
|
|
43
45
|
}
|
|
@@ -1,15 +1,8 @@
|
|
|
1
|
-
let { is } = require('../../../lib')
|
|
2
|
-
|
|
3
|
-
let patterns = {
|
|
4
|
-
looseName: new RegExp(/^[a-z][a-zA-Z0-9-_]+$/),
|
|
5
|
-
strictName: new RegExp(/^[a-z][a-z0-9-]+$/),
|
|
6
|
-
// DynamoDB, SNS, SQS
|
|
7
|
-
veryLooseName: new RegExp(/^[a-zA-Z0-9/\-._]*$/),
|
|
8
|
-
}
|
|
1
|
+
let { is, validationPatterns } = require('../../../lib')
|
|
9
2
|
|
|
10
3
|
function regex (value, pattern, pragmaName, errors) {
|
|
11
|
-
if (!
|
|
12
|
-
if (!
|
|
4
|
+
if (!validationPatterns[pattern]) throw ReferenceError(`Invalid validation pattern specified: ${pattern}`)
|
|
5
|
+
if (!validationPatterns[pattern].test(value)) errors.push(`Invalid ${pragmaName} item: '${value}' must match ${validationPatterns[pattern]}`)
|
|
13
6
|
}
|
|
14
7
|
|
|
15
8
|
function size (value, min, max, pragmaName, errors) {
|
|
@@ -48,8 +48,8 @@ module.exports = function configureViews ({ arc, pragmas, inventory, errors }) {
|
|
|
48
48
|
|
|
49
49
|
// Set new views settings
|
|
50
50
|
for (let view of arc.views) {
|
|
51
|
-
let method = view[0]
|
|
52
|
-
let path = view[1]
|
|
51
|
+
let method = view?.[0]?.toLowerCase()
|
|
52
|
+
let path = view?.[1]
|
|
53
53
|
if (method === 'src') continue
|
|
54
54
|
let name = `${method} ${path}`
|
|
55
55
|
let route = pragmas.http.find(n => n.name === name)
|
|
@@ -68,7 +68,7 @@ module.exports = function getProjectConfig (params) {
|
|
|
68
68
|
})
|
|
69
69
|
|
|
70
70
|
let { build, runtimes } = plugins.runtimes(params, _project)
|
|
71
|
-
if (build)
|
|
71
|
+
if (build) _project.build = join(_project.cwd, build)
|
|
72
72
|
if (runtimes) _project.customRuntimes = runtimes
|
|
73
73
|
}
|
|
74
74
|
|
|
@@ -1,33 +1,70 @@
|
|
|
1
1
|
let { is } = require('../../../lib')
|
|
2
|
+
let envs = [ 'testing', 'staging', 'production' ]
|
|
3
|
+
let str = value => {
|
|
4
|
+
if (is.object(value) || is.array(value)) return JSON.stringify(value)
|
|
5
|
+
return String(value)
|
|
6
|
+
}
|
|
2
7
|
|
|
3
8
|
module.exports = function setEnvPlugins (params, project) {
|
|
4
9
|
let { errors, inventory } = params
|
|
5
10
|
let envPlugins = inventory.plugins?._methods?.set?.env
|
|
6
11
|
if (envPlugins?.length) {
|
|
7
|
-
let env = {
|
|
12
|
+
let env = {
|
|
13
|
+
testing: null,
|
|
14
|
+
staging: null,
|
|
15
|
+
production: null,
|
|
16
|
+
}
|
|
17
|
+
|
|
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_]+$/
|
|
8
20
|
|
|
9
21
|
// inventory._project is not yet built, so provide as much as we can to plugins for now
|
|
10
22
|
let inv = { ...inventory, _project: project }
|
|
11
23
|
envPlugins.forEach(fn => {
|
|
12
24
|
let errType = `plugin: ${fn.plugin}, method: set.env`
|
|
13
25
|
try {
|
|
14
|
-
let result = fn({ inventory: inv })
|
|
26
|
+
let result = fn({ inventory: { inv } })
|
|
15
27
|
if (!is.object(result) || !Object.keys(result).length) {
|
|
16
28
|
return errors.push(`Env plugin returned invalid data, must return an Object with one or more keys + values: ${errType}`)
|
|
17
29
|
}
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
30
|
+
// Populate env vars based on environment
|
|
31
|
+
// If any keys are environment names, disregard all keys except environment names
|
|
32
|
+
if (Object.keys(result).some(k => envs.includes(k))) {
|
|
33
|
+
envs.forEach(e => {
|
|
34
|
+
if (result[e]) Object.entries(result[e]).forEach(([ k, v ]) => {
|
|
35
|
+
let errored = false, val = str(v)
|
|
36
|
+
if (!env[e]) env[e] = { [k]: val }
|
|
37
|
+
else if (env[e][k] && !errored) {
|
|
38
|
+
errored = true
|
|
39
|
+
errors.push(`Env var '${k}' already registered: ${errType}`)
|
|
40
|
+
}
|
|
41
|
+
else env[e][k] = val
|
|
42
|
+
})
|
|
43
|
+
})
|
|
44
|
+
}
|
|
45
|
+
// Populate all environments based on env var
|
|
46
|
+
else {
|
|
47
|
+
Object.entries(result).forEach(([ k, v ]) => {
|
|
48
|
+
if (!validName.test(k)) {
|
|
49
|
+
return errors.push(`Env var '${k}' is invalid, must be [a-zA-Z0-9_]`)
|
|
50
|
+
}
|
|
51
|
+
let errored = false, val = str(v)
|
|
52
|
+
envs.forEach(e => {
|
|
53
|
+
if (!env[e]) env[e] = { [k]: val }
|
|
54
|
+
else if (env[e][k] && !errored) {
|
|
55
|
+
errored = true
|
|
56
|
+
errors.push(`Env var '${k}' already registered: ${errType}`)
|
|
57
|
+
}
|
|
58
|
+
else env[e][k] = val
|
|
59
|
+
})
|
|
60
|
+
})
|
|
61
|
+
}
|
|
25
62
|
}
|
|
26
63
|
catch (err) {
|
|
27
64
|
errors.push(`Runtime plugin '${fn.plugin}' failed: ${err.message}`)
|
|
28
65
|
}
|
|
29
66
|
})
|
|
30
|
-
return
|
|
67
|
+
return env
|
|
31
68
|
}
|
|
32
69
|
return inventory._project.env.plugins
|
|
33
70
|
}
|
|
@@ -1,6 +1,9 @@
|
|
|
1
|
-
let { is } = require('../../../lib')
|
|
1
|
+
let { is, validationPatterns } = require('../../../lib')
|
|
2
2
|
let { aliases, runtimeList } = require('lambda-runtimes')
|
|
3
|
+
let { looserName } = validationPatterns
|
|
3
4
|
let allRuntimes = runtimeList.concat([ 'deno', ...Object.keys(aliases) ])
|
|
5
|
+
let validTypes = [ 'transpiled', 'compiled', 'interpreted' ]
|
|
6
|
+
let builtTypes = validTypes.filter(t => t !== 'interpreted')
|
|
4
7
|
|
|
5
8
|
module.exports = function setRuntimePlugins (params, project) {
|
|
6
9
|
let { errors, inventory } = params
|
|
@@ -15,40 +18,47 @@ module.exports = function setRuntimePlugins (params, project) {
|
|
|
15
18
|
runtimePlugins.forEach(fn => {
|
|
16
19
|
let errType = `plugin: ${fn.plugin}, method: set.runtimes`
|
|
17
20
|
try {
|
|
18
|
-
|
|
19
|
-
result = is.array(result) ? result : [ result ]
|
|
20
|
-
result.forEach(runtime => {
|
|
21
|
-
// TODO add more validation
|
|
22
|
-
let { name } = runtime
|
|
23
|
-
if (!name) {
|
|
24
|
-
let msg = `Runtime plugin must provide a name: ${errType}`
|
|
25
|
-
return errors.push(msg)
|
|
26
|
-
}
|
|
27
|
-
if (allRuntimes.includes(name)) {
|
|
28
|
-
let msg = `Runtime name '${name}' is reserved: ${errType}`
|
|
29
|
-
return errors.push(msg)
|
|
30
|
-
}
|
|
31
|
-
if (runtimes[name]) {
|
|
32
|
-
let msg = `Runtime '${name}' already registered: ${errType}`
|
|
33
|
-
return errors.push(msg)
|
|
34
|
-
}
|
|
35
|
-
if (runtime.build) {
|
|
36
|
-
if (build && build !== runtime.build) {
|
|
37
|
-
errors.push(`Runtime '${name}' cannot set a build directory, as it is already configured to: ${build}`)
|
|
38
|
-
}
|
|
39
|
-
else if (is.bool(runtime.build) ||
|
|
40
|
-
!is.string(runtime.build)) {
|
|
41
|
-
build = 'build'
|
|
42
|
-
}
|
|
43
|
-
else build = runtime.build
|
|
44
|
-
}
|
|
45
|
-
runtimes.runtimes.push(name)
|
|
46
|
-
runtimes[name] = runtime
|
|
47
|
-
})
|
|
21
|
+
var result = fn({ inventory: { inv } })
|
|
48
22
|
}
|
|
49
23
|
catch (err) {
|
|
50
|
-
|
|
24
|
+
err.message = `Runtime plugin exception: ${errType}`
|
|
25
|
+
+ `\n` + err.message
|
|
26
|
+
throw err
|
|
51
27
|
}
|
|
28
|
+
// Accept one or more results, then loop through them
|
|
29
|
+
result = is.array(result) ? result : [ result ]
|
|
30
|
+
result.forEach(runtime => {
|
|
31
|
+
let { name, type, baseRuntime } = runtime
|
|
32
|
+
if (!name || !looserName.test(name)) {
|
|
33
|
+
let msg = `Runtime plugin must provide a valid name: ${errType}`
|
|
34
|
+
return errors.push(msg)
|
|
35
|
+
}
|
|
36
|
+
if (!type || !validTypes.includes(type)) {
|
|
37
|
+
let msg = `Runtime plugin must provide a valid type: ${errType}`
|
|
38
|
+
return errors.push(msg)
|
|
39
|
+
}
|
|
40
|
+
if (allRuntimes.includes(name)) {
|
|
41
|
+
let msg = `Runtime name '${name}' is reserved: ${errType}`
|
|
42
|
+
return errors.push(msg)
|
|
43
|
+
}
|
|
44
|
+
if (runtimes[name]) {
|
|
45
|
+
let msg = `Runtime name '${name}' already registered: ${errType}`
|
|
46
|
+
return errors.push(msg)
|
|
47
|
+
}
|
|
48
|
+
if (builtTypes.includes(type)) {
|
|
49
|
+
if (build && runtime.build && build !== runtime.build) {
|
|
50
|
+
return errors.push(`Runtime '${name}' cannot set a build directory, as it is already configured to: ${build}`)
|
|
51
|
+
}
|
|
52
|
+
// Adhere to Postel's Law
|
|
53
|
+
build = 'build'
|
|
54
|
+
if (is.string(runtime.build)) build = runtime.build
|
|
55
|
+
}
|
|
56
|
+
if (type === 'transpiled' && !allRuntimes.includes(baseRuntime)) {
|
|
57
|
+
return errors.push(`Runtime '${name}' must include a valid baseRuntime property corresponding to a valid Lambda runtime (e.g. 'nodejs14.x')`)
|
|
58
|
+
}
|
|
59
|
+
runtimes.runtimes.push(name)
|
|
60
|
+
runtimes[name] = runtime
|
|
61
|
+
})
|
|
52
62
|
})
|
|
53
63
|
return { build, runtimes }
|
|
54
64
|
}
|
|
@@ -11,21 +11,38 @@ function log(message) {
|
|
|
11
11
|
console.log(`[dotenv][DEBUG] ${message}`);
|
|
12
12
|
}
|
|
13
13
|
var NEWLINE = "\n";
|
|
14
|
-
var RE_INI_KEY_VAL = /^\s*([\w.-]+)\s*=\s*(
|
|
14
|
+
var RE_INI_KEY_VAL = /^\s*([\w.-]+)\s*=\s*("[^"]*"|'[^']*'|.*?)(\s+#.*)?$/;
|
|
15
15
|
var RE_NEWLINES = /\\n/g;
|
|
16
16
|
var NEWLINES_MATCH = /\r\n|\n|\r/;
|
|
17
17
|
function parse(src, options) {
|
|
18
18
|
const debug = Boolean(options && options.debug);
|
|
19
|
+
const multiline = Boolean(options && options.multiline);
|
|
19
20
|
const obj = {};
|
|
20
|
-
src.toString().split(NEWLINES_MATCH)
|
|
21
|
+
const lines = src.toString().split(NEWLINES_MATCH);
|
|
22
|
+
for (let idx = 0; idx < lines.length; idx++) {
|
|
23
|
+
let line = lines[idx];
|
|
21
24
|
const keyValueArr = line.match(RE_INI_KEY_VAL);
|
|
22
25
|
if (keyValueArr != null) {
|
|
23
26
|
const key = keyValueArr[1];
|
|
24
27
|
let val = keyValueArr[2] || "";
|
|
25
|
-
|
|
28
|
+
let end = val.length - 1;
|
|
26
29
|
const isDoubleQuoted = val[0] === '"' && val[end] === '"';
|
|
27
30
|
const isSingleQuoted = val[0] === "'" && val[end] === "'";
|
|
28
|
-
|
|
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) {
|
|
29
46
|
val = val.substring(1, end);
|
|
30
47
|
if (isDoubleQuoted) {
|
|
31
48
|
val = val.replace(RE_NEWLINES, NEWLINE);
|
|
@@ -35,9 +52,12 @@ function parse(src, options) {
|
|
|
35
52
|
}
|
|
36
53
|
obj[key] = val;
|
|
37
54
|
} else if (debug) {
|
|
38
|
-
|
|
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
|
+
}
|
|
39
59
|
}
|
|
40
|
-
}
|
|
60
|
+
}
|
|
41
61
|
return obj;
|
|
42
62
|
}
|
|
43
63
|
function resolveHome(envPath) {
|
|
@@ -47,6 +67,8 @@ function config(options) {
|
|
|
47
67
|
let dotenvPath = path.resolve(process.cwd(), ".env");
|
|
48
68
|
let encoding = "utf8";
|
|
49
69
|
const debug = Boolean(options && options.debug);
|
|
70
|
+
const override = Boolean(options && options.override);
|
|
71
|
+
const multiline = Boolean(options && options.multiline);
|
|
50
72
|
if (options) {
|
|
51
73
|
if (options.path != null) {
|
|
52
74
|
dotenvPath = resolveHome(options.path);
|
|
@@ -56,18 +78,35 @@ function config(options) {
|
|
|
56
78
|
}
|
|
57
79
|
}
|
|
58
80
|
try {
|
|
59
|
-
const parsed = parse(fs.readFileSync(dotenvPath, { encoding }), { debug });
|
|
81
|
+
const parsed = DotenvModule.parse(fs.readFileSync(dotenvPath, { encoding }), { debug, multiline });
|
|
60
82
|
Object.keys(parsed).forEach(function(key) {
|
|
61
83
|
if (!Object.prototype.hasOwnProperty.call(process.env, key)) {
|
|
62
84
|
process.env[key] = parsed[key];
|
|
63
|
-
} else
|
|
64
|
-
|
|
85
|
+
} else {
|
|
86
|
+
if (override === true) {
|
|
87
|
+
process.env[key] = parsed[key];
|
|
88
|
+
}
|
|
89
|
+
if (debug) {
|
|
90
|
+
if (override === true) {
|
|
91
|
+
log(`"${key}" is already defined in \`process.env\` and WAS overwritten`);
|
|
92
|
+
} else {
|
|
93
|
+
log(`"${key}" is already defined in \`process.env\` and was NOT overwritten`);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
65
96
|
}
|
|
66
97
|
});
|
|
67
98
|
return { parsed };
|
|
68
99
|
} catch (e) {
|
|
100
|
+
if (debug) {
|
|
101
|
+
log(`Failed to load ${dotenvPath} ${e.message}`);
|
|
102
|
+
}
|
|
69
103
|
return { error: e };
|
|
70
104
|
}
|
|
71
105
|
}
|
|
72
|
-
|
|
73
|
-
|
|
106
|
+
var DotenvModule = {
|
|
107
|
+
config,
|
|
108
|
+
parse
|
|
109
|
+
};
|
|
110
|
+
module.exports.config = DotenvModule.config;
|
|
111
|
+
module.exports.parse = DotenvModule.parse;
|
|
112
|
+
module.exports = DotenvModule;
|
|
@@ -2,12 +2,19 @@ let { is } = require('./../../../lib')
|
|
|
2
2
|
|
|
3
3
|
module.exports = function validatePreferences (preferences, errors) {
|
|
4
4
|
// Env checks
|
|
5
|
-
let { env } = preferences
|
|
6
|
-
if (!env) return
|
|
7
|
-
if (env && !is.object(env)) errors.push(`Invalid preferences setting: @env ${env}`)
|
|
8
|
-
|
|
5
|
+
let { env, sandbox } = preferences
|
|
9
6
|
let envs = [ 'testing', 'staging', 'production' ]
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
7
|
+
|
|
8
|
+
if (env && !is.object(env)) {
|
|
9
|
+
errors.push(`Invalid preferences setting: @env ${env}`)
|
|
10
|
+
}
|
|
11
|
+
else if (env) {
|
|
12
|
+
envs.forEach(e => {
|
|
13
|
+
if (env[e] && !is.object(env[e])) errors.push(`Invalid preferences setting: @env ${e}`)
|
|
14
|
+
})
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
if (sandbox?.env && !envs.includes(sandbox.env)) {
|
|
18
|
+
errors.push(`Invalid preferences setting: @sandbox env ${sandbox.env}`)
|
|
19
|
+
}
|
|
13
20
|
}
|
package/src/defaults/index.js
CHANGED
|
@@ -33,9 +33,9 @@ module.exports = function inventoryDefaults (params = {}) {
|
|
|
33
33
|
defaultFunctionConfig, // Project-level function config
|
|
34
34
|
rootHandler: null, // null | configured | arcStaticAssetProxy | proxy
|
|
35
35
|
env: { // Env vars pulled from:
|
|
36
|
-
local: null, // Local/global prefs or .env
|
|
37
|
-
plugins: null, // Plugins
|
|
38
|
-
aws: null, // SSM
|
|
36
|
+
local: null, // - Local/global prefs or .env
|
|
37
|
+
plugins: null, // - Plugins
|
|
38
|
+
aws: null, // - SSM
|
|
39
39
|
},
|
|
40
40
|
customRuntimes: null, // Runtime plugins
|
|
41
41
|
arc: [], // Raw arc obj
|
package/src/lib/index.js
CHANGED
|
@@ -15,6 +15,13 @@ let compiledRuntimes = [ 'compiled', 'transpiled' ]
|
|
|
15
15
|
// `any` must come last for Sandbox route sorting purposes
|
|
16
16
|
let httpMethods = [ 'get', 'post', 'put', 'patch', 'delete', 'options', 'head', 'any' ]
|
|
17
17
|
|
|
18
|
+
let validationPatterns = {
|
|
19
|
+
strictName: /^[a-z][a-z0-9-]+$/,
|
|
20
|
+
looseName: /^[a-z][a-zA-Z0-9-_]+$/,
|
|
21
|
+
looserName: /^[a-z][a-zA-Z0-9-._]+$/,
|
|
22
|
+
veryLooseName: /^[a-zA-Z0-9/\-._]*$/,
|
|
23
|
+
}
|
|
24
|
+
|
|
18
25
|
module.exports = {
|
|
19
26
|
asapSrc,
|
|
20
27
|
compiledRuntimes,
|
|
@@ -25,4 +32,5 @@ module.exports = {
|
|
|
25
32
|
mergeEnvVars,
|
|
26
33
|
normalizeSrc: getLambdaDirs.normalizeSrc,
|
|
27
34
|
pragmas,
|
|
35
|
+
validationPatterns,
|
|
28
36
|
}
|
package/src/read/index.js
CHANGED
|
@@ -5,7 +5,6 @@ let projectManifest = {
|
|
|
5
5
|
arc: [ 'app.arc', '.arc' ],
|
|
6
6
|
json: [ 'arc.json' ],
|
|
7
7
|
yaml: [ 'arc.yaml', 'arc.yml' ],
|
|
8
|
-
toml: [ 'arc.toml' ],
|
|
9
8
|
manifest: [ 'package.json' ],
|
|
10
9
|
_default: `@app\napp-default\n@http\n@static`,
|
|
11
10
|
}
|
|
@@ -15,13 +14,12 @@ let functionConfig = {
|
|
|
15
14
|
arc: [ 'config.arc', '.arc-config' ],
|
|
16
15
|
json: [ 'arc.json', 'arc-config.json' ],
|
|
17
16
|
yaml: [ 'config.yaml', 'config.yml', 'arc-config.yaml', 'arc-config.yml' ],
|
|
18
|
-
toml: [ 'config.toml', 'arc-config.toml' ],
|
|
19
17
|
}
|
|
20
18
|
|
|
21
19
|
// Local preferences
|
|
22
20
|
let preferences = {
|
|
23
21
|
arc: [ 'preferences.arc', 'prefs.arc', '.preferences.arc', '.prefs.arc', ],
|
|
24
|
-
// TODO add json, yaml
|
|
22
|
+
// TODO add json, yaml later if folks want it?
|
|
25
23
|
}
|
|
26
24
|
|
|
27
25
|
let reads = { projectManifest, functionConfig, preferences }
|
package/src/read/reader.js
CHANGED
|
@@ -37,7 +37,7 @@ module.exports = function reader (reads, cwd, errors) {
|
|
|
37
37
|
if (raw.trim() === '') return errors.push(`Empty file: ${f}`)
|
|
38
38
|
arc = type === 'arc'
|
|
39
39
|
? parse(raw)
|
|
40
|
-
: parse[type](raw) // Parser has convenient json
|
|
40
|
+
: parse[type](raw) // Parser has convenient json + yaml methods!
|
|
41
41
|
}
|
|
42
42
|
else {
|
|
43
43
|
let pkg = JSON.parse(read(file))
|
package/src/validate/index.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
let config = require('./config')
|
|
2
2
|
let layers = require('./layers')
|
|
3
3
|
let tablesChildren = require('./tables-children')
|
|
4
|
-
let
|
|
4
|
+
let paths = require('./paths')
|
|
5
|
+
let { errorFmt } = require('../lib')
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* Final inventory validation
|
|
@@ -11,26 +12,32 @@ module.exports = function finalValidation (params, inventory) {
|
|
|
11
12
|
|
|
12
13
|
/**
|
|
13
14
|
* Deal with vendor configuration errors
|
|
15
|
+
* - Analyze function configuration
|
|
16
|
+
* - Ensure layer configuration will work, AWS blows up with awful errors on this
|
|
17
|
+
* - TODO add deeper policy validation
|
|
14
18
|
*/
|
|
15
|
-
// Analyze function configuration
|
|
16
19
|
config(params, inventory, errors)
|
|
17
|
-
|
|
18
|
-
// Ensure layer configuration will work, AWS blows up with awful errors on this
|
|
19
20
|
layers(params, inventory, errors)
|
|
20
|
-
|
|
21
|
-
// TODO add deeper policy validation here
|
|
22
|
-
|
|
23
21
|
if (errors.length) {
|
|
24
22
|
return errorFmt({ type: 'configuration', errors })
|
|
25
23
|
}
|
|
26
24
|
|
|
27
25
|
/**
|
|
28
26
|
* Deal with project validation errors
|
|
27
|
+
* - Ensure @tables children (@tables-streams, @tables-indexes) have parent tables present
|
|
29
28
|
*/
|
|
30
|
-
// Ensure @tables children (@tables-streams, @tables-indexes) have parent tables present
|
|
31
29
|
tablesChildren(inventory, errors)
|
|
32
|
-
|
|
33
30
|
if (errors.length) {
|
|
34
31
|
return errorFmt({ type: 'validation', errors })
|
|
35
32
|
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* File path validation
|
|
36
|
+
* - Ensure all file paths are ascii
|
|
37
|
+
*/
|
|
38
|
+
paths(inventory, errors)
|
|
39
|
+
if (errors.length) {
|
|
40
|
+
return errorFmt({ type: 'file path', errors })
|
|
41
|
+
}
|
|
42
|
+
|
|
36
43
|
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
module.exports = function checkFilePaths (inventory, errors) {
|
|
2
|
+
let ascii = /^[ -~]+$/
|
|
3
|
+
let err = str => errors.push(`${str} path must contain only ascii characters`)
|
|
4
|
+
|
|
5
|
+
let { _project: proj } = inventory
|
|
6
|
+
if (!ascii.test(proj.cwd)) return err('Project file')
|
|
7
|
+
if (!ascii.test(proj.src)) return err('Project source')
|
|
8
|
+
if (proj.build && !ascii.test(proj.build)) return err('Build')
|
|
9
|
+
|
|
10
|
+
let lambdas = inventory.lambdasBySrcDir
|
|
11
|
+
if (lambdas){
|
|
12
|
+
Object.values(lambdas).forEach(lambda => {
|
|
13
|
+
let { name, pragma, src } = lambda
|
|
14
|
+
if (!ascii.test(src)) err(`@${pragma} ${name} source`)
|
|
15
|
+
})
|
|
16
|
+
}
|
|
17
|
+
}
|