UNPKG

@embeddable.com/sdk-core

Version:

Core Embeddable SDK module responsible for web-components bundling and publishing.

118 lines (102 loc) 3.01 kB
import * as fs from "node:fs/promises"; import { ServerResponse } from "http"; import { createReadStream } from "node:fs"; /** * Wraps a ServerResponse object to prevent setting the Content-Length header. */ export function preventContentLength(res: ServerResponse) { const originalSetHeader = res.setHeader.bind(res); res.setHeader = function (key: string, value: any) { if (key.toLowerCase() === "content-length") { return this; } return originalSetHeader(key, value); }; } export function createWatcherLock() { let locked = false; let waiters: (() => void)[] = []; return { lock() { if (!locked) { locked = true; } }, unlock() { if (locked) { locked = false; waiters.forEach((fn) => fn()); waiters = []; } }, async waitUntilFree() { if (!locked) return; await new Promise<void>((resolve) => { waiters.push(resolve); }); }, }; } export const delay = (ms: number) => new Promise((res) => setTimeout(res, ms)); /** * This function waits until a file stabilizes, meaning its size does not change for a certain number of attempts * and the content ends with a specific expected tail. * It uses stream reading to check the file's content, the same wait serve-static uses. * This will help to prevent when serve-static serves a file that is still being written to. * One of the issues, related to this, is "Constructor for "embeddable-component#undefined" was not found", that we saw quite often in the past. * @param filePath * @param expectedTail * @param maxAttempts * @param stableCount */ export async function waitUntilFileStable( filePath: string, expectedTail: string, { maxAttempts = 100, requiredStableCount = 2, }: { maxAttempts?: number; requiredStableCount?: number; } = {} ): Promise<void> { let lastSize = -1; let stableCounter = 0; for (let i = 0; i < maxAttempts; i++) { try { const { size, tailMatches } = await checkFileTail(filePath, expectedTail); if (size === lastSize && size > 0 && tailMatches) { stableCounter++; if (stableCounter >= requiredStableCount) { return; } } else { stableCounter = 0; } lastSize = size; } catch { } await delay(50); } throw new Error("File did not stabilize"); } async function checkFileTail( filePath: string, expectedTail: string, tailLength = 500 ): Promise<{ size: number; tailMatches: boolean }> { const stats = await fs.stat(filePath); const size = stats.size; const start = Math.max(0, size - tailLength); return new Promise((resolve, reject) => { let tailBuffer = ""; createReadStream(filePath, { encoding: "utf-8", start }) .on("data", (chunk) => { tailBuffer += chunk; }) .on("end", () => { resolve({ size, tailMatches: tailBuffer.includes(expectedTail) }); }) .on("error", reject); }); }