DataAccessHelper

Methods

get

Argument Type Required Description
type String Yes
options Yes

has

Argument Type Required Description
type String Yes

registerType

Argument Type Required Description
type String Yes
fn Yes

Source

HtmlDataAccessHelper.d.ts
export interface HtmlDataAccessHelper {
fetchArray(
instance: any,
baseURL: string,
array: object[],
options?: object
): Promise<object[]>;
fetchJSON(instance: any, url: string, options?: object): Promise<object>;
fetchText(instance: any, url: string, options?: object): Promise<string>;
fetchBinary(
instance: any,
url: string,
options?: object
): Promise<ArrayBuffer>;
fetchImage(
instance: any,
url: string,
options?: object
): Promise<HTMLImageElement>;
}

export default HtmlDataAccessHelper;
HtmlDataAccessHelper.js
import { decompressSync, strFromU8 } from 'fflate';

import macro from 'vtk.js/Sources/macros';
import Base64 from 'vtk.js/Sources/Common/Core/Base64';
import Endian from 'vtk.js/Sources/Common/Core/Endian';
import { DataTypeByteSize } from 'vtk.js/Sources/Common/Core/DataArray/Constants';
import { registerType } from 'vtk.js/Sources/IO/Core/DataAccessHelper';

const { vtkErrorMacro, vtkDebugMacro } = macro;

let requestCount = 0;

function getContent(url) {
const el = document.querySelector(`.webResource[data-url="${url}"]`);
return el ? el.innerHTML : null;
}

function getElement(url) {
return document.querySelector(`.webResource[data-url="${url}"]`);
}

function removeLeadingSlash(str) {
return str[0] === '/' ? str.substr(1) : str;
}

function fetchText(instance, url, options = {}) {
return new Promise((resolve, reject) => {
const txt = getContent(url);
if (txt === null) {
reject(new Error(`No such text ${url}`));
} else {
resolve(txt);
}
});
}

function fetchJSON(instance, url, options = {}) {
return new Promise((resolve, reject) => {
const txt = getContent(removeLeadingSlash(url));
if (txt === null) {
reject(new Error(`No such JSON ${url}`));
} else {
resolve(JSON.parse(txt));
}
});
}

function fetchArray(instance, baseURL, array, options = {}) {
return new Promise((resolve, reject) => {
const url = removeLeadingSlash(
[
baseURL,
array.ref.basepath,
options.compression ? `${array.ref.id}.gz` : array.ref.id,
].join('/')
);

const txt = getContent(url);
if (txt === null) {
reject(new Error(`No such array ${url}`));
} else {
if (array.dataType === 'string') {
let bText = atob(txt);
if (options.compression) {
bText = strFromU8(decompressSync(bText));
}
array.values = JSON.parse(bText);
} else {
const uint8array = new Uint8Array(Base64.toArrayBuffer(txt));

array.buffer = new ArrayBuffer(uint8array.length);

// copy uint8array to buffer
const view = new Uint8Array(array.buffer);
view.set(uint8array);

if (options.compression) {
if (array.dataType === 'string' || array.dataType === 'JSON') {
array.buffer = strFromU8(
decompressSync(new Uint8Array(array.buffer))
);
} else {
array.buffer = decompressSync(new Uint8Array(array.buffer)).buffer;
}
}

if (array.ref.encode === 'JSON') {
array.values = JSON.parse(array.buffer);
} else {
if (Endian.ENDIANNESS !== array.ref.encode && Endian.ENDIANNESS) {
// Need to swap bytes
vtkDebugMacro(`Swap bytes of ${array.name}`);
Endian.swapBytes(array.buffer, DataTypeByteSize[array.dataType]);
}

array.values = macro.newTypedArray(array.dataType, array.buffer);
}

if (array.values.length !== array.size) {
vtkErrorMacro(
`Error in FetchArray: ${array.name} does not have the proper array size. Got ${array.values.length}, instead of ${array.size}`
);
}
}

// Done with the ref and work
delete array.ref;
if (--requestCount === 0 && instance?.invokeBusy) {
instance.invokeBusy(false);
}
if (instance?.modified) {
instance.modified();
}

resolve(array);
}
});
}

