@cenk1cenk2/md-printer 3.0.0 → 3.1.1

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/README.md CHANGED
@@ -141,7 +141,7 @@ md-printer document.md out.pdf --preview --no-open
141
141
  ```
142
142
 
143
143
  - `-p, --preview` rebuilds when the input file, the template (components, imports, `settings.json`, translations) or a local asset used by the page changes. Every build rewrites the output file, the same one a normal run writes; reading from stdin without an output argument only previews. `-O` is not supported.
144
- - It starts a local preview server and opens it in an app window of the discovered browser, using a throwaway profile. The page has HTML, Split and PDF tabs. Build errors, such as Svelte compile errors, are shown in an overlay and cleared by the next successful build. Closing the window ends the session, as does Ctrl+C.
144
+ - It starts a local preview server and opens it in an app window of the discovered browser, using a throwaway profile. The page has HTML, Split and PDF tabs. Build errors, such as Svelte compile errors, are shown in an overlay and cleared by the next successful build. Closing the window ends the session. Ctrl+C ends it too, after closing the browser, the preview server and the watcher, and exits with 130; a second Ctrl+C exits at once.
145
145
  - `--host` sets the loopback host, default `127.0.0.1`.
146
146
  - `--port` sets the port, default `0` (random). A port in use fails the run with an error.
147
147
  - It serves the files next to the input, except dotfiles such as `.env`.
@@ -303,7 +303,7 @@ USAGE
303
303
  silent|direct|fatal|error|warn|info|verbose|debug|trace] [-I] [-f
304
304
  md|yaml|yml] [-F pdf|html] [-O] [-t <value>] [-T <value>] [-l <value>] [-b
305
305
  <value>] [--continuous] [-p] [--host 127.0.0.1|localhost|::1] [--port
306
- <value>] [--open] [--sign] [--sign-provider <value>] [--sign-certificate
306
+ <value>] [--open] [--sign] [--sign-provider a-trust] [--sign-certificate
307
307
  <value>]
308
308
 
309
309
  ARGUMENTS
@@ -349,9 +349,10 @@ FLAGS
349
349
  --sign-certificate=<value> Signer certificate (DER or PEM) used for the
350
350
  visible signature block and to verify the
351
351
  returned signature.
352
- --sign-provider=<value> Signature provider, defaults to a-trust.
352
+ --sign-provider=<option> Signature provider, defaults to a-trust.
353
353
  Overwrites the one defined in front-matter and
354
354
  template settings.
355
+ <options: a-trust>
355
356
 
356
357
  CLI FLAGS
357
358
  --log-level=<option> [default: INFO, env: LOG_LEVEL] Set the log level of the
package/bin/dev.js CHANGED
@@ -26,8 +26,6 @@ async function main() {
26
26
 
27
27
  await import('source-map-support').then((lib) => lib.install())
28
28
 
29
- await import('@cenk1cenk2/oclif-common').then((mod) => mod.setup())
30
-
31
29
  const { execute } = await import('@oclif/core')
32
30
 
33
31
  return execute({ development: true, dir: import.meta.url })
package/bin/run.js CHANGED
@@ -4,7 +4,6 @@ async function main() {
4
4
  const { execute } = await import('@oclif/core')
5
5
 
6
6
  await import('source-map-support').then((lib) => lib.install())
7
- await import('@cenk1cenk2/oclif-common').then((lib) => lib.setup())
8
7
 
9
8
  await execute({ dir: import.meta.url })
10
9
  }
@@ -1,190 +1,39 @@
1
- import { InputFileType, OutputFileType } from "../constants/file.constants.mjs";
2
- import { BrowserService } from "../services/renderer/browser.service.mjs";
3
- import { PrinterService } from "../services/printer.service.mjs";
4
- import { PreviewService } from "../services/session/preview.service.mjs";
5
- import { SessionService } from "../services/session/session.service.mjs";
6
- import { PrinterModule } from "../services/printer.module.mjs";
7
- import "../services/index.mjs";
8
- import { mkdir } from "fs/promises";
9
- import { dirname } from "path";
10
- import { text } from "stream/consumers";
11
- import writeFileAtomic from "write-file-atomic";
12
- import { Args, CliModule, Command, Flags, JsonParser, ParserService, YamlParser } from "@cenk1cenk2/oclif-common";
1
+ import __decorate from "../_virtual/_@oxc-project_runtime@0.153.0/helpers/esm/decorate.mjs";
2
+ import { MdPrinterHandler } from "../modules/md-printer/md-printer.handler.mjs";
3
+ import { MdPrinterModule } from "../modules/md-printer/md-printer.module.mjs";
4
+ import { MdPrinterSchema } from "../modules/md-printer/md-printer.schema.mjs";
5
+ import { Args, Command, Flags, UseHandler } from "@cenk1cenk2/oclif-common";
13
6
  //#region src/commands/index.ts
14
- var MDPrinter = class extends Command {
7
+ let MDPrinter = class MDPrinter extends Command {
15
8
  static description = "Renders a markdown or yaml file through a Svelte template into a PDF or HTML document.";
16
9
  static flags = {
17
- stdin: Flags.boolean({
18
- char: "I",
19
- description: "Read the input from stdin."
20
- }),
21
- ["input-filetype"]: Flags.string({
22
- char: "f",
23
- options: Object.values(InputFileType),
24
- description: "Input file type. Detected from the file extension, markdown for stdin and unknown extensions."
25
- }),
26
- ["output-filetype"]: Flags.string({
27
- char: "F",
28
- options: Object.values(OutputFileType),
29
- description: "Output file type, defaults to pdf. Overwrites the one defined in front-matter."
30
- }),
31
- stdout: Flags.boolean({
32
- char: "O",
33
- description: "Write the output to stdout instead of a file."
34
- }),
35
- template: Flags.string({
36
- char: "t",
37
- description: "Built-in template name or path to a template directory, defaults to \"default\". Overwrites the one defined in front-matter."
38
- }),
39
- title: Flags.string({
40
- char: "T",
41
- description: "Document title. Overwrites the one defined in front-matter or the template."
42
- }),
43
- language: Flags.string({
44
- char: "l",
45
- description: "Language for template translations. Overwrites the one defined in front-matter and template settings."
46
- }),
47
- browser: Flags.string({
48
- char: "b",
49
- description: "Browser executable used for printing. Discovered automatically when omitted (Brave, Chrome, Chromium, Edge)."
50
- }),
51
- continuous: Flags.boolean({ description: "Render the PDF as a single continuous page without page breaks." }),
52
- preview: Flags.boolean({
53
- char: "p",
54
- description: "Rebuild whenever the input, the template or its assets change, show the HTML and the PDF in a live preview and rewrite the output file when there is one."
55
- }),
56
- host: Flags.string({
57
- description: "Loopback host for the preview server, it serves the files next to the input and is never exposed to the network.",
58
- options: [
59
- "127.0.0.1",
60
- "localhost",
61
- "::1"
62
- ],
63
- default: "127.0.0.1"
64
- }),
65
- port: Flags.integer({
66
- description: "Port for the preview server, random when 0.",
67
- default: 0
68
- }),
69
- open: Flags.boolean({
70
- description: "Open the preview window, --no-open keeps rebuilding and writing without one.",
71
- default: true,
72
- allowNo: true
73
- }),
74
- sign: Flags.boolean({ description: "Sign the PDF with a qualified electronic signature through ID Austria (A-Trust)." }),
75
- ["sign-provider"]: Flags.string({ description: "Signature provider, defaults to a-trust. Overwrites the one defined in front-matter and template settings." }),
76
- ["sign-certificate"]: Flags.string({ description: "Signer certificate (DER or PEM) used for the visible signature block and to verify the returned signature." })
10
+ stdin: Flags.boolean({ char: "I" }),
11
+ ["input-filetype"]: Flags.string({ char: "f" }),
12
+ ["output-filetype"]: Flags.string({ char: "F" }),
13
+ stdout: Flags.boolean({ char: "O" }),
14
+ template: Flags.string({ char: "t" }),
15
+ title: Flags.string({ char: "T" }),
16
+ language: Flags.string({ char: "l" }),
17
+ browser: Flags.string({ char: "b" }),
18
+ continuous: Flags.boolean(),
19
+ preview: Flags.boolean({ char: "p" }),
20
+ host: Flags.string(),
21
+ port: Flags.integer(),
22
+ open: Flags.boolean({ allowNo: true }),
23
+ sign: Flags.boolean(),
24
+ ["sign-provider"]: Flags.string(),
25
+ ["sign-certificate"]: Flags.string()
77
26
  };
78
27
  static args = {
79
- file: Args.string({
80
- description: "Markdown or yaml file to render.",
81
- ignoreStdin: true
82
- }),
83
- output: Args.string({
84
- description: "Output file. Overwrites the one defined in front-matter.",
85
- required: false,
86
- ignoreStdin: true
87
- })
28
+ file: Args.string({ ignoreStdin: true }),
29
+ output: Args.string({ ignoreStdin: true })
88
30
  };
89
- printer;
90
- browser;
91
- register(_cli, options) {
92
- const cli = CliModule.forRoot({
93
- ...options,
94
- logger: { stderr: true }
95
- });
96
- cli.imports.push(PrinterModule.forRoot({ browser: this.flags.browser }));
97
- return cli;
98
- }
99
- async shouldRunBefore() {
100
- await this.app.get(ParserService).register(JsonParser, YamlParser);
101
- this.printer = this.app.get(PrinterService);
102
- this.browser = this.app.get(BrowserService);
103
- }
104
- async run() {
105
- const flags = {
106
- file: this.flags.stdin ? void 0 : this.args.file,
107
- output: this.flags.stdin ? this.args.output ?? this.args.file : this.args.output,
108
- stdin: this.flags.stdin ? await text(process.stdin) : void 0,
109
- inputFiletype: this.flags["input-filetype"],
110
- outputFiletype: this.flags["output-filetype"],
111
- stdout: this.flags.stdout,
112
- template: this.flags.template,
113
- title: this.flags.title,
114
- language: this.flags.language,
115
- continuous: this.flags.continuous,
116
- sign: this.flags.sign,
117
- signProvider: this.flags["sign-provider"],
118
- signCertificate: this.flags["sign-certificate"]
119
- };
120
- if (this.flags.preview) await this.session(flags);
121
- else await this.print(flags);
122
- }
123
- async print(flags) {
124
- const input = await this.printer.input(flags);
125
- const ctx = {
126
- input,
127
- options: await this.printer.options(flags, input)
128
- };
129
- this.logger.debug("Options: %o", ctx.options);
130
- await this.printer.prepare(ctx);
131
- const document = await this.printer.document(ctx, await this.printer.bundle(ctx.options));
132
- let { content } = await this.printer.output(ctx, document);
133
- if (ctx.signature) {
134
- content = await this.printer.sign(ctx, content);
135
- this.logger.info("Document is signed.");
136
- }
137
- await this.write(ctx, content);
138
- }
139
- async session(flags) {
140
- if (flags.stdout) throw new Error("Writing to stdout is not supported with --preview.");
141
- const session = this.app.get(SessionService);
142
- const preview = this.app.get(PreviewService);
143
- let writes = Promise.resolve();
144
- session.on("building", () => preview.building());
145
- session.on("failed", (err) => {
146
- this.logger.error(err.message);
147
- preview.failed(err);
148
- });
149
- session.on("ready", async (build) => {
150
- preview.ready(build);
151
- if (!build.ctx.options.dest) {
152
- this.logger.info("Rebuilt in %dms.", build.ms);
153
- return;
154
- }
155
- session.ignore(build.ctx.options.dest);
156
- writes = writes.then(() => this.write(build.ctx, build.output)).catch((err) => this.logger.error(err.message));
157
- await writes;
158
- });
159
- if (flags.sign) this.logger.warn("Signing is skipped while previewing, run without --preview to sign the document.");
160
- process.removeAllListeners("SIGINT");
161
- process.removeAllListeners("SIGTERM");
162
- const stopped = new Promise((resolve) => {
163
- process.once("SIGINT", () => resolve());
164
- process.once("SIGTERM", () => resolve());
165
- });
166
- await session.start(flags);
167
- const url = await preview.listen({
168
- host: this.flags.host,
169
- port: this.flags.port
170
- });
171
- this.logger.info("Preview is running at %s, press Ctrl+C to stop.", url);
172
- await Promise.race([stopped, this.flags.open ? preview.open(this.browser.executable(), url) : new Promise(() => void 0)]);
173
- await preview.close();
174
- await session.stop();
175
- await writes;
176
- }
177
- async write(ctx, content) {
178
- const { dest } = ctx.options;
179
- if (!dest) {
180
- if (!this.flags.stdout) throw new Error("No output file to write, pass an output argument, set dest in front-matter or use --stdout.");
181
- process.stdout.write(content);
182
- return;
183
- }
184
- await mkdir(dirname(dest), { recursive: true });
185
- await writeFileAtomic(dest, content);
186
- this.logger.info("Written: %s", dest);
187
- }
188
31
  };
32
+ MDPrinter = __decorate([UseHandler({
33
+ module: MdPrinterModule,
34
+ handler: MdPrinterHandler,
35
+ schema: MdPrinterSchema
36
+ })], MDPrinter);
37
+ var commands_default = MDPrinter;
189
38
  //#endregion
190
- export { MDPrinter as default };
39
+ export { commands_default as default };
@@ -1,3 +1,5 @@
1
1
  import { ContextKey, DEFAULT_HIGHLIGHT_STYLE, DEFAULT_LANGUAGE, DEFAULT_PDF_OPTIONS, DEFAULT_TEMPLATE, DIRECTIVE_TAG, Directory, I18N_ACCEPTED_TYPES, MediaType, ModuleSpecifier, PAGE_BREAK_CLASS, PRINTER_MODULE_OPTIONS, RenderMode, TAILWIND_STYLE_LANG, TemplateFiles } from "./template.constants.mjs";
2
2
  import { InputFileType, OutputFileType } from "./file.constants.mjs";
3
- export { ContextKey, DEFAULT_HIGHLIGHT_STYLE, DEFAULT_LANGUAGE, DEFAULT_PDF_OPTIONS, DEFAULT_TEMPLATE, DIRECTIVE_TAG, Directory, I18N_ACCEPTED_TYPES, InputFileType, MediaType, ModuleSpecifier, OutputFileType, PAGE_BREAK_CLASS, PRINTER_MODULE_OPTIONS, RenderMode, TAILWIND_STYLE_LANG, TemplateFiles };
3
+ import { PreviewHost } from "./preview.constants.mjs";
4
+ import { SignatureProvider } from "./signature.constants.mjs";
5
+ export { ContextKey, DEFAULT_HIGHLIGHT_STYLE, DEFAULT_LANGUAGE, DEFAULT_PDF_OPTIONS, DEFAULT_TEMPLATE, DIRECTIVE_TAG, Directory, I18N_ACCEPTED_TYPES, InputFileType, MediaType, ModuleSpecifier, OutputFileType, PAGE_BREAK_CLASS, PRINTER_MODULE_OPTIONS, PreviewHost, RenderMode, SignatureProvider, TAILWIND_STYLE_LANG, TemplateFiles };
@@ -0,0 +1,9 @@
1
+ //#region src/constants/preview.constants.ts
2
+ let PreviewHost = /* @__PURE__ */ function(PreviewHost) {
3
+ PreviewHost["IPV4"] = "127.0.0.1";
4
+ PreviewHost["LOCALHOST"] = "localhost";
5
+ PreviewHost["IPV6"] = "::1";
6
+ return PreviewHost;
7
+ }({});
8
+ //#endregion
9
+ export { PreviewHost };
@@ -0,0 +1,7 @@
1
+ //#region src/constants/signature.constants.ts
2
+ let SignatureProvider = /* @__PURE__ */ function(SignatureProvider) {
3
+ SignatureProvider["A_TRUST"] = "a-trust";
4
+ return SignatureProvider;
5
+ }({});
6
+ //#endregion
7
+ export { SignatureProvider };
@@ -0,0 +1,113 @@
1
+ import __decorateMetadata from "../../_virtual/_@oxc-project_runtime@0.153.0/helpers/esm/decorateMetadata.mjs";
2
+ import __decorate from "../../_virtual/_@oxc-project_runtime@0.153.0/helpers/esm/decorate.mjs";
3
+ import { BrowserService } from "../../services/renderer/browser.service.mjs";
4
+ import { PrinterService } from "../../services/printer.service.mjs";
5
+ import { PreviewService } from "../../services/session/preview.service.mjs";
6
+ import { SessionService } from "../../services/session/session.service.mjs";
7
+ import "../../services/index.mjs";
8
+ import { Injectable } from "@nestjs/common";
9
+ import { mkdir } from "fs/promises";
10
+ import { dirname } from "path";
11
+ import { text } from "stream/consumers";
12
+ import writeFileAtomic from "write-file-atomic";
13
+ import { LoggerService } from "@cenk1cenk2/oclif-common";
14
+ //#region src/modules/md-printer/md-printer.handler.ts
15
+ let MdPrinterHandler = class MdPrinterHandler {
16
+ logger;
17
+ printer;
18
+ browser;
19
+ session;
20
+ preview;
21
+ constructor(logger, printer, browser, session, preview) {
22
+ this.logger = logger;
23
+ this.printer = printer;
24
+ this.browser = browser;
25
+ this.session = session;
26
+ this.preview = preview;
27
+ }
28
+ async run(context) {
29
+ const { flags, args } = context;
30
+ const options = {
31
+ ...args.file !== void 0 ? { file: args.file } : { stdin: await text(process.stdin) },
32
+ output: args.output,
33
+ inputFiletype: flags["input-filetype"],
34
+ outputFiletype: flags["output-filetype"],
35
+ stdout: flags.stdout,
36
+ template: flags.template,
37
+ title: flags.title,
38
+ language: flags.language,
39
+ continuous: flags.continuous,
40
+ sign: flags.sign,
41
+ signProvider: flags["sign-provider"],
42
+ signCertificate: flags["sign-certificate"]
43
+ };
44
+ if (flags.preview) await this.watch(context, options);
45
+ else await this.print(options);
46
+ }
47
+ async print(flags) {
48
+ const input = await this.printer.input(flags);
49
+ const ctx = {
50
+ input,
51
+ options: await this.printer.options(flags, input)
52
+ };
53
+ this.logger.debug("Options: %o", ctx.options);
54
+ await this.printer.prepare(ctx);
55
+ const document = await this.printer.document(ctx, await this.printer.bundle(ctx.options));
56
+ let { content } = await this.printer.output(ctx, document);
57
+ if (ctx.signature) {
58
+ content = await this.printer.sign(ctx, content);
59
+ this.logger.info("Document is signed.");
60
+ }
61
+ await this.write(ctx, content, flags.stdout);
62
+ }
63
+ async watch({ flags, signal }, options) {
64
+ let writes = Promise.resolve();
65
+ this.session.on("building", () => this.preview.building());
66
+ this.session.on("failed", (err) => {
67
+ this.logger.error(err.message);
68
+ this.preview.failed(err);
69
+ });
70
+ this.session.on("ready", async (build) => {
71
+ this.preview.ready(build);
72
+ if (!build.ctx.options.dest) {
73
+ this.logger.info("Rebuilt in %dms.", build.ms);
74
+ return;
75
+ }
76
+ this.session.ignore(build.ctx.options.dest);
77
+ writes = writes.then(() => this.write(build.ctx, build.output, options.stdout)).catch((err) => this.logger.error(err.message));
78
+ await writes;
79
+ });
80
+ if (options.sign) this.logger.warn("Signing is skipped while previewing, run without --preview to sign the document.");
81
+ const stopped = new Promise((resolve) => signal.addEventListener("abort", () => resolve(), { once: true }));
82
+ await this.session.start(options);
83
+ const url = await this.preview.listen({
84
+ host: flags.host,
85
+ port: flags.port
86
+ });
87
+ this.logger.info("Preview is running at %s, press Ctrl+C to stop.", url);
88
+ await Promise.race([stopped, flags.open ? this.preview.open(this.browser.executable(), url) : new Promise(() => void 0)]);
89
+ await this.preview.close();
90
+ await this.session.stop();
91
+ await writes;
92
+ }
93
+ async write(ctx, content, stdout) {
94
+ const { dest } = ctx.options;
95
+ if (!dest) {
96
+ if (!stdout) throw new Error("No output file to write, pass an output argument, set dest in front-matter or use --stdout.");
97
+ process.stdout.write(content);
98
+ return;
99
+ }
100
+ await mkdir(dirname(dest), { recursive: true });
101
+ await writeFileAtomic(dest, content);
102
+ this.logger.info("Written: %s", dest);
103
+ }
104
+ };
105
+ MdPrinterHandler = __decorate([Injectable(), __decorateMetadata("design:paramtypes", [
106
+ typeof LoggerService === "undefined" ? Object : LoggerService,
107
+ typeof PrinterService === "undefined" ? Object : PrinterService,
108
+ typeof BrowserService === "undefined" ? Object : BrowserService,
109
+ typeof SessionService === "undefined" ? Object : SessionService,
110
+ typeof PreviewService === "undefined" ? Object : PreviewService
111
+ ])], MdPrinterHandler);
112
+ //#endregion
113
+ export { MdPrinterHandler };
@@ -0,0 +1,21 @@
1
+ import __decorate from "../../_virtual/_@oxc-project_runtime@0.153.0/helpers/esm/decorate.mjs";
2
+ import { PrinterModule } from "../../services/printer.module.mjs";
3
+ import "../../services/index.mjs";
4
+ import { MdPrinterHandler } from "./md-printer.handler.mjs";
5
+ import { Module } from "@nestjs/common";
6
+ import { CommandContext, LogLevels, LoggerModule, LogoModule } from "@cenk1cenk2/oclif-common";
7
+ //#region src/modules/md-printer/md-printer.module.ts
8
+ let MdPrinterModule = class MdPrinterModule {};
9
+ MdPrinterModule = __decorate([Module({
10
+ imports: [
11
+ LoggerModule.forRoot({ stderr: Object.values(LogLevels) }),
12
+ LogoModule.forRoot(),
13
+ PrinterModule.forRootAsync({
14
+ inject: [CommandContext],
15
+ useFactory: ({ flags }) => ({ browser: flags.browser })
16
+ })
17
+ ],
18
+ providers: [MdPrinterHandler]
19
+ })], MdPrinterModule);
20
+ //#endregion
21
+ export { MdPrinterModule };
@@ -0,0 +1,50 @@
1
+ import { InputFileType, OutputFileType } from "../../constants/file.constants.mjs";
2
+ import { PreviewHost } from "../../constants/preview.constants.mjs";
3
+ import { SignatureProvider } from "../../constants/signature.constants.mjs";
4
+ import { z } from "zod";
5
+ //#region src/modules/md-printer/md-printer.schema.ts
6
+ const MdPrinterSchema = z.object({
7
+ flags: z.object({
8
+ stdin: z.boolean().default(false).describe("Read the input from stdin."),
9
+ ["input-filetype"]: z.enum(InputFileType).optional().describe("Input file type. Detected from the file extension, markdown for stdin and unknown extensions."),
10
+ ["output-filetype"]: z.enum(OutputFileType).optional().describe("Output file type, defaults to pdf. Overwrites the one defined in front-matter."),
11
+ stdout: z.boolean().default(false).describe("Write the output to stdout instead of a file."),
12
+ template: z.string().optional().describe("Built-in template name or path to a template directory, defaults to \"default\". Overwrites the one defined in front-matter."),
13
+ title: z.string().optional().describe("Document title. Overwrites the one defined in front-matter or the template."),
14
+ language: z.string().optional().describe("Language for template translations. Overwrites the one defined in front-matter and template settings."),
15
+ browser: z.string().optional().describe("Browser executable used for printing. Discovered automatically when omitted (Brave, Chrome, Chromium, Edge)."),
16
+ continuous: z.boolean().default(false).describe("Render the PDF as a single continuous page without page breaks."),
17
+ preview: z.boolean().default(false).describe("Rebuild whenever the input, the template or its assets change, show the HTML and the PDF in a live preview and rewrite the output file when there is one."),
18
+ host: z.enum(PreviewHost).default("127.0.0.1").describe("Loopback host for the preview server, it serves the files next to the input and is never exposed to the network."),
19
+ port: z.number().int().min(0).max(65535).default(0).describe("Port for the preview server, random when 0."),
20
+ open: z.boolean().default(true).describe("Open the preview window, --no-open keeps rebuilding and writing without one."),
21
+ sign: z.boolean().default(false).describe("Sign the PDF with a qualified electronic signature through ID Austria (A-Trust)."),
22
+ ["sign-provider"]: z.enum(SignatureProvider).optional().describe("Signature provider, defaults to a-trust. Overwrites the one defined in front-matter and template settings."),
23
+ ["sign-certificate"]: z.string().optional().describe("Signer certificate (DER or PEM) used for the visible signature block and to verify the returned signature.")
24
+ }),
25
+ args: z.object({
26
+ file: z.string().optional().describe("Markdown or yaml file to render."),
27
+ output: z.string().optional().describe("Output file. Overwrites the one defined in front-matter.")
28
+ })
29
+ }).superRefine(({ flags, args }, ctx) => {
30
+ if (!flags.stdin && !args.file) ctx.addIssue({
31
+ code: "custom",
32
+ message: "Either --stdin or file argument must be provided."
33
+ });
34
+ if (flags.stdin && args.output) ctx.addIssue({
35
+ code: "custom",
36
+ message: "Reading from --stdin takes only the output file argument."
37
+ });
38
+ if (flags.stdout && flags.preview) ctx.addIssue({
39
+ code: "custom",
40
+ message: "Writing to stdout is not supported with --preview."
41
+ });
42
+ }).transform(({ flags, args }) => ({
43
+ flags,
44
+ args: flags.stdin ? {
45
+ file: void 0,
46
+ output: args.file
47
+ } : args
48
+ }));
49
+ //#endregion
50
+ export { MdPrinterSchema };
@@ -1,17 +1,18 @@
1
1
  import { I18N_ACCEPTED_TYPES } from "../constants/template.constants.mjs";
2
2
  import __decorateMetadata from "../_virtual/_@oxc-project_runtime@0.153.0/helpers/esm/decorateMetadata.mjs";
3
3
  import __decorate from "../_virtual/_@oxc-project_runtime@0.153.0/helpers/esm/decorate.mjs";
4
- import { basename, extname, join } from "path";
5
- import { ConfigService, FileSystemService } from "@cenk1cenk2/oclif-common";
6
4
  import { Injectable } from "@nestjs/common";
5
+ import { readdir } from "fs/promises";
6
+ import { basename, extname, join } from "path";
7
+ import { FileSystemService, ParserService } from "@cenk1cenk2/oclif-common";
7
8
  import i18next from "i18next";
8
9
  //#region src/services/i18n.service.ts
9
10
  let I18nService = class I18nService {
10
11
  fs;
11
- cs;
12
- constructor(fs, cs) {
12
+ parser;
13
+ constructor(fs, parser) {
13
14
  this.fs = fs;
14
- this.cs = cs;
15
+ this.parser = parser;
15
16
  }
16
17
  async load(directory, options) {
17
18
  const instance = i18next.createInstance();
@@ -25,12 +26,12 @@ let I18nService = class I18nService {
25
26
  }
26
27
  async resources(directory) {
27
28
  const resources = {};
28
- await Promise.all((await this.fs.extra.readdir(directory)).filter((file) => I18N_ACCEPTED_TYPES.includes(extname(file))).map(async (file) => {
29
- resources[basename(file, extname(file))] = { translation: await this.cs.read(join(directory, file)) };
29
+ await Promise.all((await readdir(directory)).filter((file) => I18N_ACCEPTED_TYPES.includes(extname(file))).map(async (file) => {
30
+ resources[basename(file, extname(file))] = { translation: await this.parser.read(join(directory, file)) };
30
31
  }));
31
32
  return resources;
32
33
  }
33
34
  };
34
- I18nService = __decorate([Injectable(), __decorateMetadata("design:paramtypes", [typeof FileSystemService === "undefined" ? Object : FileSystemService, typeof ConfigService === "undefined" ? Object : ConfigService])], I18nService);
35
+ I18nService = __decorate([Injectable(), __decorateMetadata("design:paramtypes", [typeof FileSystemService === "undefined" ? Object : FileSystemService, typeof ParserService === "undefined" ? Object : ParserService])], I18nService);
35
36
  //#endregion
36
37
  export { I18nService };
@@ -10,32 +10,30 @@ import { TemplateService } from "./template/template.service.mjs";
10
10
  import { PrinterService } from "./printer.service.mjs";
11
11
  import { PreviewService } from "./session/preview.service.mjs";
12
12
  import { SessionService } from "./session/session.service.mjs";
13
- import { Module } from "@nestjs/common";
13
+ import { ConfigurableModuleBuilder, Module } from "@nestjs/common";
14
+ import { FilesystemModule, JsonParser, ParserModule, YamlParser } from "@cenk1cenk2/oclif-common";
14
15
  //#region src/services/printer.module.ts
15
- var _PrinterModule;
16
- let PrinterModule = _PrinterModule = class PrinterModule {
17
- static forRoot(options) {
18
- return {
19
- module: _PrinterModule,
20
- providers: [
21
- {
22
- provide: PRINTER_MODULE_OPTIONS,
23
- useValue: options
24
- },
25
- I18nService,
26
- MarkdownService,
27
- TemplateService,
28
- BrowserService,
29
- RendererService,
30
- ATrustAdapter,
31
- SignatureService,
32
- PrinterService,
33
- SessionService,
34
- PreviewService
35
- ]
36
- };
37
- }
38
- };
39
- PrinterModule = _PrinterModule = __decorate([Module({})], PrinterModule);
16
+ let PrinterModule = class PrinterModule extends new ConfigurableModuleBuilder({ optionsInjectionToken: PRINTER_MODULE_OPTIONS }).setClassMethodName("forRoot").build().ConfigurableModuleClass {};
17
+ PrinterModule = __decorate([Module({
18
+ imports: [FilesystemModule.forRoot(), ParserModule.forRoot({ parsers: [JsonParser, YamlParser] })],
19
+ providers: [
20
+ I18nService,
21
+ MarkdownService,
22
+ TemplateService,
23
+ BrowserService,
24
+ RendererService,
25
+ ATrustAdapter,
26
+ SignatureService,
27
+ PrinterService,
28
+ SessionService,
29
+ PreviewService
30
+ ],
31
+ exports: [
32
+ PrinterService,
33
+ BrowserService,
34
+ SessionService,
35
+ PreviewService
36
+ ]
37
+ })], PrinterModule);
40
38
  //#endregion
41
39
  export { PrinterModule };
@@ -8,9 +8,10 @@ import { inlineHtml } from "./template/assets.mjs";
8
8
  import { RendererService } from "./renderer/renderer.service.mjs";
9
9
  import { SignatureService } from "./signature/signature.service.mjs";
10
10
  import { TemplateService } from "./template/template.service.mjs";
11
- import { basename, dirname, extname, join, resolve } from "path";
12
- import { FileSystemService, LoggerService, MergeStrategy, merge } from "@cenk1cenk2/oclif-common";
13
11
  import { Injectable } from "@nestjs/common";
12
+ import { basename, dirname, extname, join, resolve } from "path";
13
+ import { FileSystemService, LoggerService } from "@cenk1cenk2/oclif-common";
14
+ import { cloneDeep, mergeWith, uniq } from "es-toolkit";
14
15
  import { parse } from "yaml";
15
16
  //#region src/services/printer.service.ts
16
17
  let PrinterService = class PrinterService {
@@ -34,12 +35,11 @@ let PrinterService = class PrinterService {
34
35
  async input(flags) {
35
36
  let file;
36
37
  let source;
37
- if (flags.file) {
38
+ if (flags.file !== void 0) {
38
39
  file = resolve(flags.file);
39
40
  if (!this.fs.exists(file)) throw new Error(`File does not exist: ${file}`);
40
41
  source = await this.fs.read(file);
41
- } else if (flags.stdin !== void 0) source = flags.stdin;
42
- else throw new Error("Either --stdin or file argument must be provided.");
42
+ } else source = flags.stdin;
43
43
  const type = flags.inputFiletype ?? (file ? extname(file).replace(/^\./, "") : "md");
44
44
  if (type === "yaml" || type === "yml") return {
45
45
  file,
@@ -145,7 +145,7 @@ let PrinterService = class PrinterService {
145
145
  };
146
146
  }
147
147
  pdfOptions(...sources) {
148
- const pdf = merge(MergeStrategy.EXTEND, {}, DEFAULT_PDF_OPTIONS, ...sources.map((source) => source ?? {}));
148
+ const pdf = sources.reduce((merged, source) => mergeWith(merged, source ?? {}, (current, value) => Array.isArray(current) && Array.isArray(value) ? uniq([...current, ...value]) : void 0), cloneDeep(DEFAULT_PDF_OPTIONS));
149
149
  if (typeof pdf.margin === "string") {
150
150
  const [top, right = top, bottom = top, left = right] = pdf.margin.trim().split(/\s+/);
151
151
  pdf.margin = {
@@ -0,0 +1,44 @@
1
+ import { mkdtemp, rm } from "fs/promises";
2
+ import { join } from "path";
3
+ import { tmpdir } from "os";
4
+ import { spawn } from "child_process";
5
+ import { once } from "events";
6
+ //#region src/services/renderer/app-window.ts
7
+ /**
8
+ * A browser window in app mode with a throwaway profile, so it shares no tabs, cookies or history with the browser of the user.
9
+ */
10
+ var AppWindow = class AppWindow {
11
+ process;
12
+ profile;
13
+ /**
14
+ * Resolves when the window exits, rejects when the browser can not be started.
15
+ */
16
+ closed;
17
+ constructor(process, profile) {
18
+ this.process = process;
19
+ this.profile = profile;
20
+ this.closed = new Promise((resolve, reject) => {
21
+ process.once("exit", () => resolve());
22
+ process.once("error", reject);
23
+ });
24
+ this.closed.catch(() => void 0);
25
+ }
26
+ static async open(executable, url) {
27
+ const profile = await mkdtemp(join(tmpdir(), "md-printer-window-"));
28
+ return new AppWindow(spawn(executable, [
29
+ `--app=${url}`,
30
+ `--user-data-dir=${profile}`,
31
+ "--no-first-run",
32
+ "--no-default-browser-check"
33
+ ], { stdio: "ignore" }), profile);
34
+ }
35
+ async close() {
36
+ if (this.process.exitCode === null && this.process.signalCode === null && this.process.kill()) await once(this.process, "exit");
37
+ await rm(this.profile, {
38
+ recursive: true,
39
+ force: true
40
+ });
41
+ }
42
+ };
43
+ //#endregion
44
+ export { AppWindow };
@@ -3,8 +3,8 @@ import __decorateMetadata from "../../_virtual/_@oxc-project_runtime@0.153.0/hel
3
3
  import __decorate from "../../_virtual/_@oxc-project_runtime@0.153.0/helpers/esm/decorate.mjs";
4
4
  import { discoverBrowser } from "./browser.mjs";
5
5
  import __decorateParam from "../../_virtual/_@oxc-project_runtime@0.153.0/helpers/esm/decorateParam.mjs";
6
- import { LoggerService } from "@cenk1cenk2/oclif-common";
7
6
  import { Inject, Injectable } from "@nestjs/common";
7
+ import { LoggerService } from "@cenk1cenk2/oclif-common";
8
8
  import puppeteer from "puppeteer-core";
9
9
  //#region src/services/renderer/browser.service.ts
10
10
  let BrowserService = class BrowserService {
@@ -3,10 +3,10 @@ import __decorateMetadata from "../../_virtual/_@oxc-project_runtime@0.153.0/hel
3
3
  import __decorate from "../../_virtual/_@oxc-project_runtime@0.153.0/helpers/esm/decorate.mjs";
4
4
  import { BrowserService } from "./browser.service.mjs";
5
5
  import { mimeType, resolveConfined } from "../template/assets.mjs";
6
+ import { Injectable } from "@nestjs/common";
6
7
  import { readFile } from "fs/promises";
7
8
  import { resolve } from "path";
8
9
  import { LoggerService } from "@cenk1cenk2/oclif-common";
9
- import { Injectable } from "@nestjs/common";
10
10
  import { fileURLToPath, pathToFileURL } from "url";
11
11
  //#region src/services/renderer/renderer.service.ts
12
12
  var _RendererService;
@@ -3,14 +3,12 @@ import __decorateMetadata from "../../_virtual/_@oxc-project_runtime@0.153.0/hel
3
3
  import __decorate from "../../_virtual/_@oxc-project_runtime@0.153.0/helpers/esm/decorate.mjs";
4
4
  import { resolveConfined } from "../template/assets.mjs";
5
5
  import { isLoopbackRequest } from "../loopback.mjs";
6
+ import { AppWindow } from "../renderer/app-window.mjs";
6
7
  import { TemplateService } from "../template/template.service.mjs";
7
- import { mkdtemp, rm } from "fs/promises";
8
+ import { Injectable } from "@nestjs/common";
8
9
  import { join } from "path";
9
10
  import { LoggerService } from "@cenk1cenk2/oclif-common";
10
- import { Injectable } from "@nestjs/common";
11
11
  import { fileURLToPath } from "url";
12
- import { tmpdir } from "os";
13
- import { spawn } from "child_process";
14
12
  import { once } from "events";
15
13
  import { createServer } from "http";
16
14
  import serveStatic from "serve-static";
@@ -22,7 +20,6 @@ let PreviewService = class PreviewService {
22
20
  clients = /* @__PURE__ */ new Set();
23
21
  build;
24
22
  error;
25
- profile;
26
23
  window;
27
24
  constructor(logger, template) {
28
25
  this.logger = logger;
@@ -60,39 +57,26 @@ let PreviewService = class PreviewService {
60
57
  this.send("failed", this.error);
61
58
  }
62
59
  async open(executable, url) {
63
- this.profile = await mkdtemp(join(tmpdir(), "md-printer-preview-"));
64
- this.window = spawn(executable, [
65
- `--app=${url}`,
66
- `--user-data-dir=${this.profile}`,
67
- "--no-first-run",
68
- "--no-default-browser-check"
69
- ], { stdio: "ignore" });
70
- await new Promise((done) => {
71
- this.window.on("exit", () => done());
72
- this.window.on("error", (err) => {
73
- this.logger.warn("Can not open the preview window, open it manually: %s (%s)", url, err.message);
74
- });
60
+ this.window = await AppWindow.open(executable, url);
61
+ await this.window.closed.catch((err) => {
62
+ this.logger.warn("Can not open the preview window, open it manually: %s (%s)", url, err.message);
63
+ return new Promise(() => void 0);
75
64
  });
76
65
  }
77
66
  async onModuleDestroy() {
78
67
  await this.close();
79
68
  }
80
69
  async close() {
81
- const { server, window, profile } = this;
70
+ const { server, window } = this;
82
71
  this.server = void 0;
83
72
  this.window = void 0;
84
- this.profile = void 0;
85
73
  for (const client of this.clients) client.end();
86
74
  this.clients.clear();
87
- if (window && window.exitCode === null && window.signalCode === null && window.kill()) await once(window, "exit");
75
+ await window?.close();
88
76
  if (server?.listening) {
89
77
  server.close();
90
78
  await once(server, "close");
91
79
  }
92
- if (profile) await rm(profile, {
93
- recursive: true,
94
- force: true
95
- });
96
80
  }
97
81
  async handle(req, res, shell) {
98
82
  if (!isLoopbackRequest(req)) {
@@ -2,9 +2,9 @@ import "../../constants/template.constants.mjs";
2
2
  import __decorateMetadata from "../../_virtual/_@oxc-project_runtime@0.153.0/helpers/esm/decorateMetadata.mjs";
3
3
  import __decorate from "../../_virtual/_@oxc-project_runtime@0.153.0/helpers/esm/decorate.mjs";
4
4
  import { PrinterService } from "../printer.service.mjs";
5
+ import { Injectable } from "@nestjs/common";
5
6
  import { dirname, join, resolve } from "path";
6
7
  import { LoggerService } from "@cenk1cenk2/oclif-common";
7
- import { Injectable } from "@nestjs/common";
8
8
  import { EventEmitter } from "events";
9
9
  import { watch } from "chokidar";
10
10
  import { debounce } from "perfect-debounce";
@@ -1,10 +1,14 @@
1
+ import "../../../constants/signature.constants.mjs";
1
2
  import __decorateMetadata from "../../../_virtual/_@oxc-project_runtime@0.153.0/helpers/esm/decorateMetadata.mjs";
2
3
  import __decorate from "../../../_virtual/_@oxc-project_runtime@0.153.0/helpers/esm/decorate.mjs";
4
+ import { BrowserNotFoundError } from "../../renderer/browser.mjs";
5
+ import { BrowserService } from "../../renderer/browser.service.mjs";
3
6
  import { isLoopbackRequest } from "../../loopback.mjs";
7
+ import { AppWindow } from "../../renderer/app-window.mjs";
4
8
  import { SignatureAdapter } from "../signature.adapter.mjs";
5
- import { readFile } from "fs/promises";
6
- import { LoggerService } from "@cenk1cenk2/oclif-common";
7
9
  import { Injectable } from "@nestjs/common";
10
+ import { readFile } from "fs/promises";
11
+ import { CommandContext, LoggerService } from "@cenk1cenk2/oclif-common";
8
12
  import { spawn } from "child_process";
9
13
  import { once } from "events";
10
14
  import { createServer } from "http";
@@ -12,10 +16,14 @@ import getRawBody from "raw-body";
12
16
  //#region src/services/signature/adapters/a-trust.adapter.ts
13
17
  let ATrustAdapter = class ATrustAdapter extends SignatureAdapter {
14
18
  logger;
19
+ context;
20
+ browser;
15
21
  name = "a-trust";
16
- constructor(logger) {
22
+ constructor(logger, context, browser) {
17
23
  super();
18
24
  this.logger = logger;
25
+ this.context = context;
26
+ this.browser = browser;
19
27
  this.logger.setup(this.constructor.name);
20
28
  }
21
29
  async sign(request) {
@@ -51,8 +59,10 @@ let ATrustAdapter = class ATrustAdapter extends SignatureAdapter {
51
59
  }
52
60
  async collect(login) {
53
61
  let resolve;
54
- const result = new Promise((done) => {
62
+ let reject;
63
+ const result = new Promise((done, fail) => {
55
64
  resolve = done;
65
+ reject = fail;
56
66
  });
57
67
  const page = await readFile(new URL("./a-trust.html", import.meta.url));
58
68
  const server = createServer(async (req, res) => {
@@ -84,23 +94,44 @@ let ATrustAdapter = class ATrustAdapter extends SignatureAdapter {
84
94
  return res.end("This does not look like a signature, it should start with MII.");
85
95
  }
86
96
  res.writeHead(200);
87
- res.end("Signature received, you can close this tab.");
97
+ res.end("Signature received.");
88
98
  resolve(Buffer.from(cms, "base64"));
89
99
  });
90
100
  server.listen(0, "127.0.0.1");
91
101
  await once(server, "listening");
92
102
  const url = `http://127.0.0.1:${server.address().port}/`;
