@sap/cds 6.1.3 → 6.2.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/CHANGELOG.md +77 -8
- package/apis/cds.d.ts +18 -6
- package/apis/connect.d.ts +1 -1
- package/apis/cqn.d.ts +1 -1
- package/apis/log.d.ts +23 -5
- package/apis/ql.d.ts +128 -61
- package/apis/services.d.ts +11 -0
- package/apis/test.d.ts +61 -0
- package/apis/utils.d.ts +15 -0
- package/app/fiori/preview.js +1 -0
- package/bin/build/buildTaskEngine.js +70 -22
- package/bin/build/buildTaskFactory.js +18 -11
- package/bin/build/buildTaskHandler.js +1 -1
- package/bin/build/buildTaskProviderFactory.js +3 -13
- package/bin/build/constants.js +0 -1
- package/bin/build/index.js +14 -6
- package/bin/build/provider/buildTaskHandlerEdmx.js +2 -3
- package/bin/build/provider/buildTaskHandlerFeatureToggles.js +2 -2
- package/bin/build/provider/buildTaskHandlerInternal.js +3 -6
- package/bin/build/provider/buildTaskProviderInternal.js +51 -39
- package/bin/build/provider/fiori/index.js +3 -3
- package/bin/build/provider/hana/2migration.js +1 -1
- package/bin/build/provider/hana/index.js +34 -27
- package/bin/build/provider/java/index.js +6 -7
- package/bin/build/provider/mtx/index.js +20 -18
- package/bin/build/provider/mtx/resourcesTarBuilder.js +8 -11
- package/bin/build/provider/mtx-sidecar/index.js +13 -17
- package/bin/build/provider/nodejs/index.js +8 -7
- package/bin/build/util.js +22 -4
- package/bin/cds.js +8 -4
- package/bin/deploy/to-hana/cfUtil.js +53 -18
- package/bin/mtx/in-cds.js +1 -0
- package/bin/serve.js +37 -30
- package/lib/auth/basic-auth.js +33 -0
- package/lib/auth/dummy-auth.js +7 -0
- package/lib/auth/ias-auth.js +2 -0
- package/lib/auth/index.js +31 -0
- package/lib/auth/jwt-auth.js +3 -0
- package/lib/auth/mocked-users.js +72 -0
- package/lib/auth/passport-basic.js +12 -0
- package/lib/auth/passport-digest.js +14 -0
- package/lib/auth/xsuaa-auth.js +3 -0
- package/lib/compile/cds-compile.js +3 -3
- package/lib/compile/to/cdl.js +5 -1
- package/lib/compile/to/edm.js +8 -0
- package/lib/compile/to/gql.js +1 -0
- package/lib/compile/to/json.js +30 -5
- package/lib/compile/to/sql.js +3 -1
- package/lib/core/index.js +5 -1
- package/lib/dbs/cds-deploy.js +36 -6
- package/lib/env/cds-env.js +15 -5
- package/lib/env/cds-requires.js +51 -58
- package/lib/env/defaults.js +1 -0
- package/lib/env/schemas/cds-package.json +4 -0
- package/lib/env/schemas/cds-rc.json +63 -77
- package/lib/i18n/localize.js +16 -5
- package/lib/index.js +9 -4
- package/lib/log/cds-error.js +4 -6
- package/lib/log/cds-log.js +89 -53
- package/lib/log/service/index.js +1 -0
- package/lib/ql/CREATE.js +2 -5
- package/lib/ql/DELETE.js +1 -1
- package/lib/ql/DROP.js +1 -3
- package/lib/ql/INSERT.js +3 -3
- package/lib/ql/Query.js +10 -23
- package/lib/ql/SELECT.js +1 -2
- package/lib/ql/UPDATE.js +2 -2
- package/lib/ql/Whereable.js +7 -15
- package/lib/ql/cds-ql.js +9 -3
- package/lib/req/cds-context.js +11 -3
- package/lib/req/context.js +29 -23
- package/lib/req/locale.js +9 -5
- package/lib/req/request.js +1 -0
- package/lib/req/user.js +2 -1
- package/lib/srv/cds-connect.js +1 -1
- package/lib/srv/cds-serve.js +21 -14
- package/lib/srv/middlewares/cds-context.js +29 -0
- package/lib/srv/middlewares/ctx-model.js +24 -0
- package/lib/srv/middlewares/errors.js +9 -0
- package/lib/srv/middlewares/index.js +22 -0
- package/lib/srv/middlewares/sap-statistics.js +13 -0
- package/lib/srv/middlewares/trace.js +102 -0
- package/lib/srv/protocols/_legacy.js +42 -0
- package/lib/srv/protocols/graphql.js +39 -0
- package/lib/srv/protocols/hcql.js +37 -0
- package/lib/srv/protocols/index.js +86 -0
- package/lib/srv/protocols/odata-v2-proxy.js +3767 -0
- package/lib/srv/protocols/odata-v2.js +26 -0
- package/lib/srv/protocols/odata-v4.js +16 -0
- package/lib/srv/protocols/rest.js +13 -0
- package/lib/srv/srv-api.js +5 -0
- package/lib/srv/srv-models.js +4 -6
- package/lib/utils/axios.js +3 -2
- package/lib/utils/cds-test.js +27 -21
- package/lib/utils/cds-utils.js +19 -20
- package/lib/utils/tar.js +175 -0
- package/libx/_runtime/audit/generic/personal/utils.js +18 -7
- package/libx/_runtime/audit/utils/v2.js +1 -0
- package/libx/_runtime/auth/index.js +4 -0
- package/libx/_runtime/auth/strategies/ias-auth.js +76 -0
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/error.js +8 -3
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/request.js +15 -4
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/expandToCQN.js +1 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/orderByToCQN.js +1 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/utils.js +1 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/uri/ResourcePathParser.js +9 -0
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/uri/UriInfo.js +5 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/uri/UriParser.js +12 -0
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/utils/BufferedWriter.js +6 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/validator/RequestValidator.js +47 -7
- package/libx/_runtime/cds-services/adapter/odata-v4/to.js +1 -1
- package/libx/_runtime/cds-services/util/assert.js +4 -0
- package/libx/_runtime/common/aspects/relation.js +1 -1
- package/libx/_runtime/common/composition/data.js +61 -15
- package/libx/_runtime/common/composition/delete.js +0 -1
- package/libx/_runtime/common/composition/insert.js +0 -1
- package/libx/_runtime/common/composition/tree.js +4 -10
- package/libx/_runtime/common/composition/update.js +44 -21
- package/libx/_runtime/common/generic/auth/capabilities.js +8 -10
- package/libx/_runtime/common/generic/crud.js +1 -2
- package/libx/_runtime/common/generic/etag.js +4 -4
- package/libx/_runtime/common/generic/input.js +4 -4
- package/libx/_runtime/common/generic/paging.js +3 -3
- package/libx/_runtime/common/generic/put.js +3 -3
- package/libx/_runtime/common/generic/sorting.js +4 -4
- package/libx/_runtime/common/generic/temporal.js +3 -3
- package/libx/_runtime/common/i18n/messages.properties +0 -7
- package/libx/_runtime/common/utils/cqn2cqn4sql.js +11 -6
- package/libx/_runtime/common/utils/csn.js +0 -28
- package/libx/_runtime/common/utils/draft.js +8 -1
- package/libx/_runtime/common/utils/path.js +7 -1
- package/libx/_runtime/common/utils/resolveView.js +2 -3
- package/libx/_runtime/db/data-conversion/post-processing.js +3 -44
- package/libx/_runtime/db/generic/input.js +3 -3
- package/libx/_runtime/db/sql-builder/dataTypes.js +4 -0
- package/libx/_runtime/fiori/generic/activate.js +2 -2
- package/libx/_runtime/fiori/generic/before.js +40 -72
- package/libx/_runtime/fiori/generic/cancel.js +2 -2
- package/libx/_runtime/fiori/generic/delete.js +2 -2
- package/libx/_runtime/fiori/generic/edit.js +2 -2
- package/libx/_runtime/fiori/generic/new.js +2 -2
- package/libx/_runtime/fiori/generic/patch.js +49 -37
- package/libx/_runtime/fiori/generic/prepare.js +2 -2
- package/libx/_runtime/fiori/generic/read.js +27 -37
- package/libx/_runtime/fiori/utils/where.js +4 -2
- package/libx/_runtime/hana/Service.js +1 -3
- package/libx/_runtime/hana/conversion.js +3 -0
- package/libx/_runtime/hana/driver.js +33 -3
- package/libx/_runtime/hana/dynatrace.js +1 -0
- package/libx/_runtime/hana/search2Contains.js +12 -1
- package/libx/_runtime/hana/search2cqn4sql.js +10 -27
- package/libx/_runtime/hana/streaming.js +1 -0
- package/libx/_runtime/messaging/AMQPWebhookMessaging.js +4 -2
- package/libx/_runtime/messaging/common-utils/AMQPClient.js +1 -0
- package/libx/_runtime/messaging/enterprise-messaging-utils/getTenantInfo.js +5 -2
- package/libx/_runtime/messaging/enterprise-messaging-utils/registerEndpoints.js +2 -0
- package/libx/_runtime/messaging/enterprise-messaging.js +62 -3
- package/libx/_runtime/messaging/outbox/utils.js +1 -1
- package/libx/_runtime/messaging/redis-messaging.js +1 -0
- package/libx/_runtime/remote/Service.js +2 -2
- package/libx/_runtime/remote/utils/client.js +8 -3
- package/libx/_runtime/remote/utils/data.js +7 -2
- package/libx/_runtime/sqlite/Service.js +18 -7
- package/libx/_runtime/sqlite/conversion.js +3 -0
- package/libx/_runtime/sqlite/convertAssocToOneManaged.js +3 -3
- package/libx/_runtime/sqlite/localized.js +8 -8
- package/libx/odata/afterburner.js +39 -7
- package/libx/odata/cqn2odata.js +6 -3
- package/libx/odata/grammar.pegjs +66 -18
- package/libx/odata/index.js +3 -2
- package/libx/odata/parser.js +1 -1
- package/libx/odata/utils.js +2 -0
- package/libx/rest/RestAdapter.js +62 -43
- package/libx/rest/middleware/parse.js +2 -1
- package/libx/rest/middleware/update.js +1 -1
- package/package.json +2 -2
- package/server.js +5 -4
- package/srv/mtx.cds +1 -1
- package/srv/mtx.js +4 -33
- package/lib/srv/adapters.js +0 -85
- package/lib/utils/resources/index.js +0 -48
- package/lib/utils/resources/tar.js +0 -49
- package/lib/utils/resources/utils.js +0 -11
- package/libx/_runtime/extensibility/activate.js +0 -69
- package/libx/_runtime/extensibility/add.js +0 -50
- package/libx/_runtime/extensibility/addExtension.js +0 -72
- package/libx/_runtime/extensibility/defaults.js +0 -34
- package/libx/_runtime/extensibility/handler/transformREAD.js +0 -121
- package/libx/_runtime/extensibility/handler/transformRESULT.js +0 -51
- package/libx/_runtime/extensibility/handler/transformWRITE.js +0 -64
- package/libx/_runtime/extensibility/linter/allowlist_checker.js +0 -373
- package/libx/_runtime/extensibility/linter/annotations_checker.js +0 -113
- package/libx/_runtime/extensibility/linter/checker_base.js +0 -20
- package/libx/_runtime/extensibility/linter/namespace_checker.js +0 -180
- package/libx/_runtime/extensibility/linter.js +0 -32
- package/libx/_runtime/extensibility/push.js +0 -118
- package/libx/_runtime/extensibility/service.js +0 -38
- package/libx/_runtime/extensibility/token.js +0 -57
- package/libx/_runtime/extensibility/utils.js +0 -131
- package/libx/_runtime/extensibility/validation.js +0 -50
- package/libx/_runtime/extensibility/views.js +0 -12
- package/srv/extensibility-service.cds +0 -60
- package/srv/extensibility-service.js +0 -1
- package/srv/extensions.cds +0 -8
- package/srv/model-provider.cds +0 -61
- package/srv/model-provider.js +0 -143
package/bin/mtx/in-cds.js
CHANGED
|
@@ -7,6 +7,7 @@ const _is_streamlined_mtx = ()=>{
|
|
|
7
7
|
|
|
8
8
|
if (!cds.requires.multitenancy || _is_streamlined_mtx()) module.exports = undefined
|
|
9
9
|
else try {
|
|
10
|
+
// eslint-disable-next-line cds/no-missing-dependencies
|
|
10
11
|
const mtx = module.exports = require ('@sap/cds-mtx')()
|
|
11
12
|
mtx.inject (cds)
|
|
12
13
|
cds.on('served', () => cds.emit('mtx'))
|
package/bin/serve.js
CHANGED
|
@@ -129,6 +129,7 @@ const cds = require('../lib'), { exists, isfile, local, path } = cds.utils
|
|
|
129
129
|
let log = console.log
|
|
130
130
|
let debug = false
|
|
131
131
|
|
|
132
|
+
|
|
132
133
|
/**
|
|
133
134
|
* The main function which dispatches into the respective usage variants.
|
|
134
135
|
* @param {string[]} all - project folder, model filenames, or service name
|
|
@@ -148,6 +149,9 @@ async function serve (all=[], o={}) { // NOSONAR
|
|
|
148
149
|
// handle --watch and --project
|
|
149
150
|
if (o.watch) return _watch.call(this, o.project,o) // cds serve --watch <project>
|
|
150
151
|
if (o.project) _chdir_to (o.project) // cds run --project <project>
|
|
152
|
+
|
|
153
|
+
// Load local server.js early in order to allow setting custom cds.log.Loggers
|
|
154
|
+
const cds_server = await _local_server_js() || cds.server
|
|
151
155
|
if (!o.silent) _prepare_logging ()
|
|
152
156
|
|
|
153
157
|
// The following things are meant for dev mode, which can be overruled by feature flagse...
|
|
@@ -185,30 +189,15 @@ async function serve (all=[], o={}) { // NOSONAR
|
|
|
185
189
|
}
|
|
186
190
|
|
|
187
191
|
// bootstrap server from project-local server.js or from @sap/cds/server.js
|
|
188
|
-
const cds_server = await _local_server_js() || cds.server
|
|
189
192
|
const server = await cds_server(o)
|
|
190
193
|
|
|
191
194
|
// return a promise which resolves to the created http server when listening
|
|
192
195
|
return cds.server.listening = new Promise ((_resolve,_reject) => {
|
|
193
|
-
|
|
194
|
-
const url = cds.server.url = `http://localhost:${server.address().port}`
|
|
195
|
-
cds.emit ('listening', {server,url}) //> inform local listeners
|
|
196
|
-
_resolve (server)
|
|
197
|
-
}
|
|
196
|
+
|
|
198
197
|
server.listening ? _started(server) : server.once('listening',_started)
|
|
199
198
|
server.on ('error',_reject) // startup errors like EADDRINUSE
|
|
200
199
|
server.on ('close', ()=> shutdown()) // in case of server.close() was called, like in cds.test
|
|
201
200
|
|
|
202
|
-
let shutdownCalled = false
|
|
203
|
-
const shutdown = async sig => {
|
|
204
|
-
if (shutdownCalled) return
|
|
205
|
-
shutdownCalled = true
|
|
206
|
-
global.it || cds.watched || console.log() // blank line makes the ^C look pretty in terminals
|
|
207
|
-
debug && debug(`${sig}, shutting down, calling ${cds.listeners('shutdown').length} listeners`)
|
|
208
|
-
await Promise.all(cds.listeners('shutdown').map((fn) => fn()))
|
|
209
|
-
if (process.env.NODE_ENV !== 'test' && !global.it) process.exit()
|
|
210
|
-
}
|
|
211
|
-
|
|
212
201
|
process.once('SIGTERM', shutdown)
|
|
213
202
|
process.once('SIGINT', shutdown)
|
|
214
203
|
process.once('SIGHUP', shutdown)
|
|
@@ -217,6 +206,21 @@ async function serve (all=[], o={}) { // NOSONAR
|
|
|
217
206
|
process.on('message', (msg) => { if (msg.close||msg.exit) shutdown() }) // by `cds watch` on Windows
|
|
218
207
|
|
|
219
208
|
return server
|
|
209
|
+
|
|
210
|
+
async function _started() {
|
|
211
|
+
_warn_if_cds_was_loaded_from_different_locations()
|
|
212
|
+
const url = cds.server.url = `http://localhost:${server.address().port}`
|
|
213
|
+
cds.emit ('listening', {server,url}) //> inform local listeners
|
|
214
|
+
_resolve (server)
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
async function shutdown (sig) {
|
|
218
|
+
if (shutdown.called) return; else shutdown.called = true // only do that once
|
|
219
|
+
global.it || cds.watched || console.log() // blank line makes the ^C look pretty in terminals
|
|
220
|
+
debug && debug(`${sig}, shutting down, calling ${cds.listeners('shutdown').length} listeners`)
|
|
221
|
+
await Promise.all(cds.listeners('shutdown').map((fn) => fn()))
|
|
222
|
+
if (process.env.NODE_ENV !== 'test' && !global.it) process.exit()
|
|
223
|
+
}
|
|
220
224
|
})
|
|
221
225
|
}
|
|
222
226
|
|
|
@@ -224,7 +228,7 @@ async function _local_server_js() {
|
|
|
224
228
|
const _local = file => isfile(file) || isfile (path.join(cds.env.folders.srv,file))
|
|
225
229
|
let server_js = process.env.CDS_TYPESCRIPT && _local('server.ts') || _local('server.js')
|
|
226
230
|
if (server_js) {
|
|
227
|
-
log ('
|
|
231
|
+
console.log ('[cds] - loading server from', { file: local(server_js) })
|
|
228
232
|
let fn = await cds.utils._import(server_js)
|
|
229
233
|
if (fn && fn.default) fn = fn.default // default ESM export
|
|
230
234
|
return typeof fn === 'function' ? fn : cds.server
|
|
@@ -233,25 +237,21 @@ async function _local_server_js() {
|
|
|
233
237
|
|
|
234
238
|
|
|
235
239
|
function _prepare_logging () { // NOSONAR
|
|
236
|
-
// change `log` function to cds.log
|
|
237
|
-
const LOG = cds.log('serve', { prefix:'cds' })
|
|
238
|
-
log = LOG._info && LOG.info
|
|
239
|
-
if (!log) return log = ()=>{}
|
|
240
|
-
debug = cds.debug('cli')
|
|
241
240
|
|
|
242
|
-
const
|
|
241
|
+
const LOG = cds.log('serve|server',{label:'cds'}); if (!LOG._info) return; else log = LOG.info
|
|
242
|
+
const _timer = `[cds] - launched at ${new Date().toLocaleString()}, version: ${cds.version}, in`
|
|
243
243
|
console.time (_timer)
|
|
244
244
|
|
|
245
245
|
// print information when model is loaded
|
|
246
246
|
cds.on ('loaded', (model)=>{
|
|
247
|
-
|
|
247
|
+
LOG.info (`loaded model from ${model.$sources.length} file(s):\n\x1b[2m`)
|
|
248
248
|
for (let each of model.$sources) console.log (' ', local(each))
|
|
249
249
|
console.log ('\x1b[0m')
|
|
250
250
|
})
|
|
251
251
|
|
|
252
252
|
// print information about each connected service
|
|
253
253
|
cds.on ('connect', ({name,kind,options:{use,credentials,impl}})=>{
|
|
254
|
-
|
|
254
|
+
LOG.info (`connect to ${name} > ${use||kind||impl}`, credentials ? _redacted(credentials) : '')
|
|
255
255
|
})
|
|
256
256
|
|
|
257
257
|
// print information about each provided service
|
|
@@ -260,17 +260,15 @@ function _prepare_logging () { // NOSONAR
|
|
|
260
260
|
if (srv.path) details.path = srv.path
|
|
261
261
|
if (srv._source && !srv._source.startsWith('@sap'))
|
|
262
262
|
details.impl = local(srv._source)
|
|
263
|
-
|
|
263
|
+
LOG.info (`${srv.mocked ? 'mocking' : 'serving'} ${srv.name}`, details)
|
|
264
264
|
})
|
|
265
265
|
|
|
266
|
-
cds.on ('served', ()=> console.log())
|
|
267
|
-
|
|
268
266
|
// print info when we are finally on air
|
|
269
267
|
cds.once ('listening', ({url})=>{
|
|
270
268
|
console.log()
|
|
271
|
-
|
|
269
|
+
LOG.info ('server listening on',{url})
|
|
272
270
|
_timer && console.timeEnd (_timer)
|
|
273
|
-
if (process.stdin.isTTY)
|
|
271
|
+
if (process.stdin.isTTY) LOG.info (`[ terminate with ^C ]\n`)
|
|
274
272
|
})
|
|
275
273
|
}
|
|
276
274
|
|
|
@@ -336,4 +334,13 @@ function _redacted(cred) {
|
|
|
336
334
|
return cred
|
|
337
335
|
}
|
|
338
336
|
|
|
337
|
+
|
|
338
|
+
const _warn_if_cds_was_loaded_from_different_locations = ()=> global.__cds_loaded_from?.size > 1 && console.warn(`
|
|
339
|
+
-----------------------------------------------------------------------
|
|
340
|
+
WARNING: Package '@sap/cds' was loaded from different installations:`,
|
|
341
|
+
[ ...global.__cds_loaded_from ],
|
|
342
|
+
`Rather ensure a single install only to avoid hard-to-resolve errors.
|
|
343
|
+
-----------------------------------------------------------------------
|
|
344
|
+
`)
|
|
345
|
+
|
|
339
346
|
/* eslint no-console:off */
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
module.exports = function basic_auth (options) {
|
|
2
|
+
|
|
3
|
+
const cds = require ('../index'), LOG = cds.log('auth'), { decodeURIComponent } = cds.utils
|
|
4
|
+
const users = require ('./mocked-users') (options)
|
|
5
|
+
const login_required = options.login === 'required' || process.env.NODE_ENV === 'production' && options.credentials || cds.requires.multitenancy
|
|
6
|
+
|
|
7
|
+
/** @type { import('express').Handler } express_handler */
|
|
8
|
+
return async function basic_auth (req, res, next) {
|
|
9
|
+
// allow subsequent code to request a user login
|
|
10
|
+
req.login = login
|
|
11
|
+
// get basic authorization header
|
|
12
|
+
let auth = req.headers.authorization
|
|
13
|
+
// enforce login if requested
|
|
14
|
+
if (!auth) return login_required ? req.login('Logged in user required!') : next()
|
|
15
|
+
// decode user credentials from autorization header
|
|
16
|
+
let [id,pwd] = Buffer.from(auth.slice(6),'base64').toString().split(':')
|
|
17
|
+
// verify user credentials and set req.user
|
|
18
|
+
let u = req.user = await users.verify (id, pwd)
|
|
19
|
+
// re-request login in case of wrong credentials
|
|
20
|
+
if (u.failed) return req.login (u)
|
|
21
|
+
// support for feature toggles via req.headers.features
|
|
22
|
+
if (req.headers.features) u = req.user = { ...u, features: req.headers.features } // NOTE: need to clone u
|
|
23
|
+
// done...
|
|
24
|
+
if (LOG._debug) LOG.debug('authenticated user:', u)
|
|
25
|
+
next()
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function login (reason='') {
|
|
29
|
+
const req=this, res=req.res
|
|
30
|
+
res.set ('WWW-Authenticate', `Basic realm="Users"`) .sendStatus (401)
|
|
31
|
+
LOG.info (req.method, decodeURIComponent(req.path), '>', res.statusCode, res.statusMessage, ...(!reason ? [] : ['-', reason]))
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
|
|
2
|
+
function auth_factory (options) {
|
|
3
|
+
const cds = require ('../index'), { path, local } = cds.utils
|
|
4
|
+
const o = { ...options, ...cds.requires.auth }
|
|
5
|
+
let kind = o.kind || o.strategy
|
|
6
|
+
let middleware = cds.auth[kind]
|
|
7
|
+
if (middleware) {
|
|
8
|
+
cds.log().info ('using auth strategy:', { kind }, '\n')
|
|
9
|
+
} else {
|
|
10
|
+
let impl = o.impl || path.resolve (__dirname, kind)
|
|
11
|
+
try { impl = require.resolve (impl) } catch {
|
|
12
|
+
throw cds.error `Cannot find auth impl: ${impl}`
|
|
13
|
+
}
|
|
14
|
+
cds.log().info ('using auth strategy:', { kind, impl: local(impl) }, '\n')
|
|
15
|
+
middleware = require(impl)
|
|
16
|
+
}
|
|
17
|
+
return middleware(o)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const { lazified } = require('../lazy')
|
|
21
|
+
const _require = require; require = lazified (module) // eslint-disable-line no-global-assign
|
|
22
|
+
|
|
23
|
+
module.exports = lazified (Object.assign (auth_factory, {
|
|
24
|
+
mocked: require('./basic-auth'),
|
|
25
|
+
basic: require('./basic-auth'),
|
|
26
|
+
dummy: require('./dummy-auth'),
|
|
27
|
+
ias: require('./ias-auth'),
|
|
28
|
+
xsuaa: require('./xsuaa-auth'),
|
|
29
|
+
}))
|
|
30
|
+
|
|
31
|
+
require = _require // eslint-disable-line no-global-assign
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
const cds = require ('../index'), { User } = cds
|
|
2
|
+
const LOG = cds.log('auth')
|
|
3
|
+
|
|
4
|
+
class MockedUsers {
|
|
5
|
+
|
|
6
|
+
constructor (options) {
|
|
7
|
+
const tenants = this.tenants = options.tenants || {}
|
|
8
|
+
const users = this.users = options.users || {}
|
|
9
|
+
for (let [k,v] of Object.entries(users)) {
|
|
10
|
+
if (typeof v === 'boolean') continue
|
|
11
|
+
if (typeof v === 'string') v = { password:v }
|
|
12
|
+
let id = _configured(v).id || k
|
|
13
|
+
let u = users[id] = new User ({ id, ...v })
|
|
14
|
+
let fts = tenants[u.tenant]?.features
|
|
15
|
+
if (fts && !u.features) u.features = fts
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Verifies a username / password combination against configured users.
|
|
21
|
+
* @returns { {id:string} | {failed:string} }
|
|
22
|
+
* - `{id,...}` → a user object for successfully authenticated users
|
|
23
|
+
* - `{failed}` → for failed authentication, i.e., invalid credentials
|
|
24
|
+
*/
|
|
25
|
+
verify (id, pwd) {
|
|
26
|
+
let u = this.users[id]
|
|
27
|
+
if (!u) return id && this.users['*'] ? { id } : { failed: `User '${id}' not found` }
|
|
28
|
+
if (u.password && pwd !== u.password) return { failed: `Wrong password for user '${id}'` }
|
|
29
|
+
return u
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const _configured = (u,x) => {
|
|
34
|
+
if ((x = _deprecated (u.ID, 'ID','id'))) {
|
|
35
|
+
u.id = x
|
|
36
|
+
}
|
|
37
|
+
if ((x = _deprecated (u.userAttributes, 'userAttributes','attr'))) {
|
|
38
|
+
u.attr = { ...u.attr, ...x }
|
|
39
|
+
}
|
|
40
|
+
if (u.jwt) {
|
|
41
|
+
if ((x = _deprecated (u.jwt.zid, 'jwt.zid','tenant'))) {
|
|
42
|
+
u.tenant = u.jwt.zid
|
|
43
|
+
}
|
|
44
|
+
if ((x = _deprecated (u.jwt.attributes, 'jwt.attributes','attr'))) {
|
|
45
|
+
u.attr = { ...u.attr, ...x }
|
|
46
|
+
}
|
|
47
|
+
if ((x = _deprecated (u.jwt.userInfo, 'jwt.attributes','attr'))) {
|
|
48
|
+
u.attr = { ...u.attr, ...x }
|
|
49
|
+
}
|
|
50
|
+
if ((x = _deprecated (u.jwt.scope || u.jwt.scopes, 'jwt.scopes','roles'))) {
|
|
51
|
+
const {aud} = u.jwt; if (aud) x = x.map (s => {
|
|
52
|
+
for (const each of aud) s = s.replace(`${each}.`, '')
|
|
53
|
+
return s
|
|
54
|
+
})
|
|
55
|
+
u.roles = [ ...u.roles||[], ...x ]
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return u
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const _deprecated = (v,x,y) => {
|
|
62
|
+
if (!v || x in _deprecated) return v
|
|
63
|
+
else LOG.warn(`WARNING: \n
|
|
64
|
+
Usage of '${x}' in user configurations is deprecated and won't be
|
|
65
|
+
supported in future releases. → Please use property '${y}' instead.
|
|
66
|
+
`)
|
|
67
|
+
return _deprecated[x] = v
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
// allows calling with or without new
|
|
72
|
+
module.exports = function(o) { return new MockedUsers(o) }
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/* eslint-disable cds/no-missing-dependencies */
|
|
2
|
+
module.exports = function passport_basic_auth (options) {
|
|
3
|
+
// const session = require('express-session')({ secret:'secret', resave:false, saveUninitialized:true, })
|
|
4
|
+
const { BasicStrategy } = require('passport-http')
|
|
5
|
+
const users = require ('./mocked-users') (options)
|
|
6
|
+
const passport = require('passport') .use (new BasicStrategy ((id, pwd, done) => {
|
|
7
|
+
let user = users.verify (id,pwd)
|
|
8
|
+
if (user.failed) return done (null, false, { message: user.failed })
|
|
9
|
+
else return done (null, user)
|
|
10
|
+
}))
|
|
11
|
+
return passport.authenticate('basic', {session:false})
|
|
12
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/* eslint-disable cds/no-missing-dependencies */
|
|
2
|
+
module.exports = function passport_digest_auth (options) {
|
|
3
|
+
// const session = require('express-session')({ secret:'secret', resave:false, saveUninitialized:true, })
|
|
4
|
+
const { users } = require ('./mocked-users') (options)
|
|
5
|
+
const { DigestStrategy } = require('passport-http')
|
|
6
|
+
const passport = require('passport') .use (new DigestStrategy ((id, done) => {
|
|
7
|
+
// REVISIT: this is never called -> no clue why
|
|
8
|
+
console.trace (id)
|
|
9
|
+
const u = users[id]
|
|
10
|
+
if (!u) return done (null, false)
|
|
11
|
+
else return done (null, u, u.password)
|
|
12
|
+
}))
|
|
13
|
+
return passport.authenticate('digest', {session:false})
|
|
14
|
+
}
|
|
@@ -51,8 +51,8 @@ function cds_compile (model, options, _flavor) {
|
|
|
51
51
|
if (!model) throw cds.error (`Argument 'model' must be specified`)
|
|
52
52
|
if (_is_csn(model) && _assert_flavor(model,_flavor,options)) return _fluent(model) //> already parsed csn
|
|
53
53
|
const o = _options4 (options,_flavor)
|
|
54
|
-
const files = _is_files (model)
|
|
55
54
|
const cwd = o.cwd || cds.root
|
|
55
|
+
const files = _is_files (model,cwd)
|
|
56
56
|
if (files) {
|
|
57
57
|
if (o.sync) return _fluent (_finalize (cdsc.compileSync(files,cwd,o))) //> compile files synchroneously
|
|
58
58
|
else return _fluent (cdsc.compile(files,cwd,o) .then (_finalize)) //> compile files asynchroneously
|
|
@@ -75,9 +75,9 @@ function cds_compile (model, options, _flavor) {
|
|
|
75
75
|
|
|
76
76
|
|
|
77
77
|
const _is_csn = (x) => (x.definitions || x.extensions) && !x.$builtins
|
|
78
|
-
const _is_files = m => {
|
|
78
|
+
const _is_files = (m,root) => {
|
|
79
79
|
if (Array.isArray(m) || /^file:/.test(m) && (m = m.slice(5)))
|
|
80
|
-
return cds.resolve(m) || cds.error ( `Couldn't find a CDS model for '${m}' in ${cds.root}`,{ code:'MODEL_NOT_FOUND', model: m })
|
|
80
|
+
return cds.resolve(m,{root}) || cds.error ( `Couldn't find a CDS model for '${m}' in ${root||cds.root}`,{ code:'MODEL_NOT_FOUND', model: m })
|
|
81
81
|
}
|
|
82
82
|
const _assert_flavor = (m,_flavor,options) => {
|
|
83
83
|
if (!m.meta) return true; const f = _flavor || _flavor4 (options)
|
package/lib/compile/to/cdl.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const cdsc = require ('../cdsc')
|
|
2
|
+
const keywords = require("@sap/cds-compiler").to.cdl.keywords
|
|
2
3
|
|
|
3
4
|
function cds_compile_to_cdl (csn,o) {
|
|
4
5
|
const results = cdsc.to.cdl (csn,o)
|
|
@@ -15,4 +16,7 @@ function* _many(all) {
|
|
|
15
16
|
}
|
|
16
17
|
|
|
17
18
|
const _cdl = cdl => cdl.replace(/^\/\/ generated.+\n/,'')
|
|
18
|
-
module.exports =
|
|
19
|
+
module.exports = Object.assign(
|
|
20
|
+
cds_compile_to_cdl,
|
|
21
|
+
{ keywords }
|
|
22
|
+
)
|
package/lib/compile/to/edm.js
CHANGED
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
const cdsc = require ('../cdsc')
|
|
2
2
|
const cds = require ('../../index')
|
|
3
3
|
|
|
4
|
+
if (cds.env.features.precompile_edms !== false) {
|
|
5
|
+
const _precompiled = new WeakMap
|
|
6
|
+
cdsc.to.edm = Object.assign ((csn,o)=>{
|
|
7
|
+
if (!_precompiled.has(csn)) _precompiled.set (csn, cdsc.to.edm.all (csn,o))
|
|
8
|
+
return _precompiled.get(csn) [o.service]
|
|
9
|
+
}, { all: cdsc.to.edm.all })
|
|
10
|
+
}
|
|
11
|
+
|
|
4
12
|
function cds_compile_to_edm (csn,_o) {
|
|
5
13
|
const o = cdsc._options.for.edm(_o) //> used twice below...
|
|
6
14
|
csn = _4odata(csn,o)
|
package/lib/compile/to/gql.js
CHANGED
package/lib/compile/to/json.js
CHANGED
|
@@ -4,6 +4,7 @@ const path = require('path')
|
|
|
4
4
|
module.exports = (csn,o={}) => {
|
|
5
5
|
const relative = filename => (o.src !== o.cwd) ? path.relative(o.src, path.join(o.cwd, filename)) : filename
|
|
6
6
|
const relative_cds_home = RegExp ('^' + path.relative (o.src || o.cwd || cds.root, cds.home) + '/')
|
|
7
|
+
|
|
7
8
|
const resolver = (_,v) => {
|
|
8
9
|
|
|
9
10
|
if (!v) return v
|
|
@@ -18,10 +19,15 @@ module.exports = (csn,o={}) => {
|
|
|
18
19
|
else if (v.kind === "service" && !v['@source'] && v.$location?.file) {
|
|
19
20
|
// Preserve original sources for services so we can use them for finding
|
|
20
21
|
// sibling implementation files when reloaded from csn.json.
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
22
|
+
let file = relative(v.$location.file)
|
|
23
|
+
.replace(relative_cds_home,'@sap/cds/')
|
|
24
|
+
.replace('node_modules/','')
|
|
25
|
+
.replace(/\\/g,'/')
|
|
26
|
+
// If there is still a relative path pointing outside of cwd, convert it to a module path
|
|
27
|
+
// e.g. ../bookshop/srv/cat-service.cds -> @capire/bookshop/srv/cat-service.cds
|
|
28
|
+
if (file.startsWith('../')) {
|
|
29
|
+
file = to_module_path(file, o.cwd)
|
|
30
|
+
}
|
|
25
31
|
return { '@source': file, ...v }
|
|
26
32
|
}
|
|
27
33
|
|
|
@@ -29,4 +35,23 @@ module.exports = (csn,o={}) => {
|
|
|
29
35
|
|
|
30
36
|
}
|
|
31
37
|
return JSON.stringify (csn, resolver, o && o.indents || 2)
|
|
32
|
-
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// go upwards, find a package.json and try resolving with this module name
|
|
41
|
+
function to_module_path (file, cwd=cds.root) {
|
|
42
|
+
let dir = path.dirname(file)
|
|
43
|
+
while (dir && dir.length > 1) {
|
|
44
|
+
try {
|
|
45
|
+
const pkg = require(path.join(cwd, dir, 'package.json'))
|
|
46
|
+
const module_path = file.replace(dir, pkg.name)
|
|
47
|
+
require.resolve(module_path, { paths:[cwd] }) // check if result is resovable, note that this assumes NPM install
|
|
48
|
+
return module_path
|
|
49
|
+
} catch (err) {
|
|
50
|
+
if (err.code !== 'MODULE_NOT_FOUND') throw err
|
|
51
|
+
dir = path.dirname(dir)
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return file
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// module.exports.to_module_path = to_module_path
|
package/lib/compile/to/sql.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
const cds = require ('../..')
|
|
2
2
|
const cdsc = require ('../cdsc')
|
|
3
|
-
|
|
3
|
+
const keywords = require("@sap/cds-compiler").to.sql.sqlite.keywords
|
|
4
4
|
|
|
5
5
|
function cds_compile_to_sql (csn,_o) {
|
|
6
6
|
csn = _extended(cds.minify(csn))
|
|
@@ -38,10 +38,12 @@ function* _2many (all,_file=f=>f) {
|
|
|
38
38
|
module.exports = Object.assign (cds_compile_to_sql, {
|
|
39
39
|
hdbcds: cds_compile_to_hdbcds,
|
|
40
40
|
hdbtable: cds_compile_to_hdbtable,
|
|
41
|
+
sqlite: { keywords }
|
|
41
42
|
})
|
|
42
43
|
|
|
43
44
|
|
|
44
45
|
|
|
46
|
+
|
|
45
47
|
/////////////////////////////////////////////////////////////////////////////
|
|
46
48
|
// UI Flex - read extensions__ to views, when ext fields are read
|
|
47
49
|
const _extended = (csn) => {
|
package/lib/core/index.js
CHANGED
|
@@ -52,6 +52,10 @@ const types = _common ({ __proto__: roots,
|
|
|
52
52
|
UUID: {type:'string',length:36,isUUID:true},
|
|
53
53
|
Boolean: {type:'boolean'},
|
|
54
54
|
Integer: {type:'number'},
|
|
55
|
+
UInt8: {type:'Integer'},
|
|
56
|
+
Int16: {type:'Integer'},
|
|
57
|
+
Int32: {type:'Integer'},
|
|
58
|
+
Int64: {type:'Integer'},
|
|
55
59
|
Integer16: {type:'Integer'},
|
|
56
60
|
Integer32: {type:'Integer'},
|
|
57
61
|
Integer64: {type:'Integer'},
|
|
@@ -66,7 +70,7 @@ const types = _common ({ __proto__: roots,
|
|
|
66
70
|
String: {type:'string'},
|
|
67
71
|
Binary: {type:'string'},
|
|
68
72
|
LargeString: {type:'string'},
|
|
69
|
-
LargeBinary: {type:'string'}
|
|
73
|
+
LargeBinary: {type:'string'}
|
|
70
74
|
})
|
|
71
75
|
|
|
72
76
|
/**
|
package/lib/dbs/cds-deploy.js
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
const cds = require('../index'), { local, inspect } = cds.utils
|
|
2
2
|
const DEBUG = cds.debug('deploy')
|
|
3
|
-
|
|
3
|
+
|
|
4
|
+
const colors = !!process.stdout.isTTY && !!process.stderr.isTTY
|
|
5
|
+
const term = {
|
|
6
|
+
x1b2: colors ? '\x1b[2m' : '',
|
|
7
|
+
x1b0: colors ? '\x1b[0m' : '',
|
|
8
|
+
info: colors ? (s => term.x1b2 + s + term.x1b0) : (s => s)
|
|
9
|
+
}
|
|
4
10
|
|
|
5
11
|
/**
|
|
6
12
|
* Implementation of `cds.deploy` common to all databases.
|
|
@@ -18,10 +24,10 @@ exports = module.exports = function cds_deploy (model,options) { return {
|
|
|
18
24
|
if (model && !model.definitions) {
|
|
19
25
|
model = await cds.load (model) .then (cds.minify)
|
|
20
26
|
if (DEBUG) try {
|
|
21
|
-
DEBUG (`model
|
|
27
|
+
DEBUG (`loaded model from ${model.$sources.length} file(s):\n${term.x1b2}`)
|
|
22
28
|
for (let each of model.$sources) console.log (' ', local(each))
|
|
23
29
|
} finally {
|
|
24
|
-
console.log (
|
|
30
|
+
console.log (term.x1b0)
|
|
25
31
|
}
|
|
26
32
|
}
|
|
27
33
|
|
|
@@ -38,11 +44,11 @@ exports = module.exports = function cds_deploy (model,options) { return {
|
|
|
38
44
|
|
|
39
45
|
// fill in initial data...
|
|
40
46
|
await exports.init (db,model, file => LOG(
|
|
41
|
-
|
|
47
|
+
term.info(` > init from ${local(file)}`)
|
|
42
48
|
))
|
|
43
49
|
|
|
44
50
|
// done
|
|
45
|
-
const
|
|
51
|
+
const file = db.getDbUrl(cds.context?.tenant)
|
|
46
52
|
if (file !== ':memory:') LOG (`/> successfully deployed to ./${file}\n`)
|
|
47
53
|
else LOG (`/> successfully deployed to sqlite in-memory db\n`)
|
|
48
54
|
return db
|
|
@@ -116,7 +122,8 @@ exports.create = async function (db, csn=db.model, o) {
|
|
|
116
122
|
await tx.run(creates)
|
|
117
123
|
return true
|
|
118
124
|
})
|
|
119
|
-
}
|
|
125
|
+
}
|
|
126
|
+
else return db.deploy (csn,o)
|
|
120
127
|
}
|
|
121
128
|
|
|
122
129
|
|
|
@@ -133,6 +140,7 @@ exports.init = (db, csn=db.model, log=()=>{}) => db.run (async tx => {
|
|
|
133
140
|
const INSERT_into = _from_csv_or_json [path.extname(file)]
|
|
134
141
|
const src = await read(file,'utf8'); if (!src) continue
|
|
135
142
|
const q = INSERT_into (e,src); if (!q) continue
|
|
143
|
+
if (db.kind === 'better-sqlite') _add_missing_pks2(q)
|
|
136
144
|
log (file,e)
|
|
137
145
|
inits.push (tx.run(q) .catch (e => {
|
|
138
146
|
Error.captureStackTrace(e)
|
|
@@ -141,6 +149,19 @@ exports.init = (db, csn=db.model, log=()=>{}) => db.run (async tx => {
|
|
|
141
149
|
}
|
|
142
150
|
}
|
|
143
151
|
await Promise.all (inits)
|
|
152
|
+
|
|
153
|
+
function _add_missing_pks2 (q) {
|
|
154
|
+
const {columns,rows} = q.INSERT // REVISIT: .entries are covered by current runtime. Should eventually also be handled here, as we likely don't do so in new db services
|
|
155
|
+
if (columns) {
|
|
156
|
+
const entity = csn.definitions[q._target.name], {uuid} = cds.utils
|
|
157
|
+
for (let k in entity.keys) if (!columns.includes(k)) {
|
|
158
|
+
columns.push(k)
|
|
159
|
+
const t = entity.keys[k]._type, pk = t === 'cds.UUID' ? uuid : index => index+1
|
|
160
|
+
rows.forEach ((row,index) => row.push(pk(index)))
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
144
165
|
})
|
|
145
166
|
|
|
146
167
|
|
|
@@ -165,6 +186,13 @@ exports.resources = async function (csn, opts) {
|
|
|
165
186
|
DEBUG && DEBUG (`ignoring '${fx}' in favor of translated ones`); continue
|
|
166
187
|
}
|
|
167
188
|
const e = _entity4(f,csn); if (_skip(e)) continue
|
|
189
|
+
if (cds.env.features.deploy_data_onconflict === 'replace' && !/[._]texts_/.test(f)) {
|
|
190
|
+
const seenBefore = Object.entries(found).find(([_, entity]) => entity === e.name )
|
|
191
|
+
if (seenBefore) {
|
|
192
|
+
DEBUG && DEBUG(`Conflict for '${e.name}': replacing '${local(seenBefore[0])}' with '${local(path.join(subdir,fx))}'`)
|
|
193
|
+
continue
|
|
194
|
+
}
|
|
195
|
+
}
|
|
168
196
|
found[path.join(subdir,fx)] = e.name
|
|
169
197
|
}
|
|
170
198
|
}
|
|
@@ -213,3 +241,5 @@ const INSERT_from_json = (entity, json) => {
|
|
|
213
241
|
|
|
214
242
|
const _from_csv_or_json = { '.json': INSERT_from_json, '.csv': INSERT_from_csv, }
|
|
215
243
|
const _skip = e => !e || e['@cds.persistence.skip'] === true
|
|
244
|
+
|
|
245
|
+
/* eslint-disable no-console */
|
package/lib/env/cds-env.js
CHANGED
|
@@ -106,7 +106,11 @@ class Config {
|
|
|
106
106
|
let sources = [
|
|
107
107
|
{ name: 'USER_HOME', path: user_home, file: '.cdsrc.json' },
|
|
108
108
|
{ name: 'PROJECT', path: home, file: '.cdsrc.json' },
|
|
109
|
-
{ name: 'PACKAGE', path: home, file: 'package.json', mapper: p =>
|
|
109
|
+
{ name: 'PACKAGE', path: home, file: 'package.json', mapper: p => {
|
|
110
|
+
const obj = p[context] || {}
|
|
111
|
+
obj.extends = obj.extends || p.extends // fill cds.extends from .extends
|
|
112
|
+
return obj
|
|
113
|
+
}},
|
|
110
114
|
{ name: 'PRIVATE', path: home, file: '.cdsrc-private.json' }
|
|
111
115
|
]
|
|
112
116
|
|
|
@@ -429,9 +433,11 @@ function _merge (dst, src, _profiles, _cloned, _profiles_only = false) {
|
|
|
429
433
|
}
|
|
430
434
|
|
|
431
435
|
if (_profiles && p[0] === '[') {
|
|
432
|
-
|
|
433
|
-
if (_profiles.
|
|
434
|
-
|
|
436
|
+
const profile = p.slice(1,-1)
|
|
437
|
+
if (_profiles._defined) _profiles._defined.add (profile)
|
|
438
|
+
if (_profiles.has(profile)) {
|
|
439
|
+
profiled.push ({ profile, merge: () => _merge (dst, src[p], _profiles, _cloned, false)})
|
|
440
|
+
}
|
|
435
441
|
continue
|
|
436
442
|
}
|
|
437
443
|
|
|
@@ -445,7 +451,11 @@ function _merge (dst, src, _profiles, _cloned, _profiles_only = false) {
|
|
|
445
451
|
|
|
446
452
|
if (!_profiles_only && v !== undefined) dst[p] = v
|
|
447
453
|
}
|
|
448
|
-
|
|
454
|
+
if (profiled.length > 0 && !_profiles.has('production')) {
|
|
455
|
+
const profiles = Array.from(_profiles)
|
|
456
|
+
profiled.sort((a,b) => profiles.indexOf(b.profile) - profiles.indexOf(a.profile))
|
|
457
|
+
}
|
|
458
|
+
for (let each of profiled) each.merge()
|
|
449
459
|
return dst
|
|
450
460
|
}
|
|
451
461
|
|