// ----------------------------------------------------------------------------

function fetchImage(instance, url, options = {}) {
return new Promise((resolve, reject) => {
const img = getElement(url);
if (img) {
resolve(img);
} else {
reject(new Error(`No such image ${url}`));
}
});
}

// ----------------------------------------------------------------------------

const HtmlDataAccessHelper = {
fetchJSON,
fetchText,
fetchArray,
fetchImage,
};

registerType('html', (options) => HtmlDataAccessHelper);

// Export fetch methods
export default HtmlDataAccessHelper;
HttpDataAccessHelper.d.ts
export interface HttpDataAccessHelper {
fetchArray(
instance: any,
baseURL: string,
array: object[],
options?: object
): Promise<object[]>;
fetchJSON(instance: any, url: string, options?: object): Promise<object>;
fetchText(instance: any, url: string, options?: object): Promise<string>;
fetchBinary(
instance: any,
url: string,
options?: object
): Promise<ArrayBuffer>;
fetchImage(
instance: any,
url: string,
options?: object
): Promise<HTMLImageElement>;
}

export default HttpDataAccessHelper;
HttpDataAccessHelper.js
import { decompressSync, strFromU8 } from 'fflate';

import macro from 'vtk.js/Sources/macros';
import Endian from 'vtk.js/Sources/Common/Core/Endian';
import { DataTypeByteSize } from 'vtk.js/Sources/Common/Core/DataArray/Constants';
import { registerType } from 'vtk.js/Sources/IO/Core/DataAccessHelper';

const { vtkErrorMacro, vtkDebugMacro } = macro;

/* eslint-disable prefer-promise-reject-errors */

let requestCount = 0;

function openAsyncXHR(method, url, options = {}) {
const xhr = new XMLHttpRequest();
xhr.open(method, url, true);

if (options.headers) {
Object.entries(options.headers).forEach(([key, value]) =>
xhr.setRequestHeader(key, value)
);
}

if (options.progressCallback) {
xhr.addEventListener('progress', options.progressCallback);
}

return xhr;
}

function fetchBinary(url, options = {}) {
if (options && options.compression && options.compression !== 'gz') {
vtkErrorMacro('Supported algorithms are: [gz]');
vtkErrorMacro(`Unkown compression algorithm: ${options.compression}`);
}

return new Promise((resolve, reject) => {
const xhr = openAsyncXHR('GET', url, options);

xhr.onreadystatechange = (e) => {
if (xhr.readyState === 4) {
if (xhr.status === 200 || xhr.status === 0) {
if (options.compression) {
resolve(decompressSync(new Uint8Array(xhr.response)).buffer);
} else {
resolve(xhr.response);
}
} else {
reject({ xhr, e });
}
}
};

// Make request
xhr.responseType = 'arraybuffer';
xhr.send();
});
}