93
103
  this.logger.info("Sign the document in the browser, then paste the signature back: %s", url);
94
- this.open(url);
95
- return result.finally(() => server.close());
104
+ const window = await this.open(url).catch((err) => {
105
+ server.close();
106
+ throw err;
107
+ });
108
+ this.context.signal.addEventListener("abort", () => reject(/* @__PURE__ */ new Error("Signing is aborted.")), { once: true });
109
+ window?.closed.then(() => reject(/* @__PURE__ */ new Error("The signing window is closed before the signature arrived.")), (err) => this.logger.warn("Can not open the signing window, open the link manually: %s (%s)", url, err.message));
110
+ return result.finally(async () => {
111
+ server.close();
112
+ await window?.close();
113
+ });
96
114
  }
97
- open(url) {
98
- spawn("xdg-open", [url], {
99
- stdio: "ignore",
100
- detached: true
101
- }).on("error", (err) => this.logger.warn("Can not open the browser, open the link manually: %s", err.message)).unref();
115
+ async open(url) {
116
+ let executable;
117
+ try {
118
+ executable = this.browser.executable();
119
+ } catch (err) {
120
+ if (!(err instanceof BrowserNotFoundError)) throw err;
121
+ this.logger.warn("Can not find a browser to sign in a window of its own, close the tabs of any earlier signing before you continue. %s", err.message);
122
+ spawn("xdg-open", [url], {
123
+ stdio: "ignore",
124
+ detached: true
125
+ }).on("error", (err) => this.logger.warn("Can not open the browser, open the link manually: %s", err.message)).unref();
126
+ return;
127
+ }
128
+ return AppWindow.open(executable, url);
102
129
  }
103
130
  };
