mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-15 05:36:17 -08:00
* docs(performance): plan Xtream benchmark * feat(xtream-mock-server): add deterministic 100k fixture * style(xtream-mock-server): apply repository formatting * fix(xtream-mock-server): harden performance fixture data * feat(xtream-mock-server): add performance control plane * docs(performance): correct Xtream capture plan * fix(xtream-mock-server): harden performance controls * fix(xtream-mock-server): harden control lifecycle * feat(performance): add Xtream preload markers * feat(performance): trace Xtream main phases * feat(performance): mark Xtream store publications * feat(performance): trace Xtream database phases * feat(performance): trace Xtream delete cancellation * feat(performance): capture Xtream phase attribution * feat(performance): mark Sources Xtream refresh * test(performance): define Xtream benchmark evidence contracts * test(performance): add Xtream benchmark runner * test(performance): surface failure evidence writes * test(performance): align database read clock * test(performance): preserve capture failure contracts
89 lines
2.8 KiB
TypeScript
89 lines
2.8 KiB
TypeScript
import { constants, type BigIntStats } from 'node:fs';
|
|
import { lstat, open, realpath, type FileHandle } from 'node:fs/promises';
|
|
|
|
export interface XtreamSafeFileSnapshot {
|
|
readonly content: Buffer;
|
|
readonly realPath: string;
|
|
}
|
|
|
|
const UNSAFE_FILE_ERROR = 'unsafe-xtream-evidence-file';
|
|
|
|
export async function readXtreamSafeRegularFile(
|
|
path: string
|
|
): Promise<XtreamSafeFileSnapshot> {
|
|
let handle: FileHandle | undefined;
|
|
try {
|
|
const pathBefore = await lstat(path, { bigint: true });
|
|
assertSafeRegularFile(pathBefore);
|
|
handle = await open(
|
|
path,
|
|
constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0)
|
|
);
|
|
const handleBefore = await handle.stat({ bigint: true });
|
|
assertSafeRegularFile(handleBefore);
|
|
assertSameFile(pathBefore, handleBefore);
|
|
|
|
const content = await handle.readFile();
|
|
const handleAfter = await handle.stat({ bigint: true });
|
|
assertSafeRegularFile(handleAfter);
|
|
assertUnchanged(handleBefore, handleAfter);
|
|
|
|
const pathAfter = await lstat(path, { bigint: true });
|
|
assertSafeRegularFile(pathAfter);
|
|
assertUnchanged(handleBefore, pathAfter);
|
|
const canonicalPath = await realpath(path);
|
|
const canonicalAfter = await lstat(canonicalPath, { bigint: true });
|
|
assertSafeRegularFile(canonicalAfter);
|
|
assertUnchanged(handleBefore, canonicalAfter);
|
|
const pathFinal = await lstat(path, { bigint: true });
|
|
assertSafeRegularFile(pathFinal);
|
|
assertUnchanged(handleBefore, pathFinal);
|
|
|
|
return { content, realPath: canonicalPath };
|
|
} catch {
|
|
throw new Error(UNSAFE_FILE_ERROR);
|
|
} finally {
|
|
await closeHandle(handle);
|
|
}
|
|
}
|
|
|
|
export function isXtreamUnsafeFileError(error: unknown): boolean {
|
|
return error instanceof Error && error.message === UNSAFE_FILE_ERROR;
|
|
}
|
|
|
|
function assertSafeRegularFile(stats: BigIntStats): void {
|
|
if (
|
|
!stats.isFile() ||
|
|
stats.isSymbolicLink() ||
|
|
stats.nlink !== BigInt(1)
|
|
) {
|
|
throw new Error(UNSAFE_FILE_ERROR);
|
|
}
|
|
}
|
|
|
|
function assertSameFile(left: BigIntStats, right: BigIntStats): void {
|
|
if (left.dev !== right.dev || left.ino !== right.ino) {
|
|
throw new Error(UNSAFE_FILE_ERROR);
|
|
}
|
|
}
|
|
|
|
function assertUnchanged(before: BigIntStats, after: BigIntStats): void {
|
|
assertSameFile(before, after);
|
|
if (
|
|
before.size !== after.size ||
|
|
before.mtimeNs !== after.mtimeNs ||
|
|
before.ctimeNs !== after.ctimeNs
|
|
) {
|
|
throw new Error(UNSAFE_FILE_ERROR);
|
|
}
|
|
}
|
|
|
|
async function closeHandle(handle: FileHandle | undefined): Promise<void> {
|
|
if (!handle) return;
|
|
try {
|
|
await handle.close();
|
|
} catch {
|
|
throw new Error(UNSAFE_FILE_ERROR);
|
|
}
|
|
}
|