function fetchArray(instance, baseURL, array, options = {}) {
if (array.ref && !array.ref.pending) {
return new Promise((resolve, reject) => {
let url = null;

if (array.ref.url) {
url = array.ref.url;
} else {
url = [
baseURL,
array.ref.basepath,
options.compression ? `${array.ref.id}.gz` : array.ref.id,
].join('/');
}

const xhr = openAsyncXHR('GET', url, options);

xhr.onreadystatechange = (e) => {
if (xhr.readyState === 1) {
array.ref.pending = true;
if (++requestCount === 1 && instance?.invokeBusy) {
instance.invokeBusy(true);
}
}
if (xhr.readyState === 4) {
array.ref.pending = false;
if (xhr.status === 200 || xhr.status === 0) {
array.buffer = xhr.response;

if (options.compression) {
if (array.dataType === 'string' || array.dataType === 'JSON') {
array.buffer = strFromU8(
decompressSync(new Uint8Array(array.buffer))
);
} else {
array.buffer = decompressSync(
new Uint8Array(array.buffer)
).buffer;
}
}

if (array.ref.encode === 'JSON') {
array.values = JSON.parse(array.buffer);
} else {
if (Endian.ENDIANNESS !== array.ref.encode && Endian.ENDIANNESS) {
// Need to swap bytes
vtkDebugMacro(`Swap bytes of ${array.name}`);
Endian.swapBytes(
array.buffer,
DataTypeByteSize[array.dataType]
);
}

array.values = macro.newTypedArray(array.dataType, array.buffer);
}

if (array.values.length !== array.size) {
vtkErrorMacro(
`Error in FetchArray: ${array.name}, does not have the proper array size. Got ${array.values.length}, instead of ${array.size}`
);
}

// Done with the ref and work
delete array.ref;
if (--requestCount === 0 && instance?.invokeBusy) {
instance.invokeBusy(false);
}
if (instance?.modified) {
instance.modified();
}
resolve(array);
} else {
reject({ xhr, e });
}
}
};

// Make request
xhr.responseType =
options.compression || array.dataType !== 'string'
? 'arraybuffer'
: 'text';
xhr.send();
});
}

return Promise.resolve(array);
}

// ----------------------------------------------------------------------------

function fetchJSON(instance, url, options = {}) {
return new Promise((resolve, reject) => {
const xhr = openAsyncXHR('GET', url, options);

xhr.onreadystatechange = (e) => {
if (xhr.readyState === 1) {
if (++requestCount === 1 && instance?.invokeBusy) {
instance.invokeBusy(true);
}
}
if (xhr.readyState === 4) {
if (--requestCount === 0 && instance?.invokeBusy) {
instance.invokeBusy(false);
}
if (xhr.status === 200 || xhr.status === 0) {
if (options.compression) {
resolve(
JSON.parse(
strFromU8(decompressSync(new Uint8Array(xhr.response)))
)
);
} else {
resolve(JSON.parse(xhr.responseText));
}
} else {
reject({ xhr, e });
}
}
};

// Make request
xhr.responseType = options.compression ? 'arraybuffer' : 'text';
xhr.send();
});
}

// ----------------------------------------------------------------------------

function fetchText(instance, url, options = {}) {
if (options && options.compression && options.compression !== 'gz') {
vtkErrorMacro('Supported algorithms are: [gz]');
vtkErrorMacro(`Unkown compression algorithm: ${options.compression}`);
}

return new Promise((resolve, reject) => {
const xhr = openAsyncXHR('GET', url, options);

xhr.onreadystatechange = (e) => {
if (xhr.readyState === 1) {
if (++requestCount === 1 && instance?.invokeBusy) {
instance.invokeBusy(true);
}
}
if (xhr.readyState === 4) {
if (--requestCount === 0 && instance?.invokeBusy) {
instance.invokeBusy(false);
}
if (xhr.status === 200 || xhr.status === 0) {
if (options.compression) {
resolve(strFromU8(decompressSync(new Uint8Array(xhr.response))));
} else {
resolve(xhr.responseText);
}
} else {
reject({ xhr, e });
}
}
};

// Make request
xhr.responseType = options.compression ? 'arraybuffer' : 'text';
xhr.send();
});
}

// ----------------------------------------------------------------------------

function fetchImage(instance, url, options = {}) {
return new Promise((resolve, reject) => {
const img = new Image();
if (options.crossOrigin) {
img.crossOrigin = options.crossOrigin;
}
img.onload = () => resolve(img);
img.onerror = reject;
img.src = url;
});
}
/* eslint-enable prefer-promise-reject-errors */

// ----------------------------------------------------------------------------

const HttpDataAccessHelper = {
fetchArray,
fetchJSON,
fetchText,
fetchBinary, // Only for HTTP
fetchImage,
};

