@cap-js/audit-logging 1.2.0 → 1.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/lib/utils.js CHANGED
@@ -1,278 +1,291 @@
1
- const cds = require('@sap/cds')
1
+ const cds = require("@sap/cds");
2
2
 
3
- const { Relation, exposeRelation, relationHandler } = require('./_relation')
3
+ const { Relation, exposeRelation, relationHandler } = require("./_relation");
4
4
 
5
- const WRITE = { CREATE: 1, UPDATE: 1, DELETE: 1 }
5
+ const WRITE = { CREATE: 1, UPDATE: 1, DELETE: 1 };
6
6
 
7
- const $hasPersonalData = Symbol('@cap-js/audit-logging:hasPersonalData')
8
- const $dataSubject = Symbol('@cap-js/audit-logging:dataSubject')
9
- const $parents = Symbol('@cap-js/audit-logging:parents')
10
- const $visitedUp = Symbol('@cap-js/audit-logging:visitedUp')
11
- const $visitedDown = Symbol('@cap-js/audit-logging:visitedDown')
7
+ const $hasPersonalData = Symbol("@cap-js/audit-logging:hasPersonalData");
8
+ const $dataSubject = Symbol("@cap-js/audit-logging:dataSubject");
9
+ const $parents = Symbol("@cap-js/audit-logging:parents");
10
+ const $visitedUp = Symbol("@cap-js/audit-logging:visitedUp");
11
+ const $visitedDown = Symbol("@cap-js/audit-logging:visitedDown");
12
12
 
13
- const hasPersonalData = entity => {
13
+ const hasPersonalData = (entity) => {
14
14
  if (entity.own($hasPersonalData) == null) {
15
- if (!entity['@PersonalData.EntitySemantics']) entity.set($hasPersonalData, false)
15
+ if (!entity["@PersonalData.EntitySemantics"]) entity.set($hasPersonalData, false);
16
16
  else {
17
17
  // default role to entity name
18
- if (entity['@PersonalData.EntitySemantics'] === 'DataSubject' && !entity['@PersonalData.DataSubjectRole'])
19
- entity['@PersonalData.DataSubjectRole'] = entity.name.match(/\w+/g).pop()
18
+ if (
19
+ entity["@PersonalData.EntitySemantics"] === "DataSubject" &&
20
+ !entity["@PersonalData.DataSubjectRole"]
21
+ )
22
+ entity["@PersonalData.DataSubjectRole"] = entity.name.match(/\w+/g).pop();
20
23
  // prettier-ignore
21
24
  const hasPersonalData = !!Object.values(entity.elements).some(element =>
22
25
  element['@PersonalData.IsPotentiallyPersonal'] ||
23
26
  element['@PersonalData.IsPotentiallySensitive'] ||
24
27
  (element['@PersonalData.FieldSemantics'] && element['@PersonalData.FieldSemantics'] === 'DataSubjectID'))
25
- entity.set($hasPersonalData, hasPersonalData)
28
+ entity.set($hasPersonalData, hasPersonalData);
26
29
  }
27
30
  }
28
- return entity.own($hasPersonalData)
29
- }
31
+ return entity.own($hasPersonalData);
32
+ };
30
33
 
31
- const getMapKeyForCurrentRequest = req => {
34
+ const getMapKeyForCurrentRequest = (req) => {
32
35
  // running in srv or db layer? -> srv's req.query used as key of diff and logs maps at req.context
33
36
  // REVISIT: req._tx should not be used like that!
34
- return req.tx.isDatabaseService ? req._.query : req.query
35
- }
37
+ return req.tx.isDatabaseService ? req._.query : req.query;
38
+ };
36
39
 
37
- const getRootEntity = element => {
38
- let entity = element.parent
39
- while (entity.kind !== 'entity') entity = entity.parent
40
- return entity
41
- }
40
+ const getRootEntity = (element) => {
41
+ let entity = element.parent;
42
+ while (entity.kind !== "entity") entity = entity.parent;
43
+ return entity;
44
+ };
42
45
 
43
46
  const _isDataSubject = (element, target) => {
44
47
  return (
45
48
  !element.isAssociation &&
46
- element['@PersonalData.FieldSemantics'] === 'DataSubjectID' &&
47
- target['@PersonalData.EntitySemantics'] === 'DataSubject'
48
- )
49
- }
49
+ element["@PersonalData.FieldSemantics"] === "DataSubjectID" &&
50
+ target["@PersonalData.EntitySemantics"] === "DataSubject"
51
+ );
52
+ };
50
53
 
