UNPKG

ownfiles

Version:

A library to manage files in a Solid User's Pod

225 lines (213 loc) 7.77 kB
import ns from 'solid-namespace'; import mime from 'mime'; import * as rdf from 'rdflib'; import FileClient from './fileClient'; import { FileType, FolderType } from './read'; const typeNamespaceUrl = 'http://www.w3.org/ns/iana/media-types/'; const types = rdf.Namespace(typeNamespaceUrl); export interface IndexEntry { url: string; types: string[]; } export const deepRead = async function( this: FileClient, folderUrl: string, options: Partial<{ auth: any; verbose: boolean; foundCallback: (item: string) => unknown; }> = { auth: null, verbose: false, }, ) { const deepRead = await this.read(folderUrl, { verbose: true, headOnly: true, headers: { Accept: 'text/turtle' }, }) .catch((err) => { if ( err?.response?.url && err.response.status !== 404 && mime.getType(folderUrl) ) { return Promise.resolve( options.verbose ? [ { name: err.response.url, type: mime.getType(err.response.url), }, ] : err.response.url, ); } if (err?.response?.url && err.response.status !== 404) return Promise.resolve({ files: [], folders: [] }); }) .then((folder) => { const file = folder as FileType; folder = folder as FolderType; if (file?.name && file.type) { return Promise.resolve( options.verbose ? [ { url: file.name, type: types(file.type + '#Resource').value, }, ] : [file.name], ); } else if (folder?.files && folder.folders) { const folderList = folder.folders.map((folder: string) => this.deepRead(folder, options), ); const fileList = folder.files.map( (file) => new Promise(function(resolve) { if (options.verbose) { options.foundCallback && options.foundCallback( (file as FileType).name, ); resolve({ url: (file as FileType).name, type: types( (file as FileType).type + '#Resource', ).value, }); } else { options.foundCallback && options.foundCallback( (file as FileType).name, ); resolve((file as FileType).name); } }), ); return Promise.all([ ...folderList, ...fileList, new Promise(function(resolve) { if (options.verbose) { options.foundCallback && options.foundCallback(folderUrl); resolve([ { url: folderUrl.endsWith('/') ? folderUrl : folderUrl + '/', type: 'folder', }, ]); } else { options.foundCallback && options.foundCallback(folderUrl); resolve([ folderUrl.endsWith('/') ? folderUrl : folderUrl + '/', ]); } }), ]); } return Promise.resolve([]); }) .then((results): ( | FileType | FolderType | { url: string; type: string } | { url: string; types: string[] } | string )[] => { return flattenArray(results) .sort((fileA: string, fileB: string) => sortByDepth(fileA, fileB, !!options.verbose), ) .reverse(); }); if (options.verbose) { const verboseDeepRead = deepRead.map((resource, _, deepRead) => { const previousIndexResource = resource as { url: string; types: string[]; }; const unseenResource = resource as { url: string; type: string }; if (previousIndexResource.types) return resource; if (unseenResource?.type) { return unseenResource.type === 'folder' ? { url: unseenResource.url, types: [ ns().ldp('Container'), ...getContainedTypes(unseenResource, deepRead), ], } : { url: unseenResource.url, types: [unseenResource.type, ns().ldp('Resource')], }; } }); return verboseDeepRead as IndexEntry[]; } return deepRead as string[]; }; const getContainedTypes = (item, index) => { return index .reduce( ( allTypes: string[], currentItem: { url: string; type: string }, ) => { return currentItem.url.includes(item.url) && currentItem.type && currentItem.type !== null && currentItem.type !== 'folder' ? [...allTypes, currentItem.type] : allTypes; }, [], ) .filter( (currentType: string, index: number, allNodes: string[]) => allNodes.findIndex((type: string) => currentType === type) === index, ); }; function flattenArray(array): string[] { let result: string[] = []; for (let i = 0; i < array.length; i++) { const element = array[i]; if (Array.isArray(element)) { result = [...result, ...flattenArray(element)]; } else { result = [...result, element]; } } return result; } function sortByDepth( fileA: string | { url: string }, fileB: string | { url: string }, verbose: boolean, ) { if (verbose && typeof fileA !== 'string' && typeof fileB !== 'string') { fileA = fileA.url; fileB = fileB.url; } let depthA = 0; let depthB = 0; if (typeof fileA === 'string' && typeof fileB === 'string') { depthA = fileA.split('/').length; depthB = fileB.split('/').length; } if (depthA === depthB) { return (fileA as string).length - (fileB as string).length; } else { return depthA - depthB; } }