ownfiles
Version:
A library to manage files in a Solid User's Pod
180 lines (169 loc) • 5.1 kB
text/typescript
import url from 'url';
import mime from 'mime';
import FileClient from './fileClient';
import * as rdf from 'rdflib';
import ns from 'solid-namespace';
export interface ReadOptions {
auth: any;
verbose: boolean;
headOnly: boolean;
headers: Record<string, string>;
}
export interface FolderType {
folders: string[];
files: (FileType | string)[];
}
export interface FileType {
name: string;
type: string;
}
export interface SingleFileType {
body: string;
contentType: string;
}
export function ReadException(
this: { response: Response },
response: Response,
) {
this.response = response;
}
export const read = async function(
this: FileClient,
resource: string,
{ auth, verbose, headOnly, headers }: Partial<ReadOptions> = {
auth: false,
verbose: false,
headOnly: false,
headers: {
Accept: mime.getType(resource) ?? 'text/turtle',
},
},
) {
const store = this.graph;
const fetcher = this.fetcher;
store.removeDocument(rdf.sym(resource));
let fetch;
if (auth) {
fetch = auth.fetch;
} else {
fetch = fetcher._fetch;
}
const headResponse = await fetch(resource, {
method: 'HEAD',
});
if (headResponse.status !== 200) {
throw new ReadException(headResponse);
}
const isFolder =
headResponse &&
(headResponse.headers
.get('Link')
?.includes('http://www.w3.org/ns/ldp#Container') ||
(headResponse.headers
.get('Link')
?.includes('http://www.w3.org/ns/ldp#Resource') &&
resource.endsWith('/')));
if (!isFolder && headOnly) {
return Promise.resolve(
verbose
? {
name: resource,
type: mime.getType(resource),
}
: resource,
);
}
resource = isFolder
? resource.endsWith('/')
? resource
: resource + '/'
: resource;
const response = await fetch(resource, {
headers: headers,
});
const contentType = response.headers.get('Content-Type');
if (
contentType.includes('text') ||
(contentType.includes('application') &&
contentType !== 'application/octet-stream')
) {
const text = await response.text();
if (
response.headers.get('Content-Type') === 'text/turtle' &&
isFolder
) {
rdf.parse(text, store, resource);
return getFolderResult(resource, store, verbose);
} else {
if (verbose) {
return { body: text, contentType: contentType };
} else {
return text;
}
}
} else {
const body = response.blob
? await response.blob()
: await response.buffer();
if (verbose) {
return { body: body, contentType: contentType };
} else {
return body;
}
}
};
export const getFolderResult = (
resource: string,
store: any,
verbose = false,
) => {
const containments: FolderType = { folders: [], files: [] };
store
.each(rdf.sym(resource), ns(rdf).ldp('contains'), null)
.map((result: { value: string }) => {
return result.value;
})
.forEach((result: string) => {
const type = store.statementsMatching(
rdf.sym(result),
ns(rdf).rdf('type'),
)[0].object;
const isLinked = ((type) =>
type.value ===
'http://www.w3.org/ns/iana/media-types/text/turtle#Resource')(
type,
);
let resultFragments = url.parse(result).pathname?.split('/');
if (resultFragments && resultFragments[1] === '') {
resultFragments.shift();
const path =
url.parse(result).pathname ??
result.substr(
result.replace('https://', '').indexOf('/'),
result.length,
);
result = result.replace(path, resultFragments.join('/'));
resultFragments = path.split('/');
}
if (
resultFragments &&
resultFragments[resultFragments.length - 1] === ''
) {
containments.folders.push(
result.endsWith('/') ? result : result + '/',
);
} else {
if (verbose) {
containments.files.push({
name: result,
type: !isLinked
? mime.getType(result) ?? 'text/plain'
: 'text/turtle',
});
} else {
containments.files.push(result);
}
}
});
return containments;
};