decap-cms-lib-util 2.16.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/CHANGELOG.md +601 -0
  2. package/LICENSE +22 -0
  3. package/README.md +22 -0
  4. package/dist/decap-cms-lib-util.js +3 -0
  5. package/dist/decap-cms-lib-util.js.LICENSE.txt +15 -0
  6. package/dist/decap-cms-lib-util.js.map +1 -0
  7. package/dist/esm/API.js +177 -0
  8. package/dist/esm/APIError.js +47 -0
  9. package/dist/esm/APIUtils.js +48 -0
  10. package/dist/esm/AccessTokenError.js +41 -0
  11. package/dist/esm/Cursor.js +135 -0
  12. package/dist/esm/EditorialWorkflowError.js +43 -0
  13. package/dist/esm/asyncLock.js +45 -0
  14. package/dist/esm/backendUtil.js +98 -0
  15. package/dist/esm/getBlobSHA.js +19 -0
  16. package/dist/esm/git-lfs.js +123 -0
  17. package/dist/esm/implementation.js +304 -0
  18. package/dist/esm/index.js +403 -0
  19. package/dist/esm/loadScript.js +27 -0
  20. package/dist/esm/localForage.js +25 -0
  21. package/dist/esm/path.js +93 -0
  22. package/dist/esm/promise.js +23 -0
  23. package/dist/esm/types/semaphore.d.js +1 -0
  24. package/dist/esm/unsentRequest.js +123 -0
  25. package/package.json +29 -0
  26. package/src/API.ts +220 -0
  27. package/src/APIError.ts +17 -0
  28. package/src/APIUtils.ts +38 -0
  29. package/src/AccessTokenError.ts +11 -0
  30. package/src/Cursor.ts +178 -0
  31. package/src/EditorialWorkflowError.ts +12 -0
  32. package/src/__tests__/api.spec.js +12 -0
  33. package/src/__tests__/apiUtils.spec.js +74 -0
  34. package/src/__tests__/asyncLock.spec.js +85 -0
  35. package/src/__tests__/backendUtil.spec.js +97 -0
  36. package/src/__tests__/implementation.spec.js +58 -0
  37. package/src/__tests__/path.spec.js +53 -0
  38. package/src/__tests__/unsentRequest.spec.js +19 -0
  39. package/src/asyncLock.ts +43 -0
  40. package/src/backendUtil.ts +120 -0
  41. package/src/getBlobSHA.ts +12 -0
  42. package/src/git-lfs.ts +133 -0
  43. package/src/implementation.ts +573 -0
  44. package/src/index.ts +210 -0
  45. package/src/loadScript.js +24 -0
  46. package/src/localForage.ts +21 -0
  47. package/src/path.ts +86 -0
  48. package/src/promise.ts +21 -0
  49. package/src/types/semaphore.d.ts +5 -0
  50. package/src/unsentRequest.js +133 -0
  51. package/webpack.config.js +3 -0
