getstream
Version:
The official low-level GetStream.io client for Node.js and the browser.
121 lines (98 loc) • 3 kB
text/typescript
import FormData from 'form-data';
import { FeedError } from './errors';
const validFeedSlugRe = /^[\w]+$/;
const validUserIdRe = /^[\w-]+$/;
/*
* Validate that the feedSlug matches \w
*/
function validateFeedSlug(feedSlug: string) {
if (!validFeedSlugRe.test(feedSlug)) {
throw new FeedError(`Invalid feedSlug, please use letters, numbers or _: ${feedSlug}`);
}
return feedSlug;
}
/*
* Validate the userId matches \w
*/
function validateUserId(userId: string) {
if (!validUserIdRe.test(userId)) {
throw new FeedError(`Invalid userId, please use letters, numbers, - or _: ${userId}`);
}
return userId;
}
function rfc3986(str: string) {
return str.replace(/[!'()*]/g, (c) => `%${c.charCodeAt(0).toString(16).toUpperCase()}`);
}
function isReadableStream(obj: unknown): obj is NodeJS.ReadStream {
return (
obj !== null &&
typeof obj === 'object' &&
((obj as NodeJS.ReadStream).readable || typeof (obj as NodeJS.ReadStream)._read === 'function')
);
}
function isBuffer(obj: unknown): obj is Buffer {
return (
obj != null &&
(obj as Buffer).constructor != null &&
// @ts-expect-error
typeof obj.constructor.isBuffer === 'function' &&
// @ts-expect-error
obj.constructor.isBuffer(obj)
);
}
function isFileWebAPI(uri: unknown): uri is File {
return typeof window !== 'undefined' && 'File' in window && uri instanceof File;
}
/*
* Validate that the feedId matches the spec user:1
*/
function validateFeedId(feedId: string) {
const parts = feedId.split(':');
if (parts.length !== 2) {
throw new FeedError(`Invalid feedId, expected something like user:1 got ${feedId}`);
}
const [feedSlug, userId] = parts;
validateFeedSlug(feedSlug);
validateUserId(userId);
return feedId;
}
function addFileToFormData(uri: string | File | Buffer | NodeJS.ReadStream, name?: string, contentType?: string) {
const data = new FormData();
if (isReadableStream(uri) || isBuffer(uri) || isFileWebAPI(uri)) {
if (name) data.append('file', uri, name);
else data.append('file', uri);
} else {
data.append('file', {
uri,
name: name || (uri as string).split('/').reverse()[0],
type: contentType || undefined,
contentType: contentType || undefined,
});
}
return data;
}
// TODO: refactor and add proper types
function replaceStreamObjects<T, V>(obj: T): V {
// @ts-expect-error
if (Array.isArray(obj)) return obj.map((v) => replaceStreamObjects(v));
// @ts-expect-error
if (Object.prototype.toString.call(obj) !== '[object Object]') return obj;
// @ts-expect-error
if (typeof obj.ref === 'function') return obj.ref();
const cloned = {};
Object.getOwnPropertyNames(obj).forEach((k) => {
// @ts-expect-error
cloned[k] = replaceStreamObjects(obj[k]);
});
// @ts-expect-error
return cloned;
}
export default {
validateFeedId,
validateFeedSlug,
validateUserId,
rfc3986,
isReadableStream,
addFileToFormData,
replaceStreamObjects,
};