decap-cms-lib-util 3.2.0 → 3.3.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.
Files changed (45) hide show
  1. package/README.md +1 -1
  2. package/dist/decap-cms-lib-util.js +1 -1
  3. package/dist/decap-cms-lib-util.js.LICENSE.txt +9 -0
  4. package/dist/decap-cms-lib-util.js.map +1 -1
  5. package/dist/esm/API.js +17 -25
  6. package/dist/esm/APIError.js +0 -7
  7. package/dist/esm/AccessTokenError.js +0 -4
  8. package/dist/esm/Cursor.js +4 -9
  9. package/dist/esm/EditorialWorkflowError.js +0 -5
  10. package/dist/esm/backendUtil.js +6 -7
  11. package/dist/esm/getBlobSHA.js +3 -3
  12. package/dist/esm/git-lfs.js +21 -29
  13. package/dist/esm/implementation.js +14 -17
  14. package/dist/esm/unsentRequest.js +9 -12
  15. package/package.json +5 -2
  16. package/CHANGELOG.md +0 -382
  17. package/dist/esm/stega.js +0 -129
  18. package/src/API.ts +0 -376
  19. package/src/APIError.ts +0 -17
  20. package/src/APIUtils.ts +0 -38
  21. package/src/AccessTokenError.ts +0 -11
  22. package/src/Cursor.ts +0 -178
  23. package/src/EditorialWorkflowError.ts +0 -12
  24. package/src/__tests__/api.spec.js +0 -13
  25. package/src/__tests__/apiUtils.spec.js +0 -74
  26. package/src/__tests__/asyncLock.spec.js +0 -85
  27. package/src/__tests__/backendUtil.spec.js +0 -97
  28. package/src/__tests__/implementation.spec.js +0 -58
  29. package/src/__tests__/path.spec.js +0 -53
  30. package/src/__tests__/unsentRequest.spec.js +0 -19
  31. package/src/asyncLock.ts +0 -43
  32. package/src/backendUtil.ts +0 -120
  33. package/src/getBlobSHA.ts +0 -12
  34. package/src/git-lfs.ts +0 -133
  35. package/src/implementation.ts +0 -575
  36. package/src/index.ts +0 -213
  37. package/src/loadScript.js +0 -24
  38. package/src/localForage.ts +0 -21
  39. package/src/path.ts +0 -86
  40. package/src/promise.ts +0 -21
  41. package/src/stega.ts +0 -134
  42. package/src/types/semaphore.d.ts +0 -5
  43. package/src/types.ts +0 -9
  44. package/src/unsentRequest.js +0 -133
  45. package/webpack.config.js +0 -3