registerType('http', (options) => HttpDataAccessHelper);

export default HttpDataAccessHelper;
JSZipDataAccessHelper.d.ts
interface IJSZipDataAccessHelper {
fetchArray(
instance: any,
baseURL: string,
array: object[],
options?: object
): Promise<object[]>;
fetchJSON(instance: any, url: string, options?: object): Promise<object>;
fetchText(instance: any, url: string, options?: object): Promise<string>;
fetchBinary(
instance: any,
url: string,
options?: object
): Promise<ArrayBuffer>;
fetchImage(
instance: any,
url: string,
options?: object
): Promise<HTMLImageElement>;
}

export interface JSZipDataAccessHelper {
create(createOptions: object): IJSZipDataAccessHelper;
}

export default JSZipDataAccessHelper;
JSZipDataAccessHelper.js
import { decompressSync, strFromU8, strToU8, unzipSync } from 'fflate';

import macro from 'vtk.js/Sources/macros';
import Endian from 'vtk.js/Sources/Common/Core/Endian';
import { DataTypeByteSize } from 'vtk.js/Sources/Common/Core/DataArray/Constants';
import { registerType } from 'vtk.js/Sources/IO/Core/DataAccessHelper';
import { fromArrayBuffer } from 'vtk.js/Sources/Common/Core/Base64';

const { vtkErrorMacro, vtkDebugMacro } = macro;

function toMimeType(url) {
const ext = url.split('.').pop().toLowerCase();
if (ext === 'jpg') {
return 'jpeg';
}
return ext;
}

function handleUint8Array(array, compression, done) {
return (uint8array) => {
array.buffer = new ArrayBuffer(uint8array.length);

// copy uint8array to buffer
const view = new Uint8Array(array.buffer);
view.set(uint8array);

if (compression) {
if (array.dataType === 'string' || array.dataType === 'JSON') {
array.buffer = strFromU8(decompressSync(new Uint8Array(array.buffer)));
} else {
array.buffer = decompressSync(new Uint8Array(array.buffer)).buffer;
}
}

if (array.ref.encode === 'JSON') {
array.values = JSON.parse(array.buffer);
} else {
if (Endian.ENDIANNESS !== array.ref.encode && Endian.ENDIANNESS) {
// Need to swap bytes
vtkDebugMacro(`Swap bytes of ${array.name}`);
Endian.swapBytes(array.buffer, DataTypeByteSize[array.dataType]);
}

array.values = macro.newTypedArray(array.dataType, array.buffer);
}

if (array.values.length !== array.size) {
vtkErrorMacro(
`Error in FetchArray: ${array.name} does not have the proper array size. Got ${array.values.length}, instead of ${array.size}`
);
}

done();
};
}

function handleString(array, compression, done) {
return (string) => {
if (compression) {
array.values = JSON.parse(strFromU8(decompressSync(string)));
} else {
array.values = JSON.parse(string);
}
done();
};
}

function removeLeadingSlash(str) {
return str[0] === '/' ? str.substr(1) : str;
}

function normalizePath(str) {
return new URL(str, 'http://any').pathname;
}

function cleanUpPath(str) {
return removeLeadingSlash(normalizePath(str));
}

function unpack(zipContent) {
return new Promise((resolve, reject) => {
if (typeof zipContent === 'string') {
resolve(strToU8(zipContent));
} else if (zipContent instanceof Blob) {
resolve(zipContent.arrayBuffer().then((ab) => new Uint8Array(ab)));
} else if (zipContent instanceof ArrayBuffer) {
resolve(new Uint8Array(zipContent));
} else if (zipContent?.buffer instanceof ArrayBuffer) {
resolve(new Uint8Array(zipContent.buffer));
} else {
reject(new Error('Invalid datatype to unpack.'));
}
});
}

