@tstdl/base 0.93.68 → 0.93.69

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/latex/render.d.ts CHANGED
@@ -9,11 +9,15 @@ export type LatexRenderOptions = {
9
9
  /**
10
10
  * Renders LaTeX source code to a PDF file.
11
11
  *
12
+ * ## WARNING
13
+ * **This function should not be used with untrusted LaTeX source, as it can lead to arbitrary code execution on the system.**
14
+ *
12
15
  * Requires latexmk and LuaTeX to be installed on the system.
13
16
  *
14
17
  * **Minimal recommendation:**
15
18
  * - **Arch Linux:** texlive-binextra texlive-luatex texlive-latexrecommended texlive-fontsrecommended
16
19
  * @param source The LaTeX source code to render
20
+ * @param options Rendering options
17
21
  * @returns A TemporaryFile representing the generated PDF
18
22
  */
19
23
  export declare function renderLatex(source: string, options?: LatexRenderOptions): Promise<TemporaryFile>;
package/latex/render.js CHANGED
@@ -51,7 +51,8 @@ var __disposeResources = (this && this.__disposeResources) || (function (Suppres
51
51
  return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
52
52
  });
53
53
  import { TemporaryFile } from '../file/server/temporary-file.js';
54
- import { spawnCommand, spawnWaitCommand } from '../process/spawn.js';
54
+ import { spawnWaitCommand, spawnWaitReadCommand } from '../process/spawn.js';
55
+ import { withTimeout } from '../utils/timing.js';
55
56
  import { tryIgnoreAsync } from '../utils/try-ignore.js';
56
57
  const engineMapping = {
57
58
  pdflatex: '-pdflatex',
@@ -61,11 +62,15 @@ const engineMapping = {
61
62
  /**
62
63
  * Renders LaTeX source code to a PDF file.
63
64
  *
65
+ * ## WARNING
66
+ * **This function should not be used with untrusted LaTeX source, as it can lead to arbitrary code execution on the system.**
67
+ *
64
68
  * Requires latexmk and LuaTeX to be installed on the system.
65
69
  *
66
70
  * **Minimal recommendation:**
67
71
  * - **Arch Linux:** texlive-binextra texlive-luatex texlive-latexrecommended texlive-fontsrecommended
68
72
  * @param source The LaTeX source code to render
73
+ * @param options Rendering options
69
74
  * @returns A TemporaryFile representing the generated PDF
70
75
  */
71
76
  export async function renderLatex(source, options) {
@@ -73,14 +78,16 @@ export async function renderLatex(source, options) {
73
78
  try {
74
79
  const latexFile = __addDisposableResource(env_1, await TemporaryFile.from(source, '.tex'), true);
75
80
  const engineFlag = engineMapping[options?.engine ?? 'lualatex'];
76
- const process = await spawnCommand('latexmk', ['-interaction=nonstopmode', engineFlag, '-cd', latexFile.path]);
77
- console.log(latexFile.path);
78
- const { code } = await process.wait();
79
- if (code != 0) {
80
- const [out, err] = await Promise.all([process.readOutput(), process.readError()]);
81
- throw new Error(`LaTeX compilation failed with exit code ${code}. Output:\n${out}\nError Output:\n${err}`);
81
+ const { code, output, error } = await spawnWaitReadCommand('string', 'latexmk', ['-interaction=nonstopmode', engineFlag, '-cd', latexFile.path], { throwOnNonZeroExitCode: false });
82
+ await tryIgnoreAsync(async () => await withTimeout(1000, spawnWaitCommand('latexmk', ['-c', '-cd', engineFlag, latexFile.path])));
83
+ if (code !== 0) {
84
+ throw new Error(`
85
+ LaTeX compilation failed with exit code ${code}.\n
86
+ File: ${latexFile.path}\n
87
+ Output:\n${output}\n
88
+ Error Output:\n${error}
89
+ `.trim());
82
90
  }
83
- await tryIgnoreAsync(async () => await spawnWaitCommand('latexmk', ['-interaction=nonstopmode', '-pdflua', '-cd', '-c', latexFile.path]));
84
91
  return TemporaryFile.adopt(`${latexFile.path.slice(0, -4)}.pdf`);
85
92
  }
86
93
  catch (e_1) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tstdl/base",
3
- "version": "0.93.68",
3
+ "version": "0.93.69",
4
4
  "author": "Patrick Hein",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -1,12 +1,18 @@
1
1
  import type { ChildProcessWithoutNullStreams } from 'node:child_process';
2
2
  import type { Record } from '../types/types.js';
3
+ type WaitReadResultFormat = 'string' | 'binary';
4
+ type WaitReadResultFormatType<T extends WaitReadResultFormat> = T extends 'string' ? string : Uint8Array<ArrayBuffer>;
3
5
  export type WaitOptions = {
4
6
  throwOnNonZeroExitCode?: boolean;
5
7
  };
6
- export type ProcessResult = {
8
+ export type WaitResult = {
7
9
  code: number | null;
8
10
  signal: string | null;
9
11
  };
12
+ export type WaitReadResult<Format extends WaitReadResultFormat> = WaitResult & {
13
+ output: WaitReadResultFormatType<Format>;
14
+ error: WaitReadResultFormatType<Format>;
15
+ };
10
16
  export type SpawnOptions = {
11
17
  arguments?: string[];
12
18
  workingDirectory?: string;
@@ -22,11 +28,16 @@ export type SpawnCommandResult = TransformStream<Uint8Array, Uint8Array> & {
22
28
  readErrorBytes(): Promise<Uint8Array>;
23
29
  readError(): Promise<string>;
24
30
  handleNonZeroExitCode(): void;
25
- wait(options?: WaitOptions): Promise<ProcessResult>;
31
+ wait(options?: WaitOptions): Promise<WaitResult>;
32
+ waitRead<F extends WaitReadResultFormat>(format: F, options?: WaitOptions): Promise<WaitReadResult<F>>;
26
33
  };
27
34
  /** spwans a command and waits for it to complete */
28
- export declare function spawnWaitCommand(command: string, args?: string[], options?: SpawnOptions & WaitOptions): Promise<ProcessResult>;
29
- export declare function spawnWaitCommand(command: string, options?: SpawnOptions & WaitOptions): Promise<ProcessResult>;
35
+ export declare function spawnWaitCommand(command: string, args?: string[], options?: SpawnOptions & WaitOptions): Promise<WaitResult>;
36
+ export declare function spawnWaitCommand(command: string, options?: SpawnOptions & WaitOptions): Promise<WaitResult>;
37
+ /** spwans a command, waits for it to complete and reads its output */
38
+ export declare function spawnWaitReadCommand<F extends WaitReadResultFormat>(format: F, command: string, args?: string[], options?: SpawnOptions & WaitOptions): Promise<WaitReadResult<F>>;
39
+ export declare function spawnWaitReadCommand<F extends WaitReadResultFormat>(format: F, command: string, options?: SpawnOptions & WaitOptions): Promise<WaitReadResult<F>>;
30
40
  /** Spawns a command as a child process. */
31
41
  export declare function spawnCommand(command: string, args?: string[], options?: SpawnOptions): Promise<SpawnCommandResult>;
32
42
  export declare function spawnCommand(command: string, options?: SpawnOptions): Promise<SpawnCommandResult>;
43
+ export {};
package/process/spawn.js CHANGED
@@ -9,6 +9,11 @@ export async function spawnWaitCommand(command, argsOrOptions, optionsOrNothing)
9
9
  const process = await spawnCommand(command, args, options);
10
10
  return await process.wait({ throwOnNonZeroExitCode: options?.throwOnNonZeroExitCode });
11
11
  }
12
+ export async function spawnWaitReadCommand(format, command, argsOrOptions, optionsOrNothing) {
13
+ const [args, options] = isArray(argsOrOptions) ? [argsOrOptions, optionsOrNothing] : [undefined, argsOrOptions];
14
+ const process = await spawnCommand(command, args, options);
15
+ return await process.waitRead(format, { throwOnNonZeroExitCode: options?.throwOnNonZeroExitCode });
16
+ }
12
17
  export async function spawnCommand(command, argsOrOptions, optionsOrNothing) {
13
18
  const { spawn } = await dynamicImport('node:child_process');
14
19
  const { Readable, Writable } = await dynamicImport('node:stream');
@@ -81,6 +86,18 @@ export async function spawnCommand(command, argsOrOptions, optionsOrNothing) {
81
86
  }
82
87
  return result;
83
88
  }
89
+ async function waitRead(format, { throwOnNonZeroExitCode = true } = {}) {
90
+ const [result, output, error] = await Promise.all([
91
+ wait({ throwOnNonZeroExitCode }),
92
+ (format === 'string') ? readOutput() : readOutputBytes(),
93
+ (format === 'string') ? readError() : readErrorBytes(),
94
+ ]);
95
+ return {
96
+ ...result,
97
+ output: output,
98
+ error: error,
99
+ };
100
+ }
84
101
  return {
85
102
  process,
86
103
  readable,
@@ -94,5 +111,6 @@ export async function spawnCommand(command, argsOrOptions, optionsOrNothing) {
94
111
  readError,
95
112
  handleNonZeroExitCode: () => void handleNonZeroExitCode(),
96
113
  wait,
114
+ waitRead,
97
115
  };
98
116
  }
@@ -1,9 +1,9 @@
1
1
  import type { Logger } from '../logger/logger.js';
2
- export declare function tryIgnore<R>(fn: () => R): R;
2
+ export declare function tryIgnore<R>(fn: () => R): R | undefined;
3
3
  export declare function tryIgnore<R, F>(fn: () => R, fallback: F): R | F;
4
- export declare function tryIgnoreAsync<R>(fn: () => Promise<R>): Promise<R>;
4
+ export declare function tryIgnoreAsync<R>(fn: () => Promise<R>): Promise<R | undefined>;
5
5
  export declare function tryIgnoreAsync<R, F>(fn: () => Promise<R>, fallback: F): Promise<F>;
6
- export declare function tryIgnoreLog<R>(logger: Logger, fn: () => R): R;
6
+ export declare function tryIgnoreLog<R>(logger: Logger, fn: () => R): R | undefined;
7
7
  export declare function tryIgnoreLog<R, F>(logger: Logger, fn: () => R, fallback: F): R | F;
8
- export declare function tryIgnoreLogAsync<R>(logger: Logger, fn: () => Promise<R>): Promise<R>;
8
+ export declare function tryIgnoreLogAsync<R>(logger: Logger, fn: () => Promise<R>): Promise<R | undefined>;
9
9
  export declare function tryIgnoreLogAsync<R, F>(logger: Logger, fn: () => Promise<R>, fallback: F): Promise<F>;