@cenk1cenk2/md-printer 3.1.2 → 3.2.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 +2 -1
- package/dist/services/index.mjs +2 -1
- package/dist/services/printer.service.mjs +13 -2
- package/dist/services/session/preview.service.mjs +23 -7
- package/dist/services/signature/adapters/a-trust.adapter.mjs +22 -22
- package/dist/services/signature/cms.mjs +35 -0
- package/dist/services/signature/index.mjs +2 -1
- package/dist/services/signature/signature.service.mjs +18 -8
- package/oclif.manifest.json +1 -1
- package/package.json +7 -7
- package/templates/invoice/components/Client.svelte +4 -4
- package/dist/services/renderer/app-window.mjs +0 -44
package/README.md
CHANGED
|
@@ -275,7 +275,8 @@ md-printer invoice.yaml -t invoice --sign --sign-certificate ~/id-austria.pem
|
|
|
275
275
|
- `--sign` enables signing. `--sign-provider` selects the provider, `a-trust` is the only one and the default. `--sign-certificate` is a DER or PEM file of your signer certificate.
|
|
276
276
|
- The same keys can be set as `sign` in front-matter or `settings.json`: `enabled`, `provider`, `certificate`, `reason`, `location`, `contact`. Flags win. `reason`, `location` and `contact` are stored in the signature.
|
|
277
277
|
- Signing only works for PDF output and is skipped with `--preview`.
|
|
278
|
-
- The signing session has to run in your browser. md-printer opens a
|
|
278
|
+
- The signing session has to run in your browser. md-printer opens a temporary local page in an app window of the discovered browser, using your normal profile (or with `xdg-open` when none is found), you log in with ID Austria in the A-Trust page, and paste the resulting signature (starts with `MII`) back into the local page. A paste that is cut short is refused on the page, so you can copy it again. It waits until the signature arrives, press Ctrl+C to abort.
|
|
279
|
+
- The returned signature is always verified before it is embedded: its digest must match this document and it must be cryptographically valid. The result is printed with the signer name, serial and issuer. A failed verification fails the run, and with `--log-level debug` the returned signature is written next to the output as `<output>.cms.der`.
|
|
279
280
|
- With a certificate, the signer name, issuer and serial are passed to the template as `signature`, so `SignatureBlock` can show who signed and when. The certificate must be valid at signing time, and the returned signature is rejected if it was not made with it. Without a certificate only the date is available.
|
|
280
281
|
- Verify the result at https://www.signaturpruefung.gv.at.
|
|
281
282
|
|
package/dist/services/index.mjs
CHANGED
|
@@ -2,6 +2,7 @@ import { I18nService } from "./i18n.service.mjs";
|
|
|
2
2
|
import { MarkdownService } from "./markdown.service.mjs";
|
|
3
3
|
import { BrowserService } from "./renderer/browser.service.mjs";
|
|
4
4
|
import { RendererService } from "./renderer/renderer.service.mjs";
|
|
5
|
+
import { IncompleteSignatureError, SignatureVerificationError, parseSignedData } from "./signature/cms.mjs";
|
|
5
6
|
import { SignatureAdapter } from "./signature/signature.adapter.mjs";
|
|
6
7
|
import { ATrustAdapter } from "./signature/adapters/a-trust.adapter.mjs";
|
|
7
8
|
import { SignatureService } from "./signature/signature.service.mjs";
|
|
@@ -11,4 +12,4 @@ import { PreviewService } from "./session/preview.service.mjs";
|
|
|
11
12
|
import { SessionService } from "./session/session.service.mjs";
|
|
12
13
|
import { PrinterModule } from "./printer.module.mjs";
|
|
13
14
|
import "./signature/index.mjs";
|
|
14
|
-
export { ATrustAdapter, BrowserService, I18nService, MarkdownService, PreviewService, PrinterModule, PrinterService, RendererService, SessionService, SignatureAdapter, SignatureService, TemplateService };
|
|
15
|
+
export { ATrustAdapter, BrowserService, I18nService, IncompleteSignatureError, MarkdownService, PreviewService, PrinterModule, PrinterService, RendererService, SessionService, SignatureAdapter, SignatureService, SignatureVerificationError, TemplateService, parseSignedData };
|
|
@@ -6,25 +6,28 @@ import { I18nService } from "./i18n.service.mjs";
|
|
|
6
6
|
import { MarkdownService } from "./markdown.service.mjs";
|
|
7
7
|
import { inlineHtml } from "./template/assets.mjs";
|
|
8
8
|
import { RendererService } from "./renderer/renderer.service.mjs";
|
|
9
|
+
import { SignatureVerificationError } from "./signature/cms.mjs";
|
|
9
10
|
import { SignatureService } from "./signature/signature.service.mjs";
|
|
10
11
|
import { TemplateService } from "./template/template.service.mjs";
|
|
11
12
|
import { Injectable } from "@nestjs/common";
|
|
12
13
|
import { basename, dirname, extname, join, resolve } from "path";
|
|
13
|
-
import { FileSystemService, LoggerService } from "@cenk1cenk2/oclif-common";
|
|
14
|
+
import { ConfigService, FileSystemService, LoggerService } from "@cenk1cenk2/oclif-common";
|
|
14
15
|
import { cloneDeep, mergeWith, uniq } from "es-toolkit";
|
|
15
16
|
import { parse } from "yaml";
|
|
16
17
|
//#region src/services/printer.service.ts
|
|
17
18
|
let PrinterService = class PrinterService {
|
|
18
19
|
fs;
|
|
19
20
|
logger;
|
|
21
|
+
config;
|
|
20
22
|
i18n;
|
|
21
23
|
markdown;
|
|
22
24
|
template;
|
|
23
25
|
renderer;
|
|
24
26
|
signature;
|
|
25
|
-
constructor(fs, logger, i18n, markdown, template, renderer, signature) {
|
|
27
|
+
constructor(fs, logger, config, i18n, markdown, template, renderer, signature) {
|
|
26
28
|
this.fs = fs;
|
|
27
29
|
this.logger = logger;
|
|
30
|
+
this.config = config;
|
|
28
31
|
this.i18n = i18n;
|
|
29
32
|
this.markdown = markdown;
|
|
30
33
|
this.template = template;
|
|
@@ -134,6 +137,13 @@ let PrinterService = class PrinterService {
|
|
|
134
137
|
...ctx.options.sign,
|
|
135
138
|
date: ctx.signingTime,
|
|
136
139
|
certificate: ctx.certificate
|
|
140
|
+
}).catch(async (err) => {
|
|
141
|
+
if (err instanceof SignatureVerificationError && this.config.isDebug && ctx.options.dest) {
|
|
142
|
+
const path = `${ctx.options.dest}.cms.der`;
|
|
143
|
+
await this.fs.write(path, err.cms);
|
|
144
|
+
this.logger.debug("The returned signature is written to: %s", path);
|
|
145
|
+
}
|
|
146
|
+
throw err;
|
|
137
147
|
});
|
|
138
148
|
}
|
|
139
149
|
signOptions(flags, ...sources) {
|
|
@@ -161,6 +171,7 @@ let PrinterService = class PrinterService {
|
|
|
161
171
|
PrinterService = __decorate([Injectable(), __decorateMetadata("design:paramtypes", [
|
|
162
172
|
typeof FileSystemService === "undefined" ? Object : FileSystemService,
|
|
163
173
|
typeof LoggerService === "undefined" ? Object : LoggerService,
|
|
174
|
+
typeof ConfigService === "undefined" ? Object : ConfigService,
|
|
164
175
|
typeof I18nService === "undefined" ? Object : I18nService,
|
|
165
176
|
typeof MarkdownService === "undefined" ? Object : MarkdownService,
|
|
166
177
|
typeof TemplateService === "undefined" ? Object : TemplateService,
|
|
@@ -3,12 +3,14 @@ import __decorateMetadata from "../../_virtual/_@oxc-project_runtime@0.154.0/hel
|
|
|
3
3
|
import __decorate from "../../_virtual/_@oxc-project_runtime@0.154.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";
|
|
7
6
|
import { TemplateService } from "../template/template.service.mjs";
|
|
8
7
|
import { Injectable } from "@nestjs/common";
|
|
8
|
+
import { mkdtemp, rm } from "fs/promises";
|
|
9
9
|
import { join } from "path";
|
|
10
10
|
import { LoggerService } from "@cenk1cenk2/oclif-common";
|
|
11
11
|
import { fileURLToPath } from "url";
|
|
12
|
+
import { tmpdir } from "os";
|
|
13
|
+
import { spawn } from "child_process";
|
|
12
14
|
import { once } from "events";
|
|
13
15
|
import { createServer } from "http";
|
|
14
16
|
import serveStatic from "serve-static";
|
|
@@ -20,6 +22,7 @@ let PreviewService = class PreviewService {
|
|
|
20
22
|
clients = /* @__PURE__ */ new Set();
|
|
21
23
|
build;
|
|
22
24
|
error;
|
|
25
|
+
profile;
|
|
23
26
|
window;
|
|
24
27
|
constructor(logger, template) {
|
|
25
28
|
this.logger = logger;
|
|
@@ -57,26 +60,39 @@ let PreviewService = class PreviewService {
|
|
|
57
60
|
this.send("failed", this.error);
|
|
58
61
|
}
|
|
59
62
|
async open(executable, url) {
|
|
60
|
-
this.
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
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
|
+
});
|
|
64
75
|
});
|
|
65
76
|
}
|
|
66
77
|
async onModuleDestroy() {
|
|
67
78
|
await this.close();
|
|
68
79
|
}
|
|
69
80
|
async close() {
|
|
70
|
-
const { server, window } = this;
|
|
81
|
+
const { server, window, profile } = this;
|
|
71
82
|
this.server = void 0;
|
|
72
83
|
this.window = void 0;
|
|
84
|
+
this.profile = void 0;
|
|
73
85
|
for (const client of this.clients) client.end();
|
|
74
86
|
this.clients.clear();
|
|
75
|
-
|
|
87
|
+
if (window && window.exitCode === null && window.signalCode === null && window.kill()) await once(window, "exit");
|
|
76
88
|
if (server?.listening) {
|
|
77
89
|
server.close();
|
|
78
90
|
await once(server, "close");
|
|
79
91
|
}
|
|
92
|
+
if (profile) await rm(profile, {
|
|
93
|
+
recursive: true,
|
|
94
|
+
force: true
|
|
95
|
+
});
|
|
80
96
|
}
|
|
81
97
|
async handle(req, res, shell) {
|
|
82
98
|
if (!isLoopbackRequest(req)) {
|
|
@@ -3,8 +3,8 @@ import __decorateMetadata from "../../../_virtual/_@oxc-project_runtime@0.154.0/
|
|
|
3
3
|
import __decorate from "../../../_virtual/_@oxc-project_runtime@0.154.0/helpers/esm/decorate.mjs";
|
|
4
4
|
import { BrowserNotFoundError } from "../../renderer/browser.mjs";
|
|
5
5
|
import { BrowserService } from "../../renderer/browser.service.mjs";
|
|
6
|
+
import { IncompleteSignatureError, parseSignedData } from "../cms.mjs";
|
|
6
7
|
import { isLoopbackRequest } from "../../loopback.mjs";
|
|
7
|
-
import { AppWindow } from "../../renderer/app-window.mjs";
|
|
8
8
|
import { SignatureAdapter } from "../signature.adapter.mjs";
|
|
9
9
|
import { Injectable } from "@nestjs/common";
|
|
10
10
|
import { readFile } from "fs/promises";
|
|
@@ -93,39 +93,39 @@ let ATrustAdapter = class ATrustAdapter extends SignatureAdapter {
|
|
|
93
93
|
res.writeHead(400);
|
|
94
94
|
return res.end("This does not look like a signature, it should start with MII.");
|
|
95
95
|
}
|
|
96
|
+
const signature = Buffer.from(cms, "base64");
|
|
97
|
+
try {
|
|
98
|
+
parseSignedData(signature);
|
|
99
|
+
} catch (err) {
|
|
100
|
+
res.writeHead(400);
|
|
101
|
+
if (err instanceof IncompleteSignatureError) return res.end(`The pasted signature is incomplete (got ${err.received} of ${err.declared} bytes), copy the whole signature text from the A-Trust page.`);
|
|
102
|
+
return res.end(`The pasted text is not a CMS signature: ${err.message}`);
|
|
103
|
+
}
|
|
96
104
|
res.writeHead(200);
|
|
97
|
-
res.end("Signature received.");
|
|
98
|
-
resolve(
|
|
105
|
+
res.end("Signature received, you can close this window.");
|
|
106
|
+
resolve(signature);
|
|
99
107
|
});
|
|
100
108
|
server.listen(0, "127.0.0.1");
|
|
101
109
|
await once(server, "listening");
|
|
102
110
|
const url = `http://127.0.0.1:${server.address().port}/`;
|
|
103
111
|
this.logger.info("Sign the document in the browser, then paste the signature back: %s", url);
|
|
104
|
-
|
|
105
|
-
server.close();
|
|
106
|
-
throw err;
|
|
107
|
-
});
|
|
112
|
+
this.open(url);
|
|
108
113
|
this.context.signal.addEventListener("abort", () => reject(/* @__PURE__ */ new Error("Signing is aborted.")), { once: true });
|
|
109
|
-
|
|
110
|
-
return result.finally(async () => {
|
|
111
|
-
server.close();
|
|
112
|
-
await window?.close();
|
|
113
|
-
});
|
|
114
|
+
return result.finally(() => server.close());
|
|
114
115
|
}
|
|
115
|
-
|
|
116
|
-
let
|
|
116
|
+
open(url) {
|
|
117
|
+
let command;
|
|
117
118
|
try {
|
|
118
|
-
|
|
119
|
+
command = [this.browser.executable(), [`--app=${url}`]];
|
|
119
120
|
} catch (err) {
|
|
120
121
|
if (!(err instanceof BrowserNotFoundError)) throw err;
|
|
121
|
-
this.logger.warn("Can not find a browser to sign in a window
|
|
122
|
-
|
|
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;
|
|
122
|
+
this.logger.warn("Can not find a browser to sign in a new window, close the tabs of any earlier signing before you continue. %s", err.message);
|
|
123
|
+
command = ["xdg-open", [url]];
|
|
127
124
|
}
|
|
128
|
-
|
|
125
|
+
spawn(...command, {
|
|
126
|
+
stdio: "ignore",
|
|
127
|
+
detached: true
|
|
128
|
+
}).on("error", (err) => this.logger.warn("Can not open the browser, open the link manually: %s", err.message)).unref();
|
|
129
129
|
}
|
|
130
130
|
};
|
|
131
131
|
ATrustAdapter = __decorate([Injectable(), __decorateMetadata("design:paramtypes", [
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { fromBER } from "asn1js";
|
|
2
|
+
import { ContentInfo, SignedData } from "pkijs";
|
|
3
|
+
//#region src/services/signature/cms.ts
|
|
4
|
+
var IncompleteSignatureError = class extends Error {
|
|
5
|
+
received;
|
|
6
|
+
declared;
|
|
7
|
+
constructor(received, declared) {
|
|
8
|
+
super(`The signature is incomplete, got ${received} of ${declared} bytes.`);
|
|
9
|
+
this.received = received;
|
|
10
|
+
this.declared = declared;
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
var SignatureVerificationError = class extends Error {
|
|
14
|
+
cms;
|
|
15
|
+
constructor(message, cms, options) {
|
|
16
|
+
super(message, options);
|
|
17
|
+
this.cms = cms;
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Parses a DER encoded CMS SignedData, telling a signature that is cut short apart from one that is not a signature at all.
|
|
22
|
+
*/
|
|
23
|
+
function parseSignedData(cms) {
|
|
24
|
+
const { offset, result } = fromBER(new Uint8Array(cms));
|
|
25
|
+
if (offset === -1) {
|
|
26
|
+
const declared = result.idBlock.blockLength + result.lenBlock.blockLength + result.lenBlock.length;
|
|
27
|
+
if (declared > cms.length) throw new IncompleteSignatureError(cms.length, declared);
|
|
28
|
+
throw new Error(result.error);
|
|
29
|
+
}
|
|
30
|
+
const info = new ContentInfo({ schema: result });
|
|
31
|
+
if (info.contentType !== ContentInfo.SIGNED_DATA) throw new Error(`Expected CMS SignedData but got content type ${info.contentType}.`);
|
|
32
|
+
return new SignedData({ schema: info.content });
|
|
33
|
+
}
|
|
34
|
+
//#endregion
|
|
35
|
+
export { IncompleteSignatureError, SignatureVerificationError, parseSignedData };
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
+
import { IncompleteSignatureError, SignatureVerificationError, parseSignedData } from "./cms.mjs";
|
|
1
2
|
import { SignatureAdapter } from "./signature.adapter.mjs";
|
|
2
3
|
import { ATrustAdapter } from "./adapters/a-trust.adapter.mjs";
|
|
3
4
|
import { SignatureService } from "./signature.service.mjs";
|
|
4
|
-
export { ATrustAdapter, SignatureAdapter, SignatureService };
|
|
5
|
+
export { ATrustAdapter, IncompleteSignatureError, SignatureAdapter, SignatureService, SignatureVerificationError, parseSignedData };
|
|
@@ -1,24 +1,29 @@
|
|
|
1
1
|
import __decorateMetadata from "../../_virtual/_@oxc-project_runtime@0.154.0/helpers/esm/decorateMetadata.mjs";
|
|
2
2
|
import __decorate from "../../_virtual/_@oxc-project_runtime@0.154.0/helpers/esm/decorate.mjs";
|
|
3
|
+
import { IncompleteSignatureError, SignatureVerificationError, parseSignedData } from "./cms.mjs";
|
|
3
4
|
import { ATrustAdapter } from "./adapters/a-trust.adapter.mjs";
|
|
4
5
|
import { Injectable } from "@nestjs/common";
|
|
5
6
|
import { readFile } from "fs/promises";
|
|
6
7
|
import { isAbsolute, join } from "path";
|
|
8
|
+
import { LoggerService } from "@cenk1cenk2/oclif-common";
|
|
9
|
+
import { getAlgorithmByOID } from "pkijs";
|
|
7
10
|
import { plainAddPlaceholder } from "@signpdf/placeholder-plain";
|
|
8
11
|
import { SUBFILTER_ETSI_CADES_DETACHED, findByteRange, removeTrailingNewLine } from "@signpdf/utils";
|
|
9
12
|
import { X509Certificate, webcrypto } from "crypto";
|
|
10
13
|
import { homedir } from "os";
|
|
11
|
-
import { ContentInfo, SignedData, getAlgorithmByOID } from "pkijs";
|
|
12
14
|
//#region src/services/signature/signature.service.ts
|
|
13
15
|
var _SignatureService;
|
|
14
16
|
let SignatureService = class SignatureService {
|
|
15
17
|
static {
|
|
16
18
|
_SignatureService = this;
|
|
17
19
|
}
|
|
20
|
+
logger;
|
|
18
21
|
static LENGTH = 16384;
|
|
19
22
|
static MESSAGE_DIGEST = "1.2.840.113549.1.9.4";
|
|
20
23
|
adapters;
|
|
21
|
-
constructor(aTrust) {
|
|
24
|
+
constructor(logger, aTrust) {
|
|
25
|
+
this.logger = logger;
|
|
26
|
+
this.logger.setup(this.constructor.name);
|
|
22
27
|
this.adapters = [aTrust];
|
|
23
28
|
}
|
|
24
29
|
get providers() {
|
|
@@ -41,7 +46,10 @@ let SignatureService = class SignatureService {
|
|
|
41
46
|
async sign(pdf, options) {
|
|
42
47
|
const request = this.prepare(pdf, options);
|
|
43
48
|
const cms = await this.adapter(options.provider).sign(request);
|
|
44
|
-
await this.verify(request, cms, options.certificate)
|
|
49
|
+
const signer = await this.verify(request, cms, options.certificate).catch((err) => {
|
|
50
|
+
throw new SignatureVerificationError(err.message, cms, { cause: err });
|
|
51
|
+
});
|
|
52
|
+
this.logger.info("Signature is verified, signed by %s: the document digest matches and the signature is valid.", this.identify(signer));
|
|
45
53
|
return this.embed(request, cms);
|
|
46
54
|
}
|
|
47
55
|
adapter(provider) {
|
|
@@ -80,15 +88,16 @@ let SignatureService = class SignatureService {
|
|
|
80
88
|
const content = new Uint8Array(Buffer.concat([pdf.subarray(0, from), pdf.subarray(to + 1)])).buffer;
|
|
81
89
|
let signed;
|
|
82
90
|
try {
|
|
83
|
-
signed =
|
|
91
|
+
signed = parseSignedData(cms);
|
|
84
92
|
} catch (err) {
|
|
85
|
-
throw new Error(`The returned signature is
|
|
93
|
+
if (err instanceof IncompleteSignatureError) throw new Error(`The returned signature is incomplete, got ${err.received} of ${err.declared} bytes.`, { cause: err });
|
|
94
|
+
throw new Error(`The returned signature can not be parsed as a CMS signature: ${err.message}`, { cause: err });
|
|
86
95
|
}
|
|
87
96
|
const [info] = signed.signerInfos;
|
|
88
97
|
const digest = info?.signedAttrs?.attributes.find((attribute) => attribute.type === _SignatureService.MESSAGE_DIGEST);
|
|
89
98
|
if (!digest) throw new Error("The returned signature does not carry the digest of the document it signs.");
|
|
90
99
|
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
|
|
100
|
+
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.");
|
|
92
101
|
const { signatureVerified, signerCertificate } = await signed.verify({
|
|
93
102
|
signer: 0,
|
|
94
103
|
data: content,
|
|
@@ -99,6 +108,7 @@ let SignatureService = class SignatureService {
|
|
|
99
108
|
const signer = new X509Certificate(Buffer.from(signerCertificate.toSchema().toBER()));
|
|
100
109
|
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
110
|
if (!signatureVerified) throw new Error(`The returned signature does not verify against the certificate of ${this.identify(signer)}.`);
|
|
111
|
+
return signer;
|
|
102
112
|
}
|
|
103
113
|
embed({ pdf, excluded: [from, to] }, cms) {
|
|
104
114
|
const signature = cms.toString("hex");
|
|
@@ -111,12 +121,12 @@ let SignatureService = class SignatureService {
|
|
|
111
121
|
]);
|
|
112
122
|
}
|
|
113
123
|
identify(certificate) {
|
|
114
|
-
return `${this.commonName(certificate.subject)} (serial ${certificate.serialNumber})`;
|
|
124
|
+
return `${this.commonName(certificate.subject)} (serial ${certificate.serialNumber}, issuer ${this.commonName(certificate.issuer)})`;
|
|
115
125
|
}
|
|
116
126
|
commonName(name) {
|
|
117
127
|
return name.split("\n").find((part) => part.startsWith("CN="))?.slice(3);
|
|
118
128
|
}
|
|
119
129
|
};
|
|
120
|
-
SignatureService = _SignatureService = __decorate([Injectable(), __decorateMetadata("design:paramtypes", [typeof ATrustAdapter === "undefined" ? Object : ATrustAdapter])], SignatureService);
|
|
130
|
+
SignatureService = _SignatureService = __decorate([Injectable(), __decorateMetadata("design:paramtypes", [typeof LoggerService === "undefined" ? Object : LoggerService, typeof ATrustAdapter === "undefined" ? Object : ATrustAdapter])], SignatureService);
|
|
121
131
|
//#endregion
|
|
122
132
|
export { SignatureService };
|
package/oclif.manifest.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cenk1cenk2/md-printer",
|
|
3
|
-
"version": "3.1
|
|
3
|
+
"version": "3.2.1",
|
|
4
4
|
"description": "Render markdown or yaml through Svelte templates into PDF or HTML with Puppeteer.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|
|
@@ -51,15 +51,16 @@
|
|
|
51
51
|
"dependencies": {
|
|
52
52
|
"@cenk1cenk2/oclif-common": "^8.0.0",
|
|
53
53
|
"@fontsource/montserrat": "^5.3.0",
|
|
54
|
-
"@listr2/manager": "^4.3.
|
|
54
|
+
"@listr2/manager": "^4.3.2",
|
|
55
55
|
"@nestjs/common": "^12.1.2",
|
|
56
|
-
"@oclif/core": "^5.1.
|
|
57
|
-
"@oclif/plugin-help": "^7.0.
|
|
56
|
+
"@oclif/core": "^5.1.7",
|
|
57
|
+
"@oclif/plugin-help": "^7.0.3",
|
|
58
58
|
"@signpdf/placeholder-plain": "^3.3.0",
|
|
59
59
|
"@signpdf/utils": "^3.3.0",
|
|
60
60
|
"@tailwindcss/node": "^4.3.3",
|
|
61
61
|
"@tailwindcss/oxide": "^4.3.3",
|
|
62
62
|
"@tailwindcss/typography": "^0.5.20",
|
|
63
|
+
"asn1js": "^3.0.10",
|
|
63
64
|
"change-case": "^5.4.4",
|
|
64
65
|
"chokidar": "^5.0.0",
|
|
65
66
|
"enhanced-resolve": "^5.26.0",
|
|
@@ -71,7 +72,7 @@
|
|
|
71
72
|
"highlight.js": "^11.12.0",
|
|
72
73
|
"i18next": "^26.4.2",
|
|
73
74
|
"lightningcss": "^1.33.0",
|
|
74
|
-
"listr2": "^11.
|
|
75
|
+
"listr2": "^11.3.0",
|
|
75
76
|
"lowlight": "^3.3.0",
|
|
76
77
|
"mime": "^4.1.1",
|
|
77
78
|
"perfect-debounce": "^2.1.0",
|
|
@@ -113,11 +114,10 @@
|
|
|
113
114
|
"@types/serve-static": "^2.2.0",
|
|
114
115
|
"@types/which": "^3.0.4",
|
|
115
116
|
"@types/write-file-atomic": "^4.0.3",
|
|
116
|
-
"asn1js": "^3.0.10",
|
|
117
117
|
"eslint": "^10.12.0",
|
|
118
118
|
"eslint-plugin-svelte": "^3.23.1",
|
|
119
119
|
"execa": "^10.1.0",
|
|
120
|
-
"oclif": "^6.0.
|
|
120
|
+
"oclif": "^6.0.6",
|
|
121
121
|
"prettier": "^3.9.9",
|
|
122
122
|
"prettier-plugin-svelte": "^4.1.1",
|
|
123
123
|
"svelte-eslint-parser": "^1.9.0",
|
|
@@ -29,12 +29,12 @@
|
|
|
29
29
|
<table class="w-full table-auto my-0!">
|
|
30
30
|
<tbody>
|
|
31
31
|
<tr>
|
|
32
|
-
<td class="w-[30%] text-left align-middle font-bold">{t('fields.date')}</td>
|
|
33
|
-
<td class="w-[70%] text-right align-middle font-semibold">{date}</td>
|
|
32
|
+
<td class="w-[30%] text-left align-middle text-sm font-bold">{t('fields.date')}</td>
|
|
33
|
+
<td class="w-[70%] text-right align-middle text-sm font-semibold whitespace-nowrap">{date}</td>
|
|
34
34
|
</tr>
|
|
35
35
|
<tr>
|
|
36
|
-
<td class="w-[30%] text-left align-middle font-bold border-t! border-t-gray-100/30!">{t('fields.id')}</td>
|
|
37
|
-
<td class="w-[70%] text-right align-middle font-semibold border-t! border-t-gray-100/30!">{id}</td>
|
|
36
|
+
<td class="w-[30%] text-left align-middle text-sm font-bold border-t! border-t-gray-100/30!">{t('fields.id')}</td>
|
|
37
|
+
<td class="w-[70%] text-right align-middle text-sm font-semibold whitespace-nowrap border-t! border-t-gray-100/30!">{id}</td>
|
|
38
38
|
</tr>
|
|
39
39
|
</tbody>
|
|
40
40
|
</table>
|
|
@@ -1,44 +0,0 @@
|
|
|
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 };
|