function create(createOptions) {
let ready = false;
let requestCount = 0;
let decompressedFiles = null;
let fullRootPath = '';

unpack(createOptions.zipContent).then((zipFileData) => {
decompressedFiles = unzipSync(zipFileData);
ready = true;

// Find root index.json
const metaFiles = [];
Object.keys(decompressedFiles).forEach((relativePath) => {
if (relativePath.endsWith('index.json')) {
metaFiles.push(relativePath);
}
});
metaFiles.sort((a, b) => a.length - b.length);
// if not empty, then fullRootPath will have a forward slash suffix
fullRootPath = metaFiles[0].replace(/index\.json$/, '');

if (createOptions.callback) {
createOptions.callback(decompressedFiles);
}
});

return {
fetchArray(instance, baseURL, array, options = {}) {
return new Promise((resolve, reject) => {
if (!ready) {
vtkErrorMacro('ERROR!!! zip not ready...');
}
const url = cleanUpPath(
[
baseURL,
array.ref.basepath,
options.compression ? `${array.ref.id}.gz` : array.ref.id,
].join('/')
);

if (++requestCount === 1 && instance?.invokeBusy) {
instance.invokeBusy(true);
}

function doneCleanUp() {
// Done with the ref and work
delete array.ref;
if (--requestCount === 0 && instance?.invokeBusy) {
instance.invokeBusy(false);
}
if (instance?.modified) {
instance.modified();
}
resolve(array);
}

const fileData = decompressedFiles[`${fullRootPath}${url}`];

if (array.dataType === 'string' && !options.compression) {
// string
const handler = handleString(array, options.compression, doneCleanUp);
handler(strFromU8(fileData));
} else {
// uint8array
const handler = handleUint8Array(
array,
options.compression,
doneCleanUp
);
handler(fileData);
}
});
},

fetchJSON(instance, url, options = {}) {
const path = cleanUpPath(url);
if (!ready) {
vtkErrorMacro('ERROR!!! zip not ready...');
}
const fileData = decompressedFiles[`${fullRootPath}${path}`];
if (options.compression) {
if (options.compression === 'gz') {
const str = strFromU8(decompressSync(fileData));
return Promise.resolve(JSON.parse(str));
}
return Promise.reject(new Error('Invalid compression'));
}
return Promise.resolve(JSON.parse(strFromU8(fileData)));
},

fetchText(instance, url, options = {}) {
const path = cleanUpPath(url);
if (!ready) {
vtkErrorMacro('ERROR!!! zip not ready...');
}
const fileData = decompressedFiles[`${fullRootPath}${path}`];
if (options.compression) {
if (options.compression === 'gz') {
return Promise.resolve(strFromU8(unzipSync(fileData)));
}
return Promise.reject(new Error('Invalid compression'));
}
return Promise.resolve(strFromU8(fileData));
},

fetchImage(instance, url, options = {}) {
const path = cleanUpPath(url);
if (!ready) {
vtkErrorMacro('ERROR!!! zip not ready...');
}
const fileData = decompressedFiles[`${fullRootPath}${path}`];
return new Promise((resolve, reject) => {
const img = new Image();
img.onload = () => resolve(img);
img.onerror = reject;
const str = fromArrayBuffer(fileData.buffer);
img.src = `data:image/${toMimeType(path)};base64,${str}`;
});
},

fetchBinary(instance, url, options = {}) {
const path = cleanUpPath(url);
if (!ready) {
vtkErrorMacro('ERROR!!! zip not ready...');
}
const fileData = decompressedFiles[`${fullRootPath}${path}`];
if (options.compression) {
if (options.compression === 'gz') {
return Promise.resolve(decompressSync(fileData).buffer);
}
return Promise.reject(new Error('Invalid compression'));
}
return Promise.resolve(fileData.buffer);
},
};
}

const JSZipDataAccessHelper = {
create,
};

registerType('zip', (options) => JSZipDataAccessHelper.create(options));

