refactor: Use declarations style for functions.

This commit is contained in:
Ruben Verborgh 2021-01-06 11:24:43 +01:00
parent e70e060225
commit f9a20799eb
23 changed files with 208 additions and 162 deletions

View File

@ -48,6 +48,8 @@ module.exports = {
'class-methods-use-this': 'off',
'comma-dangle': [ 'error', 'always-multiline' ],
'dot-location': [ 'error', 'property' ],
// Allow declaring overloads in TypeScript (https://eslint.org/docs/rules/func-style)
'func-style': [ 'error', 'declaration' ],
'generator-star-spacing': [ 'error', 'after' ],
// Conflicts with padded-blocks
'lines-around-comment': 'off',

View File

@ -12,9 +12,9 @@ export type MetadataOverrideValue = NamedNode | Literal | string | (NamedNode |
/**
* Determines whether the object is a `RepresentationMetadata`.
*/
export const isRepresentationMetadata = function(object: any): object is RepresentationMetadata {
export function isRepresentationMetadata(object: any): object is RepresentationMetadata {
return typeof object?.setMetadata === 'function';
};
}
/**
* Stores the metadata triples and provides methods for easy access.

View File

@ -11,6 +11,6 @@ export interface ResourceIdentifier {
/**
* Determines whether the object is a `ResourceIdentifier`.
*/
export const isResourceIdentifier = function(object: any): object is ResourceIdentifier {
export function isResourceIdentifier(object: any): object is ResourceIdentifier {
return object && (typeof object.path === 'string');
};
}

View File

@ -20,17 +20,19 @@ import type { LoggerFactory } from './LoggerFactory';
*
* @param loggable - A class instance or a class string name.
*/
export const getLoggerFor = (loggable: string | Instance): Logger => LazyLoggerFactory.getInstance()
.createLogger(typeof loggable === 'string' ? loggable : loggable.constructor.name);
export function getLoggerFor(loggable: string | Instance): Logger {
return LazyLoggerFactory.getInstance()
.createLogger(typeof loggable === 'string' ? loggable : loggable.constructor.name);
}
/**
* Sets the global logger factory.
* This will cause all loggers created by {@link getLoggerFor} to be delegated to a logger from the given factory.
* @param loggerFactory - A logger factory.
*/
export const setGlobalLoggerFactory = (loggerFactory: LoggerFactory): void => {
export function setGlobalLoggerFactory(loggerFactory: LoggerFactory): void {
LazyLoggerFactory.getInstance().loggerFactory = loggerFactory;
};
}
/**
* Resets the global logger factory to undefined.
@ -38,7 +40,9 @@ export const setGlobalLoggerFactory = (loggerFactory: LoggerFactory): void => {
* This typically only needs to be called during testing.
* Call this at your own risk.
*/
export const resetGlobalLoggerFactory = (): void => LazyLoggerFactory.getInstance().resetLoggerFactory();
export function resetGlobalLoggerFactory(): void {
LazyLoggerFactory.getInstance().resetLoggerFactory();
}
/**
* Helper interface to identify class instances.

View File

@ -18,8 +18,8 @@ import { NotImplementedHttpError } from '../../util/errors/NotImplementedHttpErr
*
* @returns The weighted and filtered list of matching types.
*/
export const matchingMediaTypes = (preferredTypes: ValuePreferences = {}, availableTypes: ValuePreferences = {}):
string[] => {
export function matchingMediaTypes(preferredTypes: ValuePreferences = {}, availableTypes: ValuePreferences = {}):
string[] {
// No preference means anything is acceptable
const preferred = { ...preferredTypes };
if (Object.keys(preferredTypes).length === 0) {
@ -53,7 +53,7 @@ string[] => {
.filter(([ , weight ]): boolean => weight !== 0)
.sort(([ , weightA ], [ , weightB ]): number => weightB - weightA)
.map(([ type ]): string => type);
};
}
/**
* Checks if the given two media types/ranges match each other.
@ -63,7 +63,7 @@ string[] => {
*
* @returns True if the media type patterns can match each other.
*/
export const matchesMediaType = (mediaA: string, mediaB: string): boolean => {
export function matchesMediaType(mediaA: string, mediaB: string): boolean {
if (mediaA === mediaB) {
return true;
}
@ -80,7 +80,7 @@ export const matchesMediaType = (mediaA: string, mediaB: string): boolean => {
return true;
}
return subTypeA === subTypeB;
};
}
/**
* Determines whether the given conversion request is supported,
@ -94,10 +94,10 @@ export const matchesMediaType = (mediaA: string, mediaB: string): boolean => {
* @param convertorIn - Media types that can be parsed by the converter.
* @param convertorOut - Media types that can be produced by the converter.
*/
export const supportsMediaTypeConversion = (
export function supportsMediaTypeConversion(
inputType = 'unknown', outputTypes: ValuePreferences = {},
convertorIn: ValuePreferences = {}, convertorOut: ValuePreferences = {},
): void => {
): void {
if (!Object.keys(convertorIn).some((type): boolean => matchesMediaType(inputType, type)) ||
matchingMediaTypes(outputTypes, convertorOut).length === 0) {
throw new NotImplementedHttpError(
@ -105,4 +105,4 @@ export const supportsMediaTypeConversion = (
}, only from ${Object.keys(convertorIn)} to ${Object.keys(convertorOut)}.`,
);
}
};
}

View File

@ -14,8 +14,9 @@ const logger = getLoggerFor('MapperUtil');
*
* @returns Absolute path of the file.
*/
export const getAbsolutePath = (rootFilepath: string, path: string, identifier = ''): string =>
joinFilePath(rootFilepath, path, identifier);
export function getAbsolutePath(rootFilepath: string, path: string, identifier = ''): string {
return joinFilePath(rootFilepath, path, identifier);
}
/**
* Strips the baseRequestURI from the identifier and checks if the stripped base URI matches the store's one.
@ -27,13 +28,13 @@ export const getAbsolutePath = (rootFilepath: string, path: string, identifier =
*
* @returns A string representing the relative path.
*/
export const getRelativePath = (baseRequestURI: string, identifier: ResourceIdentifier): string => {
export function getRelativePath(baseRequestURI: string, identifier: ResourceIdentifier): string {
if (!identifier.path.startsWith(baseRequestURI)) {
logger.warn(`The URL ${identifier.path} is outside of the scope ${baseRequestURI}`);
throw new NotFoundHttpError();
}
return decodeUriPathComponents(identifier.path.slice(baseRequestURI.length));
};
}
/**
* Check if the given relative path is valid.
@ -44,7 +45,7 @@ export const getRelativePath = (baseRequestURI: string, identifier: ResourceIden
* @param path - A relative path, as generated by {@link getRelativePath}.
* @param identifier - A resource identifier.
*/
export const validateRelativePath = (path: string, identifier: ResourceIdentifier): void => {
export function validateRelativePath(path: string, identifier: ResourceIdentifier): void {
if (!path.startsWith('/')) {
logger.warn(`URL ${identifier.path} needs a / after the base`);
throw new BadRequestHttpError('URL needs a / after the base');
@ -54,4 +55,4 @@ export const validateRelativePath = (path: string, identifier: ResourceIdentifie
logger.warn(`Disallowed /.. segment in URL ${identifier.path}.`);
throw new BadRequestHttpError('Disallowed /.. segment in URL');
}
};
}

View File

@ -24,7 +24,9 @@ export type Guarded<T extends NodeJS.EventEmitter = NodeJS.EventEmitter> = T & G
/**
* Determines whether the stream is guarded from emitting errors.
*/
export const isGuarded = <T extends NodeJS.EventEmitter>(stream: T): stream is Guarded<T> => guardedErrors in stream;
export function isGuarded<T extends NodeJS.EventEmitter>(stream: T): stream is Guarded<T> {
return guardedErrors in stream;
}
/**
* Makes sure that listeners always receive the error event of a stream,
@ -34,20 +36,20 @@ export const isGuarded = <T extends NodeJS.EventEmitter>(stream: T): stream is G
*
* @returns The stream.
*/
export const guardStream = <T extends NodeJS.EventEmitter>(stream: T): Guarded<T> => {
export function guardStream<T extends NodeJS.EventEmitter>(stream: T): Guarded<T> {
const guarded = stream as Guarded<T>;
if (!isGuarded(stream)) {
guarded[guardedErrors] = [];
attachDefaultErrorListener.call(guarded, 'error');
}
return guarded;
};
}
/**
* Callback that is used when a stream emits an error and no error listener is attached.
* Used to store the error and start the logger timer.
*/
const defaultErrorListener = function(this: Guarded, error: Error): void {
function defaultErrorListener(this: Guarded, error: Error): void {
this[guardedErrors].push(error);
if (!this[guardedTimeout]) {
this[guardedTimeout] = setTimeout((): void => {
@ -55,14 +57,13 @@ const defaultErrorListener = function(this: Guarded, error: Error): void {
logger.error(message, { error });
}, 1000);
}
};
}
/**
* Callback that is used when a new listener is attached to remove the current error-related fallback functions,
* or to emit an error if one was thrown in the meantime.
*/
const removeDefaultErrorListener = function(this: Guarded, event: string):
void {
function removeDefaultErrorListener(this: Guarded, event: string): void {
if (event === 'error') {
// Remove default guard listeners (but reattach when all error listeners are removed)
this.removeListener('error', defaultErrorListener);
@ -86,7 +87,7 @@ void {
});
}
}
};
}
/**
* Callback that is used to make sure the error-related fallback functions are re-applied

View File

@ -108,7 +108,7 @@ const token = /^[a-zA-Z0-9!#$%&'*+-.^_`|~]+$/u;
*
* @returns The transformed string and a map with keys `"0"`, etc. and values the original string that was there.
*/
export const transformQuotedStrings = (input: string): { result: string; replacements: Record<string, string> } => {
export function transformQuotedStrings(input: string): { result: string; replacements: Record<string, string> } {
let idx = 0;
const replacements: Record<string, string> = {};
const result = input.replace(/"(?:[^"\\]|\\.)*"/gu, (match): string => {
@ -123,17 +123,18 @@ export const transformQuotedStrings = (input: string): { result: string; replace
return replacement;
});
return { result, replacements };
};
}
/**
* Splits the input string on commas, trims all parts and filters out empty ones.
*
* @param input - Input header string.
*/
export const splitAndClean = (input: string): string[] =>
input.split(',')
export function splitAndClean(input: string): string[] {
return input.split(',')
.map((part): string => part.trim())
.filter((part): boolean => part.length > 0);
}
/**
* Checks if the input string matches the qvalue regex.
@ -143,14 +144,14 @@ export const splitAndClean = (input: string): string[] =>
* @throws {@link BadRequestHttpError}
* Thrown on invalid syntax.
*/
const testQValue = (qvalue: string): void => {
function testQValue(qvalue: string): void {
if (!/^(?:(?:0(?:\.\d{0,3})?)|(?:1(?:\.0{0,3})?))$/u.test(qvalue)) {
logger.warn(`Invalid q value: ${qvalue}`);
throw new BadRequestHttpError(
`Invalid q value: ${qvalue} does not match ( "0" [ "." 0*3DIGIT ] ) / ( "1" [ "." 0*3("0") ] ).`,
);
}
};
}
/**
* Parses a list of split parameters and checks their validity.
@ -164,28 +165,30 @@ const testQValue = (qvalue: string): void => {
*
* @returns An array of name/value objects corresponding to the parameters.
*/
export const parseParameters = (parameters: string[], replacements: Record<string, string>):
{ name: string; value: string }[] => parameters.map((param): { name: string; value: string } => {
const [ name, rawValue ] = param.split('=').map((str): string => str.trim());
export function parseParameters(parameters: string[], replacements: Record<string, string>):
{ name: string; value: string }[] {
return parameters.map((param): { name: string; value: string } => {
const [ name, rawValue ] = param.split('=').map((str): string => str.trim());
// Test replaced string for easier check
// parameter = token "=" ( token / quoted-string )
// second part is optional for certain parameters
if (!(token.test(name) && (!rawValue || /^"\d+"$/u.test(rawValue) || token.test(rawValue)))) {
logger.warn(`Invalid parameter value: ${name}=${replacements[rawValue] || rawValue}`);
throw new BadRequestHttpError(
`Invalid parameter value: ${name}=${replacements[rawValue] || rawValue} ` +
`does not match (token ( "=" ( token / quoted-string ))?). `,
);
}
// Test replaced string for easier check
// parameter = token "=" ( token / quoted-string )
// second part is optional for certain parameters
if (!(token.test(name) && (!rawValue || /^"\d+"$/u.test(rawValue) || token.test(rawValue)))) {
logger.warn(`Invalid parameter value: ${name}=${replacements[rawValue] || rawValue}`);
throw new BadRequestHttpError(
`Invalid parameter value: ${name}=${replacements[rawValue] || rawValue} ` +
`does not match (token ( "=" ( token / quoted-string ))?). `,
);
}
let value = rawValue;
if (value in replacements) {
value = replacements[rawValue];
}
let value = rawValue;
if (value in replacements) {
value = replacements[rawValue];
}
return { name, value };
});
return { name, value };
});
}
/**
* Parses a single media range with corresponding parameters from an Accept header.
@ -201,7 +204,7 @@ export const parseParameters = (parameters: string[], replacements: Record<strin
*
* @returns {@link Accept} object corresponding to the header string.
*/
const parseAcceptPart = (part: string, replacements: Record<string, string>): Accept => {
function parseAcceptPart(part: string, replacements: Record<string, string>): Accept {
const [ range, ...parameters ] = part.split(';').map((param): string => param.trim());
// No reason to test differently for * since we don't check if the type exists
@ -242,7 +245,7 @@ const parseAcceptPart = (part: string, replacements: Record<string, string>): Ac
extension: extensionParams,
},
};
};
}
/**
* Parses an Accept-* header where each part is only a value and a weight, so roughly /.*(q=.*)?/ separated by commas.
@ -253,7 +256,7 @@ const parseAcceptPart = (part: string, replacements: Record<string, string>): Ac
*
* @returns An array of ranges and weights.
*/
const parseNoParameters = (input: string): AcceptHeader[] => {
function parseNoParameters(input: string): AcceptHeader[] {
const parts = splitAndClean(input);
return parts.map((part): AcceptHeader => {
@ -270,7 +273,7 @@ const parseNoParameters = (input: string): AcceptHeader[] => {
}
return result;
}).sort((left, right): number => right.weight - left.weight);
};
}
// EXPORTED FUNCTIONS
@ -284,13 +287,13 @@ const parseNoParameters = (input: string): AcceptHeader[] => {
*
* @returns An array of {@link Accept} objects, sorted by weight.
*/
export const parseAccept = (input: string): Accept[] => {
export function parseAccept(input: string): Accept[] {
// Quoted strings could prevent split from having correct results
const { result, replacements } = transformQuotedStrings(input);
return splitAndClean(result)
.map((part): Accept => parseAcceptPart(part, replacements))
.sort((left, right): number => right.weight - left.weight);
};
}
/**
* Parses an Accept-Charset header string.
@ -302,7 +305,7 @@ export const parseAccept = (input: string): Accept[] => {
*
* @returns An array of {@link AcceptCharset} objects, sorted by weight.
*/
export const parseAcceptCharset = (input: string): AcceptCharset[] => {
export function parseAcceptCharset(input: string): AcceptCharset[] {
const results = parseNoParameters(input);
results.forEach((result): void => {
if (!token.test(result.range)) {
@ -313,7 +316,7 @@ export const parseAcceptCharset = (input: string): AcceptCharset[] => {
}
});
return results;
};
}
/**
* Parses an Accept-Encoding header string.
@ -325,7 +328,7 @@ export const parseAcceptCharset = (input: string): AcceptCharset[] => {
*
* @returns An array of {@link AcceptEncoding} objects, sorted by weight.
*/
export const parseAcceptEncoding = (input: string): AcceptEncoding[] => {
export function parseAcceptEncoding(input: string): AcceptEncoding[] {
const results = parseNoParameters(input);
results.forEach((result): void => {
if (!token.test(result.range)) {
@ -334,7 +337,7 @@ export const parseAcceptEncoding = (input: string): AcceptEncoding[] => {
}
});
return results;
};
}
/**
* Parses an Accept-Language header string.
@ -346,7 +349,7 @@ export const parseAcceptEncoding = (input: string): AcceptEncoding[] => {
*
* @returns An array of {@link AcceptLanguage} objects, sorted by weight.
*/
export const parseAcceptLanguage = (input: string): AcceptLanguage[] => {
export function parseAcceptLanguage(input: string): AcceptLanguage[] {
const results = parseNoParameters(input);
results.forEach((result): void => {
// (1*8ALPHA *("-" 1*8alphanum)) / "*"
@ -360,7 +363,7 @@ export const parseAcceptLanguage = (input: string): AcceptLanguage[] => {
}
});
return results;
};
}
// eslint-disable-next-line max-len
const rfc1123Date = /^(?:Mon|Tue|Wed|Thu|Fri|Sat|Sun), \d{2} (?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) \d{4} \d{2}:\d{2}:\d{2} GMT$/u;
@ -372,7 +375,7 @@ const rfc1123Date = /^(?:Mon|Tue|Wed|Thu|Fri|Sat|Sun), \d{2} (?:Jan|Feb|Mar|Apr|
*
* @returns An array with a single {@link AcceptDatetime} object.
*/
export const parseAcceptDateTime = (input: string): AcceptDatetime[] => {
export function parseAcceptDateTime(input: string): AcceptDatetime[] {
const results: AcceptDatetime[] = [];
const range = input.trim();
if (range) {
@ -387,12 +390,12 @@ export const parseAcceptDateTime = (input: string): AcceptDatetime[] => {
results.push({ range, weight: 1 });
}
return results;
};
}
/**
* Adds a header value without overriding previous values.
*/
export const addHeader = (response: HttpResponse, name: string, value: string | string[]): void => {
export function addHeader(response: HttpResponse, name: string, value: string | string[]): void {
let allValues: string[] = [];
if (response.hasHeader(name)) {
let oldValues = response.getHeader(name)!;
@ -409,7 +412,7 @@ export const addHeader = (response: HttpResponse, name: string, value: string |
allValues.push(value);
}
response.setHeader(name, allValues.length === 1 ? allValues[0] : allValues);
};
}
/**
* The Forwarded header from RFC7239
@ -432,7 +435,7 @@ export interface Forwarded {
*
* @returns The parsed Forwarded header.
*/
export const parseForwarded = (value = ''): Forwarded => {
export function parseForwarded(value = ''): Forwarded {
const forwarded: Record<string, string> = {};
if (value) {
for (const pair of value.replace(/\s*,.*$/u, '').split(';')) {
@ -443,4 +446,4 @@ export const parseForwarded = (value = ''): Forwarded => {
}
}
return forwarded;
};
}

View File

@ -8,7 +8,9 @@ import type { ResourceIdentifier } from '../ldp/representation/ResourceIdentifie
*
* @returns The potentially changed path (POSIX).
*/
const windowsToPosixPath = (path: string): string => path.replace(/\\+/gu, '/');
function windowsToPosixPath(path: string): string {
return path.replace(/\\+/gu, '/');
}
/**
* Resolves relative segments in the path/
@ -17,8 +19,9 @@ const windowsToPosixPath = (path: string): string => path.replace(/\\+/gu, '/');
*
* @returns The potentially changed path (POSIX).
*/
export const normalizeFilePath = (path: string): string =>
posix.normalize(windowsToPosixPath(path));
export function normalizeFilePath(path: string): string {
return posix.normalize(windowsToPosixPath(path));
}
/**
* Adds the paths to the base path.
@ -28,8 +31,9 @@ export const normalizeFilePath = (path: string): string =>
*
* @returns The potentially changed path (POSIX).
*/
export const joinFilePath = (basePath: string, ...paths: string[]): string =>
posix.join(windowsToPosixPath(basePath), ...paths);
export function joinFilePath(basePath: string, ...paths: string[]): string {
return posix.join(windowsToPosixPath(basePath), ...paths);
}
/**
* Converts the path into an OS-dependent path.
@ -38,8 +42,9 @@ export const joinFilePath = (basePath: string, ...paths: string[]): string =>
*
* @returns The potentially changed path (OS-dependent).
*/
export const toSystemFilePath = (path: string): string =>
platform.normalize(path);
export function toSystemFilePath(path: string): string {
return platform.normalize(path);
}
/**
* Makes sure the input path has exactly 1 slash at the end.
@ -50,7 +55,9 @@ export const toSystemFilePath = (path: string): string =>
*
* @returns The potentially changed path.
*/
export const ensureTrailingSlash = (path: string): string => path.replace(/\/*$/u, '/');
export function ensureTrailingSlash(path: string): string {
return path.replace(/\/*$/u, '/');
}
/**
* Makes sure the input path has no slashes at the end.
@ -59,34 +66,46 @@ export const ensureTrailingSlash = (path: string): string => path.replace(/\/*$/
*
* @returns The potentially changed path.
*/
export const trimTrailingSlashes = (path: string): string => path.replace(/\/+$/u, '');
export function trimTrailingSlashes(path: string): string {
return path.replace(/\/+$/u, '');
}
/**
* Converts a URI path to the canonical version by splitting on slashes,
* decoding any percent-based encodings,
* and then encoding any special characters.
*/
export const toCanonicalUriPath = (path: string): string => path.split('/').map((part): string =>
encodeURIComponent(decodeURIComponent(part))).join('/');
export function toCanonicalUriPath(path: string): string {
return path.split('/').map((part): string =>
encodeURIComponent(decodeURIComponent(part))).join('/');
}
/**
* Decodes all components of a URI path.
*/
export const decodeUriPathComponents = (path: string): string => path.split('/').map(decodeURIComponent).join('/');
export function decodeUriPathComponents(path: string): string {
return path.split('/').map(decodeURIComponent).join('/');
}
/**
* Encodes all (non-slash) special characters in a URI path.
*/
export const encodeUriPathComponents = (path: string): string => path.split('/').map(encodeURIComponent).join('/');
export function encodeUriPathComponents(path: string): string {
return path.split('/').map(encodeURIComponent).join('/');
}
/**
* Checks if the path corresponds to a container path (ending in a /).
* @param path - Path to check.
*/
export const isContainerPath = (path: string): boolean => path.endsWith('/');
export function isContainerPath(path: string): boolean {
return path.endsWith('/');
}
/**
* Checks if the identifier corresponds to a container identifier.
* @param identifier - Identifier to check.
*/
export const isContainerIdentifier = (identifier: ResourceIdentifier): boolean => isContainerPath(identifier.path);
export function isContainerIdentifier(identifier: ResourceIdentifier): boolean {
return isContainerPath(identifier.path);
}

View File

@ -10,14 +10,14 @@ import { toSubjectTerm, toPredicateTerm, toObjectTerm } from './TermUtil';
/**
* Generates a quad with the given subject/predicate/object and pushes it to the given array.
*/
export const pushQuad = (
export function pushQuad(
quads: Quad[] | PassThrough,
subject: string | NamedNode,
predicate: string | NamedNode,
object: string | NamedNode | Literal,
): void => {
): void {
quads.push(DataFactory.quad(toSubjectTerm(subject), toPredicateTerm(predicate), toObjectTerm(object)));
};
}
/**
* Helper function for serializing an array of quads, with as result a Readable object.
@ -26,8 +26,9 @@ export const pushQuad = (
*
* @returns The Readable object.
*/
export const serializeQuads = (quads: Quad[], contentType?: string): Guarded<Readable> =>
pipeSafely(streamifyArray(quads), new StreamWriter({ format: contentType }));
export function serializeQuads(quads: Quad[], contentType?: string): Guarded<Readable> {
return pipeSafely(streamifyArray(quads), new StreamWriter({ format: contentType }));
}
/**
* Helper function to convert a Readable into an array of quads.
@ -36,5 +37,6 @@ export const serializeQuads = (quads: Quad[], contentType?: string): Guarded<Rea
*
* @returns A promise containing the array of quads.
*/
export const parseQuads = async(readable: Guarded<Readable>, contentType?: string): Promise<Quad[]> =>
arrayifyStream(pipeSafely(readable, new StreamParser({ format: contentType })));
export async function parseQuads(readable: Guarded<Readable>, contentType?: string): Promise<Quad[]> {
return arrayifyStream(pipeSafely(readable, new StreamParser({ format: contentType })));
}

View File

@ -11,7 +11,7 @@ import { LDP, RDF } from './Vocabularies';
*
* @returns The generated quads.
*/
export const generateResourceQuads = (subject: NamedNode, isContainer: boolean): Quad[] => {
export function generateResourceQuads(subject: NamedNode, isContainer: boolean): Quad[] {
const quads: Quad[] = [];
if (isContainer) {
pushQuad(quads, subject, RDF.terms.type, LDP.terms.Container);
@ -20,7 +20,7 @@ export const generateResourceQuads = (subject: NamedNode, isContainer: boolean):
pushQuad(quads, subject, RDF.terms.type, LDP.terms.Resource);
return quads;
};
}
/**
* Helper function to generate the quads describing that the resource URIs are children of the container URI.
@ -29,5 +29,7 @@ export const generateResourceQuads = (subject: NamedNode, isContainer: boolean):
*
* @returns The generated quads.
*/
export const generateContainmentQuads = (containerURI: NamedNode, childURIs: string[]): Quad[] =>
new RepresentationMetadata(containerURI, { [LDP.contains]: childURIs.map(DataFactory.namedNode) }).quads();
export function generateContainmentQuads(containerURI: NamedNode, childURIs: string[]): Quad[] {
return new RepresentationMetadata(containerURI,
{ [LDP.contains]: childURIs.map(DataFactory.namedNode) }).quads();
}

View File

@ -13,7 +13,9 @@ const logger = getLoggerFor('StreamUtil');
*
* @returns The joined string.
*/
export const readableToString = async(stream: Readable): Promise<string> => (await arrayifyStream(stream)).join('');
export async function readableToString(stream: Readable): Promise<string> {
return (await arrayifyStream(stream)).join('');
}
/**
* Pipes one stream into another and emits errors of the first stream with the second.
@ -25,8 +27,8 @@ export const readableToString = async(stream: Readable): Promise<string> => (awa
*
* @returns The destination stream.
*/
export const pipeSafely = <T extends Writable>(readable: NodeJS.ReadableStream, destination: T,
mapError?: (error: Error) => Error): Guarded<T> => {
export function pipeSafely<T extends Writable>(readable: NodeJS.ReadableStream, destination: T,
mapError?: (error: Error) => Error): Guarded<T> {
// Not using `stream.pipeline` since the result there only emits an error event if the last stream has the error
readable.pipe(destination);
readable.on('error', (error): void => {
@ -39,12 +41,13 @@ export const pipeSafely = <T extends Writable>(readable: NodeJS.ReadableStream,
destination.destroy(mapError ? mapError(error) : error);
});
return guardStream(destination);
};
}
/**
* Converts an iterable to a stream and applies an error guard so that it is {@link Guarded}.
* @param iterable - Data to stream.
* @param options - Options to pass to the Readable constructor. See {@link Readable.from}.
*/
export const guardedStreamFrom = (iterable: Iterable<any>, options?: ReadableOptions): Guarded<Readable> =>
guardStream(Readable.from(iterable, options));
export function guardedStreamFrom(iterable: Iterable<any>, options?: ReadableOptions): Guarded<Readable> {
return guardStream(Readable.from(iterable, options));
}

View File

@ -20,7 +20,7 @@ const cachedNamedNodes: Record<string, NamedNode> = {
* so only use this for internal constants!
* @param name - Predicate to potentially transform.
*/
export const toCachedNamedNode = (name: NamedNode | string): NamedNode => {
export function toCachedNamedNode(name: NamedNode | string): NamedNode {
if (typeof name !== 'string') {
return name;
}
@ -28,20 +28,22 @@ export const toCachedNamedNode = (name: NamedNode | string): NamedNode => {
cachedNamedNodes[name] = namedNode(name);
}
return cachedNamedNodes[name];
};
}
/**
* @param input - Checks if this is a {@link Term}.
*/
export const isTerm = (input?: any): input is Term =>
input && typeof input.termType === 'string';
export function isTerm(input?: any): input is Term {
return input && typeof input.termType === 'string';
}
/**
* Converts a subject to a named node when needed.
* @param subject - Subject to potentially transform.
*/
export const toSubjectTerm = (subject: NamedNode | string): NamedNode =>
typeof subject === 'string' ? namedNode(subject) : subject;
export function toSubjectTerm(subject: NamedNode | string): NamedNode {
return typeof subject === 'string' ? namedNode(subject) : subject;
}
export const toPredicateTerm = toSubjectTerm;
@ -50,17 +52,18 @@ export const toPredicateTerm = toSubjectTerm;
* @param object - Object to potentially transform.
* @param preferLiteral - Whether strings are converted to literals or named nodes.
*/
export const toObjectTerm = <T extends Term>(object: T | string, preferLiteral = false): T => {
export function toObjectTerm<T extends Term>(object: T | string, preferLiteral = false): T {
if (typeof object === 'string') {
return (preferLiteral ? literal(object) : namedNode(object)) as any;
}
return object;
};
}
/**
* Creates a literal by first converting the dataType string to a named node.
* @param object - Object value.
* @param dataType - Object data type (as string).
*/
export const toLiteral = (object: string | number, dataType: NamedNode): Literal =>
literal(object, dataType);
export function toLiteral(object: string | number, dataType: NamedNode): Literal {
return literal(`${object}`, dataType);
}

View File

@ -12,11 +12,11 @@ export type Namespace<TKey extends any[], TValue> =
* Creates a function that expands local names from the given base URI,
* and exports the given local names as properties on the returned object.
*/
export const createNamespace = <TKey extends string, TValue>(
export function createNamespace<TKey extends string, TValue>(
baseUri: string,
toValue: (expanded: string) => TValue,
...localNames: TKey[]):
Namespace<typeof localNames, TValue> => {
Namespace<typeof localNames, TValue> {
// Create a function that expands local names
const expanded = {} as Record<string, TValue>;
const namespace = ((localName: string): TValue => {
@ -31,33 +31,36 @@ Namespace<typeof localNames, TValue> => {
(namespace as RecordOf<typeof localNames, TValue>)[localName] = namespace(localName);
}
return namespace;
};
}
/**
* Creates a function that expands local names from the given base URI into strings,
* and exports the given local names as properties on the returned object.
*/
export const createUriNamespace = <T extends string>(baseUri: string, ...localNames: T[]):
Namespace<typeof localNames, string> =>
createNamespace(baseUri, (expanded): string => expanded, ...localNames);
export function createUriNamespace<T extends string>(baseUri: string, ...localNames: T[]):
Namespace<typeof localNames, string> {
return createNamespace(baseUri, (expanded): string => expanded, ...localNames);
}
/**
* Creates a function that expands local names from the given base URI into named nodes,
* and exports the given local names as properties on the returned object.
*/
export const createTermNamespace = <T extends string>(baseUri: string, ...localNames: T[]):
Namespace<typeof localNames, NamedNode> =>
createNamespace(baseUri, namedNode, ...localNames);
export function createTermNamespace<T extends string>(baseUri: string, ...localNames: T[]):
Namespace<typeof localNames, NamedNode> {
return createNamespace(baseUri, namedNode, ...localNames);
}
/**
* Creates a function that expands local names from the given base URI into string,
* and exports the given local names as properties on the returned object.
* Under the `terms` property, it exposes the expanded local names as named nodes.
*/
export const createUriAndTermNamespace = <T extends string>(baseUri: string, ...localNames: T[]):
Namespace<typeof localNames, string> & { terms: Namespace<typeof localNames, NamedNode> } =>
Object.assign(createUriNamespace(baseUri, ...localNames),
export function createUriAndTermNamespace<T extends string>(baseUri: string, ...localNames: T[]):
Namespace<typeof localNames, string> & { terms: Namespace<typeof localNames, NamedNode> } {
return Object.assign(createUriNamespace(baseUri, ...localNames),
{ terms: createTermNamespace(baseUri, ...localNames) });
}
export const ACL = createUriAndTermNamespace('http://www.w3.org/ns/auth/acl#',
'accessTo',

View File

@ -41,4 +41,6 @@ export interface SystemError extends Error {
syscall: string;
}
export const isSystemError = (error: any): error is SystemError => error.code && error.syscall;
export function isSystemError(error: any): error is SystemError {
return error.code && error.syscall;
}

View File

@ -8,8 +8,8 @@ export const BASE = 'http://test.com';
/**
* Returns a component instantiated from a Components.js configuration.
*/
export const instantiateFromConfig = async(componentUrl: string, configFile: string,
variables?: Record<string, any>): Promise<any> => {
export async function instantiateFromConfig(componentUrl: string, configFile: string,
variables?: Record<string, any>): Promise<any> {
// Initialize the Components.js loader
const mainModulePath = joinFilePath(__dirname, '../../');
const loader = new Loader({ mainModulePath });
@ -18,15 +18,16 @@ export const instantiateFromConfig = async(componentUrl: string, configFile: str
// Instantiate the component from the config
const configPath = toSystemFilePath(joinFilePath(__dirname, 'config', configFile));
return loader.instantiateFromUrl(componentUrl, configPath, undefined, { variables });
};
}
export const getTestFolder = (name: string): string =>
joinFilePath(__dirname, '../tmp', name);
export function getTestFolder(name: string): string {
return joinFilePath(__dirname, '../tmp', name);
}
export const createFolder = (folder: string): void => {
export function createFolder(folder: string): void {
mkdirSync(folder, { recursive: true });
};
}
export const removeFolder = (folder: string): void => {
export function removeFolder(folder: string): void {
rimraf.sync(folder, { glob: false });
};
}

View File

@ -29,13 +29,13 @@ class DummyFactory implements FileIdentifierMapperFactory {
}
}
const genToArray = async<T>(iterable: AsyncIterable<T>): Promise<T[]> => {
async function genToArray<T>(iterable: AsyncIterable<T>): Promise<T[]> {
const arr: T[] = [];
for await (const result of iterable) {
arr.push(result);
}
return arr;
};
}
describe('A TemplatedResourcesGenerator', (): void => {
const rootFilePath = 'templates';

View File

@ -21,13 +21,13 @@ describe('A LockingResourceStore', (): void => {
jest.clearAllMocks();
order = [];
const delayedResolve = (resolve: (resolveParams: any) => void, name: string, resolveParams?: any): void => {
function delayedResolve(resolve: (value: any) => void, name: string, resolveValue?: any): void {
// `setImmediate` is introduced to make sure the promise doesn't execute immediately
setImmediate((): void => {
order.push(name);
resolve(resolveParams);
resolve(resolveValue);
});
};
}
const readable = streamifyArray([ 1, 2, 3 ]);
source = {
@ -70,14 +70,14 @@ describe('A LockingResourceStore', (): void => {
store = new LockingResourceStore(source, locker);
});
const registerEventOrder = async(eventSource: EventEmitter, event: string): Promise<void> => {
async function registerEventOrder(eventSource: EventEmitter, event: string): Promise<void> {
await new Promise((resolve): any => {
eventSource.prependListener(event, (): any => {
order.push(event);
resolve();
});
});
};
}
it('acquires a lock on the container when adding a representation.', async(): Promise<void> => {
await store.addResource({ path: 'path' }, {} as Representation);

View File

@ -20,12 +20,12 @@ const { literal, namedNode, quad } = DataFactory;
jest.mock('fetch-sparql-endpoint');
const simplifyQuery = (query: string | string[]): string => {
function simplifyQuery(query: string | string[]): string {
if (Array.isArray(query)) {
query = query.join(' ');
}
return query.replace(/\n/gu, ' ').trim();
};
}
describe('A SparqlDataAccessor', (): void => {
const endpoint = 'http://test.com/sparql';

View File

@ -65,7 +65,7 @@ describe('A SparqlUpdatePatchHandler', (): void => {
handler = new SparqlUpdatePatchHandler(source, locker);
});
const basicChecks = async(quads: Quad[]): Promise<boolean> => {
async function basicChecks(quads: Quad[]): Promise<boolean> {
expect(source.getRepresentation).toHaveBeenCalledTimes(1);
expect(source.getRepresentation).toHaveBeenLastCalledWith(
{ path: 'path' }, { type: { [INTERNAL_QUADS]: 1 }},
@ -81,7 +81,7 @@ describe('A SparqlUpdatePatchHandler', (): void => {
expect(setParams[1].metadata.contentType).toEqual(INTERNAL_QUADS);
await expect(arrayifyStream(setParams[1].data)).resolves.toBeRdfIsomorphic(quads);
return true;
};
}
it('only accepts SPARQL updates.', async(): Promise<void> => {
const input = { identifier: { path: 'path' },

View File

@ -9,14 +9,14 @@ describe('A WrappedExpiringResourceLocker', (): void => {
order = [];
});
const registerEventOrder = async(eventSource: EventEmitter, event: string): Promise<void> => {
async function registerEventOrder(eventSource: EventEmitter, event: string): Promise<void> {
await new Promise((resolve): any => {
eventSource.prependListener(event, (): any => {
order.push(event);
resolve();
});
});
};
}
it('emits an error event when releasing the lock errors.', async(): Promise<void> => {
jest.useFakeTimers();

View File

@ -199,9 +199,9 @@ export class ResourceHelper {
}
}
export const describeIf = (envFlag: string, name: string, fn: () => void): void => {
export function describeIf(envFlag: string, name: string, fn: () => void): void {
const flag = `TEST_${envFlag.toUpperCase()}`;
const enabled = !/^(|0|false)$/iu.test(process.env[flag] ?? '');
// eslint-disable-next-line jest/valid-describe, jest/valid-title, jest/no-disabled-tests
return enabled ? describe(name, fn) : describe.skip(name, fn);
};
}

View File

@ -9,13 +9,13 @@ import type { HttpHandler } from '../../src/server/HttpHandler';
import type { HttpRequest } from '../../src/server/HttpRequest';
import type { SystemError } from '../../src/util/errors/SystemError';
export const performRequest = async(
export async function performRequest(
handler: HttpHandler,
requestUrl: URL,
method: string,
headers: IncomingHttpHeaders,
data: string[],
): Promise<MockResponse<any>> => {
): Promise<MockResponse<any>> {
const request = streamifyArray(data) as HttpRequest;
request.url = requestUrl.pathname;
request.method = method;
@ -36,7 +36,7 @@ export const performRequest = async(
await endPromise;
return response;
};
}
/**
* Mocks (some) functions of the fs system library.
@ -56,21 +56,21 @@ export const performRequest = async(
* @param rootFilepath - The name of the root folder in which fs will start.
* @param time - The date object to use for time functions (currently only mtime from lstats)
*/
export const mockFs = (rootFilepath?: string, time?: Date): { data: any } => {
export function mockFs(rootFilepath?: string, time?: Date): { data: any } {
const cache: { data: any } = { data: {}};
rootFilepath = rootFilepath ?? 'folder';
time = time ?? new Date();
// eslint-disable-next-line unicorn/consistent-function-scoping
const throwSystemError = (code: string): void => {
function throwSystemError(code: string): void {
const error = new Error('error') as SystemError;
error.code = code;
error.syscall = 'this exists for isSystemError';
throw error;
};
}
const getFolder = (path: string): { folder: any; name: string } => {
function getFolder(path: string): { folder: any; name: string } {
let parts = path.slice(rootFilepath!.length).split('/').filter((part): boolean => part.length > 0);
if (parts.length === 0) {
@ -91,7 +91,7 @@ export const mockFs = (rootFilepath?: string, time?: Date): { data: any } => {
});
return { folder, name };
};
}
const mock = {
createReadStream(path: string): any {
@ -171,4 +171,4 @@ export const mockFs = (rootFilepath?: string, time?: Date): { data: any } => {
Object.assign(fs, mock);
return cache;
};
}