@@ -0,0 +1,123 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.createPointerFile = createPointerFile;
7
+ exports.getLargeMediaFilteredMediaFiles = getLargeMediaFilteredMediaFiles;
8
+ exports.getLargeMediaPatternsFromGitAttributesFile = void 0;
9
+ exports.getPointerFileForMediaFileObj = getPointerFileForMediaFileObj;
10
+ exports.parsePointerFile = void 0;
11
+ var _map2 = _interopRequireDefault(require("lodash/fp/map"));
12
+ var _fromPairs2 = _interopRequireDefault(require("lodash/fp/fromPairs"));
13
+ var _flow2 = _interopRequireDefault(require("lodash/fp/flow"));
14
+ var _filter2 = _interopRequireDefault(require("lodash/fp/filter"));
15
+ var _getBlobSHA = _interopRequireDefault(require("./getBlobSHA"));
16
+ const _excluded = ["size", "oid"]; //
17
+ // Pointer file parsing
18
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
19
+ function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
20
+ function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
21
+ function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
22
+ function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return typeof key === "symbol" ? key : String(key); }
23
+ function _toPrimitive(input, hint) { if (typeof input !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (typeof res !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); }
24
+ function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }
25
+ function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }
26
+ function splitIntoLines(str) {
27
+ return str.split('\n');
28
+ }
29
+ function splitIntoWords(str) {
30
+ return str.split(/\s+/g);
31
+ }
32
+ function isNonEmptyString(str) {
33
+ return str !== '';
34
+ }
35
+ const withoutEmptyLines = (0, _flow2.default)([(0, _map2.default)(str => str.trim()), (0, _filter2.default)(isNonEmptyString)]);
36
+ const parsePointerFile = (0, _flow2.default)([splitIntoLines, withoutEmptyLines, (0, _map2.default)(splitIntoWords), _fromPairs2.default, _ref => {
37
+ let {
38
+ size,
39
+ oid
40
+ } = _ref,
41
+ rest = _objectWithoutProperties(_ref, _excluded);
42
+ return _objectSpread({
43
+ size: parseInt(size),
44
+ sha: oid === null || oid === void 0 ? void 0 : oid.split(':')[1]
45
+ }, rest);
46
+ }]);
47
+
48
+ //
49
+ // .gitattributes file parsing
50
+ exports.parsePointerFile = parsePointerFile;
51
+ function removeGitAttributesCommentsFromLine(line) {
52
+ return line.split('#')[0];
53
+ }
54
+ function parseGitPatternAttribute(attributeString) {
55
+ // There are three kinds of attribute settings:
56
+ // - a key=val pair sets an attribute to a specific value
57
+ // - a key without a value and a leading hyphen sets an attribute to false
58
+ // - a key without a value and no leading hyphen sets an attribute
59
+ // to true
60
+ if (attributeString.includes('=')) {
61
+ return attributeString.split('=');
62
+ }
63
+ if (attributeString.startsWith('-')) {
64
+ return [attributeString.slice(1), false];
65
+ }
66
+ return [attributeString, true];
67
+ }
68
+ const parseGitPatternAttributes = (0, _flow2.default)([(0, _map2.default)(parseGitPatternAttribute), _fromPairs2.default]);
69
+ const parseGitAttributesPatternLine = (0, _flow2.default)([splitIntoWords, ([pattern, ...attributes]) => [pattern, parseGitPatternAttributes(attributes)]]);
70
+ const parseGitAttributesFileToPatternAttributePairs = (0, _flow2.default)([splitIntoLines, (0, _map2.default)(removeGitAttributesCommentsFromLine), withoutEmptyLines, (0, _map2.default)(parseGitAttributesPatternLine)]);
71
+ const getLargeMediaPatternsFromGitAttributesFile = (0, _flow2.default)([parseGitAttributesFileToPatternAttributePairs, (0, _filter2.default)(([, attributes]) => attributes.filter === 'lfs' && attributes.diff === 'lfs' && attributes.merge === 'lfs'), (0, _map2.default)(([pattern]) => pattern)]);
72
+ exports.getLargeMediaPatternsFromGitAttributesFile = getLargeMediaPatternsFromGitAttributesFile;
73
+ function createPointerFile({
74
+ size,
75
+ sha
76
+ }) {
77
+ return `\
78
+ version https://git-lfs.github.com/spec/v1
79
+ oid sha256:${sha}
80
+ size ${size}
81
+ `;
82
+ }
83
+ async function getPointerFileForMediaFileObj(client, fileObj, path) {
84
+ const {
85
+ name,
86
+ size
87
+ } = fileObj;
88
+ const sha = await (0, _getBlobSHA.default)(fileObj);
89
+ await client.uploadResource({
90
+ sha,
91
+ size
92
+ }, fileObj);
93
+ const pointerFileString = createPointerFile({
94
+ sha,
95
+ size
96
+ });
97
+ const pointerFileBlob = new Blob([pointerFileString]);
98
+ const pointerFile = new File([pointerFileBlob], name, {
99
+ type: 'text/plain'
100
+ });
101
+ const pointerFileSHA = await (0, _getBlobSHA.default)(pointerFile);
102
+ return {
103
+ fileObj: pointerFile,
104
+ size: pointerFileBlob.size,
105
+ sha: pointerFileSHA,
106
+ raw: pointerFileString,
107
+ path
108
+ };
109
+ }
110
+ async function getLargeMediaFilteredMediaFiles(client, mediaFiles) {
111
+ return await Promise.all(mediaFiles.map(async mediaFile => {
112
+ const {
113
+ fileObj,
114
+ path
115
+ } = mediaFile;
116
+ const fixedPath = path.startsWith('/') ? path.slice(1) : path;
117
+ if (!client.matchPath(fixedPath)) {
118
+ return mediaFile;
119
+ }
120
+ const pointerFileDetails = await getPointerFileForMediaFileObj(client, fileObj, path);
121
+ return _objectSpread(_objectSpread({}, mediaFile), pointerFileDetails);
122
+ }));
123
+ }
@@ -0,0 +1,304 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.allEntriesByFolder = allEntriesByFolder;
7
+ exports.blobToFileObj = blobToFileObj;
8
+ exports.entriesByFiles = entriesByFiles;
9
+ exports.entriesByFolder = entriesByFolder;
10
+ exports.getLocalTree = getLocalTree;
11
+ exports.getMediaAsBlob = getMediaAsBlob;
12
+ exports.getMediaDisplayURL = getMediaDisplayURL;
13
+ exports.persistLocalTree = persistLocalTree;
14
+ exports.runWithLock = runWithLock;
15
+ exports.unpublishedEntries = unpublishedEntries;
16
+ var _sortBy2 = _interopRequireDefault(require("lodash/sortBy"));
17
+ var _unionBy2 = _interopRequireDefault(require("lodash/unionBy"));
18
+ var _semaphore = _interopRequireDefault(require("semaphore"));
19
+ var _path = require("./path");
20
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
21
+ function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
22
+ function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
23
+ function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
24
+ function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return typeof key === "symbol" ? key : String(key); }
25
+ function _toPrimitive(input, hint) { if (typeof input !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (typeof res !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); }
26
+ const MAX_CONCURRENT_DOWNLOADS = 10;
27
+ async function fetchFiles(files, readFile, readFileMetadata, apiName) {
28
+ const sem = (0, _semaphore.default)(MAX_CONCURRENT_DOWNLOADS);
29
+ const promises = [];
30
+ files.forEach(file => {
31
+ promises.push(new Promise(resolve => sem.take(async () => {
32
+ try {
33
+ const [data, fileMetadata] = await Promise.all([readFile(file.path, file.id, {
34
+ parseText: true
35
+ }), readFileMetadata(file.path, file.id)]);
36
+ resolve({
37
+ file: _objectSpread(_objectSpread({}, file), fileMetadata),
38
+ data: data
39
+ });
40
+ sem.leave();
41
+ } catch (error) {
42
+ sem.leave();
43
+ console.error(`failed to load file from ${apiName}: ${file.path}`);
44
+ resolve({
45
+ error: true
46
+ });
47
+ }
48
+ })));
49
+ });
50
+ return Promise.all(promises).then(loadedEntries => loadedEntries.filter(loadedEntry => !loadedEntry.error));
51
+ }
52
+ async function entriesByFolder(listFiles, readFile, readFileMetadata, apiName) {
53
+ const files = await listFiles();
54
+ return fetchFiles(files, readFile, readFileMetadata, apiName);
55
+ }
56
+ async function entriesByFiles(files, readFile, readFileMetadata, apiName) {
57
+ return fetchFiles(files, readFile, readFileMetadata, apiName);
58
+ }
59
+ async function unpublishedEntries(listEntriesKeys) {
60
+ try {
61
+ const keys = await listEntriesKeys();
62
+ return keys;
63
+ } catch (error) {
64
+ if (error.message === 'Not Found') {
65
+ return Promise.resolve([]);
66
+ }
67
+ throw error;
68
+ }
69
+ }
70
+ function blobToFileObj(name, blob) {
71
+ const options = name.match(/.svg$/) ? {
72
+ type: 'image/svg+xml'
73
+ } : {};
74
+ return new File([blob], name, options);
75
+ }
76
+ async function getMediaAsBlob(path, id, readFile) {
77
+ let blob;
78
+ if (path.match(/.svg$/)) {
79
+ const text = await readFile(path, id, {
80
+ parseText: true
81
+ });
82
+ blob = new Blob([text], {
83
+ type: 'image/svg+xml'
84
+ });
85
+ } else {
86
+ blob = await readFile(path, id, {
87
+ parseText: false
88
+ });
89
+ }
90
+ return blob;
91
+ }
92
+ async function getMediaDisplayURL(displayURL, readFile, semaphore) {
93
+ const {
94
+ path,
95
+ id
96
+ } = displayURL;
97
+ return new Promise((resolve, reject) => semaphore.take(() => getMediaAsBlob(path, id, readFile).then(blob => URL.createObjectURL(blob)).then(resolve, reject).finally(() => semaphore.leave())));
98
+ }
99
+ async function runWithLock(lock, func, message) {
100
+ try {
101
+ const acquired = await lock.acquire();
102
+ if (!acquired) {
103
+ console.warn(message);
104
+ }
105
+ const result = await func();
106
+ return result;
107
+ } finally {
108
+ lock.release();
109
+ }
110
+ }
111
+ const LOCAL_KEY = 'git.local';
112
+ function getLocalKey({
113
+ branch,
114
+ folder,
115
+ extension,
116
+ depth
117
+ }) {
118
+ return `${LOCAL_KEY}.${branch}.${folder}.${extension}.${depth}`;
119
+ }
120
+ async function persistLocalTree({
121
+ localForage,
122
+ localTree,
123
+ branch,
124
+ folder,
125
+ extension,
126
+ depth
127
+ }) {
128
+ await localForage.setItem(getLocalKey({
129
+ branch,
130
+ folder,
131
+ extension,
132
+ depth
133
+ }), localTree);
134
+ }
135
+ async function getLocalTree({
136
+ localForage,
137
+ branch,
138
+ folder,
139
+ extension,
140
+ depth
141
+ }) {
142
+ const localTree = await localForage.getItem(getLocalKey({
143
+ branch,
144
+ folder,
145
+ extension,
146
+ depth
147
+ }));
148
+ return localTree;
149
+ }
150
+ async function getDiffFromLocalTree({
151
+ branch,
152
+ localTree,
153
+ folder,
154
+ getDifferences,
155
+ filterFile,
156
+ getFileId
157
+ }) {
158
+ const diff = await getDifferences(branch.sha, localTree.head);
159
+ const diffFiles = diff.filter(d => {
160
+ var _d$oldPath, _d$newPath;
161
+ return ((_d$oldPath = d.oldPath) === null || _d$oldPath === void 0 ? void 0 : _d$oldPath.startsWith(folder)) || ((_d$newPath = d.newPath) === null || _d$newPath === void 0 ? void 0 : _d$newPath.startsWith(folder));
162
+ }).reduce((acc, d) => {
163
+ if (d.status === 'renamed') {
164
+ acc.push({
165
+ path: d.oldPath,
166
+ name: (0, _path.basename)(d.oldPath),
167
+ deleted: true
168
+ });
169
+ acc.push({
170
+ path: d.newPath,
171
+ name: (0, _path.basename)(d.newPath),
172
+ deleted: false
173
+ });
174
+ } else if (d.status === 'deleted') {
175
+ acc.push({
176
+ path: d.oldPath,
177
+ name: (0, _path.basename)(d.oldPath),
178
+ deleted: true
179
+ });
180
+ } else {
181
+ acc.push({
182
+ path: d.newPath || d.oldPath,
183
+ name: (0, _path.basename)(d.newPath || d.oldPath),
184
+ deleted: false
185
+ });
186
+ }
187
+ return acc;
188
+ }, []).filter(filterFile);
189
+ const diffFilesWithIds = await Promise.all(diffFiles.map(async file => {
190
+ if (!file.deleted) {
191
+ const id = await getFileId(file.path);
192
+ return _objectSpread(_objectSpread({}, file), {}, {
193
+ id
194
+ });
195
+ } else {
196
+ return _objectSpread(_objectSpread({}, file), {}, {
197
+ id: ''
198
+ });
199
+ }
200
+ }));
201
+ return diffFilesWithIds;
202
+ }
203
+ async function allEntriesByFolder({
204
+ listAllFiles,
205
+ readFile,
206
+ readFileMetadata,
207
+ apiName,
208
+ branch,
209
+ localForage,
210
+ folder,
211
+ extension,
212
+ depth,
213
+ getDefaultBranch,
214
+ isShaExistsInBranch,
215
+ getDifferences,
216
+ getFileId,
217
+ filterFile,
218
+ customFetch
219
+ }) {
220
+ async function listAllFilesAndPersist() {
221
+ const files = await listAllFiles(folder, extension, depth);
222
+ const branch = await getDefaultBranch();
223
+ await persistLocalTree({
224
+ localForage,
225
+ localTree: {
226
+ head: branch.sha,
227
+ files: files.map(f => ({
228
+ id: f.id,
229
+ path: f.path,
230
+ name: (0, _path.basename)(f.path)
231
+ }))
232
+ },
233
+ branch: branch.name,
234
+ depth,
235
+ extension,
236
+ folder
237
+ });
238
+ return files;
239
+ }
240
+ async function listFiles() {
241
+ const localTree = await getLocalTree({
242
+ localForage,
243
+ branch,
244
+ folder,
245
+ extension,
246
+ depth
247
+ });
248
+ if (localTree) {
249
+ const branch = await getDefaultBranch();
250
+ // if the branch was forced pushed the local tree sha can be removed from the remote tree
251
+ const localTreeInBranch = await isShaExistsInBranch(branch.name, localTree.head);
252
+ if (!localTreeInBranch) {
253
+ console.log(`Can't find local tree head '${localTree.head}' in branch '${branch.name}', rebuilding local tree`);
254
+ return listAllFilesAndPersist();
255
+ }
256
+ const diff = await getDiffFromLocalTree({
257
+ branch,
258
+ localTree,
259
+ folder,
260
+ extension,
261
+ depth,
262
+ getDifferences,
263
+ getFileId,
264
+ filterFile
265
+ }).catch(e => {
266
+ console.log('Failed getting diff from local tree:', e);
267
+ return null;
268
+ });
269
+ if (!diff) {
270
+ console.log(`Diff is null, rebuilding local tree`);
271
+ return listAllFilesAndPersist();
272
+ }
273
+ if (diff.length === 0) {
274
+ // return local copy
275
+ return localTree.files;
276
+ } else {
277
+ const deleted = diff.reduce((acc, d) => {
278
+ acc[d.path] = d.deleted;
279
+ return acc;
280
+ }, {});
281
+ const newCopy = (0, _sortBy2.default)((0, _unionBy2.default)(diff.filter(d => !deleted[d.path]), localTree.files.filter(f => !deleted[f.path]), file => file.path), file => file.path);
282
+ await persistLocalTree({
283
+ localForage,
284
+ localTree: {
285
+ head: branch.sha,
286
+ files: newCopy
287
+ },
288
+ branch: branch.name,
289
+ depth,
290
+ extension,
291
+ folder
292
+ });
293
+ return newCopy;
294
+ }
295
+ } else {
296
+ return listAllFilesAndPersist();
297
+ }
298
+ }
299
+ const files = await listFiles();
300
+ if (customFetch) {
301
+ return await customFetch(files);
302
+ }
303
+ return await fetchFiles(files, readFile, readFileMetadata, apiName);
304
+ }