@finos/legend-application
Version:
Legend application core
132 lines • 5.32 kB
JavaScript
/**
* Copyright (c) 2020-present, Goldman Sachs
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { downloadFileUsingDataURI, guaranteeNonNullable, } from '@finos/legend-shared';
var DOWNLOAD_EVENTS;
(function (DOWNLOAD_EVENTS) {
DOWNLOAD_EVENTS["TAG_REQUEST"] = "download-request";
DOWNLOAD_EVENTS["TAG_RESPONSE"] = "download-response";
DOWNLOAD_EVENTS["STREAM_CLOSED"] = "#stream-closed";
DOWNLOAD_EVENTS["STREAM_ABORTED"] = "#stream-aborted";
})(DOWNLOAD_EVENTS || (DOWNLOAD_EVENTS = {}));
function createWritableStreamFromMessageChannel(channel) {
return new WritableStream({
write(chunk) {
channel.port1.postMessage(chunk);
},
close() {
channel.port1.postMessage(DOWNLOAD_EVENTS.STREAM_CLOSED);
},
abort() {
channel.port1.postMessage(DOWNLOAD_EVENTS.STREAM_ABORTED);
closeMessagePort(channel.port1);
closeMessagePort(channel.port2);
},
});
}
function closeMessagePort(port) {
port.onmessage = null;
port.close();
}
async function getServiceWorker() {
if (!('serviceWorker' in navigator)) {
return Promise.reject(new Error('Service worker is not available. Service Worker requires HTTPS protocol'));
}
return navigator.serviceWorker
.getRegistration()
.then((workerRegistration) => {
if (workerRegistration === undefined) {
return undefined;
}
const pending = workerRegistration.installing ?? workerRegistration.waiting;
return (workerRegistration.active ??
new Promise((resolve) => {
// if not activated, add listener to waiting or installing registration
const listener = () => {
if (pending?.state === 'activated') {
pending.removeEventListener('statechange', listener);
resolve(workerRegistration.active ?? undefined);
}
};
pending?.addEventListener('statechange', listener);
}));
});
}
function createDownloadRequest(filename) {
const PREFIX = 6;
const prefix = String(Math.random()).slice(-PREFIX);
const url = new URL(`${prefix}/${filename}`, window.location.href).toString();
return { tag: DOWNLOAD_EVENTS.TAG_REQUEST, filename, url };
}
function handleServiceWorkerDownloadResponse(event) {
const data = event.data;
if (data?.tag === DOWNLOAD_EVENTS.TAG_RESPONSE && data.downloadUrl.length) {
openInIframe(data.downloadUrl);
}
}
function openInIframe(src) {
const iframe = document.createElement('iframe');
iframe.hidden = true;
iframe.src = src;
document.body.appendChild(iframe);
return iframe;
}
export async function downloadStream(response, filename, contentType) {
const responseBody = guaranteeNonNullable(response.body);
// creates communication channel with service worker with response handler
const channel = new MessageChannel();
channel.port1.onmessage = handleServiceWorkerDownloadResponse;
// grabs service worker and handles it download along with response channel port
const serviceWorker = await getServiceWorker();
if (!serviceWorker) {
// TODO: remove once service worker workflow is tested
const text = await response.text();
// eslint-disable-next-line no-console
console.debug('service worker not found. Using in memory file download');
downloadFileUsingDataURI(filename, text, contentType);
return;
}
// eslint-disable-next-line no-console
console.debug('Service worker found. Continuing download');
const downloadRequest = createDownloadRequest(filename);
serviceWorker.postMessage(downloadRequest, [channel.port2]);
// creates new data stream over communication channel and pipes given stream in it
responseBody
.pipeTo(createWritableStreamFromMessageChannel(channel))
.then(() => {
// TODO: trace success
})
.catch(() => {
// TODO: fail
});
}
export function registerDownloadHelperServiceWorker(workerPath) {
if ('serviceWorker' in navigator) {
const path = workerPath ?? '/ServiceWorker.js';
navigator.serviceWorker
.register(path)
.then((reg) => {
// TODO: add trace
// eslint-disable-next-line no-console
console.debug(`register service worker success with path: ${path}`, reg);
})
.catch((error) => {
// TODO: add trace
// eslint-disable-next-line no-console
console.debug(`register service worker error with path: ${path}`, error);
});
}
}
//# sourceMappingURL=DownloadHelperServiceWorker.js.map