104
- ATrustAdapter = __decorate([Injectable(), __decorateMetadata("design:paramtypes", [typeof LoggerService === "undefined" ? Object : LoggerService])], ATrustAdapter);
131
+ ATrustAdapter = __decorate([Injectable(), __decorateMetadata("design:paramtypes", [
132
+ typeof LoggerService === "undefined" ? Object : LoggerService,
133
+ typeof CommandContext === "undefined" ? Object : CommandContext,
134
+ typeof BrowserService === "undefined" ? Object : BrowserService
135
+ ])], ATrustAdapter);
105
136
  //#endregion
106
137
  export { ATrustAdapter };
@@ -1,24 +1,24 @@
1
1
  import __decorateMetadata from "../../_virtual/_@oxc-project_runtime@0.153.0/helpers/esm/decorateMetadata.mjs";
2
2
  import __decorate from "../../_virtual/_@oxc-project_runtime@0.153.0/helpers/esm/decorate.mjs";
3
3
  import { ATrustAdapter } from "./adapters/a-trust.adapter.mjs";
4
- import { isAbsolute, join } from "path";
5
- import { FileSystemService } from "@cenk1cenk2/oclif-common";
6
4
  import { Injectable } from "@nestjs/common";
5
+ import { readFile } from "fs/promises";
6
+ import { isAbsolute, join } from "path";
7
7
  import { plainAddPlaceholder } from "@signpdf/placeholder-plain";
