@cap-js-community/common 0.2.2 → 0.2.3
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 +79 -0
- package/README.md +3 -0
- package/package.json +5 -4
- package/src/replication-cache/ReplicationCache.js +114 -43
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
|
|
6
|
+
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## Version 0.2.3 - 2025-07-01
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
|
|
12
|
+
- Improve reference detection
|
|
13
|
+
|
|
14
|
+
## Version 0.2.2 - 2025-06-26
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
|
|
18
|
+
- Improve reference detection for aliases
|
|
19
|
+
|
|
20
|
+
## Version 0.2.1 - 2025-06-26
|
|
21
|
+
|
|
22
|
+
### Fixed
|
|
23
|
+
|
|
24
|
+
- Improve reference detection
|
|
25
|
+
- Static non-tenant aware replication via `cds.replicate.static`
|
|
26
|
+
|
|
27
|
+
## Version 0.2.0 - 2025-06-03
|
|
28
|
+
|
|
29
|
+
### Fixed
|
|
30
|
+
|
|
31
|
+
- CDS 9 compatibility
|
|
32
|
+
|
|
33
|
+
## Version 0.1.7 - 2025-05-08
|
|
34
|
+
|
|
35
|
+
### Fixed
|
|
36
|
+
|
|
37
|
+
- Enabling journal mode and changing entity in same cycle is not allowed
|
|
38
|
+
|
|
39
|
+
## Version 0.1.6 - 2025-05-07
|
|
40
|
+
|
|
41
|
+
### Fixed
|
|
42
|
+
|
|
43
|
+
- Redis 4
|
|
44
|
+
|
|
45
|
+
## Version 0.1.5 - 2025-05-05
|
|
46
|
+
|
|
47
|
+
### Fixed
|
|
48
|
+
|
|
49
|
+
- Dependencies
|
|
50
|
+
|
|
51
|
+
## Version 0.1.4 - 2025-04-10
|
|
52
|
+
|
|
53
|
+
### Fixed
|
|
54
|
+
|
|
55
|
+
- Improvements
|
|
56
|
+
|
|
57
|
+
## Version 0.1.3 - 2025-04-10
|
|
58
|
+
|
|
59
|
+
### Fixed
|
|
60
|
+
|
|
61
|
+
- Redis client improvements
|
|
62
|
+
|
|
63
|
+
## Version 0.1.2 - 2025-04-09
|
|
64
|
+
|
|
65
|
+
### Fixed
|
|
66
|
+
|
|
67
|
+
- Improvements
|
|
68
|
+
|
|
69
|
+
## Version 0.1.1 - 2025-04-09
|
|
70
|
+
|
|
71
|
+
### Fixed
|
|
72
|
+
|
|
73
|
+
- Improvements
|
|
74
|
+
|
|
75
|
+
## Version 0.1.0 - 2025-04-09
|
|
76
|
+
|
|
77
|
+
### Added
|
|
78
|
+
|
|
79
|
+
- Internal release
|
package/README.md
CHANGED
|
@@ -121,6 +121,7 @@ Options can be passed to migration check via CDS environment via `cds.migrationC
|
|
|
121
121
|
|
|
122
122
|
- Maintain the whitelist extension file `migration-extension-whitelist.json` for compatible changes:
|
|
123
123
|
- **Whitelist Entity**:
|
|
124
|
+
|
|
124
125
|
```json
|
|
125
126
|
{
|
|
126
127
|
"definitions": {
|
|
@@ -128,7 +129,9 @@ Options can be passed to migration check via CDS environment via `cds.migrationC
|
|
|
128
129
|
}
|
|
129
130
|
}
|
|
130
131
|
```
|
|
132
|
+
|
|
131
133
|
- **Whitelist Entity Element**:
|
|
134
|
+
|
|
132
135
|
```json
|
|
133
136
|
{
|
|
134
137
|
"definitions": {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cap-js-community/common",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.3",
|
|
4
4
|
"description": "CAP Node.js Community Common",
|
|
5
5
|
"homepage": "https://cap.cloud.sap/",
|
|
6
6
|
"engines": {
|
|
@@ -20,7 +20,8 @@
|
|
|
20
20
|
"bin",
|
|
21
21
|
"src",
|
|
22
22
|
"LICENSE",
|
|
23
|
-
"cds-plugin.js"
|
|
23
|
+
"cds-plugin.js",
|
|
24
|
+
"CHANGELOG.md"
|
|
24
25
|
],
|
|
25
26
|
"main": "index.js",
|
|
26
27
|
"types": "index.d.ts",
|
|
@@ -55,14 +56,14 @@
|
|
|
55
56
|
"@sap/cds": "^9.0.4",
|
|
56
57
|
"@sap/cds-common-content": "^3.0.1",
|
|
57
58
|
"@sap/cds-dk": "^9.0.5",
|
|
58
|
-
"eslint": "9.
|
|
59
|
+
"eslint": "9.30.0",
|
|
59
60
|
"eslint-config-prettier": "10.1.5",
|
|
60
61
|
"eslint-plugin-jest": "29.0.1",
|
|
61
62
|
"eslint-plugin-n": "^17.20.0",
|
|
62
63
|
"jest": "30.0.3",
|
|
63
64
|
"jest-html-reporters": "3.1.7",
|
|
64
65
|
"jest-junit": "16.0.0",
|
|
65
|
-
"prettier": "3.6.
|
|
66
|
+
"prettier": "3.6.2",
|
|
66
67
|
"shelljs": "^0.10.0"
|
|
67
68
|
},
|
|
68
69
|
"cds": {
|
|
@@ -178,7 +178,7 @@ class ReplicationCache {
|
|
|
178
178
|
if (fromRefs.length === 0 || !this.relevant(fromRefs)) {
|
|
179
179
|
return await next();
|
|
180
180
|
}
|
|
181
|
-
let refs = queryRefs(model, req.query);
|
|
181
|
+
let refs = queryRefs(model, req.query, req.target);
|
|
182
182
|
if (!this.options.deploy) {
|
|
183
183
|
if (!this.localized(req.query, refs)) {
|
|
184
184
|
return await next();
|
|
@@ -818,13 +818,18 @@ function baseRefs(model, refs) {
|
|
|
818
818
|
const baseRefs = [];
|
|
819
819
|
let currentRefs = refs;
|
|
820
820
|
let nextRefs = [];
|
|
821
|
+
const visited = new Set();
|
|
821
822
|
while (currentRefs.length > 0) {
|
|
822
823
|
for (const ref of currentRefs) {
|
|
824
|
+
if (visited.has(ref)) {
|
|
825
|
+
continue;
|
|
826
|
+
}
|
|
827
|
+
visited.add(ref);
|
|
823
828
|
const definition = model.definitions[ref];
|
|
824
829
|
if (!definition.query) {
|
|
825
830
|
baseRefs.push(ref);
|
|
826
831
|
} else {
|
|
827
|
-
nextRefs = nextRefs.concat(queryRefs(model, definition.query));
|
|
832
|
+
nextRefs = nextRefs.concat(queryRefs(model, definition.query, definition));
|
|
828
833
|
}
|
|
829
834
|
}
|
|
830
835
|
currentRefs = nextRefs;
|
|
@@ -853,30 +858,53 @@ function queryFromRefs(model, query) {
|
|
|
853
858
|
return unique(selectFromRefs(model, query));
|
|
854
859
|
}
|
|
855
860
|
|
|
856
|
-
function queryRefs(model, query) {
|
|
861
|
+
function queryRefs(model, query, definition) {
|
|
857
862
|
if (!query.SELECT) {
|
|
858
863
|
return [];
|
|
859
864
|
}
|
|
860
|
-
return unique(selectRefs(model, query));
|
|
865
|
+
return unique(selectRefs(model, definition, query));
|
|
861
866
|
}
|
|
862
867
|
|
|
863
868
|
function selectFromRefs(model, query) {
|
|
864
869
|
let refs = [];
|
|
865
|
-
if (query.SELECT.from.
|
|
866
|
-
refs = selectFromRefs(model, query.SELECT.from);
|
|
867
|
-
} else if (query.SELECT.from.ref) {
|
|
870
|
+
if (query.SELECT.from.ref) {
|
|
868
871
|
refs = resolveRefs(model, query.SELECT.from.ref);
|
|
869
|
-
} else if (
|
|
872
|
+
} else if (query.SELECT.from.args) {
|
|
870
873
|
refs = query.SELECT.from.args.reduce((refs, arg) => {
|
|
871
|
-
|
|
874
|
+
if (arg.ref) {
|
|
875
|
+
refs = refs.concat(resolveRefs(model, arg.ref));
|
|
876
|
+
} else if (arg.args) {
|
|
877
|
+
refs = refs.concat(selectFromRefs(model, { SELECT: { from: { args: arg.args } } }));
|
|
878
|
+
}
|
|
872
879
|
return refs;
|
|
873
880
|
}, []);
|
|
881
|
+
} else if (query.SELECT.from.SELECT) {
|
|
882
|
+
refs = selectFromRefs(model, query.SELECT.from);
|
|
874
883
|
}
|
|
875
884
|
return refs;
|
|
876
885
|
}
|
|
877
886
|
|
|
878
|
-
function
|
|
887
|
+
function selectFromPrimaryRef(model, query) {
|
|
888
|
+
if (query.SELECT.from.ref) {
|
|
889
|
+
return resolveRef(model, query.SELECT.from.ref);
|
|
890
|
+
} else if (query.SELECT.from.args) {
|
|
891
|
+
for (const arg of query.SELECT.from.args) {
|
|
892
|
+
if (arg.ref) {
|
|
893
|
+
return resolveRef(model, arg.ref);
|
|
894
|
+
} else if (arg.args) {
|
|
895
|
+
return selectFromPrimaryRef(model, { SELECT: { from: { args: arg.args } } });
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
} else if (query.SELECT.from.SELECT) {
|
|
899
|
+
return selectFromPrimaryRef(model, query.SELECT.from);
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
function selectFromAliases(model, definition, query) {
|
|
879
904
|
let aliases = {};
|
|
905
|
+
if (definition?.name) {
|
|
906
|
+
aliases["$self"] = definition.name;
|
|
907
|
+
}
|
|
880
908
|
if (query.SELECT.from.SELECT) {
|
|
881
909
|
// Sub-select aliases are not (yet) supported
|
|
882
910
|
} else if (query.SELECT.from.ref) {
|
|
@@ -887,39 +915,74 @@ function selectFromAliases(model, query) {
|
|
|
887
915
|
const as = ref.split(".").pop();
|
|
888
916
|
aliases[as] = ref;
|
|
889
917
|
}
|
|
890
|
-
} else if (
|
|
918
|
+
} else if (query.SELECT.from.args) {
|
|
891
919
|
for (const arg of query.SELECT.from.args) {
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
920
|
+
if (arg.ref) {
|
|
921
|
+
const ref = resolveRef(model, arg.ref);
|
|
922
|
+
if (arg.as) {
|
|
923
|
+
aliases[arg.as] = ref;
|
|
924
|
+
} else {
|
|
925
|
+
const as = ref.split(".").pop();
|
|
926
|
+
aliases[as] = ref;
|
|
927
|
+
}
|
|
928
|
+
} else if (arg.args) {
|
|
929
|
+
for (const subArg of arg.args) {
|
|
930
|
+
aliases = {
|
|
931
|
+
...aliases,
|
|
932
|
+
...selectFromAliases(model, definition, { SELECT: { from: { args: subArg.args } } }),
|
|
933
|
+
};
|
|
934
|
+
}
|
|
898
935
|
}
|
|
899
936
|
}
|
|
900
937
|
}
|
|
901
938
|
return aliases;
|
|
902
939
|
}
|
|
903
940
|
|
|
904
|
-
function
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
if (query.SELECT.having) {
|
|
920
|
-
refs = refs.concat(expressionRefs(model, target, query.SELECT.having, query.SELECT.mixin, aliases));
|
|
941
|
+
function selectOuterAliases(model, target, columns, mixins, aliases) {
|
|
942
|
+
const outerAliases = {};
|
|
943
|
+
if (columns) {
|
|
944
|
+
for (const column of columns) {
|
|
945
|
+
if (column.ref) {
|
|
946
|
+
const as = column.as || column.ref[column.ref.length - 1];
|
|
947
|
+
let current = target;
|
|
948
|
+
for (const ref of column.ref) {
|
|
949
|
+
const currentTarget = targetEntity(model, current, mixins, aliases, ref);
|
|
950
|
+
if (currentTarget != null) {
|
|
951
|
+
current = currentTarget;
|
|
952
|
+
}
|
|
953
|
+
}
|
|
954
|
+
outerAliases[as] = (current || target).name;
|
|
955
|
+
}
|
|
921
956
|
}
|
|
922
957
|
}
|
|
958
|
+
return outerAliases;
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
function selectRefs(model, definition, query, aliases) {
|
|
962
|
+
let targetName = selectFromPrimaryRef(model, query);
|
|
963
|
+
const target = targetName ? model.definitions[targetName] : undefined;
|
|
964
|
+
let refs = selectFromRefs(model, query);
|
|
965
|
+
aliases = {
|
|
966
|
+
...aliases,
|
|
967
|
+
...selectFromAliases(model, definition, query),
|
|
968
|
+
};
|
|
969
|
+
if (query.SELECT.columns) {
|
|
970
|
+
refs = refs.concat(expressionRefs(model, target, query.SELECT.columns, query.SELECT.mixin, aliases));
|
|
971
|
+
refs = refs.concat(expandRefs(model, target, query.SELECT.columns, query.SELECT.mixin, aliases));
|
|
972
|
+
}
|
|
973
|
+
if (query.SELECT.where) {
|
|
974
|
+
refs = refs.concat(expressionRefs(model, target, query.SELECT.where, query.SELECT.mixin, aliases));
|
|
975
|
+
}
|
|
976
|
+
const outerAliases = {
|
|
977
|
+
...aliases,
|
|
978
|
+
...selectOuterAliases(model, target, query.SELECT.columns, query.SELECT.mixin, aliases),
|
|
979
|
+
};
|
|
980
|
+
if (query.SELECT.having) {
|
|
981
|
+
refs = refs.concat(expressionRefs(model, target, query.SELECT.having, query.SELECT.mixin, outerAliases));
|
|
982
|
+
}
|
|
983
|
+
if (query.SELECT.orderBy) {
|
|
984
|
+
refs = refs.concat(expressionRefs(model, target, query.SELECT.orderBy, query.SELECT.mixin, outerAliases));
|
|
985
|
+
}
|
|
923
986
|
return refs;
|
|
924
987
|
}
|
|
925
988
|
|
|
@@ -965,11 +1028,14 @@ function identifierRefs(model, definition, expressions, mixins, aliases) {
|
|
|
965
1028
|
let current = definition;
|
|
966
1029
|
let currentMixins = mixins;
|
|
967
1030
|
for (const ref of expression.ref) {
|
|
968
|
-
|
|
969
|
-
if (current !== null) {
|
|
970
|
-
currentMixins = {};
|
|
1031
|
+
if (current?.name !== definition?.name) {
|
|
971
1032
|
refs.push(current.name);
|
|
972
1033
|
}
|
|
1034
|
+
if (ref.startsWith("$")) {
|
|
1035
|
+
break;
|
|
1036
|
+
}
|
|
1037
|
+
current = targetEntity(model, current, currentMixins, aliases, ref);
|
|
1038
|
+
currentMixins = {};
|
|
973
1039
|
}
|
|
974
1040
|
}
|
|
975
1041
|
}
|
|
@@ -984,7 +1050,12 @@ function expressionRefs(model, definition, expressions, mixins, aliases) {
|
|
|
984
1050
|
} else if (expression.args) {
|
|
985
1051
|
refs = refs.concat(expressionRefs(model, definition, expression.args, mixins, aliases));
|
|
986
1052
|
} else if (expression.SELECT) {
|
|
987
|
-
refs = refs.concat(
|
|
1053
|
+
refs = refs.concat(
|
|
1054
|
+
selectRefs(model, undefined, expression, {
|
|
1055
|
+
...aliases,
|
|
1056
|
+
["$self"]: undefined,
|
|
1057
|
+
}),
|
|
1058
|
+
);
|
|
988
1059
|
}
|
|
989
1060
|
}
|
|
990
1061
|
return refs;
|
|
@@ -997,7 +1068,7 @@ function expandRefs(model, definition, columns, mixins, aliases) {
|
|
|
997
1068
|
let current = definition;
|
|
998
1069
|
let currentMixins = mixins;
|
|
999
1070
|
for (const ref of column.ref) {
|
|
1000
|
-
current =
|
|
1071
|
+
current = targetEntity(model, current, currentMixins, aliases, ref);
|
|
1001
1072
|
currentMixins = {};
|
|
1002
1073
|
refs.push(current.name);
|
|
1003
1074
|
}
|
|
@@ -1007,17 +1078,17 @@ function expandRefs(model, definition, columns, mixins, aliases) {
|
|
|
1007
1078
|
return refs;
|
|
1008
1079
|
}
|
|
1009
1080
|
|
|
1010
|
-
function
|
|
1081
|
+
function targetEntity(model, entity, mixins, aliases, ref) {
|
|
1011
1082
|
if (aliases?.[ref]) {
|
|
1012
1083
|
return typeof aliases[ref] === "string" ? model.definitions[aliases[ref]] : aliases[ref];
|
|
1013
1084
|
}
|
|
1014
|
-
if (entity
|
|
1085
|
+
if (entity?.name.endsWith(`.${ref}`)) {
|
|
1015
1086
|
return entity;
|
|
1016
1087
|
}
|
|
1017
|
-
const element = entity
|
|
1088
|
+
const element = entity?.elements[ref] || mixins?.[ref];
|
|
1018
1089
|
if (!element) {
|
|
1019
1090
|
cds.log(Component).warn("Reference not found in entity", {
|
|
1020
|
-
entity: entity
|
|
1091
|
+
entity: entity?.name,
|
|
1021
1092
|
ref,
|
|
1022
1093
|
});
|
|
1023
1094
|
}
|