@sap/cds 7.2.1 → 7.3.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.
Files changed (58) hide show
  1. package/CHANGELOG.md +161 -126
  2. package/README.md +1 -1
  3. package/apis/core.d.ts +6 -4
  4. package/apis/services.d.ts +24 -4
  5. package/apis/test.d.ts +24 -10
  6. package/bin/serve.js +4 -3
  7. package/lib/auth/ias-auth.js +7 -8
  8. package/lib/compile/cdsc.js +5 -7
  9. package/lib/compile/etc/csv.js +22 -11
  10. package/lib/dbs/cds-deploy.js +1 -2
  11. package/lib/env/cds-env.js +26 -20
  12. package/lib/env/defaults.js +4 -3
  13. package/lib/env/schema.js +9 -0
  14. package/lib/i18n/localize.js +83 -77
  15. package/lib/index.js +6 -2
  16. package/lib/linked/classes.js +13 -13
  17. package/lib/plugins.js +41 -45
  18. package/lib/req/user.js +2 -2
  19. package/lib/srv/protocols/_legacy.js +0 -1
  20. package/lib/srv/protocols/odata-v4.js +4 -0
  21. package/lib/utils/axios.js +7 -1
  22. package/lib/utils/cds-test.js +140 -133
  23. package/lib/utils/cds-utils.js +1 -1
  24. package/lib/utils/check-version.js +6 -0
  25. package/lib/utils/data.js +19 -6
  26. package/libx/_runtime/cds-services/adapter/odata-v4/OData.js +20 -19
  27. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/action.js +10 -1
  28. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/error.js +1 -1
  29. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/request.js +2 -3
  30. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/utils/ValueConverter.js +0 -14
  31. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/core/OdataRequest.js +1 -0
  32. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/deserializer/BatchRequestListBuilder.js +5 -2
  33. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/handler/MetadataHandler.js +1 -1
  34. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/handler/ServiceHandler.js +1 -1
  35. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/invocation/DispatcherCommand.js +2 -2
  36. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/utils/BufferedWriter.js +1 -3
  37. package/libx/_runtime/cds-services/adapter/odata-v4/utils/metaInfo.js +1 -1
  38. package/libx/_runtime/common/composition/update.js +18 -2
  39. package/libx/_runtime/common/error/frontend.js +46 -34
  40. package/libx/_runtime/common/generic/auth/capabilities.js +33 -14
  41. package/libx/_runtime/common/generic/input.js +1 -1
  42. package/libx/_runtime/common/utils/cqn2cqn4sql.js +3 -3
  43. package/libx/_runtime/db/query/update.js +48 -30
  44. package/libx/_runtime/fiori/lean-draft.js +2 -3
  45. package/libx/_runtime/hana/conversion.js +3 -2
  46. package/libx/_runtime/messaging/enterprise-messaging-utils/getTenantInfo.js +1 -1
  47. package/libx/_runtime/messaging/outbox/utils.js +1 -1
  48. package/libx/_runtime/remote/Service.js +1 -17
  49. package/libx/_runtime/remote/utils/client.js +3 -3
  50. package/libx/_runtime/remote/utils/data.js +5 -7
  51. package/libx/odata/{grammar.pegjs → grammar.peggy} +1 -1
  52. package/libx/odata/metadata.js +121 -0
  53. package/libx/odata/parser.js +1 -1
  54. package/libx/odata/service-document.js +61 -0
  55. package/libx/odata/utils.js +102 -48
  56. package/libx/rest/RestAdapter.js +2 -2
  57. package/libx/rest/middleware/error.js +1 -1
  58. package/package.json +1 -1
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  The API package for the [SAP Cloud Application Programming Model (CAP)](https://cap.cloud.sap).
4
4
 
5
- See the [API documentation](https://cap.cloud.sap/docs/node.js/api) for more details.
5
+ See the [API documentation](https://cap.cloud.sap/docs/node.js) for more details.
6
6
 
7
7
  ## How to Obtain Support
8
8
 
package/apis/core.d.ts CHANGED
@@ -3,6 +3,9 @@ import { ReflectedModel, LinkedModel } from './reflect'
3
3
  import { CSN as csn, Definition } from './csn'
4
4
 
5
5
  type UserInput = string | { id: string; attr: Record<string, string>; roles: Record<string, string> } | User
6
+ type Intersect<T extends readonly unknown[]> = T extends [infer Head, ...infer Tail]
7
+ ? Head & Intersect<Tail>
8
+ : unknown
6
9
 
7
10
  export type Association = Definition
8
11
  export type Composition = Association
@@ -26,8 +29,9 @@ export class User {
26
29
  * @deprecated Use https://cap.cloud.sap/docs/node.js/events#tenant instead
27
30
  */
28
31
  tenant: string | undefined
32
+
29
33
  attr: Record<string, string>
30
- roles: Record<string, string>
34
+ roles: Array<string> | Record<string, string>
31
35
  static Privileged: typeof Privileged
32
36
  is(role: string): boolean
33
37
  }
@@ -112,9 +116,7 @@ declare class cds {
112
116
  * }.prototype)
113
117
  */
114
118
  extend<T>(target: T): {
115
- with<X, Y, Z>(x: X, y: Y, z: Z): T & X & Y & Z
116
- with<X, Y>(x: X, y: Y): T & X & Y
117
- with<X>(x: X): T & X
119
+ with<E extends readonly unknown[]>(...ext: E): T & Intersect<E>
118
120
  }
119
121
 
120
122
  /**
@@ -87,8 +87,18 @@ export class QueryAPI {
87
87
  * Starts or joins a transaction
88
88
  * @see [docs](https://cap.cloud.sap/docs/node.js/cds-tx)
89
89
  */
90
- tx(context?: object): Transaction
91
- transaction(context?: object): Transaction
90
+
91
+ tx: {
92
+ (fn: (tx: Transaction) => {}): Promise<unknown>
93
+ (context?: object): Transaction
94
+ (context: object, fn: (tx: Transaction) => {}): Promise<unknown>
95
+ }
96
+
97
+ transaction: {
98
+ (fn: (tx: Transaction) => {}): Promise<unknown>
99
+ (context?: object): Transaction
100
+ (context: object, fn: (tx: Transaction) => {}): Promise<unknown>
101
+ }
92
102
 
93
103
  /**
94
104
  * @see [docs](https://cap.cloud.sap/docs/node.js/cds-tx#cds-spawn)
@@ -221,8 +231,8 @@ export class Service extends QueryAPI {
221
231
  // Provider API
222
232
  prepend(fn: ServiceImpl): Promise<this>
223
233
  on<T extends Constructable>(eve: EventArg, entity: T, handler: CRUDEventHandler.On<InstanceType<T>, InstanceType<T> | void | Error>): this
224
- on<P,R>(boundAction: (args: P) => R, service: string, handler: ActionEventHandler<P, void | Error | R>): this
225
- on<P,R>(action: (args: P) => R, handler: ActionEventHandler<P, void | Error | R>): this
234
+ on<F extends CdsFunction>(boundAction: F, service: string, handler: ActionEventHandler<F['__parameters'], void | Error | F['__returns']>): this
235
+ on<F extends CdsFunction>(unboundAction: F, handler: ActionEventHandler<F['__parameters'], void | Error | F['__returns']>): this
226
236
  on(eve: EventArg, entity: Target, handler: OnEventHandler): this
227
237
  on(eve: EventArg, handler: OnEventHandler): this
228
238
 
@@ -312,6 +322,16 @@ type Partial<T> = { [Key in keyof T]: undefined | T[Key] }
312
322
  // So we always have to expect scalars as well as arrays in some callbacks.
313
323
  type OneOrMany<T> = T | T[];
314
324
 
325
+ // functions generated by cds-typer explicitly carry types for
326
+ // parameters and return type, as their names are not accessible from
327
+ // function signatures to the type system.
328
+ // This meta information is required in .on action handlers.
329
+ type CdsFunction = {
330
+ (...args: any[]): any,
331
+ __parameters: object,
332
+ __returns: unknown
333
+ }
334
+
315
335
  type TypedRequest<T> = Omit<Request, 'data'> & { data: T }
316
336
 
317
337
  // https://cap.cloud.sap/docs/node.js/core-services#srv-on-before-after
package/apis/test.d.ts CHANGED
@@ -24,13 +24,17 @@ declare class Axios {
24
24
  declare class DataUtil {
25
25
  delete(db?: Service): Promise<void>;
26
26
  reset(db?: Service): Promise<void>;
27
- autoReset(enabled: boolean): this;
27
+ /** @deprecated */ autoReset(enabled: boolean): this;
28
28
  }
29
29
 
30
30
  declare class Test extends Axios {
31
+
32
+ test : Test
33
+
31
34
  run(cmd: string, ...args: string[]): this;
32
35
  in(...paths: string[]): this;
33
- verbose(v: boolean): this;
36
+ silent(): this;
37
+ /** @deprecated */ verbose(v: boolean): this;
34
38
 
35
39
  get chai(): typeof chai;
36
40
  get expect(): typeof chai.expect;
@@ -38,6 +42,12 @@ declare class Test extends Axios {
38
42
  get data(): DataUtil;
39
43
  get cds(): typeof import('./cds').default;
40
44
 
45
+ log() : {
46
+ output: string
47
+ clear(): void
48
+ release(): void
49
+ }
50
+
41
51
  then(r: (args: { server: http.Server, url: string }) => void): void;
42
52
 
43
53
  // get sleep(): (ms: number) => Promise<void>;
@@ -56,12 +66,16 @@ declare class Test extends Axios {
56
66
  export = cds
57
67
 
58
68
  declare class cds {
59
- /**
60
- * @see [capire docs](https://cap.cloud.sap/docs/node.js/cds-test?q=cds.test#run)
61
- */
62
- test(projectDir: string): Test;
63
- /**
64
- * @see [capire docs](https://cap.cloud.sap/docs/node.js/cds-test?q=cds.test#run-2)
65
- */
66
- test(command: string, ...args: string[]): Test;
69
+ test: {
70
+ Test: typeof Test
71
+ /**
72
+ * @see [capire docs](https://cap.cloud.sap/docs/node.js/cds-test?q=cds.test#run)
73
+ */
74
+ (projectDir: string): Test;
75
+ /**
76
+ * @see [capire docs](https://cap.cloud.sap/docs/node.js/cds-test?q=cds.test#run-2)
77
+ */
78
+ (command: string, ...args: string[]): Test;
79
+ in (string) : Test
80
+ }
67
81
  }
package/bin/serve.js CHANGED
@@ -128,6 +128,7 @@ module.exports = Object.assign ( serve, {
128
128
 
129
129
 
130
130
  const cds = require('../lib'), { exists, isfile, local, path } = cds.utils
131
+ const COLORS = !!process.stdout.isTTY && !!process.stderr.isTTY && !process.env.NO_COLOR
131
132
 
132
133
  // provisional loggers, see _prepare_logging
133
134
  let log = console.log
@@ -201,7 +202,7 @@ async function serve (all=[], o={}) {
201
202
  _warn_if_cds_was_loaded_from_different_locations()
202
203
  const url = cds.server.url = `http://localhost:${server.address().port}`
203
204
  cds.emit ('listening', {server,url}) //> inform local listeners
204
- _resolve (server)
205
+ _resolve ({ server, url })
205
206
  }
206
207
 
207
208
  const LOG = cds.log('cli|server')
@@ -256,9 +257,9 @@ function _prepare_logging () { // NOSONAR
256
257
 
257
258
  // print information when model is loaded
258
259
  cds.on ('loaded', (model)=>{
259
- LOG.info (`loaded model from ${model.$sources.length} file(s):\n\x1b[2m`)
260
+ LOG.info (`loaded model from ${model.$sources.length} file(s):\n${COLORS ? '\x1b[2m' : ''}`)
260
261
  for (let each of model.$sources) console.log (' ', local(each))
261
- console.log ('\x1b[0m')
262
+ COLORS && console.log ('\x1b[0m')
262
263
  })
263
264
 
264
265
  // print information about each connected service
@@ -41,15 +41,14 @@ module.exports = function ias_auth(config) {
41
41
  .use((req, res, next) => {
42
42
  if (!('authInfo' in req)) return next()
43
43
 
44
- if (req.tokenInfo.getClientId() === req.tokenInfo.getSubject()) //> grant_type === client_credentials or x509
45
- req.user = new cds.User({
46
- id: 'system',
47
- roles: ['authenticated-user', 'system-user'],
48
- attr: {}
49
- })
50
- else {
44
+ const payload = req.tokenInfo.getPayload()
45
+ if (req.tokenInfo.getClientId() === req.tokenInfo.getSubject()) {
46
+ //> grant_type === client_credentials or x509
47
+ const roles = ['authenticated-user', 'system-user']
48
+ if (payload.azp === config.credentials.clientid) roles.push('internal-user')
49
+ req.user = new cds.User({ id: 'system', roles, attr: {} })
50
+ } else {
51
51
  // add all unknown attributes to req.user.attr in order to keep public API small
52
- const payload = req.tokenInfo.getPayload()
53
52
  const attributes = Object.keys(payload)
54
53
  .filter(k => !RESERVED_ATTRIBUTES.has(k))
55
54
  .reduce((attrs, k) => { attrs[k] = payload[k]; return attrs }, {})
@@ -1,8 +1,4 @@
1
1
  const cds = require ('../index')
2
- const {cdsc,odata,sql,features} = cds.env
3
- const constraints = {
4
- assertIntegrity: features.assert_integrity
5
- }
6
2
  const compile = require ('@sap/cds-compiler')
7
3
  const _4cdsc = Symbol('_4cdsc')
8
4
 
@@ -24,7 +20,7 @@ function _options4 (src, ...mappings) {
24
20
  // Optionally add .messages array to avoid compiler writing messages to stderr
25
21
  dst.messages = dst.messages || []
26
22
  // Finally override with options from cds.env.cdsc
27
- return Object.assign(dst,cdsc)
23
+ return Object.assign(dst,cds.env.cdsc)
28
24
  }
29
25
 
30
26
 
@@ -38,6 +34,7 @@ function _options4 (src, ...mappings) {
38
34
  const _options = {for: Object.assign (_options4, {
39
35
 
40
36
  odata(o,_more) {
37
+ const odata = cds.env.odata
41
38
  if (o && o[_4cdsc]) return o
42
39
  let f = o && o.flavor || odata.flavor || o, flavor = odata.flavors && odata.flavors[f] || {}
43
40
  let v = o && o.version || flavor.version || odata.version //> env.odata.flavors.version overrides env.odata.version!
@@ -66,8 +63,9 @@ const _options = {for: Object.assign (_options4, {
66
63
 
67
64
  sql(o,_env) {
68
65
  // REVISIT: compiler requires to only provide assertIntegrityType if defined
69
- if(constraints.assertIntegrity) constraints.assertIntegrityType = features.assert_integrity_type
70
- return _options4 ({ ...constraints, ..._env||sql, ...o }, {
66
+ let assertIntegrity = cds.env.features.assert_integrity
67
+ let constraints = assertIntegrity && { assertIntegrityType: cds.env.features.assert_integrity_type }
68
+ return _options4 ({ ...constraints, ..._env||cds.env.sql, ...o }, {
71
69
  sql_mapping : 'names', //> legacy
72
70
  sqlDialect : 'dialect', //> legacy
73
71
  sqlMapping : 'names',
@@ -12,12 +12,19 @@ function read (res) {
12
12
  function parse (csv) {
13
13
  if (csv[0] === BOM) csv = csv.slice(1)
14
14
  let sep
15
- const lines = csv.split(/\s*\n/)
15
+ const lines = csv.split('\n')
16
16
  const rows = [], headers = []
17
- for (let line of lines) {
17
+
18
+ let val, values=[]
19
+ let inString=false, quoted=false
20
+ for (let l = 0; l < lines.length; l++) {
21
+ const line = lines[l]
18
22
  if (!rows.length && _ignoreLine (line)) continue
19
23
  if (!sep) [sep] = SEPARATOR.exec(line)||[';']
20
- const values=[]; let val, currCol=0, c, inString=false, quoted=false
24
+ if (inString) val += '\n' // overflow from last line
25
+ else val = undefined
26
+
27
+ let currCol=0, c
21
28
  for (let i=0; i<line.length; ) {
22
29
  c = line[i++]
23
30
  if (c === sep && !inString) { // separator
@@ -39,19 +46,23 @@ function parse (csv) {
39
46
  val += c === '\\' ? '\\\\' : c
40
47
  }
41
48
  }
42
- // remaining value
43
- currCol++
44
- if (!rows.length && val !== undefined) headers.push(currCol) // skip column if header value is empty
45
- if ((val !== undefined || c === sep) && headers.includes(currCol)) values.push (_value4(val, quoted))
46
- if (values.length > 0) rows.push (values)
49
+
50
+ // finish line w/ remaining value
51
+ if (!inString || l === lines.length-1 ) { // unless unterminated string and more lines to come
52
+ currCol++
53
+ if (!rows.length && val !== undefined) headers.push(currCol) // skip column if header value is empty
54
+ if ((val !== undefined || c === sep) && headers.includes(currCol)) values.push (_value4(val, quoted))
55
+ if (values.length > 0) { rows.push (values); values = [] }
56
+ }
47
57
  }
48
58
  return rows
49
59
  }
50
60
 
51
- function _value4 (val, quoted = false) { //NOSONAR
61
+ function _value4 (val, quoted = false) {
62
+ if (quoted) return val
52
63
  if (val) val = val.trim()
53
- if (!quoted && val === 'true') return true
54
- if (!quoted && val === 'false') return false
64
+ if (val === 'true') return true
65
+ else if (val === 'false') return false
55
66
  else return val
56
67
  }
57
68
 
@@ -3,7 +3,7 @@ const cds = require('../index'), { local } = cds.utils
3
3
  const COLORS = !!process.stdout.isTTY && !!process.stderr.isTTY && !process.env.NO_COLOR
4
4
  const GREY = COLORS ? '\x1b[2m' : ''
5
5
  const RESET = COLORS ? '\x1b[0m' : ''
6
- let DEBUG // IMPORTANT: initialized later after await cds.plugins
6
+ const DEBUG = cds.debug('deploy')
7
7
 
8
8
  /**
9
9
  * Implementation of `cds.deploy` common to all databases.
@@ -12,7 +12,6 @@ let DEBUG // IMPORTANT: initialized later after await cds.plugins
12
12
  * in initial data, if present.
13
13
  */
14
14
  module.exports = exports = function cds_deploy (model,options,csvs) {
15
- DEBUG = cds.debug('deploy')
16
15
  return {
17
16
  /** @param {import('@sap/cds/lib/srv/srv-api')} db */
18
17
  async to(db, o = options || {}) {
@@ -18,7 +18,9 @@ class Config {
18
18
  */
19
19
  for (context, cwd, _defaults=true) {
20
20
  if (!cwd) cwd = this._home || require('..').root || process.cwd()
21
- return new Config (context, cwd, _defaults)
21
+ let env = new Config (context, cwd, _defaults)
22
+ global.cds?.emit('env',env)
23
+ return env
22
24
  }
23
25
 
24
26
 
@@ -28,7 +30,10 @@ class Config {
28
30
  constructor (_context, _home, _defaults=true) {
29
31
  Object.assign (this, { _context, _home, _sources:[] })
30
32
 
31
- // 0. determine profiles from NODE_ENV + CDS_ENV
33
+ // Capture stack trace to report cds.env usage before cds.test()
34
+ if (global.test) Error.captureStackTrace(this,Config.prototype.for)
35
+
36
+ // Determine profiles from NODE_ENV + CDS_ENV
32
37
  const { NODE_ENV, CDS_ENV } = process.env, profiles = []
33
38
  if (NODE_ENV) profiles.push (NODE_ENV)
34
39
  if (CDS_ENV) profiles.push (...CDS_ENV.split(/\s*,\s*/))
@@ -39,21 +44,17 @@ class Config {
39
44
  this._profiles._defined = new Set
40
45
  this._profiles._important = []
41
46
 
42
- // 1. set compat requires default values
47
+ // Set compat requires default values
43
48
  if (_context === 'cds' && _defaults) this.add (DEFAULTS, defaults)
44
49
  if (_context === 'cds' && _defaults) compat (this)
45
50
  if (!_home) return
46
51
 
47
- // fill-in defaults to process.env, unless already defined => 4.
52
+ // Fill-in process.env from .env files...
48
53
  this._add_to_process_env (_home, 'default-env.json')
54
+ if (this._profiles.has('development')) this._add_to_process_env (_home, '.env')
49
55
 
50
- // additional env for dev => 4.
51
- if (!this._profiles.has('production')) {
52
- this._add_to_process_env (_home, '.env')
53
- }
54
-
55
- // 2. read config sources in defined order
56
- const sources = Config.sources(_home, _context)
56
+ // Read config sources in defined order
57
+ const sources = this.sources(_home, _context)
57
58
  for (let { path:dir, file, mapper = x=>x } of sources) {
58
59
  let src = path.join(dir,file)
59
60
  let json = _readJson(src,{paths:[_home]}); if (!json) continue
@@ -64,17 +65,17 @@ class Config {
64
65
  this.add (mapper(json), src)
65
66
  }
66
67
 
67
- // 3. apply important (!) profiles from config sources
68
+ // Apply important (!) profiles from config sources
68
69
  for (let each of this._profiles._important) each()
69
70
  delete this._profiles._important
70
71
 
71
- // 4. add process env before linking to allow things like CDS_requires_db=sql
72
+ // Add process env before linking to allow things like CDS_requires_db=sql
72
73
  this._add_process_env(_context, _home)
73
74
 
74
- // 5. link dependent services
75
+ // Link cds.requires services to cds.requires.kinds
75
76
  this._link_required_services()
76
77
 
77
- // 6. Add compatibility and correlations for mtx
78
+ // Add compatibility and correlations for mtx
78
79
  const db = this.requires?.db
79
80
  if (this.requires?.db) {
80
81
  if (this.requires.multitenancy !== undefined)
@@ -84,10 +85,10 @@ class Config {
84
85
  }
85
86
  if (this.requires?.multitenancy && this.requires.db?.kind === 'hana' && !this.requires.db.vcap) Object.assign(this.requires.db, { vcap: { label: 'service-manager' } })
86
87
 
87
- // 7. complete service configurations from cloud service bindings
88
+ // Complete service configurations from cloud service bindings
88
89
  this._add_cloud_service_bindings(process.env)
89
90
 
90
- // 8. apply presets
91
+ // Apply presets
91
92
  presets (this)
92
93
 
93
94
 
@@ -99,18 +100,17 @@ class Config {
99
100
 
100
101
  /**
101
102
  * Get configuration sources
102
- *
103
103
  * @param {string} home Project home
104
104
  * @param {string} context configuration context literal
105
105
  */
106
- static sources (home, context = 'cds') {
106
+ sources (home, context = 'cds') {
107
107
  if (!home) throw new Error('Missing parameter "home".')
108
108
  if (context !== 'cds') return [
109
109
  { path: home, file: 'package.json', mapper: x => x[context] },
110
110
  ]
111
111
  const user_home = process.env.CDS_USER_HOME || require('os').homedir()
112
112
  return [
113
- ...( global._plugins||[] ).map (root => ({
113
+ ...Object.keys(this.plugins).map (root => ({
114
114
  path: root, file: 'package.json', mapper: x => x.cds
115
115
  })),
116
116
  { path: user_home, file: '.cdsrc.json', mapper: x => x.cds||x },
@@ -126,6 +126,12 @@ class Config {
126
126
  }
127
127
 
128
128
 
129
+ get plugins() {
130
+ let DEV = this._profiles.has('development')
131
+ return super.plugins = require('../plugins').fetch(DEV)
132
+ }
133
+
134
+
129
135
  add (conf, /*from:*/ _src, profiles = this._profiles) {
130
136
  if (!conf) return this
131
137
  if (_src) this._sources.push (_src)
@@ -1,5 +1,5 @@
1
+ const { join } = require('path')
1
2
  const production = process.env.NODE_ENV === 'production'
2
- const path = require('path')
3
3
 
4
4
  const defaults = module.exports = {
5
5
 
@@ -14,9 +14,10 @@ const defaults = module.exports = {
14
14
  port: 4004,
15
15
  },
16
16
 
17
+ // kept for backwards compatibility
17
18
  schemas: {
18
- 'cds-rc.json': path.join(__dirname, 'schemas/cds-rc.json'),
19
- 'cds-package.json': path.join(__dirname, 'schemas/cds-package.json'),
19
+ 'cds-rc.json': join(__dirname, 'schemas/cds-rc.json'),
20
+ 'cds-package.json': join(__dirname, 'schemas/cds-package.json'),
20
21
  },
21
22
 
22
23
  features: {
@@ -0,0 +1,9 @@
1
+ const cds = require('../index')
2
+ const { join } = cds.utils.path
3
+
4
+
5
+ module.exports = {
6
+ default4: async (name) => {
7
+ return cds.utils.read(join(__dirname, 'schemas', name), 'json')
8
+ }
9
+ }