8
8
  import { SUBFILTER_ETSI_CADES_DETACHED, findByteRange, removeTrailingNewLine } from "@signpdf/utils";
9
- import { X509Certificate } from "crypto";
9
+ import { X509Certificate, webcrypto } from "crypto";
10
10
  import { homedir } from "os";
11
+ import { ContentInfo, SignedData, getAlgorithmByOID } from "pkijs";
11
12
  //#region src/services/signature/signature.service.ts
12
13
  var _SignatureService;
13
14
  let SignatureService = class SignatureService {
14
15
  static {
15
16
  _SignatureService = this;
16
17
  }
17
- fs;
18
18
  static LENGTH = 16384;
19
+ static MESSAGE_DIGEST = "1.2.840.113549.1.9.4";
19
20
  adapters;
20
- constructor(fs, aTrust) {
21
- this.fs = fs;
21
+ constructor(aTrust) {
22
22
  this.adapters = [aTrust];
23
23
  }
24
24
  get providers() {
@@ -26,7 +26,7 @@ let SignatureService = class SignatureService {
26
26
  }
27
27
  async certificate(file, date) {
28
28
  const path = file.replace(/^~(?=\/|$)/, homedir());
29
- const certificate = new X509Certificate(await this.fs.extra.readFile(isAbsolute(path) ? path : join(process.cwd(), path)));
29
+ const certificate = new X509Certificate(await readFile(isAbsolute(path) ? path : join(process.cwd(), path)));
30
30
  if (date < certificate.validFromDate || date > certificate.validToDate) throw new Error(`Signing certificate is not valid at ${date.toISOString()}, it is valid from ${certificate.validFrom} to ${certificate.validTo}.`);
31
31
  return certificate;
32
32
  }
@@ -41,7 +41,7 @@ let SignatureService = class SignatureService {
41
41
  async sign(pdf, options) {
42
42
  const request = this.prepare(pdf, options);
43
43
  const cms = await this.adapter(options.provider).sign(request);
44
- if (options.certificate && !cms.includes(options.certificate.raw)) throw new Error("The returned signature was not created with the configured certificate, the signature block would show the wrong signer.");
44
+ await this.verify(request, cms, options.certificate);
45
45
  return this.embed(request, cms);
46
46
  }
47
47
  adapter(provider) {
@@ -76,6 +76,30 @@ let SignatureService = class SignatureService {
76
76
  excluded: [from, to]
77
77
  };
78
78
  }
79
+ async verify({ pdf, excluded: [from, to] }, cms, certificate) {
80
+ const content = new Uint8Array(Buffer.concat([pdf.subarray(0, from), pdf.subarray(to + 1)])).buffer;
81
+ let signed;
82
+ try {
83
+ signed = new SignedData({ schema: ContentInfo.fromBER(new Uint8Array(cms)).content });
84
+ } catch (err) {
85
+ throw new Error(`The returned signature is not a CMS signature: ${err.message}`, { cause: err });
86
+ }
87
+ const [info] = signed.signerInfos;
88
+ const digest = info?.signedAttrs?.attributes.find((attribute) => attribute.type === _SignatureService.MESSAGE_DIGEST);
89
+ if (!digest) throw new Error("The returned signature does not carry the digest of the document it signs.");
90
+ const algorithm = getAlgorithmByOID(info.digestAlgorithm.algorithmId, true, "digestAlgorithm");
91
+ if (!Buffer.from(digest.values[0].valueBlock.valueHexView).equals(Buffer.from(await webcrypto.subtle.digest(algorithm.name, content)))) throw new Error("The returned signature belongs to a different document, it is likely left over from an earlier signing.");
92
+ const { signatureVerified, signerCertificate } = await signed.verify({
93
+ signer: 0,
94
+ data: content,
95
+ extendedMode: true
96
+ }).catch((err) => {
97
+ throw new Error(`Can not verify the returned signature: ${err.message}`, { cause: err });
98
+ });
99
+ const signer = new X509Certificate(Buffer.from(signerCertificate.toSchema().toBER()));
100
+ if (certificate && !signer.raw.equals(certificate.raw)) throw new Error([`The returned signature is signed by ${this.identify(signer)}, but the configured certificate is ${this.identify(certificate)}.`, "The signature block would show the wrong signer."].join(" "));
101
+ if (!signatureVerified) throw new Error(`The returned signature does not verify against the certificate of ${this.identify(signer)}.`);
102
+ }
79
103
  embed({ pdf, excluded: [from, to] }, cms) {
80
104
  const signature = cms.toString("hex");
81
105
  const length = to - from - 1;
@@ -86,10 +110,13 @@ let SignatureService = class SignatureService {
86
110
  pdf.subarray(to)
87
111
  ]);
88
112
  }
113
+ identify(certificate) {
114
+ return `${this.commonName(certificate.subject)} (serial ${certificate.serialNumber})`;
115
+ }
89
116
  commonName(name) {
90
117
  return name.split("\n").find((part) => part.startsWith("CN="))?.slice(3);
91
118
  }
92
119
  };
93
- SignatureService = _SignatureService = __decorate([Injectable(), __decorateMetadata("design:paramtypes", [typeof FileSystemService === "undefined" ? Object : FileSystemService, typeof ATrustAdapter === "undefined" ? Object : ATrustAdapter])], SignatureService);
120
+ SignatureService = _SignatureService = __decorate([Injectable(), __decorateMetadata("design:paramtypes", [typeof ATrustAdapter === "undefined" ? Object : ATrustAdapter])], SignatureService);
94
121
  //#endregion
95
122
  export { SignatureService };
@@ -3,9 +3,9 @@ import __decorateMetadata from "../../_virtual/_@oxc-project_runtime@0.153.0/hel
3
3
  import __decorate from "../../_virtual/_@oxc-project_runtime@0.153.0/helpers/esm/decorate.mjs";
4
4
  import { inlineCss, inlineHtml } from "./assets.mjs";
5
5
  import { sveltePlugin } from "./svelte.plugin.mjs";
6
+ import { Injectable } from "@nestjs/common";
6
7
  import { dirname, isAbsolute, join, resolve } from "path";
7
8
  import { FileSystemService } from "@cenk1cenk2/oclif-common";
8
- import { Injectable } from "@nestjs/common";
9
9
  import { fileURLToPath } from "url";
10
10
  import { homedir } from "os";
11
11
  import { compile } from "@tailwindcss/node";
@@ -9,8 +9,7 @@
9
9
  },
10
10
  "output": {
11
11
  "description": "Output file. Overwrites the one defined in front-matter.",
12
- "name": "output",
13
- "required": false
12
+ "name": "output"
14
13
  }
15
14
  },