export default JSZipDataAccessHelper;
LiteHttpDataAccessHelper.d.ts
export interface LiteHttpDataAccessHelper {
fetchArray(
instance: any,
baseURL: string,
array: object[],
options?: object
): Promise<object[]>;
fetchJSON(instance: any, url: string, options?: object): Promise<object>;
fetchText(instance: any, url: string, options?: object): Promise<string>;
fetchBinary(
instance: any,
url: string,
options?: object
): Promise<ArrayBuffer>;
fetchImage(
instance: any,
url: string,
options?: object
): Promise<HTMLImageElement>;
}

export default LiteHttpDataAccessHelper;
LiteHttpDataAccessHelper.js
import macro from 'vtk.js/Sources/macros';
import Endian from 'vtk.js/Sources/Common/Core/Endian';
import { DataTypeByteSize } from 'vtk.js/Sources/Common/Core/DataArray/Constants';
import { has, registerType } from 'vtk.js/Sources/IO/Core/DataAccessHelper';

const { vtkErrorMacro, vtkDebugMacro } = macro;

const REJECT_COMPRESSION = () => {
vtkErrorMacro(
'LiteHttpDataAccessHelper does not support compression. Need to register HttpDataAccessHelper instead.'
);
return Promise.reject(
new Error(
'LiteHttpDataAccessHelper does not support compression. Need to register HttpDataAccessHelper instead.'
)
);
};

/* eslint-disable prefer-promise-reject-errors */
let requestCount = 0;

function openAsyncXHR(method, url, options = {}) {
const xhr = new XMLHttpRequest();
xhr.open(method, url, true);

if (options.headers) {
Object.entries(options.headers).forEach(([key, value]) =>
xhr.setRequestHeader(key, value)
);
}

if (options.progressCallback) {
xhr.addEventListener('progress', options.progressCallback);
}

return xhr;
}

function fetchBinary(url, options = {}) {
return new Promise((resolve, reject) => {
const xhr = openAsyncXHR('GET', url, options);

xhr.onreadystatechange = (e) => {
if (xhr.readyState === 4) {
if (xhr.status === 200 || xhr.status === 0) {
resolve(xhr.response);
} else {
reject({ xhr, e });
}
}
};

// Make request
xhr.responseType = 'arraybuffer';
xhr.send();
});
}

function fetchArray(instance, baseURL, array, options = {}) {
if (options && options.compression) {
return REJECT_COMPRESSION();
}

if (array.ref && !array.ref.pending) {
return new Promise((resolve, reject) => {
const url = [baseURL, array.ref.basepath, array.ref.id].join('/');
const xhr = openAsyncXHR('GET', url, options);

xhr.onreadystatechange = (e) => {
if (xhr.readyState === 1) {
array.ref.pending = true;
if (++requestCount === 1 && instance?.invokeBusy) {
instance.invokeBusy(true);
}
}
if (xhr.readyState === 4) {
array.ref.pending = false;
if (xhr.status === 200 || xhr.status === 0) {
array.buffer = xhr.response;

if (array.ref.encode === 'JSON') {
array.values = JSON.parse(array.buffer);
} else {
if (Endian.ENDIANNESS !== array.ref.encode && Endian.ENDIANNESS) {
// Need to swap bytes
vtkDebugMacro(`Swap bytes of ${array.name}`);
Endian.swapBytes(
array.buffer,
DataTypeByteSize[array.dataType]
);
}

array.values = macro.newTypedArray(array.dataType, array.buffer);
}

if (array.values.length !== array.size) {
vtkErrorMacro(
`Error in FetchArray: ${array.name}, does not have the proper array size. Got ${array.values.length}, instead of ${array.size}`
);
}

// Done with the ref and work
delete array.ref;
if (--requestCount === 0 && instance?.invokeBusy) {
instance.invokeBusy(false);
}
if (instance?.modified) {
instance.modified();
}
resolve(array);
} else {
reject({ xhr, e });
}
}
};

// Make request
xhr.responseType = array.dataType !== 'string' ? 'arraybuffer' : 'text';
xhr.send();
});
}

return Promise.resolve(array);
}

