@cap-js/db-service 1.0.1 → 1.2.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.
@@ -4,7 +4,6 @@ const cds = require('@sap/cds/lib')
4
4
 
5
5
  const JoinTree = require('./join-tree')
6
6
  const { pseudos } = require('./pseudos')
7
- // REVISIT: we should always return cds.linked elements
8
7
  const cdsTypes = cds.linked({
9
8
  definitions: {
10
9
  Timestamp: { type: 'cds.Timestamp' },
@@ -18,11 +17,10 @@ const cdsTypes = cds.linked({
18
17
  },
19
18
  }).definitions
20
19
  for (const each in cdsTypes) cdsTypes[`cds.${each}`] = cdsTypes[each]
21
-
22
20
  /**
23
- * @param {CQN|CQL} originalQuery
24
- * @param {CSN} [model]
25
- * @returns {InferredCQN} = q with .target and .elements
21
+ * @param {import('@sap/cds/apis/cqn').Query|string} originalQuery
22
+ * @param {import('@sap/cds/apis/csn').CSN} [model]
23
+ * @returns {import('./cqn').Query} = q with .target and .elements
26
24
  */
27
25
  function infer(originalQuery, model = cds.context?.model || cds.model) {
28
26
  if (!model) cds.error('Please specify a model')
@@ -40,6 +38,10 @@ function infer(originalQuery, model = cds.context?.model || cds.model) {
40
38
  inferred.CREATE ||
41
39
  inferred.DROP ||
42
40
  inferred.STREAM
41
+
42
+ // cache for already processed calculated elements
43
+ const alreadySeenCalcElements = new Set()
44
+
43
45
  const sources = inferTarget(_.from || _.into || _.entity, {})
44
46
  const joinTree = new JoinTree(sources)
45
47
  const aliases = Object.keys(sources)
@@ -134,11 +136,11 @@ function infer(originalQuery, model = cds.context?.model || cds.model) {
134
136
  * next 'ref' step should be looked up.
135
137
  *
136
138
  *
137
- * @param {Object} arg - The argument object that will be augmented with additional properties.
139
+ * @param {object} arg - The argument object that will be augmented with additional properties.
138
140
  * It must contain a 'ref' property, which is an array representing the steps to be processed.
139
141
  * Optionally, it can also contain an 'xpr' property, which is also processed recursively.
140
142
  *
141
- * @param {Object} $baseLink - Optional parameter. It represents the environment in which the first 'ref' step should be
143
+ * @param {object} $baseLink - Optional parameter. It represents the environment in which the first 'ref' step should be
142
144
  * resolved. It's needed for infix filter / expand columns. It must contain a 'definition'
143
145
  * property, which is an object representing the base environment.
144
146
  *
@@ -177,7 +179,7 @@ function infer(originalQuery, model = cds.context?.model || cds.model) {
177
179
  if (!expandOrExists && nextStep && !(nextStep in e.foreignKeys))
178
180
  throw new Error(`Only foreign keys of "${e.name}" can be accessed in infix filter`)
179
181
  }
180
- arg.$refLinks.push({ definition: e, target: e._target || e })
182
+ arg.$refLinks.push({ definition: e, target: definition })
181
183
  // filter paths are flattened
182
184
  // REVISIT: too much augmentation -> better remove flatName..
183
185
  Object.defineProperty(arg, 'flatName', { value: ref.join('_'), writable: true })
@@ -188,7 +190,7 @@ function infer(originalQuery, model = cds.context?.model || cds.model) {
188
190
  }
189
191
  } else {
190
192
  const recent = arg.$refLinks[i - 1]
191
- const { elements } = recent.target
193
+ const { elements } = recent.definition._target || recent.definition
192
194
  const e = elements[id]
193
195
  if (!e) throw new Error(`"${id}" not found in the elements of "${arg.$refLinks[i - 1].definition.name}"`)
194
196
  arg.$refLinks.push({ definition: e, target: e._target || e })
@@ -216,6 +218,11 @@ function infer(originalQuery, model = cds.context?.model || cds.model) {
216
218
  } else throw new Error('A filter can only be provided when navigating along associations')
217
219
  }
218
220
  })
221
+ const { definition, target } = arg.$refLinks[arg.$refLinks.length - 1]
222
+ if (definition.value) {
223
+ // nested calculated element
224
+ attachRefLinksToArg(definition.value, { definition: definition.parent, target }, true)
225
+ }
219
226
  }
220
227
 
221
228
  /**
@@ -227,7 +234,7 @@ function infer(originalQuery, model = cds.context?.model || cds.model) {
227
234
  * Each entry in the `$combinedElements` dictionary maps from the element name
228
235
  * to an array of objects containing the index and table alias where the element can be found.
229
236
  *
230
- * @returns {Object} The `$combinedElements` dictionary, which maps element names to an array of objects
237
+ * @returns {object} The `$combinedElements` dictionary, which maps element names to an array of objects
231
238
  * containing the index and table alias where the element can be found.
232
239
  */
233
240
  function inferCombinedElements() {
@@ -264,9 +271,9 @@ function infer(originalQuery, model = cds.context?.model || cds.model) {
264
271
  * Also walks over other `ref`s in the query, validates them, and attaches `$refLinks`.
265
272
  * This includes handling `where`, infix filters within column `refs`, or other `csn` paths.
266
273
  *
267
- * @param {Object} $combinedElements The `$combinedElements` dictionary of the query, which maps element names
274
+ * @param {object} $combinedElements The `$combinedElements` dictionary of the query, which maps element names
268
275
  * to an array of objects containing the index and table alias where the element can be found.
269
- * @returns {Object} The inferred `elements` dictionary of the query, which maps element names to their corresponding definitions.
276
+ * @returns {object} The inferred `elements` dictionary of the query, which maps element names to their corresponding definitions.
270
277
  */
271
278
  function inferQueryElements($combinedElements) {
272
279
  let queryElements = {}
@@ -444,7 +451,7 @@ function infer(originalQuery, model = cds.context?.model || cds.model) {
444
451
  */
445
452
 
446
453
  function inferQueryElement(column, insertIntoQueryElements = true, $baseLink = null, context) {
447
- const { inExists, inExpr, inNestedProjection } = context || {}
454
+ const { inExists, inExpr, inNestedProjection, inCalcElement, baseColumn } = context || {}
448
455
  if (column.param) return // parameter references are only resolved into values on execution e.g. :val, :1 or ?
449
456
  if (column.args) column.args.forEach(arg => inferQueryElement(arg, false, $baseLink, context)) // e.g. function in expression
450
457
  if (column.list) column.list.forEach(arg => inferQueryElement(arg, false, $baseLink, context))
@@ -486,11 +493,11 @@ function infer(originalQuery, model = cds.context?.model || cds.model) {
486
493
  const elements = definition.elements || definition._target?.elements
487
494
  if (elements && id in elements) {
488
495
  const element = elements[id]
489
- if (!inExists && !inNestedProjection && element.target) {
496
+ if (!inNestedProjection && !inCalcElement && element.target) {
490
497
  // only fk access in infix filter
491
498
  const nextStep = column.ref[1]?.id || column.ref[1]
492
499
  // no unmanaged assoc in infix filter path
493
- if (element.on)
500
+ if (!inExists && element.on)
494
501
  throw new Error(
495
502
  `"${element.name}" in path "${column.ref
496
503
  .map(idOnly)
@@ -500,7 +507,8 @@ function infer(originalQuery, model = cds.context?.model || cds.model) {
500
507
  if (nextStep && !(nextStep in element.foreignKeys))
501
508
  throw new Error(`Only foreign keys of "${element.name}" can be accessed in infix filter`)
502
509
  }
503
- column.$refLinks.push({ definition: elements[id], target })
510
+ const resolvableIn = definition.target ? definition._target : target
511
+ column.$refLinks.push({ definition: elements[id], target: resolvableIn })
504
512
  } else {
505
513
  stepNotFoundInPredecessor(id, definition.name)
506
514
  }
@@ -535,14 +543,15 @@ function infer(originalQuery, model = cds.context?.model || cds.model) {
535
543
  )
536
544
  }
537
545
 
546
+ const target = definition._target || column.$refLinks[i - 1].target
538
547
  if (element) {
539
- const $refLink = { definition: elements[id], target: column.$refLinks[i - 1].target }
548
+ const $refLink = { definition: elements[id], target }
540
549
  column.$refLinks.push($refLink)
541
550
  } else if (firstStepIsSelf) {
542
551
  stepNotFoundInColumnList(id)
543
552
  } else if (column.ref[0] === '$user' && pseudoPath) {
544
553
  // `$user.some.unknown.element` -> no error
545
- column.$refLinks.push({ definition: {}, target: column.$refLinks[i - 1].target })
554
+ column.$refLinks.push({ definition: {}, target })
546
555
  } else if (id === '$dummy') {
547
556
  // `some.known.element.$dummy` -> no error; used by cds.ql to simulate joins
548
557
  column.$refLinks.push({ definition: { name: '$dummy', parent: column.$refLinks[i - 1].target } })
@@ -568,7 +577,7 @@ function infer(originalQuery, model = cds.context?.model || cds.model) {
568
577
  }
569
578
 
570
579
  if (step.where) {
571
- const danglingFilter = !(column.ref[i + 1] || column.expand || inExists)
580
+ const danglingFilter = !(column.ref[i + 1] || column.expand || column.inline || inExists)
572
581
  if (!column.$refLinks[i].definition.target || danglingFilter)
573
582
  throw new Error(/A filter can only be provided when navigating along associations/)
574
583
  if (!column.expand) Object.defineProperty(column, 'isJoinRelevant', { value: true })
@@ -627,7 +636,8 @@ function infer(originalQuery, model = cds.context?.model || cds.model) {
627
636
  }
628
637
  if (queryElements[elementName] !== undefined)
629
638
  throw new Error(`Duplicate definition of element “${elementName}”`)
630
- queryElements[elementName] = getCopyWithAnnos(column, leafArt)
639
+ const element = getCopyWithAnnos(column, leafArt)
640
+ queryElements[elementName] = element
631
641
  }
632
642
  }
633
643
  }
@@ -644,19 +654,33 @@ function infer(originalQuery, model = cds.context?.model || cds.model) {
644
654
  return
645
655
  }
646
656
  }
647
- const virtual = (column.$refLinks[column.$refLinks.length - 1].definition.virtual || !isPersisted) && !inExpr
657
+ const leafArt = column.$refLinks[column.$refLinks.length - 1].definition
658
+ const virtual = (leafArt.virtual || !isPersisted) && !inExpr
648
659
  // check if we need to merge the column `ref` into the join tree of the query
649
- if (!inExists && !virtual && isColumnJoinRelevant(column, firstStepIsSelf)) {
650
- Object.defineProperty(column, 'isJoinRelevant', { value: true })
651
- joinTree.mergeColumn(column)
660
+ if (!inExists && !virtual && !inCalcElement) {
661
+ // for a ref inside an `inline` we need to consider the column `ref` which has the `inline` prop
662
+ const colWithBase = baseColumn
663
+ ? { ref: [...baseColumn.ref, ...column.ref], $refLinks: [...baseColumn.$refLinks, ...column.$refLinks] }
664
+ : column
665
+ if (isColumnJoinRelevant(colWithBase)) {
666
+ if (originalQuery.UPDATE)
667
+ throw cds.error(
668
+ 'Path expressions for UPDATE statements are not supported. Use “where exists” with infix filters instead.',
669
+ )
670
+ Object.defineProperty(column, 'isJoinRelevant', { value: true })
671
+ joinTree.mergeColumn(colWithBase, originalQuery.outerQueries)
672
+ }
673
+ }
674
+ if (leafArt.value && !leafArt.value.stored) {
675
+ resolveCalculatedElement(column, $baseLink, baseColumn)
652
676
  }
653
677
 
654
678
  /**
655
679
  * Resolves and processes the inline attribute of a column in a database query.
656
680
  *
657
- * @param {Object} col - The column object with properties: `inline` and `$refLinks`.
681
+ * @param {object} col - The column object with properties: `inline` and `$refLinks`.
658
682
  * @param {string} [namePrefix=col.as || col.flatName] - Prefix for naming new columns. Defaults to `col.as` or `col.flatName`.
659
- * @returns {Object} - An object with resolved and processed inline column definitions.
683
+ * @returns {object} - An object with resolved and processed inline column definitions.
660
684
  *
661
685
  * Procedure:
662
686
  * 1. Iterate through `inline` array. For each `inlineCol`:
@@ -671,7 +695,7 @@ function infer(originalQuery, model = cds.context?.model || cds.model) {
671
695
  const $leafLink = $refLinks[$refLinks.length - 1]
672
696
  let elements = {}
673
697
  inline.forEach(inlineCol => {
674
- inferQueryElement(inlineCol, false, $leafLink, { inExpr: true, inNestedProjection: true })
698
+ inferQueryElement(inlineCol, false, $leafLink, { inExpr: true, inNestedProjection: true, baseColumn: col })
675
699
  if (inlineCol === '*') {
676
700
  const wildCardElements = {}
677
701
  // either the `.elements´ of the struct or the `.elements` of the assoc target
@@ -710,8 +734,8 @@ function infer(originalQuery, model = cds.context?.model || cds.model) {
710
734
  /**
711
735
  * Resolves a query column which has an `expand` property.
712
736
  *
713
- * @param {Object} col - The column object with properties: `expand` and `$refLinks`.
714
- * @returns {Object} - A `cds.struct` object with expanded column definitions.
737
+ * @param {object} col - The column object with properties: `expand` and `$refLinks`.
738
+ * @returns {object} - A `cds.struct` object with expanded column definitions.
715
739
  *
716
740
  * Procedure:
717
741
  * - if `$leafLink` is an association, constructs an `expandSubquery` and infers a new query structure.
@@ -777,6 +801,74 @@ function infer(originalQuery, model = cds.context?.model || cds.model) {
777
801
  throw new Error(err)
778
802
  }
779
803
  }
804
+ function resolveCalculatedElement(column, baseLink, baseColumn) {
805
+ const calcElement = column.$refLinks?.[column.$refLinks.length - 1].definition || column
806
+ if (alreadySeenCalcElements.has(calcElement)) return
807
+ else alreadySeenCalcElements.add(calcElement)
808
+ const { ref, xpr, func } = calcElement.value
809
+ if (ref || xpr) {
810
+ baseLink = baseLink || { definition: calcElement.parent, target: calcElement.parent }
811
+ attachRefLinksToArg(calcElement.value, baseLink, true)
812
+ const basePath = { $refLinks: [], ref: [] }
813
+ if (baseColumn) {
814
+ basePath.$refLinks.push(...baseColumn.$refLinks)
815
+ basePath.ref.push(...baseColumn.ref)
816
+ }
817
+ // column is now fully linked, now we need to find out if we need to merge it into the join tree
818
+ // for that, we calculate all paths from a calc element and merge them into the join tree
819
+ mergePathsIntoJoinTree(calcElement.value, basePath)
820
+ }
821
+ if (func)
822
+ calcElement.value.args?.forEach(arg =>
823
+ inferQueryElement(arg, false, { definition: calcElement.parent, target: calcElement.parent }),
824
+ ) // {func}.args are optional
825
+ function mergePathsIntoJoinTree(e, basePath = null) {
826
+ basePath = basePath || { $refLinks: [], ref: [] }
827
+ if (e.ref) {
828
+ e.$refLinks.forEach((link, i) => {
829
+ const { definition } = link
830
+ if (!definition.value) {
831
+ basePath.$refLinks.push(link)
832
+ basePath.ref.push(e.ref[i])
833
+ }
834
+ })
835
+ const leafOfCalculatedElementRef = e.$refLinks[e.$refLinks.length - 1].definition
836
+ if (leafOfCalculatedElementRef.value) mergePathsIntoJoinTree(leafOfCalculatedElementRef.value, basePath)
837
+
838
+ mergePathIfNecessary(basePath, e)
839
+ } else if (e.xpr) {
840
+ e.xpr.forEach(step => {
841
+ if (step.ref) {
842
+ const subPath = { $refLinks: [...basePath.$refLinks], ref: [...basePath.ref] }
843
+ step.$refLinks.forEach((link, i) => {
844
+ const { definition } = link
845
+ if (definition.value) {
846
+ mergePathsIntoJoinTree(definition.value)
847
+ } else {
848
+ subPath.$refLinks.push(link)
849
+ subPath.ref.push(step.ref[i])
850
+ }
851
+ })
852
+ mergePathIfNecessary(subPath, step)
853
+ }
854
+ })
855
+ }
856
+
857
+ function mergePathIfNecessary(p, step) {
858
+ const calcElementIsJoinRelevant = isColumnJoinRelevant(p)
859
+ if (calcElementIsJoinRelevant) {
860
+ if (!calcElement.value.isColumnJoinRelevant)
861
+ Object.defineProperty(step, 'isJoinRelevant', { value: true, writable: true })
862
+ joinTree.mergeColumn(p, originalQuery.outerQueries)
863
+ } else {
864
+ // we need to explicitly set the value to false in this case,
865
+ // e.g. `SELECT from booksCalc.Books { ID, author.{name }, author {name } }`
866
+ // --> for the inline column, the name is join relevant, while for the expand, it is not
867
+ Object.defineProperty(step, 'isJoinRelevant', { value: false, writable: true })
868
+ }
869
+ }
870
+ }
871
+ }
780
872
 
781
873
  /**
782
874
  * Checks whether or not the `ref` of the given column is join relevant.
@@ -828,15 +920,20 @@ function infer(originalQuery, model = cds.context?.model || cds.model) {
828
920
  * if there is not already an element with the same name present.
829
921
  */
830
922
  function inferElementsFromWildCard() {
923
+ const exclude = _.excluding ? x => _.excluding.includes(x) : () => false
924
+
831
925
  if (Object.keys(queryElements).length === 0 && aliases.length === 1) {
832
926
  // only one query source and no overwritten columns
833
927
  Object.entries(sources[aliases[0]].elements).forEach(([name, element]) => {
834
- if (element.type !== 'cds.LargeBinary') queryElements[name] = element
928
+ if (!exclude(name) && element.type !== 'cds.LargeBinary') queryElements[name] = element
929
+ if (element.value) {
930
+ // we might have join relevant calculated elements
931
+ resolveCalculatedElement(element)
932
+ }
835
933
  })
836
934
  return
837
935
  }
838
936
 
839
- const exclude = _.excluding ? x => _.excluding.includes(x) : () => false
840
937
  const ambiguousElements = {}
841
938
  Object.entries($combinedElements).forEach(([name, tableAliases]) => {
842
939
  if (Object.keys(tableAliases).length > 1) {
@@ -1,50 +1,92 @@
1
1
  'use strict'
2
2
 
3
+ // REVISIT: define following unknown types
4
+
5
+ /**
6
+ * @typedef {unknown} $refLink
7
+ */
8
+
9
+ /**
10
+ * @typedef {unknown} parent
11
+ */
12
+
13
+ /**
14
+ * @typedef {unknown} where
15
+ */
16
+
17
+ /**
18
+ * @typedef {unknown} children
19
+ */
20
+
21
+ /**
22
+ * @typedef {unknown} queryArtifact
23
+ */
24
+
25
+ /**
26
+ * @typedef {string} alias
27
+ */
28
+
29
+ /**
30
+ * @typedef {Map<alias,Root>} _roots
31
+ */
32
+
33
+ /**
34
+ * @typedef {Object.<string, unknown>} sources
35
+ */
36
+
3
37
  /**
4
38
  * A class representing a Node in the join tree.
5
- *
6
- * @property {$refLink} - A reference link to this node.
7
- * @property {parent} - The parent Node of this node.
8
- * @property {where} - An optional condition to be applied to this node.
9
- * @property {children} - A Map of children nodes belonging to this node.
10
39
  */
11
40
  class Node {
41
+ /**
42
+ * @param {$refLink} $refLink
43
+ * @param {parent} parent
44
+ * @param {where} where
45
+ */
12
46
  constructor($refLink, parent, where = null) {
47
+ /** @type {$refLink} - A reference link to this node. */
13
48
  this.$refLink = $refLink
49
+ /** @type {parent} - The parent Node of this node. */
14
50
  this.parent = parent
51
+ /** @type {where} - An optional condition to be applied to this node. */
15
52
  this.where = where
53
+ /** @type {children} - A Map of children nodes belonging to this node. */
16
54
  this.children = new Map()
17
55
  }
18
56
  }
19
57
 
20
58
  /**
21
59
  * A class representing the root of the join tree.
22
- *
23
- * @property {queryArtifact} - The artifact used to make the query.
24
- * @property {alias} - The alias of the artifact.
25
- * @property {parent} - The parent Node of this root, null for the root Node.
26
- * @property {children} - A Map of children nodes belonging to this root.
27
60
  */
28
61
  class Root {
62
+ /**
63
+ * @param {[alias, queryArtifact]} querySource
64
+ */
29
65
  constructor(querySource) {
30
66
  const [alias, queryArtifact] = querySource
67
+ /** @type {queryArtifact} - The artifact used to make the query. */
31
68
  this.queryArtifact = queryArtifact
69
+ /** @type {alias} - The alias of the artifact. */
32
70
  this.alias = alias
71
+ /** @type {parent} - The parent Node of this root, null for the root Node. */
33
72
  this.parent = null
73
+ /** @type {children} - A Map of children nodes belonging to this root. */
34
74
  this.children = new Map()
35
75
  }
36
76
  }
37
77
 
38
78
  /**
39
79
  * A class representing a Join Tree.
40
- *
41
- * @property {_roots} - A Map of root nodes.
42
- * @property {isInitial} - A boolean indicating if the join tree is in its initial state.
43
- * @property {_queryAliases} - A Map of query aliases, which is used during the association to join translation.
44
80
  */
45
81
  class JoinTree {
82
+ /**
83
+ *
84
+ * @param {sources} sources
85
+ */
46
86
  constructor(sources) {
87
+ /** @type {_roots} - A Map of root nodes. */
47
88
  this._roots = new Map()
89
+ /** @type {boolean} - A boolean indicating if the join tree is in its initial state. */
48
90
  this.isInitial = true
49
91
  /**
50
92
  * A map that holds query aliases which are used during the
@@ -53,6 +95,7 @@ class JoinTree {
53
95
  *
54
96
  * The table aliases are treated case insensitive. The index of each
55
97
  * table alias entry, is the capitalized version of the alias.
98
+ * @type {Map<string, string>}
56
99
  */
57
100
  this._queryAliases = new Map()
58
101
  Object.entries(sources).forEach(entry => {
@@ -82,6 +125,7 @@ class JoinTree {
82
125
  * Calculates and adds the next available table alias to the alias map.
83
126
  *
84
127
  * @param {string} alias - The original alias name.
128
+ * @param {unknown[]} outerQueries - An array of outer queries.
85
129
  * @returns {string} - The next unambiguous table alias.
86
130
  */
87
131
  addNextAvailableTableAlias(alias, outerQueries) {
@@ -107,10 +151,10 @@ class JoinTree {
107
151
  * For each step, it checks whether it has been seen before. If so, it resets the $refLink to point to the already merged $refLink.
108
152
  * If not, it creates a new Node and ensures proper aliasing and foreign key access.
109
153
  *
110
- * @param {Object} col - The column object to be merged into the existing join tree. This object should have the properties $refLinks and ref.
154
+ * @param {object} col - The column object to be merged into the existing join tree. This object should have the properties $refLinks and ref.
111
155
  * @returns {boolean} - Always returns true, indicating the column has been successfully merged into the join tree.
112
156
  */
113
- mergeColumn(col) {
157
+ mergeColumn(col, outerQueries = null) {
114
158
  if (this.isInitial) this.isInitial = false
115
159
  const head = col.$refLinks[0]
116
160
  let node = this._roots.get(head.alias)
@@ -145,13 +189,14 @@ class JoinTree {
145
189
  }
146
190
  const child = new Node($refLink, node, where)
147
191
  if (child.$refLink.definition.isAssociation) {
148
- if (child.where) {
149
- // always join relevant
192
+ if (child.where || col.inline) {
193
+ // filter is always join relevant
194
+ // if the column ends up in an `inline` -> each assoc step is join relevant
150
195
  child.$refLink.onlyForeignKeyAccess = false
151
196
  } else {
152
197
  child.$refLink.onlyForeignKeyAccess = true
153
198
  }
154
- child.$refLink.alias = this.addNextAvailableTableAlias($refLink.alias)
199
+ child.$refLink.alias = this.addNextAvailableTableAlias($refLink.alias, outerQueries)
155
200
  }
156
201
 
157
202
  const foreignKeys = node.$refLink?.definition.foreignKeys
package/package.json CHANGED
@@ -1,14 +1,22 @@
1
1
  {
2
2
  "name": "@cap-js/db-service",
3
- "version": "1.0.1",
3
+ "version": "1.2.0",
4
4
  "description": "CDS base database service",
5
- "homepage": "https://cap.cloud.sap/",
5
+ "homepage": "https://github.com/cap-js/cds-dbs/tree/main/db-service#cds-base-database-service",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "https://github.com/cap-js/cds-dbs"
9
+ },
10
+ "bugs": {
11
+ "url": "https://github.com/cap-js/cds-dbs/issues"
12
+ },
6
13
  "keywords": [
7
14
  "CAP",
8
15
  "CDS"
9
16
  ],
10
17
  "author": "SAP SE (https://www.sap.com)",
11
18
  "main": "index.js",
19
+ "types": "./dist/index.d.ts",
12
20
  "files": [
13
21
  "lib",
14
22
  "CHANGELOG.md"
@@ -18,13 +26,14 @@
18
26
  "npm": ">=8"
19
27
  },
20
28
  "scripts": {
21
- "prettier": "npx prettier --write .",
22
- "test": "npx jest --silent",
23
- "lint": "npx eslint . && npx prettier --check . "
29
+ "test": "jest --silent"
24
30
  },
25
- "dependencies": {},
26
31
  "peerDependencies": {
27
- "@sap/cds": ">=7"
32
+ "@sap/cds": ">=7.1.1"
28
33
  },
29
- "license": "SEE LICENSE"
34
+ "license": "SEE LICENSE",
35
+ "devDependencies": {
36
+ "@typescript-eslint/eslint-plugin": "^6.2.0",
37
+ "typescript": "^5.1.6"
38
+ }
30
39
  }