51
- const getPick = event => {
54
+ const getPick = (event) => {
52
55
  return (element, target) => {
53
- if (!hasPersonalData(target)) return
54
- const categories = []
55
- if (!element.isAssociation && element.key) categories.push('ObjectID')
56
- if (_isDataSubject(element, target)) categories.push('DataSubjectID')
57
- if (event in WRITE && element['@PersonalData.IsPotentiallyPersonal']) categories.push('IsPotentiallyPersonal')
58
- if (element['@PersonalData.IsPotentiallySensitive']) categories.push('IsPotentiallySensitive')
59
- if (categories.length) return { categories }
60
- }
61
- }
56
+ if (!hasPersonalData(target)) return;
57
+ const categories = [];
58
+ if (!element.isAssociation && element.key) categories.push("ObjectID");
59
+ if (_isDataSubject(element, target)) categories.push("DataSubjectID");
60
+ if (event in WRITE && element["@PersonalData.IsPotentiallyPersonal"])
61
+ categories.push("IsPotentiallyPersonal");
62
+ if (element["@PersonalData.IsPotentiallySensitive"]) categories.push("IsPotentiallySensitive");
63
+ if (categories.length) return { categories };
64
+ };
65
+ };
62
66
 
63
67
  const _getHash = (entity, row) => {
64
68
  return `${entity.name}(${Object.keys(entity.keys)
65
- .map(k => `${k}=${row[k]}`)
66
- .join(',')})`
67
- }
69
+ .map((k) => `${k}=${row[k]}`)
70
+ .join(",")})`;
71
+ };
68
72
 
69
73
  const createLogEntry = (logs, entity, row) => {
70
- const hash = _getHash(entity, row)
71
- let log = logs[hash]
74
+ const hash = _getHash(entity, row);
75
+ let log = logs[hash];
72
76
  if (!log) {
73
77
  logs[hash] = {
74
- data_subject: { id: {}, role: entity['@PersonalData.DataSubjectRole'] },
78
+ data_subject: { id: {}, role: entity["@PersonalData.DataSubjectRole"] },
75
79
  object: { type: entity.name, id: {} },
76
80
  attributes: []
77
- }
78
- log = logs[hash]
81
+ };
82
+ log = logs[hash];
79
83
  }
80
- return log
81
- }
84
+ return log;
85
+ };
82
86
 
83
87
  const addObjectID = (log, row, key) => {
84
- if (!(key in log.object.id) && key !== 'IsActiveEntity') log.object.id[key] = row[key] || row._old?.[key]
85
- }
88
+ if (!(key in log.object.id) && key !== "IsActiveEntity")
89
+ log.object.id[key] = row[key] || row._old?.[key];
90
+ };
86
91
 
87
92
  const addDataSubject = (log, row, key, entity) => {
88
- if (!log.data_subject.type) log.data_subject.type = entity.name
93
+ if (!log.data_subject.type) log.data_subject.type = entity.name;
89
94
  if (!(key in log.data_subject.id)) {
90
- const value = row[key] || row._old?.[key]
91
- log.data_subject.id[key] = value
95
+ const value = row[key] || row._old?.[key];
96
+ log.data_subject.id[key] = value;
92
97
  }
93
- }
98
+ };
94
99
 
95
100
  const _addKeysToWhere = (keys, row, alias) => {
96
101
  return keys
97
- .filter(key => !key.isAssociation && key.name !== 'IsActiveEntity')
102
+ .filter((key) => !key.isAssociation && key.name !== "IsActiveEntity")
98
103
  .reduce((keys, key) => {
99
- if (keys.length) keys.push('and')
100
- keys.push({ ref: [alias, key.name] }, '=', { val: row[key.name] || row._old?.[key.name] })
101
- return keys
102
- }, [])
103
- }
104
+ if (keys.length) keys.push("and");
105
+ keys.push({ ref: [alias, key.name] }, "=", { val: row[key.name] || row._old?.[key.name] });
106
+ return keys;
107
+ }, []);
108
+ };
104
109
 
105
110
  const _keyColumns = (keys, alias) => {
106
111
  return keys
107
- .filter(key => !key.isAssociation && key.name !== 'IsActiveEntity')
108
- .map(key => ({ ref: [alias, key.name] }))
109
- }
112
+ .filter((key) => !key.isAssociation && key.name !== "IsActiveEntity")
113
+ .map((key) => ({ ref: [alias, key.name] }));
114
+ };
110
115
 
111
- const _alias = entity => {
116
+ const _alias = (entity) => {
112
117
  // REVISIT: we should not rely on entity._service (but I don't want to break existing behavior right now)
113
- if (!entity._service) return `${entity}`
114
- return entity.name.replace(`${entity._service.name}.`, '').replace(/\./g, '_')
115
- }
118
+ if (!entity._service) return `${entity}`;
119
+ return entity.name.replace(`${entity._service.name}.`, "").replace(/\./g, "_");
120
+ };
116
121
 
117
122
  const _buildSubSelect = (model, { entity, relative, element, next }, row, previousCqn) => {
118
123
  // relative is a parent or an entity itself
119
124
 
120
- const keys = Object.values(entity.keys)
125
+ const keys = Object.values(entity.keys);
121
126
 
122
- const entityName = entity.name
123
- const as = _alias(entity)
127
+ const entityName = entity.name;
128
+ const as = _alias(entity);
124
129
 
125
- const childCqn = SELECT.from({ ref: [entityName], as }).columns(_keyColumns(keys, as))
130
+ const childCqn = SELECT.from({ ref: [entityName], as }).columns(_keyColumns(keys, as));
126
131
 
127
- const targetAlias = _alias(element._target)
128
- const relativeAlias = _alias(relative)
132
+ const targetAlias = _alias(element._target);
133
+ const relativeAlias = _alias(relative);
129
134
 
130
135
  // REVISIT: there seems to be a caching issue in cds^9 when elemets are renamed
131
- if (!('_relations' in relative) || !relative._relations[element.name]) {
132
- const newRelation = Relation.to(relative)
133
- relative._relations = new Proxy(exposeRelation(newRelation), relationHandler(newRelation))
136
+ if (!("_relations" in relative) || !relative._relations[element.name]) {
137
+ const newRelation = Relation.to(relative);
138
+ relative._relations = new Proxy(exposeRelation(newRelation), relationHandler(newRelation));
134
139
  }
135
140
 
136
- let w = relative._relations[element.name].join(targetAlias, relativeAlias)
141
+ let w = relative._relations[element.name].join(targetAlias, relativeAlias);
137
142
 
138
143
  // REVISIT: rewrite to path expression, if alias for relative is already used in subselect to avoid sql error
139
144
  if (previousCqn?._aliases.has(relativeAlias)) {
140
- let t
141
- for (const a in entity.associations) if (entity.associations[a].target === relative.name) t = entity.associations[a]
142
- if (t && w[0]?.xpr) for (const ele of w[0].xpr) if (ele.ref?.[0] === relativeAlias) ele.ref.splice(0, 1, as, t.name)
145
+ let t;
146
+ for (const a in entity.associations)
147
+ if (entity.associations[a].target === relative.name) t = entity.associations[a];
148
+ if (t && w[0]?.xpr)
149
+ for (const ele of w[0].xpr)
150
+ if (ele.ref?.[0] === relativeAlias) ele.ref.splice(0, 1, as, t.name);
143
151
  }
144
- childCqn._aliases = new Set(previousCqn ? [...previousCqn._aliases.values(), as] : [as])
152
+ childCqn._aliases = new Set(previousCqn ? [...previousCqn._aliases.values(), as] : [as]);
145
153
 
146
- childCqn.where(w)
154
+ childCqn.where(w);
147
155
 
148
- if (previousCqn) childCqn.where('exists', previousCqn)
149
- else childCqn.where(_addKeysToWhere(keys, row, as))
156
+ if (previousCqn) childCqn.where("exists", previousCqn);
157
+ else childCqn.where(_addKeysToWhere(keys, row, as));
150
158
 
151
- if (next) return _buildSubSelect(model, next, {}, childCqn)
159
+ if (next) return _buildSubSelect(model, next, {}, childCqn);
152
160
 
153
- return childCqn
154
- }
161
+ return childCqn;
162
+ };
155
163
 
156
164
  const _getDataSubjectIdQuery = ({ dataSubjectEntity, subs }, row, model) => {
157
- const keys = Object.values(dataSubjectEntity.keys)
158
- const as = _alias(dataSubjectEntity)
165
+ const keys = Object.values(dataSubjectEntity.keys);
166
+ const as = _alias(dataSubjectEntity);
159
167
 
160
168
  const cqn = SELECT.one
161
169
  .from({ ref: [dataSubjectEntity.name], as })
162
170
  .columns(_keyColumns(keys, as))
163
- .where(['exists', _buildSubSelect(model, subs[0], row)])
171
+ .where(["exists", _buildSubSelect(model, subs[0], row)]);
164
172
 
165
173
  // entity reused in different branches => must check all
166
- for (let i = 1; i < subs.length; i++) cqn.or(['exists', _buildSubSelect(model, subs[i], row)])
174
+ for (let i = 1; i < subs.length; i++) cqn.or(["exists", _buildSubSelect(model, subs[i], row)]);
167
175
 
168
- return cqn
169
- }
176
+ return cqn;
177
+ };
170
178
 
171
179
  const _getUps = (entity, model) => {
172
180
  if (entity.own($parents) == null) {
173
- const ups = []
181
+ const ups = [];
174
182
  for (const def of Object.values(model.definitions)) {
175
- if (def.kind !== 'entity' || !def.associations) continue
183
+ if (def.kind !== "entity" || !def.associations) continue;
176
184
  for (const element of Object.values(def.associations)) {
177
- if (element.target !== entity.name || element._isBacklink || element.name === 'SiblingEntity') continue
178
- ups.push(element)
185
+ if (
186
+ element.target !== entity.name ||
187
+ element._isBacklink ||
188
+ element.name === "SiblingEntity"
189
+ )
190
+ continue;
191
+ ups.push(element);
179
192
  }
180
193
  }
181
- entity.set($parents, ups)
194
+ entity.set($parents, ups);
182
195
  }
183
- return entity.own($parents)
184
- }
196
+ return entity.own($parents);
197
+ };
185
198
 
186
199
  const _getDataSubjectUp = (root, model, entity, prev, next, result) => {
187
200
  for (const element of _getUps(entity, model)) {
188
201
  // cycle detection
189
- if (element.own($visitedUp) == null) element.set($visitedUp, new Set())
190
- if (element.own($visitedUp).has(root)) continue
191
- element.own($visitedUp).add(root)
192
-
193
- const me = { entity, relative: element.parent, element }
194
- if (prev) prev.next = me
195
- if (element.parent['@PersonalData.EntitySemantics'] === 'DataSubject') {
196
- if (!result) result = { dataSubjectEntity: element.parent, subs: [] }
197
- result.subs.push(next || me)
198
- return result
202
+ if (element.own($visitedUp) == null) element.set($visitedUp, new Set());
203
+ if (element.own($visitedUp).has(root)) continue;
204
+ element.own($visitedUp).add(root);
205
+
206
+ const me = { entity, relative: element.parent, element };
207
+ if (prev) prev.next = me;
208
+ if (element.parent["@PersonalData.EntitySemantics"] === "DataSubject") {
209
+ if (!result) result = { dataSubjectEntity: element.parent, subs: [] };
210
+ result.subs.push(next || me);
211
+ return result;
199
212
  } else {
200
213
  // dfs is a must here
201
- result = _getDataSubjectUp(root, model, element.parent, me, next || me, result)
214
+ result = _getDataSubjectUp(root, model, element.parent, me, next || me, result);
202
215
  }
203
216
  }
204
- return result
205
- }
217
+ return result;
218
+ };
206
219
 
207
220
  const _getDataSubjectDown = (root, entity, prev, next) => {
208
- const associations = Object.values(entity.associations || {}).filter(e => !e._isBacklink)
221
+ const associations = Object.values(entity.associations || {}).filter((e) => !e._isBacklink);
209
222
  // bfs makes more sense here -> check all own assocs first before going deeper
210
223
  for (const element of associations) {
211
- const me = { entity, relative: entity, element }
212
- if (element._target['@PersonalData.EntitySemantics'] === 'DataSubject') {
213
- if (prev) prev.next = me
214
- return { dataSubjectEntity: element._target, subs: [next || me] }
224
+ const me = { entity, relative: entity, element };
225
+ if (element._target["@PersonalData.EntitySemantics"] === "DataSubject") {
226
+ if (prev) prev.next = me;
227
+ return { dataSubjectEntity: element._target, subs: [next || me] };
215
228
  }
216
229
  }
217
230
  for (const element of associations) {
218
231
  // cycle detection
219
- if (element.own($visitedDown) == null) element.set($visitedDown, new Set())
220
- if (element.own($visitedDown).has(root)) continue
221
- element.own($visitedDown).add(root)
222
-
223
- const me = { entity, relative: entity, element }
224
- if (prev) prev.next = me
225
- const dataSubject = _getDataSubjectDown(root, element._target, me, next || me)
226
- if (dataSubject) return dataSubject
232
+ if (element.own($visitedDown) == null) element.set($visitedDown, new Set());
233
+ if (element.own($visitedDown).has(root)) continue;
234
+ element.own($visitedDown).add(root);
235
+
236
+ const me = { entity, relative: entity, element };
237
+ if (prev) prev.next = me;
238
+ const dataSubject = _getDataSubjectDown(root, element._target, me, next || me);
239
+ if (dataSubject) return dataSubject;
227
240
  }
228
- }
241
+ };
229
242
 
230
243
  const getDataSubject = (entity, model) => {
231
244
  if (entity.own($dataSubject) == null) {
232
245
  // entities with EntitySemantics 'DataSubjectDetails' or 'Other' must not necessarily
233
246
  // be always below or always above 'DataSubject' entity in CSN tree
234
- let dataSubjectInfo = _getDataSubjectUp(entity.name, model, entity)
235
- if (!dataSubjectInfo) dataSubjectInfo = _getDataSubjectDown(entity.name, entity)
236
- entity.set($dataSubject, dataSubjectInfo)
247
+ let dataSubjectInfo = _getDataSubjectUp(entity.name, model, entity);
248
+ if (!dataSubjectInfo) dataSubjectInfo = _getDataSubjectDown(entity.name, entity);
249
+ entity.set($dataSubject, dataSubjectInfo);
237
250
  }
238
- return entity.own($dataSubject)
239
- }
251
+ return entity.own($dataSubject);
252
+ };
240
253
 
241
- const _getDataSubjectsMap = req => {
242
- const mapKey = getMapKeyForCurrentRequest(req)
243
- const _audit = (req.context._audit ??= {})
244
- if (!_audit.dataSubjects) _audit.dataSubjects = new Map()
245
- if (!_audit.dataSubjects.has(mapKey)) _audit.dataSubjects.set(mapKey, new Map())
246
- return _audit.dataSubjects.get(mapKey)
247
- }
254
+ const _getDataSubjectsMap = (req) => {
255
+ const mapKey = getMapKeyForCurrentRequest(req);
256
+ const _audit = (req.context._audit ??= {});
257
+ if (!_audit.dataSubjects) _audit.dataSubjects = new Map();
258
+ if (!_audit.dataSubjects.has(mapKey)) _audit.dataSubjects.set(mapKey, new Map());
259
+ return _audit.dataSubjects.get(mapKey);
260
+ };
248
261
 
249
262
  const addDataSubjectForDetailsEntity = (row, log, req, entity, model) => {
250
- const dataSubjectInfo = getDataSubject(entity, model)
251
- const role = dataSubjectInfo.dataSubjectEntity['@PersonalData.DataSubjectRole']
252
- log.data_subject.role ??= role
253
- log.data_subject.type = dataSubjectInfo.dataSubjectEntity.name
263
+ const dataSubjectInfo = getDataSubject(entity, model);
264
+ const role = dataSubjectInfo.dataSubjectEntity["@PersonalData.DataSubjectRole"];
265
+ log.data_subject.role ??= role;
266
+ log.data_subject.type = dataSubjectInfo.dataSubjectEntity.name;
254
267
  /*
255
268
  * for each req (cf. $batch with atomicity) and data subject role (e.g., customer vs supplier),
256
269
  * store (in audit data structure at context) and reuse a single promise to look up the respective data subject
257
270
  */
258
- const map = _getDataSubjectsMap(req)
259
- if (map.has(role)) log.data_subject.id = map.get(role)
271
+ const map = _getDataSubjectsMap(req);
272
+ if (map.has(role)) log.data_subject.id = map.get(role);
260
273
  // REVISIT by downward lookups row might already contain ID - some potential to optimize
261
- else map.set(role, _getDataSubjectIdQuery(dataSubjectInfo, row, model))
262
- }
274
+ else map.set(role, _getDataSubjectIdQuery(dataSubjectInfo, row, model));
275
+ };
263
276
 
264
277
  const resolveDataSubjects = (logs, req) => {
265
- const ps = []
278
+ const ps = [];
266
279
 
267
- const map = _getDataSubjectsMap(req)
280
+ const map = _getDataSubjectsMap(req);
268
281
 
269
282
  for (const each of Object.values(logs)) {
270
283
  if (each.data_subject.id instanceof cds.ql.Query) {
271
- const q = each.data_subject.id
284
+ const q = each.data_subject.id;
272
285
  if (!map.has(q)) {
273
- const p = cds.run(q).then(res => map.set(q, res))
274
- map.set(q, p)
275
- ps.push(p)
286
+ const p = cds.run(q).then((res) => map.set(q, res));
287
+ map.set(q, p);
288
+ ps.push(p);
276
289
  }
277
290
  }
278
291
  }
@@ -280,11 +293,11 @@ const resolveDataSubjects = (logs, req) => {
280
293
  return Promise.all(ps).then(() => {
281
294
  for (const each of Object.values(logs)) {
282
295
  if (each.data_subject.id instanceof cds.ql.Query) {
283
- each.data_subject.id = map.get(each.data_subject.id)
296
+ each.data_subject.id = map.get(each.data_subject.id);
284
297
  }
285
298
  }
286
- })
287
- }
299
+ });
300
+ };
288
301
 
289
302
  const getAppMetadata = () => {
290
303
  const appMetadata = cds.env.app;
@@ -295,13 +308,12 @@ const getAppMetadata = () => {
295
308
  appName: appMetadata.name,
296
309
  appURL: appMetadata.url,
297
310
  organization_name: appMetadata.organization_name,
298
- space_name: appMetadata.space_name,
311
+ space_name: appMetadata.space_name
299
312
  };
300
313
  }
301
314
 
302
315
  // fallback: if the app metadata is undefined, then extract the metadata from the underlying environment (CF/Kyma/...)
303
- const vcapApplication =
304
- process.env.VCAP_APPLICATION && JSON.parse(process.env.VCAP_APPLICATION);
316
+ const vcapApplication = process.env.VCAP_APPLICATION && JSON.parse(process.env.VCAP_APPLICATION);
305
317
 
306
318
  return {
307
319
  appID: vcapApplication && vcapApplication.application_id,
@@ -312,7 +324,7 @@ const getAppMetadata = () => {
312
324
  vcapApplication &&
313
325
  vcapApplication.application_uris &&
314
326
  vcapApplication.application_uris[0] &&
315
- `https://${vcapApplication.application_uris[0].replace(/^https?:\/\//, "")}`,
327
+ `https://${vcapApplication.application_uris[0].replace(/^https?:\/\//, "")}`
316
328
  };