// ----------------------------------------------------------------------------

function fetchJSON(instance, url, options = {}) {
if (options && options.compression) {
return REJECT_COMPRESSION();
}

return new Promise((resolve, reject) => {
const xhr = openAsyncXHR('GET', url, options);

xhr.onreadystatechange = (e) => {
if (xhr.readyState === 1) {
if (++requestCount === 1 && instance?.invokeBusy) {
instance.invokeBusy(true);
}
}
if (xhr.readyState === 4) {
if (--requestCount === 0 && instance?.invokeBusy) {
instance.invokeBusy(false);
}
if (xhr.status === 200 || xhr.status === 0) {
resolve(JSON.parse(xhr.responseText));
} else {
reject({ xhr, e });
}
}
};

// Make request
xhr.responseType = 'text';
xhr.send();
});
}

// ----------------------------------------------------------------------------

function fetchText(instance, url, options = {}) {
if (options && options.compression) {
return REJECT_COMPRESSION();
}

return new Promise((resolve, reject) => {
const xhr = openAsyncXHR('GET', url, options);

xhr.onreadystatechange = (e) => {
if (xhr.readyState === 1) {
if (++requestCount === 1 && instance?.invokeBusy) {
instance.invokeBusy(true);
}
}
if (xhr.readyState === 4) {
if (--requestCount === 0 && instance?.invokeBusy) {
instance.invokeBusy(false);
}
if (xhr.status === 200 || xhr.status === 0) {
resolve(xhr.responseText);
} else {
reject({ xhr, e });
}
}
};

// Make request
xhr.responseType = 'text';
xhr.send();
});
}

// ----------------------------------------------------------------------------

function fetchImage(instance, url, options = {}) {
return new Promise((resolve, reject) => {
const img = new Image();
if (options.crossOrigin) {
img.crossOrigin = options.crossOrigin;
}
img.onload = () => resolve(img);
img.onerror = reject;
img.src = url;
});
}
/* eslint-enable prefer-promise-reject-errors */

// ----------------------------------------------------------------------------

const LiteHttpDataAccessHelper = {
fetchArray,
fetchJSON,
fetchText,
fetchBinary, // Only for HTTP
fetchImage,
};

// The lite version should never override a full feature one...
if (!has('http')) {
registerType('http', (options) => LiteHttpDataAccessHelper);
}

export default LiteHttpDataAccessHelper;
index.d.ts
import HtmlDataAccessHelper from './HtmlDataAccessHelper';
import HttpDataAccessHelper from './HttpDataAccessHelper';
import JSZipDataAccessHelper from './JSZipDataAccessHelper';
import LiteHttpDataAccessHelper from './LiteHttpDataAccessHelper';

export function has(type: string): boolean;
export function get(
type?: string,
options?: object
):
| HtmlDataAccessHelper
| HttpDataAccessHelper
| JSZipDataAccessHelper
| LiteHttpDataAccessHelper;
export function registerType(type: string, fn: any): void;

export interface DataAccessHelper {
/**
*
* @param {String} type
*/
has(type: string): boolean;

/**
*
* @param {String} type
* @param options
*/
get(
type?: string,
options?: object
):
| HtmlDataAccessHelper
| HttpDataAccessHelper
| JSZipDataAccessHelper
| LiteHttpDataAccessHelper;

/**
*
* @param {String} type
* @param fn
*/
registerType(type: string, fn: any): void;
}

export declare const DataAccessHelper: {
has: typeof has;
get: typeof get;
registerType: typeof registerType;
};

export default DataAccessHelper;
index.js
const TYPE_MAPPING = {};

export function has(type) {
return !!TYPE_MAPPING[type];
}

export function get(type = 'http', options = {}) {
return TYPE_MAPPING[type](options);
}

export function registerType(type, fn) {
TYPE_MAPPING[type] = fn;
}

export default {
get,
has,
registerType,
};