contentful-resolve-response 1.8.2 → 1.9.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/dist/cjs/index.d.ts +14 -0
- package/dist/cjs/index.js +153 -185
- package/dist/esm/index.d.ts +14 -0
- package/dist/esm/index.js +151 -176
- package/package.json +11 -23
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export default resolveResponse;
|
|
2
|
+
/**
|
|
3
|
+
* ResolveResponse Function
|
|
4
|
+
* Resolves contentful response to normalized form.
|
|
5
|
+
* @param {Object} response Contentful response
|
|
6
|
+
* @param {{removeUnresolved: Boolean, itemEntryPoints: Array<String>}|{}} options
|
|
7
|
+
* @param {Boolean} options.removeUnresolved - Remove unresolved links default:false
|
|
8
|
+
* @param {Array<String>} options.itemEntryPoints - Resolve links only in those item properties
|
|
9
|
+
* @return {Object}
|
|
10
|
+
*/
|
|
11
|
+
declare function resolveResponse(response: any, options: {
|
|
12
|
+
removeUnresolved: boolean;
|
|
13
|
+
itemEntryPoints: Array<string>;
|
|
14
|
+
} | {}): any;
|
package/dist/cjs/index.js
CHANGED
|
@@ -1,39 +1,19 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
});
|
|
6
|
-
|
|
7
|
-
var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }();
|
|
8
|
-
|
|
9
|
-
var _fastCopy = require('fast-copy');
|
|
10
|
-
|
|
11
|
-
var _fastCopy2 = _interopRequireDefault(_fastCopy);
|
|
12
|
-
|
|
13
|
-
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
14
|
-
|
|
15
|
-
function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }
|
|
16
|
-
|
|
17
|
-
var UNRESOLVED_LINK = {}; // unique object to avoid polyfill bloat using Symbol()
|
|
18
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const fast_copy_1 = require("fast-copy");
|
|
4
|
+
const UNRESOLVED_LINK = {}; // Unique object to avoid polyfill bloat using Symbol()
|
|
19
5
|
/**
|
|
20
|
-
*
|
|
6
|
+
* IsLink Function
|
|
21
7
|
* Checks if the object has sys.type "Link"
|
|
22
8
|
* @param object
|
|
23
9
|
*/
|
|
24
|
-
|
|
25
|
-
return object && object.sys && object.sys.type === 'Link';
|
|
26
|
-
};
|
|
27
|
-
|
|
10
|
+
const isLink = (object) => object && object.sys && object.sys.type === 'Link';
|
|
28
11
|
/**
|
|
29
|
-
*
|
|
12
|
+
* IsResourceLink Function
|
|
30
13
|
* Checks if the object has sys.type "ResourceLink"
|
|
31
14
|
* @param object
|
|
32
15
|
*/
|
|
33
|
-
|
|
34
|
-
return object && object.sys && object.sys.type === 'ResourceLink';
|
|
35
|
-
};
|
|
36
|
-
|
|
16
|
+
const isResourceLink = (object) => object && object.sys && object.sys.type === 'ResourceLink';
|
|
37
17
|
/**
|
|
38
18
|
* Creates a key with spaceId and a key without for entityMap
|
|
39
19
|
*
|
|
@@ -45,14 +25,12 @@ var isResourceLink = function isResourceLink(object) {
|
|
|
45
25
|
* @param {String} sys.space.id
|
|
46
26
|
* @return {string[]}
|
|
47
27
|
*/
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
return [`${sys.type}!${sys.id}`];
|
|
28
|
+
const makeEntityMapKeys = (sys) => {
|
|
29
|
+
if (sys.space && sys.environment) {
|
|
30
|
+
return [`${sys.type}!${sys.id}`, `${sys.space.sys.id}!${sys.environment.sys.id}!${sys.type}!${sys.id}`];
|
|
31
|
+
}
|
|
32
|
+
return [`${sys.type}!${sys.id}`];
|
|
54
33
|
};
|
|
55
|
-
|
|
56
34
|
/**
|
|
57
35
|
* Looks up in entityMap
|
|
58
36
|
*
|
|
@@ -64,149 +42,149 @@ var makeEntityMapKeys = function makeEntityMapKeys(sys) {
|
|
|
64
42
|
* @param {String} linkData.urn
|
|
65
43
|
* @return {String}
|
|
66
44
|
*/
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
if (spaceId && environmentId) {
|
|
75
|
-
return entityMap.get(`${spaceId}!${environmentId}!${linkType}!${entryId}`);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
return entityMap.get(`${linkType}!${entryId}`);
|
|
45
|
+
const lookupInEntityMap = (entityMap, linkData) => {
|
|
46
|
+
const { entryId, linkType, spaceId, environmentId } = linkData;
|
|
47
|
+
if (spaceId && environmentId) {
|
|
48
|
+
return entityMap.get(`${spaceId}!${environmentId}!${linkType}!${entryId}`);
|
|
49
|
+
}
|
|
50
|
+
return entityMap.get(`${linkType}!${entryId}`);
|
|
79
51
|
};
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
var _urn$match = urn.match(regExp),
|
|
89
|
-
_urn$match2 = _slicedToArray(_urn$match, 4),
|
|
90
|
-
_ = _urn$match2[0],
|
|
91
|
-
spaceId = _urn$match2[1],
|
|
92
|
-
_urn$match2$ = _urn$match2[2],
|
|
93
|
-
environmentId = _urn$match2$ === undefined ? 'master' : _urn$match2$,
|
|
94
|
-
entryId = _urn$match2[3];
|
|
95
|
-
|
|
96
|
-
return { spaceId, environmentId, entryId };
|
|
52
|
+
const getIdsFromUrn = (urn) => {
|
|
53
|
+
const regExp = /.*:spaces\/([^/]+)(?:\/environments\/([^/]+))?\/entries\/([^/]+)$/;
|
|
54
|
+
if (!regExp.test(urn)) {
|
|
55
|
+
return undefined;
|
|
56
|
+
}
|
|
57
|
+
// eslint-disable-next-line no-unused-vars
|
|
58
|
+
const [_, spaceId, environmentId = 'master', entryId] = urn.match(regExp);
|
|
59
|
+
return { spaceId, environmentId, entryId };
|
|
97
60
|
};
|
|
98
|
-
|
|
99
61
|
/**
|
|
100
|
-
*
|
|
62
|
+
* GetResolvedLink Function
|
|
101
63
|
*
|
|
102
64
|
* @param entityMap
|
|
103
65
|
* @param link
|
|
104
66
|
* @return {undefined}
|
|
105
67
|
*/
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
return lookupInEntityMap(entityMap, {
|
|
126
|
-
linkType: extractedLinkType,
|
|
127
|
-
entryId: _entryId,
|
|
128
|
-
spaceId,
|
|
129
|
-
environmentId
|
|
130
|
-
}) || UNRESOLVED_LINK;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
var entryId = link.sys.id;
|
|
134
|
-
|
|
135
|
-
return lookupInEntityMap(entityMap, { linkType, entryId }) || UNRESOLVED_LINK;
|
|
68
|
+
const getResolvedLink = (entityMap, link) => {
|
|
69
|
+
const { type, linkType } = link.sys;
|
|
70
|
+
if (type === 'ResourceLink') {
|
|
71
|
+
if (!linkType.startsWith('Contentful:')) {
|
|
72
|
+
return link;
|
|
73
|
+
}
|
|
74
|
+
const { urn } = link.sys;
|
|
75
|
+
const { spaceId, environmentId, entryId } = getIdsFromUrn(urn);
|
|
76
|
+
const extractedLinkType = linkType.split(':')[1];
|
|
77
|
+
return (lookupInEntityMap(entityMap, {
|
|
78
|
+
linkType: extractedLinkType,
|
|
79
|
+
entryId,
|
|
80
|
+
spaceId,
|
|
81
|
+
environmentId,
|
|
82
|
+
}) || UNRESOLVED_LINK);
|
|
83
|
+
}
|
|
84
|
+
const { id: entryId } = link.sys;
|
|
85
|
+
return lookupInEntityMap(entityMap, { linkType, entryId }) || UNRESOLVED_LINK;
|
|
136
86
|
};
|
|
137
|
-
|
|
138
87
|
/**
|
|
139
|
-
*
|
|
88
|
+
* CleanUpUnresolvedLinks Function
|
|
140
89
|
* - Removes unresolvable links from Arrays and Objects
|
|
141
90
|
*
|
|
142
91
|
* @param {Object[]|Object} input
|
|
143
92
|
*/
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
93
|
+
const cleanUpUnresolvedLinks = (input) => {
|
|
94
|
+
if (Array.isArray(input)) {
|
|
95
|
+
return input.filter((val) => val !== UNRESOLVED_LINK);
|
|
96
|
+
}
|
|
97
|
+
for (const key in input) {
|
|
98
|
+
if (input[key] === UNRESOLVED_LINK) {
|
|
99
|
+
delete input[key];
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return input;
|
|
103
|
+
};
|
|
104
|
+
const normalizeLink = (entityMap, link, removeUnresolved) => {
|
|
105
|
+
const resolvedLink = getResolvedLink(entityMap, link);
|
|
106
|
+
if (resolvedLink === UNRESOLVED_LINK) {
|
|
107
|
+
return removeUnresolved ? resolvedLink : link;
|
|
108
|
+
}
|
|
109
|
+
return resolvedLink;
|
|
110
|
+
};
|
|
111
|
+
const maybeNormalizeLink = (entityMap, maybeLink, removeUnresolved) => {
|
|
112
|
+
if (Array.isArray(maybeLink)) {
|
|
113
|
+
return maybeLink.reduce((acc, link) => {
|
|
114
|
+
const normalizedLink = maybeNormalizeLink(entityMap, link, removeUnresolved);
|
|
115
|
+
if (removeUnresolved && normalizedLink === UNRESOLVED_LINK) {
|
|
116
|
+
return acc;
|
|
117
|
+
}
|
|
118
|
+
acc.push(normalizedLink);
|
|
119
|
+
return acc;
|
|
120
|
+
}, []);
|
|
121
|
+
}
|
|
122
|
+
else if (typeof maybeLink === 'object') {
|
|
123
|
+
if (isLink(maybeLink) || isResourceLink(maybeLink)) {
|
|
124
|
+
return normalizeLink(entityMap, maybeLink, removeUnresolved);
|
|
125
|
+
}
|
|
153
126
|
}
|
|
154
|
-
|
|
155
|
-
return input;
|
|
127
|
+
return maybeLink;
|
|
156
128
|
};
|
|
157
|
-
|
|
158
129
|
/**
|
|
159
|
-
*
|
|
130
|
+
* WalkMutate Function
|
|
160
131
|
* @param input
|
|
161
132
|
* @param predicate
|
|
162
133
|
* @param mutator
|
|
163
134
|
* @param removeUnresolved
|
|
164
135
|
* @return {*}
|
|
165
136
|
*/
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
return input;
|
|
137
|
+
const walkMutate = (input, predicate, mutator, removeUnresolved) => {
|
|
138
|
+
if (predicate(input)) {
|
|
139
|
+
return mutator(input);
|
|
140
|
+
}
|
|
141
|
+
if (input && typeof input === 'object') {
|
|
142
|
+
for (const key in input) {
|
|
143
|
+
// eslint-disable-next-line no-prototype-builtins
|
|
144
|
+
if (input.hasOwnProperty(key)) {
|
|
145
|
+
input[key] = walkMutate(input[key], predicate, mutator, removeUnresolved);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
if (removeUnresolved) {
|
|
149
|
+
input = cleanUpUnresolvedLinks(input);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
return input;
|
|
183
153
|
};
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
154
|
+
const makeEntryObject = (item, itemEntryPoints) => {
|
|
155
|
+
if (!Array.isArray(itemEntryPoints)) {
|
|
156
|
+
return item;
|
|
157
|
+
}
|
|
158
|
+
const entryPoints = Object.keys(item).filter((ownKey) => itemEntryPoints.indexOf(ownKey) !== -1);
|
|
159
|
+
return entryPoints.reduce((entryObj, entryPoint) => {
|
|
160
|
+
entryObj[entryPoint] = item[entryPoint];
|
|
161
|
+
return entryObj;
|
|
162
|
+
}, {});
|
|
191
163
|
};
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
164
|
+
/**
|
|
165
|
+
* Only normalize the top level properties of the entrypoint (e.g. item.fields),
|
|
166
|
+
* as JSON fields can contain values that are objects that look like links, but are not.
|
|
167
|
+
*/
|
|
168
|
+
const normalizeFromEntryPoint = (entityMap, entryPoint, removeUnresolved) => {
|
|
169
|
+
if (!entryPoint) {
|
|
170
|
+
return undefined;
|
|
171
|
+
}
|
|
172
|
+
if (Array.isArray(entryPoint)) {
|
|
173
|
+
return maybeNormalizeLink(entityMap, entryPoint, removeUnresolved);
|
|
174
|
+
}
|
|
175
|
+
else if (typeof entryPoint === 'object') {
|
|
176
|
+
return Object.entries(entryPoint).reduce((acc, [key, val]) => {
|
|
177
|
+
const normalizedLink = maybeNormalizeLink(entityMap, val, removeUnresolved);
|
|
178
|
+
if (removeUnresolved && normalizedLink === UNRESOLVED_LINK) {
|
|
179
|
+
return acc;
|
|
180
|
+
}
|
|
181
|
+
acc[key] = normalizedLink;
|
|
182
|
+
return acc;
|
|
183
|
+
}, {});
|
|
184
|
+
}
|
|
206
185
|
};
|
|
207
|
-
|
|
208
186
|
/**
|
|
209
|
-
*
|
|
187
|
+
* ResolveResponse Function
|
|
210
188
|
* Resolves contentful response to normalized form.
|
|
211
189
|
* @param {Object} response Contentful response
|
|
212
190
|
* @param {{removeUnresolved: Boolean, itemEntryPoints: Array<String>}|{}} options
|
|
@@ -214,40 +192,30 @@ var makeEntryObject = function makeEntryObject(item, itemEntryPoints) {
|
|
|
214
192
|
* @param {Array<String>} options.itemEntryPoints - Resolve links only in those item properties
|
|
215
193
|
* @return {Object}
|
|
216
194
|
*/
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
195
|
+
const resolveResponse = (response, options) => {
|
|
196
|
+
options ||= {};
|
|
197
|
+
if (!response.items) {
|
|
198
|
+
return [];
|
|
199
|
+
}
|
|
200
|
+
const responseClone = (0, fast_copy_1.default)(response);
|
|
201
|
+
const allIncludes = Object.keys(responseClone.includes || {}).reduce((all, type) => [...all, ...response.includes[type]], []);
|
|
202
|
+
const allEntries = [...responseClone.items, ...allIncludes].filter((entity) => Boolean(entity.sys));
|
|
203
|
+
const entityMap = new Map(allEntries.reduce((acc, entity) => {
|
|
204
|
+
const entries = makeEntityMapKeys(entity.sys).map((key) => [key, entity]);
|
|
205
|
+
acc.push(...entries);
|
|
206
|
+
return acc;
|
|
207
|
+
}, []));
|
|
208
|
+
allEntries.forEach((item) => {
|
|
209
|
+
if (options.itemEntryPoints && options.itemEntryPoints.length) {
|
|
210
|
+
for (const entryPoint of options.itemEntryPoints) {
|
|
211
|
+
item[entryPoint] = normalizeFromEntryPoint(entityMap, item[entryPoint], options.removeUnresolved);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
else {
|
|
215
|
+
const entryObject = makeEntryObject(item, options.itemEntryPoints);
|
|
216
|
+
Object.assign(item, walkMutate(entryObject, (x) => isLink(x) || isResourceLink(x), (link) => normalizeLink(entityMap, link, options.removeUnresolved), options.removeUnresolved));
|
|
217
|
+
}
|
|
234
218
|
});
|
|
235
|
-
|
|
236
|
-
return acc;
|
|
237
|
-
}, []));
|
|
238
|
-
|
|
239
|
-
allEntries.forEach(function (item) {
|
|
240
|
-
var entryObject = makeEntryObject(item, options.itemEntryPoints);
|
|
241
|
-
|
|
242
|
-
Object.assign(item, walkMutate(entryObject, function (x) {
|
|
243
|
-
return isLink(x) || isResourceLink(x);
|
|
244
|
-
}, function (link) {
|
|
245
|
-
return normalizeLink(entityMap, link, options.removeUnresolved);
|
|
246
|
-
}, options.removeUnresolved));
|
|
247
|
-
});
|
|
248
|
-
|
|
249
|
-
return responseClone.items;
|
|
219
|
+
return responseClone.items;
|
|
250
220
|
};
|
|
251
|
-
|
|
252
221
|
exports.default = resolveResponse;
|
|
253
|
-
module.exports = exports.default;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export default resolveResponse;
|
|
2
|
+
/**
|
|
3
|
+
* ResolveResponse Function
|
|
4
|
+
* Resolves contentful response to normalized form.
|
|
5
|
+
* @param {Object} response Contentful response
|
|
6
|
+
* @param {{removeUnresolved: Boolean, itemEntryPoints: Array<String>}|{}} options
|
|
7
|
+
* @param {Boolean} options.removeUnresolved - Remove unresolved links default:false
|
|
8
|
+
* @param {Array<String>} options.itemEntryPoints - Resolve links only in those item properties
|
|
9
|
+
* @return {Object}
|
|
10
|
+
*/
|
|
11
|
+
declare function resolveResponse(response: any, options: {
|
|
12
|
+
removeUnresolved: boolean;
|
|
13
|
+
itemEntryPoints: Array<string>;
|
|
14
|
+
} | {}): any;
|
package/dist/esm/index.js
CHANGED
|
@@ -1,31 +1,17 @@
|
|
|
1
|
-
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
|
|
2
|
-
|
|
3
|
-
var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }();
|
|
4
|
-
|
|
5
|
-
function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }
|
|
6
|
-
|
|
7
1
|
import copy from 'fast-copy';
|
|
8
|
-
|
|
9
|
-
var UNRESOLVED_LINK = {}; // unique object to avoid polyfill bloat using Symbol()
|
|
10
|
-
|
|
2
|
+
const UNRESOLVED_LINK = {}; // Unique object to avoid polyfill bloat using Symbol()
|
|
11
3
|
/**
|
|
12
|
-
*
|
|
4
|
+
* IsLink Function
|
|
13
5
|
* Checks if the object has sys.type "Link"
|
|
14
6
|
* @param object
|
|
15
7
|
*/
|
|
16
|
-
|
|
17
|
-
return object && object.sys && object.sys.type === 'Link';
|
|
18
|
-
};
|
|
19
|
-
|
|
8
|
+
const isLink = (object) => object && object.sys && object.sys.type === 'Link';
|
|
20
9
|
/**
|
|
21
|
-
*
|
|
10
|
+
* IsResourceLink Function
|
|
22
11
|
* Checks if the object has sys.type "ResourceLink"
|
|
23
12
|
* @param object
|
|
24
13
|
*/
|
|
25
|
-
|
|
26
|
-
return object && object.sys && object.sys.type === 'ResourceLink';
|
|
27
|
-
};
|
|
28
|
-
|
|
14
|
+
const isResourceLink = (object) => object && object.sys && object.sys.type === 'ResourceLink';
|
|
29
15
|
/**
|
|
30
16
|
* Creates a key with spaceId and a key without for entityMap
|
|
31
17
|
*
|
|
@@ -37,14 +23,12 @@ var isResourceLink = function isResourceLink(object) {
|
|
|
37
23
|
* @param {String} sys.space.id
|
|
38
24
|
* @return {string[]}
|
|
39
25
|
*/
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
return [sys.type + '!' + sys.id];
|
|
26
|
+
const makeEntityMapKeys = (sys) => {
|
|
27
|
+
if (sys.space && sys.environment) {
|
|
28
|
+
return [`${sys.type}!${sys.id}`, `${sys.space.sys.id}!${sys.environment.sys.id}!${sys.type}!${sys.id}`];
|
|
29
|
+
}
|
|
30
|
+
return [`${sys.type}!${sys.id}`];
|
|
46
31
|
};
|
|
47
|
-
|
|
48
32
|
/**
|
|
49
33
|
* Looks up in entityMap
|
|
50
34
|
*
|
|
@@ -56,149 +40,149 @@ var makeEntityMapKeys = function makeEntityMapKeys(sys) {
|
|
|
56
40
|
* @param {String} linkData.urn
|
|
57
41
|
* @return {String}
|
|
58
42
|
*/
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
if (spaceId && environmentId) {
|
|
67
|
-
return entityMap.get(spaceId + '!' + environmentId + '!' + linkType + '!' + entryId);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
return entityMap.get(linkType + '!' + entryId);
|
|
43
|
+
const lookupInEntityMap = (entityMap, linkData) => {
|
|
44
|
+
const { entryId, linkType, spaceId, environmentId } = linkData;
|
|
45
|
+
if (spaceId && environmentId) {
|
|
46
|
+
return entityMap.get(`${spaceId}!${environmentId}!${linkType}!${entryId}`);
|
|
47
|
+
}
|
|
48
|
+
return entityMap.get(`${linkType}!${entryId}`);
|
|
71
49
|
};
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
var _urn$match = urn.match(regExp),
|
|
81
|
-
_urn$match2 = _slicedToArray(_urn$match, 4),
|
|
82
|
-
_ = _urn$match2[0],
|
|
83
|
-
spaceId = _urn$match2[1],
|
|
84
|
-
_urn$match2$ = _urn$match2[2],
|
|
85
|
-
environmentId = _urn$match2$ === undefined ? 'master' : _urn$match2$,
|
|
86
|
-
entryId = _urn$match2[3];
|
|
87
|
-
|
|
88
|
-
return { spaceId: spaceId, environmentId: environmentId, entryId: entryId };
|
|
50
|
+
const getIdsFromUrn = (urn) => {
|
|
51
|
+
const regExp = /.*:spaces\/([^/]+)(?:\/environments\/([^/]+))?\/entries\/([^/]+)$/;
|
|
52
|
+
if (!regExp.test(urn)) {
|
|
53
|
+
return undefined;
|
|
54
|
+
}
|
|
55
|
+
// eslint-disable-next-line no-unused-vars
|
|
56
|
+
const [_, spaceId, environmentId = 'master', entryId] = urn.match(regExp);
|
|
57
|
+
return { spaceId, environmentId, entryId };
|
|
89
58
|
};
|
|
90
|
-
|
|
91
59
|
/**
|
|
92
|
-
*
|
|
60
|
+
* GetResolvedLink Function
|
|
93
61
|
*
|
|
94
62
|
* @param entityMap
|
|
95
63
|
* @param link
|
|
96
64
|
* @return {undefined}
|
|
97
65
|
*/
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
return lookupInEntityMap(entityMap, {
|
|
118
|
-
linkType: extractedLinkType,
|
|
119
|
-
entryId: _entryId,
|
|
120
|
-
spaceId: spaceId,
|
|
121
|
-
environmentId: environmentId
|
|
122
|
-
}) || UNRESOLVED_LINK;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
var entryId = link.sys.id;
|
|
126
|
-
|
|
127
|
-
return lookupInEntityMap(entityMap, { linkType: linkType, entryId: entryId }) || UNRESOLVED_LINK;
|
|
66
|
+
const getResolvedLink = (entityMap, link) => {
|
|
67
|
+
const { type, linkType } = link.sys;
|
|
68
|
+
if (type === 'ResourceLink') {
|
|
69
|
+
if (!linkType.startsWith('Contentful:')) {
|
|
70
|
+
return link;
|
|
71
|
+
}
|
|
72
|
+
const { urn } = link.sys;
|
|
73
|
+
const { spaceId, environmentId, entryId } = getIdsFromUrn(urn);
|
|
74
|
+
const extractedLinkType = linkType.split(':')[1];
|
|
75
|
+
return (lookupInEntityMap(entityMap, {
|
|
76
|
+
linkType: extractedLinkType,
|
|
77
|
+
entryId,
|
|
78
|
+
spaceId,
|
|
79
|
+
environmentId,
|
|
80
|
+
}) || UNRESOLVED_LINK);
|
|
81
|
+
}
|
|
82
|
+
const { id: entryId } = link.sys;
|
|
83
|
+
return lookupInEntityMap(entityMap, { linkType, entryId }) || UNRESOLVED_LINK;
|
|
128
84
|
};
|
|
129
|
-
|
|
130
85
|
/**
|
|
131
|
-
*
|
|
86
|
+
* CleanUpUnresolvedLinks Function
|
|
132
87
|
* - Removes unresolvable links from Arrays and Objects
|
|
133
88
|
*
|
|
134
89
|
* @param {Object[]|Object} input
|
|
135
90
|
*/
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
91
|
+
const cleanUpUnresolvedLinks = (input) => {
|
|
92
|
+
if (Array.isArray(input)) {
|
|
93
|
+
return input.filter((val) => val !== UNRESOLVED_LINK);
|
|
94
|
+
}
|
|
95
|
+
for (const key in input) {
|
|
96
|
+
if (input[key] === UNRESOLVED_LINK) {
|
|
97
|
+
delete input[key];
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return input;
|
|
101
|
+
};
|
|
102
|
+
const normalizeLink = (entityMap, link, removeUnresolved) => {
|
|
103
|
+
const resolvedLink = getResolvedLink(entityMap, link);
|
|
104
|
+
if (resolvedLink === UNRESOLVED_LINK) {
|
|
105
|
+
return removeUnresolved ? resolvedLink : link;
|
|
106
|
+
}
|
|
107
|
+
return resolvedLink;
|
|
108
|
+
};
|
|
109
|
+
const maybeNormalizeLink = (entityMap, maybeLink, removeUnresolved) => {
|
|
110
|
+
if (Array.isArray(maybeLink)) {
|
|
111
|
+
return maybeLink.reduce((acc, link) => {
|
|
112
|
+
const normalizedLink = maybeNormalizeLink(entityMap, link, removeUnresolved);
|
|
113
|
+
if (removeUnresolved && normalizedLink === UNRESOLVED_LINK) {
|
|
114
|
+
return acc;
|
|
115
|
+
}
|
|
116
|
+
acc.push(normalizedLink);
|
|
117
|
+
return acc;
|
|
118
|
+
}, []);
|
|
119
|
+
}
|
|
120
|
+
else if (typeof maybeLink === 'object') {
|
|
121
|
+
if (isLink(maybeLink) || isResourceLink(maybeLink)) {
|
|
122
|
+
return normalizeLink(entityMap, maybeLink, removeUnresolved);
|
|
123
|
+
}
|
|
145
124
|
}
|
|
146
|
-
|
|
147
|
-
return input;
|
|
125
|
+
return maybeLink;
|
|
148
126
|
};
|
|
149
|
-
|
|
150
127
|
/**
|
|
151
|
-
*
|
|
128
|
+
* WalkMutate Function
|
|
152
129
|
* @param input
|
|
153
130
|
* @param predicate
|
|
154
131
|
* @param mutator
|
|
155
132
|
* @param removeUnresolved
|
|
156
133
|
* @return {*}
|
|
157
134
|
*/
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
return input;
|
|
135
|
+
const walkMutate = (input, predicate, mutator, removeUnresolved) => {
|
|
136
|
+
if (predicate(input)) {
|
|
137
|
+
return mutator(input);
|
|
138
|
+
}
|
|
139
|
+
if (input && typeof input === 'object') {
|
|
140
|
+
for (const key in input) {
|
|
141
|
+
// eslint-disable-next-line no-prototype-builtins
|
|
142
|
+
if (input.hasOwnProperty(key)) {
|
|
143
|
+
input[key] = walkMutate(input[key], predicate, mutator, removeUnresolved);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
if (removeUnresolved) {
|
|
147
|
+
input = cleanUpUnresolvedLinks(input);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
return input;
|
|
175
151
|
};
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
152
|
+
const makeEntryObject = (item, itemEntryPoints) => {
|
|
153
|
+
if (!Array.isArray(itemEntryPoints)) {
|
|
154
|
+
return item;
|
|
155
|
+
}
|
|
156
|
+
const entryPoints = Object.keys(item).filter((ownKey) => itemEntryPoints.indexOf(ownKey) !== -1);
|
|
157
|
+
return entryPoints.reduce((entryObj, entryPoint) => {
|
|
158
|
+
entryObj[entryPoint] = item[entryPoint];
|
|
159
|
+
return entryObj;
|
|
160
|
+
}, {});
|
|
183
161
|
};
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
162
|
+
/**
|
|
163
|
+
* Only normalize the top level properties of the entrypoint (e.g. item.fields),
|
|
164
|
+
* as JSON fields can contain values that are objects that look like links, but are not.
|
|
165
|
+
*/
|
|
166
|
+
const normalizeFromEntryPoint = (entityMap, entryPoint, removeUnresolved) => {
|
|
167
|
+
if (!entryPoint) {
|
|
168
|
+
return undefined;
|
|
169
|
+
}
|
|
170
|
+
if (Array.isArray(entryPoint)) {
|
|
171
|
+
return maybeNormalizeLink(entityMap, entryPoint, removeUnresolved);
|
|
172
|
+
}
|
|
173
|
+
else if (typeof entryPoint === 'object') {
|
|
174
|
+
return Object.entries(entryPoint).reduce((acc, [key, val]) => {
|
|
175
|
+
const normalizedLink = maybeNormalizeLink(entityMap, val, removeUnresolved);
|
|
176
|
+
if (removeUnresolved && normalizedLink === UNRESOLVED_LINK) {
|
|
177
|
+
return acc;
|
|
178
|
+
}
|
|
179
|
+
acc[key] = normalizedLink;
|
|
180
|
+
return acc;
|
|
181
|
+
}, {});
|
|
182
|
+
}
|
|
198
183
|
};
|
|
199
|
-
|
|
200
184
|
/**
|
|
201
|
-
*
|
|
185
|
+
* ResolveResponse Function
|
|
202
186
|
* Resolves contentful response to normalized form.
|
|
203
187
|
* @param {Object} response Contentful response
|
|
204
188
|
* @param {{removeUnresolved: Boolean, itemEntryPoints: Array<String>}|{}} options
|
|
@@ -206,39 +190,30 @@ var makeEntryObject = function makeEntryObject(item, itemEntryPoints) {
|
|
|
206
190
|
* @param {Array<String>} options.itemEntryPoints - Resolve links only in those item properties
|
|
207
191
|
* @return {Object}
|
|
208
192
|
*/
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
193
|
+
const resolveResponse = (response, options) => {
|
|
194
|
+
options ||= {};
|
|
195
|
+
if (!response.items) {
|
|
196
|
+
return [];
|
|
197
|
+
}
|
|
198
|
+
const responseClone = copy(response);
|
|
199
|
+
const allIncludes = Object.keys(responseClone.includes || {}).reduce((all, type) => [...all, ...response.includes[type]], []);
|
|
200
|
+
const allEntries = [...responseClone.items, ...allIncludes].filter((entity) => Boolean(entity.sys));
|
|
201
|
+
const entityMap = new Map(allEntries.reduce((acc, entity) => {
|
|
202
|
+
const entries = makeEntityMapKeys(entity.sys).map((key) => [key, entity]);
|
|
203
|
+
acc.push(...entries);
|
|
204
|
+
return acc;
|
|
205
|
+
}, []));
|
|
206
|
+
allEntries.forEach((item) => {
|
|
207
|
+
if (options.itemEntryPoints && options.itemEntryPoints.length) {
|
|
208
|
+
for (const entryPoint of options.itemEntryPoints) {
|
|
209
|
+
item[entryPoint] = normalizeFromEntryPoint(entityMap, item[entryPoint], options.removeUnresolved);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
else {
|
|
213
|
+
const entryObject = makeEntryObject(item, options.itemEntryPoints);
|
|
214
|
+
Object.assign(item, walkMutate(entryObject, (x) => isLink(x) || isResourceLink(x), (link) => normalizeLink(entityMap, link, options.removeUnresolved), options.removeUnresolved));
|
|
215
|
+
}
|
|
226
216
|
});
|
|
227
|
-
|
|
228
|
-
return acc;
|
|
229
|
-
}, []));
|
|
230
|
-
|
|
231
|
-
allEntries.forEach(function (item) {
|
|
232
|
-
var entryObject = makeEntryObject(item, options.itemEntryPoints);
|
|
233
|
-
|
|
234
|
-
Object.assign(item, walkMutate(entryObject, function (x) {
|
|
235
|
-
return isLink(x) || isResourceLink(x);
|
|
236
|
-
}, function (link) {
|
|
237
|
-
return normalizeLink(entityMap, link, options.removeUnresolved);
|
|
238
|
-
}, options.removeUnresolved));
|
|
239
|
-
});
|
|
240
|
-
|
|
241
|
-
return responseClone.items;
|
|
217
|
+
return responseClone.items;
|
|
242
218
|
};
|
|
243
|
-
|
|
244
|
-
export default resolveResponse;
|
|
219
|
+
export default resolveResponse;
|
package/package.json
CHANGED
|
@@ -1,20 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "contentful-resolve-response",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "",
|
|
3
|
+
"version": "1.9.1",
|
|
5
4
|
"main": "./dist/cjs/index.js",
|
|
6
5
|
"module": "./dist/esm/index.js",
|
|
7
|
-
"jsnext:main": "./dist/esm/index.js",
|
|
8
6
|
"scripts": {
|
|
9
|
-
"build": "
|
|
7
|
+
"build": "tsc --project tsconfig.cjs.json && tsc --project tsconfig.esm.json",
|
|
10
8
|
"lint": "eslint index.js test",
|
|
11
|
-
"test": "
|
|
9
|
+
"test": "mocha --import=tsx 'test/**/*-test.js'",
|
|
12
10
|
"test-watch": "npm run test -- --watch",
|
|
13
11
|
"precommit": "npm run lint",
|
|
14
12
|
"prepush": "npm run test",
|
|
15
13
|
"prepublishOnly": "npm run build",
|
|
16
14
|
"format": "prettier --config ./.prettierrc --write \"{*.js,**/*.js,*.ts,**/*.ts,*.json,**/*.json}\"",
|
|
17
|
-
"commitlint-circle": "commitlint-circle",
|
|
18
15
|
"semantic-release": "semantic-release"
|
|
19
16
|
},
|
|
20
17
|
"repository": {
|
|
@@ -28,30 +25,21 @@
|
|
|
28
25
|
"fast-copy": "^2.1.7"
|
|
29
26
|
},
|
|
30
27
|
"devDependencies": {
|
|
31
|
-
"@commitlint/cli": "^
|
|
28
|
+
"@commitlint/cli": "^19.6.0",
|
|
32
29
|
"@commitlint/config-conventional": "^18.0.0",
|
|
33
|
-
"@
|
|
30
|
+
"@eslint/js": "^9.16.0",
|
|
34
31
|
"@semantic-release/changelog": "^6.0.1",
|
|
35
32
|
"@semantic-release/git": "^10.0.1",
|
|
36
|
-
"@timbeyer/commitlint-circle": "^4.1.1",
|
|
37
|
-
"babel-cli": "^6.26.0",
|
|
38
|
-
"babel-plugin-add-module-exports": "^1.0.2",
|
|
39
|
-
"babel-preset-env": "^1.7.0",
|
|
40
|
-
"babel-register": "^6.26.0",
|
|
41
33
|
"chai": "^4.3.6",
|
|
42
34
|
"dirty-chai": "^2.0.1",
|
|
43
|
-
"eslint": "^
|
|
44
|
-
"eslint-
|
|
45
|
-
"eslint-plugin-import": "^2.25.4",
|
|
46
|
-
"eslint-plugin-mocha": "^10.0.3",
|
|
47
|
-
"eslint-plugin-node": "^11.1.0",
|
|
48
|
-
"eslint-plugin-prettier": "^5.0.0",
|
|
49
|
-
"eslint-plugin-promise": "^6.0.0",
|
|
50
|
-
"eslint-plugin-standard": "^5.0.0",
|
|
35
|
+
"eslint": "^9.15.0",
|
|
36
|
+
"eslint-plugin-mocha": "^10.5.0",
|
|
51
37
|
"husky": "^9.0.6",
|
|
52
|
-
"mocha": "^
|
|
38
|
+
"mocha": "^11.0.1",
|
|
53
39
|
"prettier": "^3.0.0",
|
|
54
|
-
"semantic-release": "^19.0.5"
|
|
40
|
+
"semantic-release": "^19.0.5",
|
|
41
|
+
"tsx": "^4.19.2",
|
|
42
|
+
"typescript": "^5.7.2"
|
|
55
43
|
},
|
|
56
44
|
"files": [
|
|
57
45
|
"dist"
|