317
329
  };
318
330
 
@@ -326,5 +338,5 @@ module.exports = {
326
338
  addDataSubject,
327
339
  addDataSubjectForDetailsEntity,
328
340
  resolveDataSubjects,
329
- appMetadata: getAppMetadata(),
341
+ appMetadata: getAppMetadata()
330
342
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cap-js/audit-logging",
3
- "version": "1.2.0",
3
+ "version": "1.2.1",
4
4
  "description": "CDS plugin providing integration to the SAP Audit Log service as well as out-of-the-box personal data-related audit logging based on annotations.",
5
5
  "repository": "cap-js/audit-logging",
6
6
  "author": "SAP SE (https://www.sap.com)",
@@ -13,7 +13,9 @@
13
13
  ],
14
14
  "scripts": {
15
15
  "lint": "npx eslint . --max-warnings=0",
16
- "test": "npx jest --silent"
16
+ "format": "npx prettier . --write && format-cds -f",
17
+ "format:check": "npx prettier . --check && format-cds --check",
18
+ "test": "npx jest --silent --testPathIgnorePatterns=integration"
17
19
  },
18
20
  "peerDependencies": {
19
21
  "@sap/cds": ">=8"
@@ -22,13 +24,10 @@
22
24
  "@cap-js/audit-logging": "file:.",
23
25
  "@cap-js/cds-test": ">=0",
24
26
  "@cap-js/sqlite": ">=1",
27
+ "@sap/cds-lsp": "^9.9.0",
25
28
  "@sap/cds-mtxs": "^3.7.1",
26
- "axios": "^1",
27
- "eslint": "^9",
28
29
  "express": "^4",
29
- "jest": "^30",
30
- "pretty-quick": "^4.2.2",
31
- "simple-git-hooks": "^2.13.1"
30
+ "jest": "^30"
32
31
  },
33
32
  "cds": {
34
33
  "requires": {