UNPKG

static-browser-server

Version:

A simple service worker used for the static template in sandpack, allowing users to develop websites like they would locally in the browser.

286 lines (281 loc) 8.91 kB
"use strict"; (() => { // node_modules/.pnpm/outvariant@1.4.3/node_modules/outvariant/lib/index.mjs var POSITIONALS_EXP = /(%?)(%([sdijo]))/g; function serializePositional(positional, flag) { switch (flag) { case "s": return positional; case "d": case "i": return Number(positional); case "j": return JSON.stringify(positional); case "o": { if (typeof positional === "string") { return positional; } const json = JSON.stringify(positional); if (json === "{}" || json === "[]" || /^\[object .+?\]$/.test(json)) { return positional; } return json; } } } function format(message, ...positionals) { if (positionals.length === 0) { return message; } let positionalIndex = 0; let formattedMessage = message.replace( POSITIONALS_EXP, (match, isEscaped, _, flag) => { const positional = positionals[positionalIndex]; const value = serializePositional(positional, flag); if (!isEscaped) { positionalIndex++; return value; } return match; } ); if (positionalIndex < positionals.length) { formattedMessage += ` ${positionals.slice(positionalIndex).join(" ")}`; } formattedMessage = formattedMessage.replace(/%{2,2}/g, "%"); return formattedMessage; } var STACK_FRAMES_TO_IGNORE = 2; function cleanErrorStack(error) { if (!error.stack) { return; } const nextStack = error.stack.split("\n"); nextStack.splice(1, STACK_FRAMES_TO_IGNORE); error.stack = nextStack.join("\n"); } var InvariantError = class extends Error { constructor(message, ...positionals) { super(message); this.message = message; this.name = "Invariant Violation"; this.message = format(message, ...positionals); cleanErrorStack(this); } }; var invariant = (predicate, message, ...positionals) => { if (!predicate) { throw new InvariantError(message, ...positionals); } }; invariant.as = (ErrorConstructor, predicate, message, ...positionals) => { if (!predicate) { const formatMessage = positionals.length === 0 ? message : format(message, ...positionals); let error; try { error = Reflect.construct(ErrorConstructor, [ formatMessage ]); } catch (err) { error = ErrorConstructor(formatMessage); } throw error; } }; // node_modules/.pnpm/@open-draft+deferred-promise@2.2.0/node_modules/@open-draft/deferred-promise/build/index.mjs function createDeferredExecutor() { const executor = (resolve, reject) => { executor.state = "pending"; executor.resolve = (data) => { if (executor.state !== "pending") { return; } executor.result = data; const onFulfilled = (value) => { executor.state = "fulfilled"; return value; }; return resolve( data instanceof Promise ? data : Promise.resolve(data).then(onFulfilled) ); }; executor.reject = (reason) => { if (executor.state !== "pending") { return; } queueMicrotask(() => { executor.state = "rejected"; }); return reject(executor.rejectionReason = reason); }; }; return executor; } var DeferredPromise = class extends Promise { #executor; resolve; reject; constructor(executor = null) { const deferredExecutor = createDeferredExecutor(); super((originalResolve, originalReject) => { deferredExecutor(originalResolve, originalReject); executor?.(deferredExecutor.resolve, deferredExecutor.reject); }); this.#executor = deferredExecutor; this.resolve = this.#executor.resolve; this.reject = this.#executor.reject; } get state() { return this.#executor.state; } get rejectionReason() { return this.#executor.rejectionReason; } then(onFulfilled, onRejected) { return this.#decorate(super.then(onFulfilled, onRejected)); } catch(onRejected) { return this.#decorate(super.catch(onRejected)); } finally(onfinally) { return this.#decorate(super.finally(onfinally)); } #decorate(promise) { return Object.defineProperties(promise, { resolve: { configurable: true, value: this.resolve }, reject: { configurable: true, value: this.reject } }); } }; // src/preview/relay/constants.ts var CHANNEL_NAME = "$CSB_RELAY"; // src/preview/relay/utils.ts function getWorkerInstance(registration) { return registration.installing || registration.waiting || registration.active; } // src/preview/relay/main.ts var workerChannel = new MessageChannel(); var workerReadyPromise = new DeferredPromise(); workerReadyPromise.then((worker) => { const workerInitMessage = { $channel: CHANNEL_NAME, $type: "worker/init" }; worker.postMessage(workerInitMessage, [workerChannel.port2]); return worker; }); var parentPortPromise = new DeferredPromise(); window.addEventListener( "message", (event) => { if (event.data.$type === "preview/init") { const parentPort = event.ports[0]; parentPort.onmessage = async (evt) => { if (typeof evt.data === "object" && evt.data.$channel === CHANNEL_NAME && evt.data.$type === "preview/response") { const msg = evt.data; workerChannel.port1.postMessage(msg); } }; parentPortPromise.resolve(parentPort); } } ); workerChannel.port1.onmessage = async (event) => { const data = event.data; if (data.$channel === CHANNEL_NAME) { const port = await parentPortPromise; const message = data; port.postMessage(message); } }; var workerUrl = new URL("/__csb_sw.sn0sgle8j083l06fsw06x8f9ltetd6e.js", location.origin).href; function preventStaleTermination(worker) { const keepaliveInterval = setInterval(() => { const pingMessage = { $channel: CHANNEL_NAME, $type: "worker/ping" }; worker.postMessage(pingMessage); }, 5e3); worker.addEventListener("statechange", () => { if (worker.state === "redundant") { clearInterval(keepaliveInterval); } }); } async function getServiceWorker() { invariant( "serviceWorker" in navigator, "Failed to start the relay Service Worker: Service Worker API is not supported in this browser" ); const registerWorker = async () => { const registration2 = await navigator.serviceWorker.register( "/__csb_sw.sn0sgle8j083l06fsw06x8f9ltetd6e.js", { scope: "/" } ); return getWorkerInstance(registration2); }; const registrations = await navigator.serviceWorker.getRegistrations(); await Promise.all( registrations.map((registration2) => { const worker = getWorkerInstance(registration2); if (worker && worker.scriptURL !== workerUrl) { return registration2.unregister(); } }) ); const { controller } = navigator.serviceWorker; if (!controller) { return registerWorker(); } if (controller.scriptURL === workerUrl) { return controller; } const [controllerRegistration, registration] = await Promise.all([ navigator.serviceWorker.getRegistration(controller.scriptURL), navigator.serviceWorker.getRegistration(workerUrl) ]); if (!registration) { await controllerRegistration?.unregister(); return registerWorker(); } if (registration.waiting) { await registration.update(); const worker = getWorkerInstance(registration); invariant( worker, "Failed to retrieve the worker instance after promotion: worked does not exist" ); invariant( registration.active, 'Failed to promove a waiting Service Worker: expected the worker state to be "active" but got "%s"', worker.state ); return worker; } return null; } async function start() { const worker = await getServiceWorker().catch((error) => { console.error( "Failed to ensure the relay has a Service Worker registered. See details below." ); console.error(error); return; }); await navigator.serviceWorker.ready; invariant(worker, "Failed to retrieve the worker instance: worker not found"); preventStaleTermination(worker); workerReadyPromise.resolve(worker); const parentPort = await parentPortPromise; const readyMessage = { $channel: CHANNEL_NAME, $type: "preview/ready" }; parentPort.postMessage(readyMessage); } start().catch(console.error); })();