16
15
  "description": "Renders a markdown or yaml file through a Svelte template into a PDF or HTML document.",
@@ -176,6 +175,9 @@
176
175
  "name": "sign-provider",
177
176
  "hasDynamicHelp": false,
178
177
  "multiple": false,
178
+ "options": [
179
+ "a-trust"
180
+ ],
179
181
  "type": "option"
180
182
  },
181
183
  "sign-certificate": {
@@ -196,5 +198,5 @@
196
198
  "enableJsonFlag": false
197
199
  }
198
200
  },
199
- "version": "3.0.0"
201
+ "version": "3.1.1"
200
202
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cenk1cenk2/md-printer",
3
- "version": "3.0.0",
3
+ "version": "3.1.1",
4
4
  "description": "Render markdown or yaml through Svelte templates into PDF or HTML with Puppeteer.",
5
5
  "type": "module",
6
6
  "repository": {
@@ -29,7 +29,8 @@
29
29
  "target": "./dist/commands/index.mjs"
30
30
  },
31
31
  "plugins": [
32
- "@oclif/plugin-help"
32
+ "@oclif/plugin-help",
33
+ "@cenk1cenk2/oclif-common"
33
34
  ],
34
35
  "topicSeparator": " "
35
36
  },
@@ -48,7 +49,7 @@
48
49
  "cenk1cenk2"
