@adobe/aio-cli-lib-app-config 1.0.1-pre.2023-07-14.sha-628de670 → 1.1.0
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/package.json +8 -12
- package/src/index.js +150 -258
- package/schema/app.config.yaml.schema.json +0 -236
package/package.json
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adobe/aio-cli-lib-app-config",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "node lib to provide a consistent interface to various config files that make up Adobe Developer App Builder applications and extensions",
|
|
5
5
|
"repository": "https://github.com/adobe/aio-cli-lib-app-config/",
|
|
6
6
|
"license": "Apache-2.0",
|
|
7
7
|
"main": "src/index.js",
|
|
8
8
|
"files": [
|
|
9
|
-
"src"
|
|
10
|
-
"schema"
|
|
9
|
+
"src"
|
|
11
10
|
],
|
|
12
11
|
"scripts": {
|
|
13
12
|
"test": "npm run lint && npm run unit-tests",
|
|
@@ -19,10 +18,8 @@
|
|
|
19
18
|
},
|
|
20
19
|
"dependencies": {
|
|
21
20
|
"@adobe/aio-lib-core-config": "^3.0.0",
|
|
22
|
-
"@adobe/aio-lib-core-logging": "
|
|
21
|
+
"@adobe/aio-lib-core-logging": "^2.0.0",
|
|
23
22
|
"@adobe/aio-lib-env": "^2.0.0",
|
|
24
|
-
"ajv": "^8.12.0",
|
|
25
|
-
"ajv-formats": "^2.1.1",
|
|
26
23
|
"fs-extra": "^9.0.1",
|
|
27
24
|
"js-yaml": "^3.14.0",
|
|
28
25
|
"lodash.clonedeep": "^4.5.0"
|
|
@@ -34,9 +31,9 @@
|
|
|
34
31
|
"eslint": "^8",
|
|
35
32
|
"eslint-config-standard": "^17",
|
|
36
33
|
"eslint-plugin-import": "^2.25.3",
|
|
37
|
-
"eslint-plugin-jest": "^
|
|
38
|
-
"eslint-plugin-jsdoc": "^
|
|
39
|
-
"eslint-plugin-n": "^
|
|
34
|
+
"eslint-plugin-jest": "^27",
|
|
35
|
+
"eslint-plugin-jsdoc": "^42",
|
|
36
|
+
"eslint-plugin-n": "^16",
|
|
40
37
|
"eslint-plugin-node": "^11.1.0",
|
|
41
38
|
"eslint-plugin-promise": "^6",
|
|
42
39
|
"eslint-plugin-standard": "^4.0.0",
|
|
@@ -74,6 +71,5 @@
|
|
|
74
71
|
"setupFilesAfterEnv": [
|
|
75
72
|
"./test/jest.setup.js"
|
|
76
73
|
]
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
}
|
|
74
|
+
}
|
|
75
|
+
}
|
package/src/index.js
CHANGED
|
@@ -15,11 +15,6 @@ const yaml = require('js-yaml')
|
|
|
15
15
|
const fs = require('fs-extra')
|
|
16
16
|
const aioConfigLoader = require('@adobe/aio-lib-core-config')
|
|
17
17
|
const aioLogger = require('@adobe/aio-lib-core-logging')('@adobe/aio-cli-lib-app-config', { provider: 'debug' })
|
|
18
|
-
const Ajv = require('ajv')
|
|
19
|
-
const ajvAddFormats = require('ajv-formats')
|
|
20
|
-
|
|
21
|
-
// eslint-disable-next-line node/no-unpublished-require
|
|
22
|
-
const schema = require('../schema/app.config.yaml.schema.json')
|
|
23
18
|
|
|
24
19
|
// give or take daylight savings, and leap seconds ...
|
|
25
20
|
const AboutAWeekInSeconds = '604800'
|
|
@@ -57,19 +52,6 @@ const HookKeys = [
|
|
|
57
52
|
'serve-static'
|
|
58
53
|
]
|
|
59
54
|
|
|
60
|
-
// please add any key that points to a path here
|
|
61
|
-
// if they are defined in an included file, those need to be rewritten to be relative to the root folder
|
|
62
|
-
const PATH_KEYS = [
|
|
63
|
-
/^(application|extensions\.[^.]+)\.web$/,
|
|
64
|
-
/^(application|extensions\.[^.]+)\.web\.src$/,
|
|
65
|
-
/^(application|extensions\.[^.]+)\.actions$/,
|
|
66
|
-
/^(application|extensions\.[^.]+)\.unitTest$/,
|
|
67
|
-
/^(application|extensions\.[^.]+)\.e2eTest$/,
|
|
68
|
-
/^(application|extensions\.[^.]+)\.dist$/,
|
|
69
|
-
/^(application|extensions\.[^.]+)\.runtimeManifest\.packages\.[^.]+\.actions\.[^.]+\.function$/,
|
|
70
|
-
/^(application|extensions\.[^.]+)\.runtimeManifest\.packages\.[^.]+\.actions\.[^.]+\.include\.\d+\.0$/
|
|
71
|
-
]
|
|
72
|
-
|
|
73
55
|
const {
|
|
74
56
|
getCliEnv, /* function */
|
|
75
57
|
STAGE_ENV /* string */
|
|
@@ -77,15 +59,10 @@ const {
|
|
|
77
59
|
const cloneDeep = require('lodash.clonedeep')
|
|
78
60
|
|
|
79
61
|
/**
|
|
80
|
-
* Loads app builder configuration in the current working directory.
|
|
81
|
-
*
|
|
82
62
|
* loading config returns following object (this config is internal, not user facing):
|
|
83
63
|
* {
|
|
84
|
-
* configSchema: { app.config.yaml configSchema field }
|
|
85
64
|
* aio: {...aioConfig...},
|
|
86
65
|
* packagejson: {...package.json...},
|
|
87
|
-
* configSchema: {...app.config.yaml configSchema field as is},
|
|
88
|
-
* productDependencies: {...app.config.yaml productDependencies field as is},
|
|
89
66
|
* all: {
|
|
90
67
|
* OPTIONAL:'application': {
|
|
91
68
|
* app: {
|
|
@@ -124,8 +101,7 @@ const cloneDeep = require('lodash.clonedeep')
|
|
|
124
101
|
* dist,
|
|
125
102
|
* remote,
|
|
126
103
|
* urls
|
|
127
|
-
* }
|
|
128
|
-
* events: {}
|
|
104
|
+
* }
|
|
129
105
|
* }
|
|
130
106
|
* },
|
|
131
107
|
* OPTIONAL:'dx/asset-compute/worker/1': {
|
|
@@ -136,51 +112,30 @@ const cloneDeep = require('lodash.clonedeep')
|
|
|
136
112
|
* },
|
|
137
113
|
* }
|
|
138
114
|
*
|
|
139
|
-
* @param {object} options options to
|
|
115
|
+
* @param {object} options options to loadConfig
|
|
140
116
|
* @param {boolean} options.allowNoImpl do not throw if there is no implementation
|
|
141
|
-
* @param {boolean} options.ignoreAioConfig do not load .aio config via aio-lib-core-config, which is loaded synchronously and blocks the main thread.
|
|
142
117
|
* @returns {object} the config
|
|
143
118
|
*/
|
|
144
|
-
|
|
145
|
-
const allowNoImpl = options.allowNoImpl === undefined ? false : options.allowNoImpl
|
|
146
|
-
const ignoreAioConfig = options.ignoreAioConfig === undefined ? false : options.ignoreAioConfig
|
|
147
|
-
// *NOTE* it would be nice to support an appFolder option to load config from a different folder.
|
|
148
|
-
// However, this requires to update aio-lib-core-config to support loading
|
|
149
|
-
// from a different folder aswell (or enforcing ignore).
|
|
150
|
-
|
|
151
|
-
// I. load common config
|
|
119
|
+
function loadConfig (options = { allowNoImpl: false }) {
|
|
152
120
|
// configuration that is shared for application and each extension config
|
|
153
121
|
// holds things like ow credentials, packagejson and aioConfig
|
|
154
|
-
const commonConfig =
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
//
|
|
158
|
-
//
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
// this will resolve $include directives and output the app config into a single object
|
|
165
|
-
// paths config values in $included files will be rewritten
|
|
166
|
-
const appConfigWithIndex = await coalesce(defaults.USER_CONFIG_FILE, { absolutePaths: true })
|
|
167
|
-
await validate(appConfigWithIndex.config, { throws: true })
|
|
168
|
-
const mergedAppConfig = await mergeLegacyAppConfig(appConfigWithIndex, legacyAppConfigWithIndex)
|
|
169
|
-
|
|
170
|
-
appConfig = mergedAppConfig.config
|
|
171
|
-
includeIndex = mergedAppConfig.includeIndex
|
|
172
|
-
}
|
|
122
|
+
const commonConfig = loadCommonConfig()
|
|
123
|
+
checkCommonConfig(commonConfig)
|
|
124
|
+
|
|
125
|
+
// user configuration is specified in app.config.yaml and holds both standalone app and extension configuration
|
|
126
|
+
// note that `$includes` directive will be resolved here
|
|
127
|
+
// also this will load and merge the standalone legacy configuration system if any
|
|
128
|
+
const { config: userConfig, includeIndex } = loadUserConfig(commonConfig)
|
|
129
|
+
|
|
130
|
+
// load the full standalone application and extension configurations
|
|
131
|
+
const all = buildAllConfigs(userConfig, commonConfig, includeIndex)
|
|
173
132
|
|
|
174
|
-
// III. build output object
|
|
175
|
-
// full standalone application and extension configurations
|
|
176
|
-
const all = await buildAllConfigs(appConfig, commonConfig, includeIndex)
|
|
177
133
|
const impl = Object.keys(all).sort() // sort for predictable configuration
|
|
178
|
-
if (!allowNoImpl && impl.length <= 0) {
|
|
134
|
+
if (!options.allowNoImpl && impl.length <= 0) {
|
|
179
135
|
throw new Error(`Couldn't find configuration in '${process.cwd()}', make sure to add at least one extension or a standalone app`)
|
|
180
136
|
}
|
|
137
|
+
|
|
181
138
|
return {
|
|
182
|
-
configSchema: appConfig?.configSchema || [],
|
|
183
|
-
productDependencies: appConfig?.productDependencies || [],
|
|
184
139
|
all,
|
|
185
140
|
implements: impl, // e.g. 'dx/excshell/1', 'application'
|
|
186
141
|
// includeIndex keeps a map from config keys to files that includes them and the relative key in the file.
|
|
@@ -192,43 +147,13 @@ async function load (options = {}) {
|
|
|
192
147
|
}
|
|
193
148
|
}
|
|
194
149
|
|
|
195
|
-
/**
|
|
196
|
-
* Validates the app configuration.
|
|
197
|
-
* To validate an app.config.yaml file, use `await validate(await coalesce('app.config.yaml'))`
|
|
198
|
-
*
|
|
199
|
-
* @param {object} coalescedAppConfigObj the resolved app config object.
|
|
200
|
-
* @param {object} options options
|
|
201
|
-
* @param {boolean} options.throws defaults to false, if true throws on validation error instead of returning the error
|
|
202
|
-
* @throws if not valid
|
|
203
|
-
*/
|
|
204
|
-
async function validate (coalescedAppConfigObj, options = {}) {
|
|
205
|
-
const throws = options.throws === undefined ? false : options.throws
|
|
206
|
-
/* eslint-disable-next-line node/no-unpublished-require */
|
|
207
|
-
const ajv = new Ajv({
|
|
208
|
-
allErrors: true,
|
|
209
|
-
allowUnionTypes: true
|
|
210
|
-
})
|
|
211
|
-
ajvAddFormats(ajv)
|
|
212
|
-
const validate = ajv.compile(schema)
|
|
213
|
-
|
|
214
|
-
const valid = validate(coalescedAppConfigObj)
|
|
215
|
-
const errors = validate.errors
|
|
216
|
-
if (!valid && throws) {
|
|
217
|
-
throw new Error(`Missing or invalid keys in ${defaults.USER_CONFIG_FILE}: ${JSON.stringify(errors, null, 2)}`)
|
|
218
|
-
}
|
|
219
|
-
return { valid, errors }
|
|
220
|
-
}
|
|
221
|
-
|
|
222
150
|
/** @private */
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
aioConfigLoader.reload()
|
|
228
|
-
aioConfig = aioConfigLoader.get() || {}
|
|
229
|
-
}
|
|
151
|
+
function loadCommonConfig () {
|
|
152
|
+
// load aio config (mostly runtime and console config)
|
|
153
|
+
aioConfigLoader.reload()
|
|
154
|
+
const aioConfig = aioConfigLoader.get() || {}
|
|
230
155
|
|
|
231
|
-
const packagejson =
|
|
156
|
+
const packagejson = fs.readJsonSync('package.json', { throws: true })
|
|
232
157
|
|
|
233
158
|
// defaults
|
|
234
159
|
// remove scoped name to use this for open whisk entities
|
|
@@ -243,7 +168,7 @@ async function loadCommonConfig (/* istanbul ignore next */options = {}) {
|
|
|
243
168
|
owConfig.defaultApihost = defaults.defaultOwApihost
|
|
244
169
|
owConfig.apihost = owConfig.apihost || defaults.defaultOwApihost // set by user
|
|
245
170
|
owConfig.apiversion = owConfig.apiversion || 'v1'
|
|
246
|
-
// default package name
|
|
171
|
+
// default package name replacing __APP_PACKAGE__ placeholder
|
|
247
172
|
owConfig.package = `${packagejson.name}-${packagejson.version}`
|
|
248
173
|
|
|
249
174
|
return {
|
|
@@ -256,57 +181,57 @@ async function loadCommonConfig (/* istanbul ignore next */options = {}) {
|
|
|
256
181
|
}
|
|
257
182
|
|
|
258
183
|
/** @private */
|
|
259
|
-
|
|
260
|
-
//
|
|
261
|
-
//
|
|
262
|
-
//
|
|
263
|
-
//
|
|
264
|
-
|
|
184
|
+
function checkCommonConfig (commonConfig) {
|
|
185
|
+
// todo this depends on the commands, expose a throwOnMissingConsoleInfo ?
|
|
186
|
+
// if (!commonConfig.aio.project || !commonConfig.ow.auth) {
|
|
187
|
+
// throw new Error('Missing project configuration, import a valid Console configuration first via \'aio app use\'')
|
|
188
|
+
// }
|
|
189
|
+
}
|
|
265
190
|
|
|
266
|
-
/**
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
* Returns the appConfig along with an index of config keys to config file. The
|
|
271
|
-
* config file paths in the index are absolute.
|
|
272
|
-
*
|
|
273
|
-
* @param {string} appConfigFile path to the app.config.yaml
|
|
274
|
-
* @param {object} options options
|
|
275
|
-
* @param {object} options.absolutePaths boolean, true for rewriting
|
|
276
|
-
* configuration paths to absolute, false for relative to the appConfigFile
|
|
277
|
-
* directory. Defaults to false. Note, that config values will never be
|
|
278
|
-
* rewritten as relative to the cwd. But also note that
|
|
279
|
-
* this option doesn't have any effect on the includeIndex paths which stay
|
|
280
|
-
* relative to the cwd.
|
|
281
|
-
* @returns {object} { config, includeIndex }
|
|
282
|
-
*/
|
|
283
|
-
async function coalesce (appConfigFile, options = {}) {
|
|
284
|
-
// this code is traversing app.config.yaml recursively to resolve all $includes directives
|
|
191
|
+
/** @private */
|
|
192
|
+
function loadUserConfig (commonConfig) {
|
|
193
|
+
const { config: legacyConfig, includeIndex: legacyIncludeIndex } = loadUserConfigLegacy(commonConfig)
|
|
194
|
+
const { config, includeIndex } = loadUserConfigAppYaml()
|
|
285
195
|
|
|
286
|
-
const
|
|
287
|
-
|
|
196
|
+
const ret = {}
|
|
197
|
+
// include legacy application configuration
|
|
198
|
+
ret.config = mergeLegacyUserConfig(config, legacyConfig)
|
|
199
|
+
// merge includeIndexes, new config index takes precedence
|
|
200
|
+
ret.includeIndex = { ...legacyIncludeIndex, ...includeIndex }
|
|
288
201
|
|
|
289
|
-
|
|
202
|
+
return ret
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/** @private */
|
|
206
|
+
function loadUserConfigAppYaml () {
|
|
207
|
+
if (!fs.existsSync(defaults.USER_CONFIG_FILE)) {
|
|
208
|
+
// no error, support for legacy configuration
|
|
209
|
+
return { config: {}, includeIndex: {} }
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// this code is traversing app.config.yaml recursively to resolve all $includes directives
|
|
213
|
+
|
|
214
|
+
// SETUP
|
|
215
|
+
// the config with $includes to be resolved
|
|
216
|
+
const config = yaml.safeLoad(fs.readFileSync(defaults.USER_CONFIG_FILE, 'utf8'))
|
|
290
217
|
// keep an index that will map keys like 'extensions.abc.runtimeManifest' to the config file where there are defined
|
|
291
218
|
const includeIndex = {}
|
|
292
219
|
// keep a cache for common included files - avoid to read a same file twice
|
|
293
220
|
const configCache = {}
|
|
294
|
-
|
|
295
|
-
// stack entries to be iterated on
|
|
221
|
+
// stack entries to be added for new iterations
|
|
296
222
|
/** @private */
|
|
297
223
|
function buildStackEntries (obj, fullKeyParent, relativeFullKeyParent, includedFiles, filterKeys = null) {
|
|
298
224
|
return Object.keys(obj || {})
|
|
299
225
|
// include filtered keys only
|
|
300
226
|
.filter(key => !filterKeys || filterKeys.includes(key))
|
|
301
|
-
//
|
|
302
|
-
//
|
|
303
|
-
//
|
|
304
|
-
//
|
|
227
|
+
// parentObj will be filled with $includes files
|
|
228
|
+
// includedFiles keep track of already included files, for cycle detection and building the index
|
|
229
|
+
// key, if its $includes will be loaded, if array or object will be recursively followed
|
|
230
|
+
// fullKey keeps track of all parents, used for building the index, relativeFullKey keeps track of the key in the included file
|
|
305
231
|
.map(key => ({ parentObj: obj, includedFiles, key, fullKey: fullKeyParent.concat(`.${key}`), relativeFullKey: relativeFullKeyParent.concat(`.${key}`) }))
|
|
306
232
|
}
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
const traverseStack = buildStackEntries(config, '', '', [appConfigFile])
|
|
233
|
+
// start with top level object
|
|
234
|
+
const traverseStack = buildStackEntries(config, '', '', [defaults.USER_CONFIG_FILE])
|
|
310
235
|
|
|
311
236
|
// ITERATIONS
|
|
312
237
|
// iterate until there are no entries
|
|
@@ -316,8 +241,7 @@ async function coalesce (appConfigFile, options = {}) {
|
|
|
316
241
|
const currConfigFile = includedFiles[includedFiles.length - 1]
|
|
317
242
|
|
|
318
243
|
// add full key to the index, slice(1) to remove initial dot
|
|
319
|
-
|
|
320
|
-
includeIndex[fullIndexKey] = {
|
|
244
|
+
includeIndex[fullKey.slice(1)] = {
|
|
321
245
|
file: currConfigFile,
|
|
322
246
|
key: relativeFullKey.slice(1)
|
|
323
247
|
}
|
|
@@ -325,7 +249,7 @@ async function coalesce (appConfigFile, options = {}) {
|
|
|
325
249
|
const value = parentObj[key]
|
|
326
250
|
|
|
327
251
|
if (typeof value === 'object') {
|
|
328
|
-
// if value is an object or an array, add
|
|
252
|
+
// if value is an object or an array, add entries for to stack
|
|
329
253
|
traverseStack.push(...buildStackEntries(value, fullKey, relativeFullKey, includedFiles))
|
|
330
254
|
continue
|
|
331
255
|
}
|
|
@@ -333,24 +257,24 @@ async function coalesce (appConfigFile, options = {}) {
|
|
|
333
257
|
if (key === defaults.INCLUDE_DIRECTIVE) {
|
|
334
258
|
// $include: 'configFile', value is string pointing to config file
|
|
335
259
|
// includes are relative to the current config file
|
|
336
|
-
|
|
337
260
|
// config path in index always as unix path, it doesn't matter but makes it easier to generate testing mock data
|
|
338
261
|
const incFile = path.join(path.dirname(currConfigFile), value)
|
|
339
262
|
const configFile = incFile.split(path.sep).join(path.posix.sep)
|
|
263
|
+
// const configFile = upath.toUnix(path.join(path.dirname(currConfigFile), value))
|
|
340
264
|
|
|
341
265
|
// 1. check for include cycles
|
|
342
266
|
if (includedFiles.includes(configFile)) {
|
|
343
267
|
throw new Error(`Detected '${defaults.INCLUDE_DIRECTIVE}' cycle: '${[...includedFiles, configFile].toString()}', please make sure that your configuration has no cycles.`)
|
|
344
268
|
}
|
|
345
269
|
// 2. check if file exists
|
|
346
|
-
if (!configCache[configFile] && !
|
|
270
|
+
if (!configCache[configFile] && !fs.existsSync(configFile)) {
|
|
347
271
|
throw new Error(`'${defaults.INCLUDE_DIRECTIVE}: ${configFile}' cannot be resolved, please make sure the file exists.`)
|
|
348
272
|
}
|
|
349
273
|
// 3. delete the $include directive to be replaced
|
|
350
274
|
delete parentObj[key]
|
|
351
275
|
// 4. load the included file
|
|
352
|
-
// Note the included file can in turn also have includes
|
|
353
|
-
const loadedConfig = configCache[configFile] || yaml.safeLoad(
|
|
276
|
+
// Note the included file can in turn also have includes
|
|
277
|
+
const loadedConfig = configCache[configFile] || yaml.safeLoad(fs.readFileSync(configFile, 'utf8'))
|
|
354
278
|
if (Array.isArray(loadedConfig) || typeof loadedConfig !== 'object') {
|
|
355
279
|
throw new Error(`'${defaults.INCLUDE_DIRECTIVE}: ${configFile}' does not resolve to an object. Including an array or primitive type config is not supported.`)
|
|
356
280
|
}
|
|
@@ -368,14 +292,13 @@ async function coalesce (appConfigFile, options = {}) {
|
|
|
368
292
|
// else primitive types: do nothing
|
|
369
293
|
}
|
|
370
294
|
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
return appConfigWithIncludeIndex
|
|
295
|
+
// RETURN
|
|
296
|
+
// $includes are now resolved
|
|
297
|
+
return { config, includeIndex }
|
|
375
298
|
}
|
|
376
299
|
|
|
377
300
|
/** @private */
|
|
378
|
-
|
|
301
|
+
function loadUserConfigLegacy (commonConfig) {
|
|
379
302
|
// load legacy user app config from manifest.yml, package.json, .aio.app
|
|
380
303
|
const includeIndex = {}
|
|
381
304
|
const legacyAppConfig = {}
|
|
@@ -395,8 +318,8 @@ async function legacyToAppConfig (commonConfig) {
|
|
|
395
318
|
}
|
|
396
319
|
|
|
397
320
|
// 2. load legacy manifest.yaml
|
|
398
|
-
if (
|
|
399
|
-
const runtimeManifest = yaml.safeLoad(
|
|
321
|
+
if (fs.existsSync(defaults.LEGACY_RUNTIME_MANIFEST)) {
|
|
322
|
+
const runtimeManifest = yaml.safeLoad(fs.readFileSync(defaults.LEGACY_RUNTIME_MANIFEST, 'utf8'))
|
|
400
323
|
legacyAppConfig.runtimeManifest = runtimeManifest
|
|
401
324
|
// populate index
|
|
402
325
|
const baseKey = `${defaults.APPLICATION_CONFIG_KEY}.runtimeManifest`
|
|
@@ -442,126 +365,83 @@ async function legacyToAppConfig (commonConfig) {
|
|
|
442
365
|
}
|
|
443
366
|
}
|
|
444
367
|
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
// todo return undefined here and for normal config, rewrite load and merge logic
|
|
449
|
-
return appConfigWithIncludeIndex
|
|
368
|
+
if (Object.keys(includeIndex).length > 0) {
|
|
369
|
+
// add the top key
|
|
370
|
+
includeIndex[`${defaults.APPLICATION_CONFIG_KEY}`] = { file: '.aio', key: 'app' }
|
|
450
371
|
}
|
|
451
372
|
|
|
452
|
-
|
|
453
|
-
includeIndex[`${defaults.APPLICATION_CONFIG_KEY}`] = { file: '.aio', key: 'app' }
|
|
454
|
-
|
|
455
|
-
/* always absolute paths for now. Note that if we were interested in relative
|
|
456
|
-
paths there would be no need to rewrite, as all paths are
|
|
457
|
-
defined in the app root folder for legacy apps */
|
|
458
|
-
rewritePathsInPlace(appConfigWithIncludeIndex, { absolutePaths: true })
|
|
459
|
-
|
|
460
|
-
return appConfigWithIncludeIndex
|
|
373
|
+
return { includeIndex, config: { [defaults.APPLICATION_CONFIG_KEY]: legacyAppConfig } }
|
|
461
374
|
}
|
|
462
375
|
|
|
463
376
|
/** @private */
|
|
464
|
-
function
|
|
465
|
-
|
|
377
|
+
function mergeLegacyUserConfig (userConfig, legacyUserConfig) {
|
|
378
|
+
// NOTE: here we do a simplified merge, deep merge with copy might be wanted in future
|
|
466
379
|
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
return { fullKey, includedFromConfigFile, key: k, parentObj: currObj }
|
|
471
|
-
})
|
|
472
|
-
const stack = buildStackEntries(appConfig)
|
|
473
|
-
|
|
474
|
-
while (stack.length > 0) {
|
|
475
|
-
const { fullKey, includedFromConfigFile, key, parentObj } = stack.pop()
|
|
476
|
-
const value = parentObj[key]
|
|
477
|
-
|
|
478
|
-
if (typeof value === 'string' && PATH_KEYS.filter(reg => fullKey.match(reg)).length) {
|
|
479
|
-
// rewrite path value to be relative to the root instead of being relative to the config file that includes it
|
|
480
|
-
parentObj[key] = resolveToRoot(value, includedFromConfigFile, options)
|
|
481
|
-
}
|
|
482
|
-
if (typeof value === 'object') {
|
|
483
|
-
// object or Array
|
|
484
|
-
stack.push(...buildStackEntries(value, fullKey))
|
|
485
|
-
}
|
|
486
|
-
}
|
|
487
|
-
}
|
|
488
|
-
|
|
489
|
-
/** @private */
|
|
490
|
-
async function mergeLegacyAppConfig (appConfigWithIncludeIndex, legacyAppConfigWithIncludeIndex) {
|
|
491
|
-
// NOTE: here we do a simplified merge, deep merge with copy might be wanted
|
|
492
|
-
|
|
493
|
-
// only need to merge application configs as legacy config system does not work with extensions
|
|
494
|
-
const application = appConfigWithIncludeIndex.config[defaults.APPLICATION_CONFIG_KEY]
|
|
495
|
-
const legacyApplication = legacyAppConfigWithIncludeIndex.config[defaults.APPLICATION_CONFIG_KEY]
|
|
380
|
+
// only need to merge application configs as legacy config system only works for standalone apps
|
|
381
|
+
const userConfigApp = userConfig[defaults.APPLICATION_CONFIG_KEY]
|
|
382
|
+
const legacyUserConfigApp = legacyUserConfig[defaults.APPLICATION_CONFIG_KEY]
|
|
496
383
|
|
|
497
384
|
// merge 1 level config fields, such as 'actions': 'path/to/actions', precedence for new config
|
|
498
|
-
const
|
|
385
|
+
const mergedApp = { ...legacyUserConfigApp, ...userConfigApp }
|
|
499
386
|
|
|
500
387
|
// special cases if both are defined
|
|
501
|
-
if (
|
|
388
|
+
if (legacyUserConfigApp && userConfigApp) {
|
|
502
389
|
// for simplicity runtimeManifest is not merged, it's one or the other
|
|
503
|
-
if (
|
|
390
|
+
if (legacyUserConfigApp.runtimeManifest && userConfigApp.runtimeManifest) {
|
|
504
391
|
aioLogger.warn('\'manifest.yml\' is ignored in favor of key \'runtimeManifest\' in \'app.config.yaml\'.')
|
|
505
392
|
}
|
|
506
393
|
// hooks are merged
|
|
507
|
-
if (
|
|
508
|
-
|
|
394
|
+
if (legacyUserConfigApp.hooks && userConfigApp.hooks) {
|
|
395
|
+
mergedApp.hooks = { ...legacyUserConfigApp.hooks, ...userConfigApp.hooks }
|
|
509
396
|
}
|
|
510
397
|
}
|
|
511
398
|
|
|
512
399
|
return {
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
[defaults.APPLICATION_CONFIG_KEY]: mergedApplication
|
|
516
|
-
},
|
|
517
|
-
// new configuration index takes precedence
|
|
518
|
-
includeIndex: { ...legacyAppConfigWithIncludeIndex.includeIndex, ...appConfigWithIncludeIndex.includeIndex }
|
|
400
|
+
...userConfig,
|
|
401
|
+
[defaults.APPLICATION_CONFIG_KEY]: mergedApp
|
|
519
402
|
}
|
|
520
403
|
}
|
|
521
404
|
|
|
522
405
|
/** @private */
|
|
523
|
-
|
|
406
|
+
function buildAllConfigs (userConfig, commonConfig, includeIndex) {
|
|
524
407
|
return {
|
|
525
|
-
...
|
|
526
|
-
...
|
|
408
|
+
...buildAppConfig(userConfig, commonConfig, includeIndex),
|
|
409
|
+
...buildExtConfigs(userConfig, commonConfig, includeIndex)
|
|
527
410
|
}
|
|
528
411
|
}
|
|
529
412
|
|
|
530
413
|
/** @private */
|
|
531
|
-
|
|
414
|
+
function buildExtConfigs (userConfig, commonConfig, includeIndex) {
|
|
532
415
|
const configs = {}
|
|
533
416
|
if (userConfig[defaults.EXTENSIONS_CONFIG_KEY]) {
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
configs[extName] = await buildSingleConfig(extName, singleUserConfig, commonConfig, includeIndex)
|
|
417
|
+
Object.entries(userConfig[defaults.EXTENSIONS_CONFIG_KEY]).forEach(([extName, singleUserConfig]) => {
|
|
418
|
+
configs[extName] = buildSingleConfig(extName, singleUserConfig, commonConfig, includeIndex)
|
|
537
419
|
// extensions have an extra operations field
|
|
538
420
|
configs[extName].operations = singleUserConfig.operations
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
}
|
|
421
|
+
if (!configs[extName].operations) {
|
|
422
|
+
throw new Error(`Missing 'operations' config field for extension point ${extName}`)
|
|
423
|
+
}
|
|
424
|
+
})
|
|
544
425
|
}
|
|
545
426
|
return configs
|
|
546
427
|
}
|
|
547
428
|
|
|
548
429
|
/** @private */
|
|
549
|
-
|
|
550
|
-
const fullAppConfig =
|
|
430
|
+
function buildAppConfig (userConfig, commonConfig, includeIndex) {
|
|
431
|
+
const fullAppConfig = buildSingleConfig(defaults.APPLICATION_CONFIG_KEY,
|
|
551
432
|
userConfig[defaults.APPLICATION_CONFIG_KEY],
|
|
552
433
|
commonConfig,
|
|
553
434
|
includeIndex)
|
|
554
435
|
|
|
555
|
-
// todo: this needs to be updated; an app doesn't exist if there is no config.
|
|
556
436
|
if (!fullAppConfig.app.hasBackend && !fullAppConfig.app.hasFrontend) {
|
|
557
|
-
// only set application config if there is an
|
|
437
|
+
// only set application config if there is an actuall app, meaning either some backend or frontend
|
|
558
438
|
return {}
|
|
559
439
|
}
|
|
560
440
|
return { [defaults.APPLICATION_CONFIG_KEY]: fullAppConfig }
|
|
561
441
|
}
|
|
562
442
|
|
|
563
443
|
/** @private */
|
|
564
|
-
|
|
444
|
+
function buildSingleConfig (configName, singleUserConfig, commonConfig, includeIndex) {
|
|
565
445
|
// used as subfolder folder in dist, converts to a single dir, e.g. dx/excshell/1 =>
|
|
566
446
|
// dx-excshell-1 and dist/dx-excshell-1/actions/action-xyz.zip
|
|
567
447
|
const subFolderName = configName.replace(/\//g, '-')
|
|
@@ -585,32 +465,28 @@ async function buildSingleConfig (configName, singleUserConfig, commonConfig, in
|
|
|
585
465
|
return config
|
|
586
466
|
}
|
|
587
467
|
|
|
588
|
-
|
|
468
|
+
const otherKeyInObject = Object.keys(singleUserConfig)[0]
|
|
469
|
+
// The default action and web path are relative to the folder holding the config file.
|
|
589
470
|
// Let's search the config path that defines a key in the same config object level as 'web' or
|
|
590
471
|
// 'action'
|
|
591
|
-
const
|
|
592
|
-
const
|
|
593
|
-
|
|
594
|
-
const
|
|
595
|
-
const defaultWebPath = resolveToRoot('web-src/', configFilePath)
|
|
596
|
-
const defaultUnitTestPath = resolveToRoot('test/', configFilePath)
|
|
597
|
-
const defaultE2eTestPath = resolveToRoot('e2e/', configFilePath)
|
|
472
|
+
const defaultActionPath = pathConfigValueToAbs('actions/', `${fullKeyPrefix}.${otherKeyInObject}`, includeIndex)
|
|
473
|
+
const defaultWebPath = pathConfigValueToAbs('web-src/', `${fullKeyPrefix}.${otherKeyInObject}`, includeIndex)
|
|
474
|
+
const defaultUnitTestPath = pathConfigValueToAbs('test/', `${fullKeyPrefix}.${otherKeyInObject}`, includeIndex)
|
|
475
|
+
const defaultE2eTestPath = pathConfigValueToAbs('e2e/', `${fullKeyPrefix}.${otherKeyInObject}`, includeIndex)
|
|
598
476
|
const defaultDistPath = 'dist/' // relative to root
|
|
599
477
|
|
|
600
478
|
// absolute paths
|
|
601
|
-
const actions = singleUserConfig.actions || defaultActionPath
|
|
602
|
-
const unitTest = singleUserConfig.unitTest || defaultUnitTestPath
|
|
603
|
-
const e2eTest = singleUserConfig.e2eTest || defaultE2eTestPath
|
|
604
|
-
const dist = singleUserConfig.dist || defaultDistPath
|
|
479
|
+
const actions = pathConfigValueToAbs(singleUserConfig.actions, fullKeyPrefix + '.actions', includeIndex) || defaultActionPath
|
|
480
|
+
const unitTest = pathConfigValueToAbs(singleUserConfig.unitTest, fullKeyPrefix + '.web', includeIndex) || defaultUnitTestPath
|
|
481
|
+
const e2eTest = pathConfigValueToAbs(singleUserConfig.e2eTest, fullKeyPrefix + '.web', includeIndex) || defaultE2eTestPath
|
|
482
|
+
const dist = pathConfigValueToAbs(singleUserConfig.dist, fullKeyPrefix + '.dist', includeIndex) || defaultDistPath
|
|
605
483
|
|
|
606
|
-
// web src folder might be defined in 'web' key or 'web.src' key
|
|
607
484
|
let web
|
|
608
|
-
if (typeof singleUserConfig.web === 'string') {
|
|
609
|
-
web
|
|
610
|
-
|
|
611
|
-
web = singleUserConfig.web.src || defaultWebPath
|
|
485
|
+
if (!singleUserConfig.web || typeof singleUserConfig.web === 'string') {
|
|
486
|
+
// keep backward compatibility - web src is directly defined as string web: web-src
|
|
487
|
+
web = pathConfigValueToAbs(singleUserConfig.web, fullKeyPrefix + '.web', includeIndex) || defaultWebPath
|
|
612
488
|
} else {
|
|
613
|
-
web = defaultWebPath
|
|
489
|
+
web = pathConfigValueToAbs(singleUserConfig.web.src, fullKeyPrefix + '.web', includeIndex) || defaultWebPath
|
|
614
490
|
}
|
|
615
491
|
|
|
616
492
|
config.tests.unit = path.resolve(unitTest)
|
|
@@ -619,7 +495,7 @@ async function buildSingleConfig (configName, singleUserConfig, commonConfig, in
|
|
|
619
495
|
const manifest = singleUserConfig.runtimeManifest
|
|
620
496
|
|
|
621
497
|
config.app.hasBackend = !!manifest
|
|
622
|
-
config.app.hasFrontend =
|
|
498
|
+
config.app.hasFrontend = fs.existsSync(web)
|
|
623
499
|
config.app.dist = path.resolve(dist, dist === defaultDistPath ? subFolderName : '')
|
|
624
500
|
|
|
625
501
|
if (singleUserConfig.events) {
|
|
@@ -633,8 +509,8 @@ async function buildSingleConfig (configName, singleUserConfig, commonConfig, in
|
|
|
633
509
|
config.actions.src = path.resolve(actions) // needed for app add first action
|
|
634
510
|
if (config.app.hasBackend) {
|
|
635
511
|
config.actions.dist = path.join(config.app.dist, 'actions')
|
|
636
|
-
config.manifest = { src: 'manifest.yml' } // even
|
|
637
|
-
config.manifest.full =
|
|
512
|
+
config.manifest = { src: 'manifest.yml' } // even if a legacy config path, it is required for runtime sync
|
|
513
|
+
config.manifest.full = rewriteRuntimeManifestPathsToRelRoot(manifest, fullKeyPrefix + '.runtimeManifest', includeIndex)
|
|
638
514
|
config.manifest.packagePlaceholder = '__APP_PACKAGE__'
|
|
639
515
|
config.manifest.package = config.manifest.full.packages && config.manifest.full.packages[config.manifest.packagePlaceholder]
|
|
640
516
|
if (config.manifest.package) {
|
|
@@ -689,27 +565,43 @@ async function buildSingleConfig (configName, singleUserConfig, commonConfig, in
|
|
|
689
565
|
return config
|
|
690
566
|
}
|
|
691
567
|
|
|
568
|
+
/** @private */
|
|
569
|
+
function rewriteRuntimeManifestPathsToRelRoot (manifestConfig, fullKeyToManifest, includeIndex) {
|
|
570
|
+
const manifestCopy = cloneDeep(manifestConfig)
|
|
571
|
+
|
|
572
|
+
Object.entries(manifestCopy.packages || {}).forEach(([pkgName, pkg]) => {
|
|
573
|
+
Object.entries(pkg.actions || {}).forEach(([actionName, action]) => {
|
|
574
|
+
const fullKeyToAction = `${fullKeyToManifest}.packages.${pkgName}.actions.${actionName}`
|
|
575
|
+
if (action.function) {
|
|
576
|
+
// absolut path
|
|
577
|
+
action.function = pathConfigValueToAbs(action.function, fullKeyToAction + '.function', includeIndex)
|
|
578
|
+
}
|
|
579
|
+
if (action.include) {
|
|
580
|
+
action.include.forEach((arr, i) => {
|
|
581
|
+
// absolut path
|
|
582
|
+
action.include[i][0] = pathConfigValueToAbs(action.include[i][0], fullKeyToAction + `.include.${i}.0`, includeIndex)
|
|
583
|
+
})
|
|
584
|
+
}
|
|
585
|
+
})
|
|
586
|
+
})
|
|
587
|
+
|
|
588
|
+
return manifestCopy
|
|
589
|
+
}
|
|
590
|
+
|
|
692
591
|
// Because of the $include directives, config paths (e.g actions: './path/to/actions') can
|
|
693
592
|
// be relative to config files in any subfolder. Config keys that define path values are
|
|
694
593
|
// identified and their value is rewritten relative to the root folder.
|
|
695
594
|
/** @private */
|
|
696
|
-
function
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
return path.resolve(path.dirname(includedFromConfigPath), pathValue)
|
|
701
|
-
}
|
|
702
|
-
|
|
703
|
-
// relative paths
|
|
704
|
-
if (options.appRoot) {
|
|
705
|
-
// make sure path is relative to appRoot and not cwd
|
|
706
|
-
includedFromConfigPath = path.relative(options.appRoot, includedFromConfigPath)
|
|
595
|
+
function pathConfigValueToAbs (pathValue, fullKeyToPathValue, includeIndex) {
|
|
596
|
+
const configData = includeIndex[fullKeyToPathValue]
|
|
597
|
+
if (!pathValue || !configData) {
|
|
598
|
+
return undefined
|
|
707
599
|
}
|
|
708
|
-
|
|
600
|
+
// if path value is defined and fullKeyToPathValyue is correct then index has an entry
|
|
601
|
+
const configPath = configData.file
|
|
602
|
+
// path.resolve => support both absolut pathValue and relative (relative joins with
|
|
603
|
+
// config dir and process.cwd, absolut returns pathValue)
|
|
604
|
+
return path.resolve(path.dirname(configPath), pathValue)
|
|
709
605
|
}
|
|
710
606
|
|
|
711
|
-
module.exports =
|
|
712
|
-
load,
|
|
713
|
-
validate,
|
|
714
|
-
coalesce
|
|
715
|
-
}
|
|
607
|
+
module.exports = loadConfig
|
|
@@ -1,236 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
-
"$id": "https://adobe.io/schemas/app-builder/app.config.yaml.json/v2",
|
|
4
|
-
"type": "object",
|
|
5
|
-
"properties": {
|
|
6
|
-
"application": { "$ref": "#/definitions/application" },
|
|
7
|
-
"extensions": { "$ref": "#/definitions/extensions" },
|
|
8
|
-
"configSchema": { "$ref": "#/definitions/configSchema"},
|
|
9
|
-
"productDependencies": { "$ref": "#/definitions/productDependencies"}
|
|
10
|
-
},
|
|
11
|
-
"anyOf": [
|
|
12
|
-
{
|
|
13
|
-
"required": ["application"]
|
|
14
|
-
},
|
|
15
|
-
{
|
|
16
|
-
"required": ["extensions"]
|
|
17
|
-
}
|
|
18
|
-
],
|
|
19
|
-
"definitions": {
|
|
20
|
-
"extensions": {
|
|
21
|
-
"type": "object",
|
|
22
|
-
"patternProperties": {
|
|
23
|
-
"^[A-Za-z0-9-_/\\-]+$": {
|
|
24
|
-
"$ref": "#/definitions/application",
|
|
25
|
-
"type": "object",
|
|
26
|
-
"properties": {
|
|
27
|
-
"operations": {
|
|
28
|
-
"type": "object",
|
|
29
|
-
"patternProperties": {
|
|
30
|
-
"^[^\n]+$": {
|
|
31
|
-
"type":"array",
|
|
32
|
-
"items": {
|
|
33
|
-
"type": "object",
|
|
34
|
-
"properties": {
|
|
35
|
-
"type": { "type": "string" },
|
|
36
|
-
"impl": { "type": "string" }
|
|
37
|
-
}
|
|
38
|
-
},
|
|
39
|
-
"minItems": 1
|
|
40
|
-
}
|
|
41
|
-
},
|
|
42
|
-
"minProperties": 1
|
|
43
|
-
}
|
|
44
|
-
},
|
|
45
|
-
"required": ["operations"]
|
|
46
|
-
}
|
|
47
|
-
},
|
|
48
|
-
"additionalProperties": false
|
|
49
|
-
},
|
|
50
|
-
"application": {
|
|
51
|
-
"type": "object",
|
|
52
|
-
"properties": {
|
|
53
|
-
"runtimeManifest": { "$ref": "#/definitions/runtimeManifest" },
|
|
54
|
-
"actions": { "type": "string" },
|
|
55
|
-
"unitTest": { "type": "string" },
|
|
56
|
-
"e2eTest": { "type": "string" },
|
|
57
|
-
"dist": { "type": "string" },
|
|
58
|
-
"tvmurl": { "type": "string" },
|
|
59
|
-
"awsaccesskeyid": { "type": "string" },
|
|
60
|
-
"awssecretaccesskey": { "type": "string" },
|
|
61
|
-
"s3bucket": { "type": "string" },
|
|
62
|
-
"events": { "$ref": "#/definitions/events" },
|
|
63
|
-
"hostname": { "type": "string" },
|
|
64
|
-
"htmlcacheduration": { "type": "number" },
|
|
65
|
-
"jscacheduration": { "type": "number" },
|
|
66
|
-
"csscacheduration": { "type": "number" },
|
|
67
|
-
"imagecacheduration": { "type": "number" },
|
|
68
|
-
"hooks": { "$ref": "#/definitions/hooks" },
|
|
69
|
-
"web": { "$ref": "#/definitions/web" }
|
|
70
|
-
},
|
|
71
|
-
"required": []
|
|
72
|
-
},
|
|
73
|
-
"web": {
|
|
74
|
-
"anyOf": [
|
|
75
|
-
{
|
|
76
|
-
"type": "string"
|
|
77
|
-
},
|
|
78
|
-
{
|
|
79
|
-
"type": "object",
|
|
80
|
-
"properties": {
|
|
81
|
-
"src": { "type": "string" },
|
|
82
|
-
"response-headers": {
|
|
83
|
-
"type": "object",
|
|
84
|
-
"patternProperties": {
|
|
85
|
-
"^[^\n]+$": {
|
|
86
|
-
"type":"object",
|
|
87
|
-
"patternProperties": { "^[^\n]+$": { "type":"string" } }
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
},
|
|
92
|
-
"additionalProperties": false
|
|
93
|
-
}
|
|
94
|
-
]
|
|
95
|
-
},
|
|
96
|
-
"runtimeManifest": {
|
|
97
|
-
"type": "object",
|
|
98
|
-
"properties": {
|
|
99
|
-
"packages": { "$ref": "#/definitions/packages" }
|
|
100
|
-
},
|
|
101
|
-
"required": ["packages"]
|
|
102
|
-
},
|
|
103
|
-
"packages": {
|
|
104
|
-
"type": "object",
|
|
105
|
-
"patternProperties": {
|
|
106
|
-
"^[^\n]+$": {
|
|
107
|
-
"$ref": "#/definitions/package"
|
|
108
|
-
}
|
|
109
|
-
},
|
|
110
|
-
"additionalProperties": false
|
|
111
|
-
},
|
|
112
|
-
"package": {
|
|
113
|
-
"type": "object",
|
|
114
|
-
"properties": {
|
|
115
|
-
"license": { "type": "string" },
|
|
116
|
-
"actions": { "$ref": "#/definitions/actions" }
|
|
117
|
-
}
|
|
118
|
-
},
|
|
119
|
-
"actions": {
|
|
120
|
-
"type": "object",
|
|
121
|
-
"patternProperties": {
|
|
122
|
-
"^[^\n]+$": {
|
|
123
|
-
"$ref": "#/definitions/action"
|
|
124
|
-
}
|
|
125
|
-
},
|
|
126
|
-
"additionalProperties": false
|
|
127
|
-
},
|
|
128
|
-
"action": {
|
|
129
|
-
"type": "object",
|
|
130
|
-
"properties": {
|
|
131
|
-
"function": { "type": "string" },
|
|
132
|
-
"web": { "type": "string" },
|
|
133
|
-
"runtime": { "type": "string" },
|
|
134
|
-
"inputs": { "$ref": "#/definitions/inputs" },
|
|
135
|
-
"annotations": { "$ref": "#/definitions/annotations" }
|
|
136
|
-
},
|
|
137
|
-
"required": []
|
|
138
|
-
},
|
|
139
|
-
"inputs": {
|
|
140
|
-
"type": "object",
|
|
141
|
-
"patternProperties": {
|
|
142
|
-
"^[^\n]+$": {
|
|
143
|
-
"type": ["string", "boolean"]
|
|
144
|
-
}
|
|
145
|
-
},
|
|
146
|
-
"additionalProperties": false
|
|
147
|
-
},
|
|
148
|
-
"annotations": {
|
|
149
|
-
"type": "object",
|
|
150
|
-
"patternProperties": {
|
|
151
|
-
"^[^\n]+$": {
|
|
152
|
-
"type": ["string", "boolean"]
|
|
153
|
-
}
|
|
154
|
-
},
|
|
155
|
-
"additionalProperties": false
|
|
156
|
-
},
|
|
157
|
-
"hooks": {
|
|
158
|
-
"type": "object",
|
|
159
|
-
"properties": {
|
|
160
|
-
"pre-app-build": { "type": "string" },
|
|
161
|
-
"post-app-build": { "type": "string" },
|
|
162
|
-
"build-actions": { "type": "string" },
|
|
163
|
-
"build-static": { "type": "string" },
|
|
164
|
-
"pre-app-deploy": { "type": "string" },
|
|
165
|
-
"post-app-deploy": { "type": "string" },
|
|
166
|
-
"deploy-actions": { "type": "string" },
|
|
167
|
-
"deploy-static": { "type": "string" },
|
|
168
|
-
"pre-app-undeploy": { "type": "string" },
|
|
169
|
-
"post-app-undeploy": { "type": "string" },
|
|
170
|
-
"undeploy-actions": { "type": "string" },
|
|
171
|
-
"undeploy-static": { "type": "string" },
|
|
172
|
-
"pre-app-run": { "type": "string" },
|
|
173
|
-
"post-app-run": { "type": "string" },
|
|
174
|
-
"serve-static": { "type": "string" }
|
|
175
|
-
}
|
|
176
|
-
},
|
|
177
|
-
"events": {
|
|
178
|
-
"type": "object",
|
|
179
|
-
"properties": {
|
|
180
|
-
"registrations": {
|
|
181
|
-
"type": "object",
|
|
182
|
-
"patternProperties": {
|
|
183
|
-
"^[^\n]+$": {
|
|
184
|
-
"type": "object",
|
|
185
|
-
"properties": {
|
|
186
|
-
"description": { "type": "string" },
|
|
187
|
-
"events_of_interest": {
|
|
188
|
-
"type": "array",
|
|
189
|
-
"items": {
|
|
190
|
-
"type": "object",
|
|
191
|
-
"properties": {
|
|
192
|
-
"provider_metadata": { "type": "string" },
|
|
193
|
-
"event_codes": { "type": "array" }
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
},
|
|
197
|
-
"runtime_action": {"type": "string" }
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
},
|
|
204
|
-
"configSchema": {
|
|
205
|
-
"type": "array",
|
|
206
|
-
"maxItems": 50,
|
|
207
|
-
"items": {
|
|
208
|
-
"type": "object",
|
|
209
|
-
"properties": {
|
|
210
|
-
"type": { "type": "string", "enum": ["string", "boolean"] },
|
|
211
|
-
"title": { "type": "string", "maxLength": 200 },
|
|
212
|
-
"envKey": { "type": "string", "pattern": "[a-zA-Z_]{1,}[a-zA-Z0-9_]{0,}", "maxLength": 100 },
|
|
213
|
-
"enum": { "type": "array", "items": { "$ref": "#/definitions/configSchemaValue" }, "minItems": 1, "maxItems": 100 },
|
|
214
|
-
"default": { "$ref": "#/definitions/configSchemaValue" },
|
|
215
|
-
"secret": { "type": "boolean" }
|
|
216
|
-
},
|
|
217
|
-
"required": ["type", "envKey"],
|
|
218
|
-
"additionalProperties": false
|
|
219
|
-
}
|
|
220
|
-
},
|
|
221
|
-
"configSchemaValue": { "type": "string", "maxLength": 1000 },
|
|
222
|
-
"productDependencies": {
|
|
223
|
-
"type": "array",
|
|
224
|
-
"items": {
|
|
225
|
-
"type": "object",
|
|
226
|
-
"properties": {
|
|
227
|
-
"code": { "type": "string" },
|
|
228
|
-
"minVersion": { "type": "string", "pattern": "^[0-9]+.[0-9]+.[0-9]+$" },
|
|
229
|
-
"maxVersion": { "type": "string", "pattern": "^[0-9]+.[0-9]+.[0-9]+$" }
|
|
230
|
-
},
|
|
231
|
-
"required": ["code", "minVersion", "maxVersion"],
|
|
232
|
-
"additionalProperties": false
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
}
|