package/src/git-lfs.ts DELETED
@@ -1,133 +0,0 @@
1
- //
2
- // Pointer file parsing
3
-
4
- import { filter, flow, fromPairs, map } from 'lodash/fp';
5
-
6
- import getBlobSHA from './getBlobSHA';
7
-
8
- import type { AssetProxy } from './implementation';
9
-
10
- export interface PointerFile {
11
- size: number;
12
- sha: string;
13
- }
14
-
15
- function splitIntoLines(str: string) {
16
- return str.split('\n');
17
- }
18
-
19
- function splitIntoWords(str: string) {
20
- return str.split(/\s+/g);
21
- }
22
-
23
- function isNonEmptyString(str: string) {
24
- return str !== '';
25
- }
26
-
27
- const withoutEmptyLines = flow([map((str: string) => str.trim()), filter(isNonEmptyString)]);
28
- export const parsePointerFile: (data: string) => PointerFile = flow([
29
- splitIntoLines,
30
- withoutEmptyLines,
31
- map(splitIntoWords),
32
- fromPairs,
33
- ({ size, oid, ...rest }) => ({
34
- size: parseInt(size),
35
- sha: oid?.split(':')[1],
36
- ...rest,
37
- }),
38
- ]);
39
-
40
- //
41
- // .gitattributes file parsing
42
-
43
- function removeGitAttributesCommentsFromLine(line: string) {
44
- return line.split('#')[0];
45
- }
46
-
47
- function parseGitPatternAttribute(attributeString: string) {
48
- // There are three kinds of attribute settings:
49
- // - a key=val pair sets an attribute to a specific value
50
- // - a key without a value and a leading hyphen sets an attribute to false
51
- // - a key without a value and no leading hyphen sets an attribute
52
- // to true
53
- if (attributeString.includes('=')) {
54
- return attributeString.split('=');
55
- }
56
- if (attributeString.startsWith('-')) {
57
- return [attributeString.slice(1), false];
58
- }
59
- return [attributeString, true];
60
- }
61
-
62
- const parseGitPatternAttributes = flow([map(parseGitPatternAttribute), fromPairs]);
63
-
64
- const parseGitAttributesPatternLine = flow([
65
- splitIntoWords,
66
- ([pattern, ...attributes]) => [pattern, parseGitPatternAttributes(attributes)],
67
- ]);
68
-
69
- const parseGitAttributesFileToPatternAttributePairs = flow([
70
- splitIntoLines,
71
- map(removeGitAttributesCommentsFromLine),
72
- withoutEmptyLines,
73
- map(parseGitAttributesPatternLine),
74
- ]);
75
-
76
- export const getLargeMediaPatternsFromGitAttributesFile = flow([
77
- parseGitAttributesFileToPatternAttributePairs,
78
- filter(
79
- ([, attributes]) =>
80
- attributes.filter === 'lfs' && attributes.diff === 'lfs' && attributes.merge === 'lfs',
81
- ),
82
- map(([pattern]) => pattern),
83
- ]);
84
-
85
- export function createPointerFile({ size, sha }: PointerFile) {
86
- return `\
87
- version https://git-lfs.github.com/spec/v1
88
- oid sha256:${sha}
89
- size ${size}
90
- `;
91
- }
92
-
93
- export async function getPointerFileForMediaFileObj(
94
- client: { uploadResource: (pointer: PointerFile, resource: Blob) => Promise<string> },
95
- fileObj: File,
96
- path: string,
97
- ) {
98
- const { name, size } = fileObj;
99
- const sha = await getBlobSHA(fileObj);
100
- await client.uploadResource({ sha, size }, fileObj);
101
- const pointerFileString = createPointerFile({ sha, size });
102
- const pointerFileBlob = new Blob([pointerFileString]);
103
- const pointerFile = new File([pointerFileBlob], name, { type: 'text/plain' });
104
- const pointerFileSHA = await getBlobSHA(pointerFile);
105
- return {
106
- fileObj: pointerFile,
107
- size: pointerFileBlob.size,
108
- sha: pointerFileSHA,
109
- raw: pointerFileString,
110
- path,
111
- };
112
- }
113
-
114
- export async function getLargeMediaFilteredMediaFiles(
115
- client: {
116
- uploadResource: (pointer: PointerFile, resource: Blob) => Promise<string>;
117
- matchPath: (path: string) => boolean;
118
- },
119
- mediaFiles: AssetProxy[],
120
- ) {
121
- return await Promise.all(
122
- mediaFiles.map(async mediaFile => {
123
- const { fileObj, path } = mediaFile;
124
- const fixedPath = path.startsWith('/') ? path.slice(1) : path;
125
- if (!client.matchPath(fixedPath)) {
126
- return mediaFile;
127
- }
128
-
129
- const pointerFileDetails = await getPointerFileForMediaFileObj(client, fileObj as File, path);
130
- return { ...mediaFile, ...pointerFileDetails };
131
- }),
132
- );
133
- }
@@ -1,575 +0,0 @@
1
- import semaphore from 'semaphore';
2
- import { unionBy, sortBy } from 'lodash';
3
-
4
- import { basename } from './path';
5
-
6
- import type { Semaphore } from 'semaphore';
7
- import type Cursor from './Cursor';
8
- import type { AsyncLock } from './asyncLock';
9
- import type { FileMetadata } from './API';
10
-
11
- export type DisplayURLObject = { id: string; path: string };
12
-
13
- export type DisplayURL = DisplayURLObject | string;
14
-
15
- export interface ImplementationMediaFile {
16
- name: string;
17
- id: string;
18
- size?: number;
19
- displayURL?: DisplayURL;
20
- path: string;
21
- draft?: boolean;
22
- url?: string;
23
- file?: File;
24
- }
25
-
26
- export interface UnpublishedEntryMediaFile {
27
- id: string;
28
- path: string;
29
- }
30
-
31
- export interface ImplementationEntry {
32
- data: string;
33
- file: { path: string; label?: string; id?: string | null; author?: string; updatedOn?: string };
34
- }
35
-
36
- export interface UnpublishedEntryDiff {
37
- id: string;
38
- path: string;
39
- newFile: boolean;
40
- }
41
-
42
- export interface UnpublishedEntry {
43
- pullRequestAuthor?: string;
44
- slug: string;
45
- collection: string;
46
- status: string;
47
- diffs: UnpublishedEntryDiff[];
48
- updatedAt: string;
49
- }
50
-
51
- export interface Map {
52
- get: <T>(key: string, defaultValue?: T) => T;
53
- getIn: <T>(key: string[], defaultValue?: T) => T;
54
- setIn: <T>(key: string[], value: T) => Map;
55
- set: <T>(key: string, value: T) => Map;
56
- }
57
-
58
- export type DataFile = {
59
- path: string;
60
- slug: string;
61
- raw: string;
62
- newPath?: string;
63
- };
64
-
65
- export type AssetProxy = {
66
- path: string;
67
- fileObj?: File;
68
- toBase64?: () => Promise<string>;
69
- };
70
-
71
- export type Entry = {
72
- dataFiles: DataFile[];
73
- assets: AssetProxy[];
74
- };
75
-
76
- export type PersistOptions = {
77
- newEntry?: boolean;
78
- commitMessage: string;
79
- collectionName?: string;
80
- useWorkflow?: boolean;
81
- unpublished?: boolean;
82
- status?: string;
83
- };
84
-
85
- export type DeleteOptions = {};
86
-
87
- export type Credentials = { token: string | {}; refresh_token?: string };
88
-
89
- export type User = Credentials & {
90
- backendName?: string;
91
- login?: string;
92
- name: string;
93
- useOpenAuthoring?: boolean;
94
- };
95
-
96
- export type Config = {
97
- backend: {
98
- repo?: string | null;
99
- open_authoring?: boolean;
100
- always_fork?: boolean;
101
- branch?: string;
102
- api_root?: string;
103
- squash_merges?: boolean;
104
- use_graphql?: boolean;
105
- graphql_api_root?: string;
106
- preview_context?: string;
107
- identity_url?: string;
108
- gateway_url?: string;
109
- large_media_url?: string;
110
- use_large_media_transforms_in_media_library?: boolean;
111
- proxy_url?: string;
112
- auth_type?: string;
113
- app_id?: string;
114
- base_url?: string;
115
- cms_label_prefix?: string;
116
- api_version?: string;
117
- };
118
- media_folder: string;
119
- base_url?: string;
120
- site_id?: string;
121
- };
122
-
123
- export interface Implementation {
124
- authComponent: () => void;
125
- restoreUser: (user: User) => Promise<User>;
126
-
127
- authenticate: (credentials: Credentials) => Promise<User>;
128
- logout: () => Promise<void> | void | null;
129
- getToken: () => Promise<string | null>;
130
-
131
- getEntry: (path: string) => Promise<ImplementationEntry>;
132
- entriesByFolder: (
133
- folder: string,
134
- extension: string,
135
- depth: number,
136
- ) => Promise<ImplementationEntry[]>;
137
- entriesByFiles: (files: ImplementationFile[]) => Promise<ImplementationEntry[]>;
138
-
139
- getMediaDisplayURL?: (displayURL: DisplayURL) => Promise<string>;
140
- getMedia: (folder?: string) => Promise<ImplementationMediaFile[]>;
141
- getMediaFile: (path: string) => Promise<ImplementationMediaFile>;
142
-
143
- persistEntry: (entry: Entry, opts: PersistOptions) => Promise<void>;
144
- persistMedia: (file: AssetProxy, opts: PersistOptions) => Promise<ImplementationMediaFile>;
145
- deleteFiles: (paths: string[], commitMessage: string) => Promise<void>;
146
-
147
- unpublishedEntries: () => Promise<string[]>;
148
- unpublishedEntry: (args: {
149
- id?: string;
150
- collection?: string;
151
- slug?: string;
152
- }) => Promise<UnpublishedEntry>;
153
- unpublishedEntryDataFile: (
154
- collection: string,
155
- slug: string,
156
- path: string,
157
- id: string,
158
- ) => Promise<string>;
159
- unpublishedEntryMediaFile: (
160
- collection: string,
161
- slug: string,
162
- path: string,
163
- id: string,
164
- ) => Promise<ImplementationMediaFile>;
165
- updateUnpublishedEntryStatus: (
166
- collection: string,
167
- slug: string,
168
- newStatus: string,
169
- ) => Promise<void>;
170
- publishUnpublishedEntry: (collection: string, slug: string) => Promise<void>;
171
- deleteUnpublishedEntry: (collection: string, slug: string) => Promise<void>;
172
- getDeployPreview: (
173
- collectionName: string,
174
- slug: string,
175
- ) => Promise<{ url: string; status: string } | null>;
176
-
177
- allEntriesByFolder?: (
178
- folder: string,
179
- extension: string,
180
- depth: number,
181
- pathRegex?: RegExp,
182
- ) => Promise<ImplementationEntry[]>;
183
- traverseCursor?: (
184
- cursor: Cursor,
185
- action: string,
186
- ) => Promise<{ entries: ImplementationEntry[]; cursor: Cursor }>;
187
-
188
- isGitBackend?: () => boolean;
189
- status: () => Promise<{
190
- auth: { status: boolean };
191
- api: { status: boolean; statusPage: string };
192
- }>;
193
- }
194
-
195
- const MAX_CONCURRENT_DOWNLOADS = 10;
196
-
197
- export type ImplementationFile = {
198
- id?: string | null | undefined;
199
- label?: string;
200
- path: string;
201
- };
202
-
203
- type ReadFile = (
204
- path: string,
205
- id: string | null | undefined,
206
- options: { parseText: boolean },
207
- ) => Promise<string | Blob>;
208
-
209
- type ReadFileMetadata = (path: string, id: string | null | undefined) => Promise<FileMetadata>;
210
-
211
- type CustomFetchFunc = (files: ImplementationFile[]) => Promise<ImplementationEntry[]>;
212
-
213
- async function fetchFiles(
214
- files: ImplementationFile[],
215
- readFile: ReadFile,
216
- readFileMetadata: ReadFileMetadata,
217
- apiName: string,
218
- ) {
219
- const sem = semaphore(MAX_CONCURRENT_DOWNLOADS);
220
- const promises = [] as Promise<ImplementationEntry | { error: boolean }>[];
221
- files.forEach(file => {
222
- promises.push(
223
- new Promise(resolve =>
224
- sem.take(async () => {
225
- try {
226
- const [data, fileMetadata] = await Promise.all([
227
- readFile(file.path, file.id, { parseText: true }),
228
- readFileMetadata(file.path, file.id),
229
- ]);
230
- resolve({ file: { ...file, ...fileMetadata }, data: data as string });
231
- sem.leave();
232
- } catch (error) {
233
- sem.leave();
234
- console.error(`failed to load file from ${apiName}: ${file.path}`);
235
- resolve({ error: true });
236
- }
237
- }),
238
- ),
239
- );
240
- });
241
- return Promise.all(promises).then(loadedEntries =>
242
- loadedEntries.filter(loadedEntry => !(loadedEntry as { error: boolean }).error),
243
- ) as Promise<ImplementationEntry[]>;
244
- }
245
-
246
- export async function entriesByFolder(
247
- listFiles: () => Promise<ImplementationFile[]>,
248
- readFile: ReadFile,
249
- readFileMetadata: ReadFileMetadata,
250
- apiName: string,
251
- ) {
252
- const files = await listFiles();
253
- return fetchFiles(files, readFile, readFileMetadata, apiName);
254
- }
255
-
256
- export async function entriesByFiles(
257
- files: ImplementationFile[],
258
- readFile: ReadFile,
259
- readFileMetadata: ReadFileMetadata,
260
- apiName: string,
261
- ) {
262
- return fetchFiles(files, readFile, readFileMetadata, apiName);
263
- }
264
-
265
- export async function unpublishedEntries(listEntriesKeys: () => Promise<string[]>) {
266
- try {
267
- const keys = await listEntriesKeys();
268
- return keys;
269
- } catch (error) {
270
- if (error.message === 'Not Found') {
271
- return Promise.resolve([]);
272
- }
273
- throw error;
274
- }
275
- }
276
-
277
- export function blobToFileObj(name: string, blob: Blob) {
278
- const options = name.match(/.svg$/) ? { type: 'image/svg+xml' } : {};
279
- return new File([blob], name, options);
280
- }
281
-
282
- export async function getMediaAsBlob(path: string, id: string | null, readFile: ReadFile) {
283
- let blob: Blob;
284
- if (path.match(/.svg$/)) {
285
- const text = (await readFile(path, id, { parseText: true })) as string;
286
- blob = new Blob([text], { type: 'image/svg+xml' });
287
- } else {
288
- blob = (await readFile(path, id, { parseText: false })) as Blob;
289
- }
290
- return blob;
291
- }
292
-
293
- export async function getMediaDisplayURL(
294
- displayURL: DisplayURL,
295
- readFile: ReadFile,
296
- semaphore: Semaphore,
297
- ) {
298
- const { path, id } = displayURL as DisplayURLObject;
299
- return new Promise<string>((resolve, reject) =>
300
- semaphore.take(() =>
301
- getMediaAsBlob(path, id, readFile)
302
- .then(blob => URL.createObjectURL(blob))
303
- .then(resolve, reject)
304
- .finally(() => semaphore.leave()),
305
- ),
306
- );
307
- }
308
-
309
- export async function runWithLock(lock: AsyncLock, func: Function, message: string) {
310
- try {
311
- const acquired = await lock.acquire();
312
- if (!acquired) {
313
- console.warn(message);
314
- }
315
-
316
- const result = await func();
317
- return result;
318
- } finally {
319
- lock.release();
320
- }
321
- }
322
-
323
- const LOCAL_KEY = 'git.local';
324
-
325
- type LocalTree = {
326
- head: string;
327
- files: { id: string; name: string; path: string }[];
328
- };
329
-
330
- type GetKeyArgs = {
331
- branch: string;
332
- folder: string;
333
- extension: string;
334
- depth: number;
335
- };
336
-
337
- function getLocalKey({ branch, folder, extension, depth }: GetKeyArgs) {
338
- return `${LOCAL_KEY}.${branch}.${folder}.${extension}.${depth}`;
339
- }
340
-
341
- type PersistLocalTreeArgs = GetKeyArgs & {
342
- localForage: LocalForage;
343
- localTree: LocalTree;
344
- };
345
-
346
- type GetLocalTreeArgs = GetKeyArgs & {
347
- localForage: LocalForage;
348
- };
349
-
350
- export async function persistLocalTree({
351
- localForage,
352
- localTree,
353
- branch,
354
- folder,
355
- extension,
356
- depth,
357
- }: PersistLocalTreeArgs) {
358
- await localForage.setItem<LocalTree>(
359
- getLocalKey({ branch, folder, extension, depth }),
360
- localTree,
361
- );
362
- }
363
-
364
- export async function getLocalTree({
365
- localForage,
366
- branch,
367
- folder,
368
- extension,
369
- depth,
370
- }: GetLocalTreeArgs) {
371
- const localTree = await localForage.getItem<LocalTree>(
372
- getLocalKey({ branch, folder, extension, depth }),
373
- );
374
- return localTree;
375
- }
376
-
377
- type GetDiffFromLocalTreeMethods = {
378
- getDifferences: (
379
- to: string,
380
- from: string,
381
- ) => Promise<
382
- {
383
- oldPath: string;
384
- newPath: string;
385
- status: string;
386
- }[]
387
- >;
388
- filterFile: (file: { path: string; name: string }) => boolean;
389
- getFileId: (path: string) => Promise<string>;
390
- };
391
-
392
- type GetDiffFromLocalTreeArgs = GetDiffFromLocalTreeMethods & {
393
- branch: { name: string; sha: string };
394
- localTree: LocalTree;
395
- folder: string;
396
- extension: string;
397
- depth: number;
398
- };
399
-
400
- async function getDiffFromLocalTree({
401
- branch,
402
- localTree,
403
- folder,
404
- getDifferences,
405
- filterFile,
406
- getFileId,
407
- }: GetDiffFromLocalTreeArgs) {
408
- const diff = await getDifferences(branch.sha, localTree.head);
409
- const diffFiles = diff
410
- .filter(d => d.oldPath?.startsWith(folder) || d.newPath?.startsWith(folder))
411
- .reduce((acc, d) => {
412
- if (d.status === 'renamed') {
413
- acc.push({
414
- path: d.oldPath,
415
- name: basename(d.oldPath),
416
- deleted: true,
417
- });
418
- acc.push({
419
- path: d.newPath,
420
- name: basename(d.newPath),
421
- deleted: false,
422
- });
423
- } else if (d.status === 'deleted') {
424
- acc.push({
425
- path: d.oldPath,
426
- name: basename(d.oldPath),
427
- deleted: true,
428
- });
429
- } else {
430
- acc.push({
431
- path: d.newPath || d.oldPath,
432
- name: basename(d.newPath || d.oldPath),
433
- deleted: false,
434
- });
435
- }
436
-
437
- return acc;
438
- }, [] as { path: string; name: string; deleted: boolean }[])
439
-
440
- .filter(filterFile);
441
-
442
- const diffFilesWithIds = await Promise.all(
443
- diffFiles.map(async file => {
444
- if (!file.deleted) {
445
- const id = await getFileId(file.path);
446
- return { ...file, id };
447
- } else {
448
- return { ...file, id: '' };
449
- }
450
- }),
451
- );
452
-
453
- return diffFilesWithIds;
454
- }
455
-
456
- type AllEntriesByFolderArgs = GetKeyArgs &
457
- GetDiffFromLocalTreeMethods & {
458
- listAllFiles: (
459
- folder: string,
460
- extension: string,
461
- depth: number,
462
- ) => Promise<ImplementationFile[]>;
463
- readFile: ReadFile;
464
- readFileMetadata: ReadFileMetadata;
465
- getDefaultBranch: () => Promise<{ name: string; sha: string }>;
466
- isShaExistsInBranch: (branch: string, sha: string) => Promise<boolean>;
467
- apiName: string;
468
- localForage: LocalForage;
469
- customFetch?: CustomFetchFunc;
470
- };
471
-
472
- export async function allEntriesByFolder({
473
- listAllFiles,
474
- readFile,
475
- readFileMetadata,
476
- apiName,
477
- branch,
478
- localForage,
479
- folder,
480
- extension,
481
- depth,
482
- getDefaultBranch,
483
- isShaExistsInBranch,
484
- getDifferences,
485
- getFileId,
486
- filterFile,
487
- customFetch,
488
- }: AllEntriesByFolderArgs) {
489
- async function listAllFilesAndPersist() {
490
- const files = await listAllFiles(folder, extension, depth);
491
- const branch = await getDefaultBranch();
492
- await persistLocalTree({
493
- localForage,
494
- localTree: {
495
- head: branch.sha,
496
- files: files.map(f => ({ id: f.id!, path: f.path, name: basename(f.path) })),
497
- },
498
- branch: branch.name,
499
- depth,
500
- extension,
501
- folder,
502
- });
503
- return files;
504
- }
505
-
506
- async function listFiles() {
507
- const localTree = await getLocalTree({ localForage, branch, folder, extension, depth });
508
- if (localTree) {
509
- const branch = await getDefaultBranch();
510
- // if the branch was forced pushed the local tree sha can be removed from the remote tree
511
- const localTreeInBranch = await isShaExistsInBranch(branch.name, localTree.head);
512
- if (!localTreeInBranch) {
513
- console.log(
514
- `Can't find local tree head '${localTree.head}' in branch '${branch.name}', rebuilding local tree`,
515
- );
516
- return listAllFilesAndPersist();
517
- }
518
- const diff = await getDiffFromLocalTree({
519
- branch,
520
- localTree,
521
- folder,
522
- extension,
523
- depth,
524
- getDifferences,
525
- getFileId,
526
- filterFile,
527
- }).catch(e => {
528
- console.log('Failed getting diff from local tree:', e);
529
- return null;
530
- });
531
-
532
- if (!diff) {
533
- console.log(`Diff is null, rebuilding local tree`);
534
- return listAllFilesAndPersist();
535
- }
536
-
537
- if (diff.length === 0) {
538
- // return local copy
539
- return localTree.files;
540
- } else {
541
- const deleted = diff.reduce((acc, d) => {
542
- acc[d.path] = d.deleted;
543
- return acc;
544
- }, {} as Record<string, boolean>);
545
- const newCopy = sortBy(
546
- unionBy(
547
- diff.filter(d => !deleted[d.path]),
548
- localTree.files.filter(f => !deleted[f.path]),
549
- file => file.path,
550
- ),
551
- file => file.path,
552
- );
553
-
554
- await persistLocalTree({
555
- localForage,
556
- localTree: { head: branch.sha, files: newCopy },
557
- branch: branch.name,
558
- depth,
559
- extension,
560
- folder,
561
- });
562
-
563
- return newCopy;
564
- }
565
- } else {
566
- return listAllFilesAndPersist();
567
- }
568
- }
569
-
570
- const files = await listFiles();
571
- if (customFetch) {
572
- return await customFetch(files);
573
- }
574
- return await fetchFiles(files, readFile, readFileMetadata, apiName);
575
- }