49
50
  ],
50
51
  "dependencies": {
51
- "@cenk1cenk2/oclif-common": "^7.2.0",
52
+ "@cenk1cenk2/oclif-common": "^8.0.0",
52
53
  "@fontsource/montserrat": "^5.3.0",
53
54
  "@listr2/manager": "^4.3.1",
54
55
  "@nestjs/common": "^12.1.2",
@@ -63,7 +64,7 @@
63
64
  "chokidar": "^5.0.0",
64
65
  "enhanced-resolve": "^5.26.0",
65
66
  "entities": "^8.1.0",
66
- "fs-extra": "^11.4.1",
67
+ "es-toolkit": "^1.52.0",
67
68
  "github-markdown-css": "^5.9.0",
68
69
  "hast-util-from-html": "^2.0.3",
69
70
  "hast-util-to-html": "^9.0.5",
@@ -74,6 +75,7 @@
74
75
  "lowlight": "^3.3.0",
75
76
  "mime": "^4.1.1",
76
77
  "perfect-debounce": "^2.1.0",
78
+ "pkijs": "^3.4.1",
77
79
  "property-information": "^7.2.0",
78
80
  "puppeteer-core": "^25.13.0",
79
81
  "raw-body": "^4.0.0",
@@ -98,19 +100,20 @@
98
100
  "vfile-matter": "^5.0.1",
99
101
  "which": "^7.0.0",
100
102
  "write-file-atomic": "^8.0.0",
101
- "yaml": "^2.9.1"
103
+ "yaml": "^2.9.1",
104
+ "zod": "^4.6.7"
102
105
  },
103
106
  "devDependencies": {
104
107
  "@cenk1cenk2/cz-cc": "^3.0.6",
105
108
  "@cenk1cenk2/eslint-config": "^3.3.9",
106
109
  "@tsconfig/node24": "^24.0.5",
107
- "@types/fs-extra": "^11.0.4",
108
110
  "@types/hast": "^3.0.5",
109
111
  "@types/mdast": "^4.0.4",
110
112
  "@types/node": "^24.19.1",
111
113
  "@types/serve-static": "^2.2.0",
112
114
  "@types/which": "^3.0.4",
113
115
  "@types/write-file-atomic": "^4.0.3",
116
+ "asn1js": "^3.0.10",
114
117
  "eslint": "^10.12.0",
115
118
  "eslint-plugin-svelte": "^3.23.1",
116
119
  "execa": "^10.1.0",