@c9up/vellum 0.1.0
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/LICENSE +21 -0
- package/README.md +473 -0
- package/dist/Vellum.d.ts +460 -0
- package/dist/Vellum.d.ts.map +1 -0
- package/dist/Vellum.js +479 -0
- package/dist/Vellum.js.map +1 -0
- package/dist/VellumProvider.d.ts +28 -0
- package/dist/VellumProvider.d.ts.map +1 -0
- package/dist/VellumProvider.js +33 -0
- package/dist/VellumProvider.js.map +1 -0
- package/dist/augmentations.d.ts +22 -0
- package/dist/augmentations.d.ts.map +1 -0
- package/dist/augmentations.js +16 -0
- package/dist/augmentations.js.map +1 -0
- package/dist/config.d.ts +33 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +31 -0
- package/dist/config.js.map +1 -0
- package/dist/configure.d.ts +17 -0
- package/dist/configure.d.ts.map +1 -0
- package/dist/configure.js +78 -0
- package/dist/configure.js.map +1 -0
- package/dist/errors.d.ts +9 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +19 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +44 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +44 -0
- package/dist/index.js.map +1 -0
- package/dist/native/generated.d.ts +255 -0
- package/dist/native/generated.d.ts.map +1 -0
- package/dist/native/generated.js +7 -0
- package/dist/native/generated.js.map +1 -0
- package/dist/native.d.ts +50 -0
- package/dist/native.d.ts.map +1 -0
- package/dist/native.js +194 -0
- package/dist/native.js.map +1 -0
- package/dist/responder.d.ts +29 -0
- package/dist/responder.d.ts.map +1 -0
- package/dist/responder.js +101 -0
- package/dist/responder.js.map +1 -0
- package/dist/services/main.d.ts +15 -0
- package/dist/services/main.d.ts.map +1 -0
- package/dist/services/main.js +39 -0
- package/dist/services/main.js.map +1 -0
- package/dist/signers.d.ts +81 -0
- package/dist/signers.d.ts.map +1 -0
- package/dist/signers.js +98 -0
- package/dist/signers.js.map +1 -0
- package/index.darwin-arm64.node +0 -0
- package/index.darwin-x64.node +0 -0
- package/index.linux-arm64-gnu.node +0 -0
- package/index.linux-x64-gnu.node +0 -0
- package/index.win32-x64-msvc.node +0 -0
- package/package.json +67 -0
- package/scripts/build-napi-types.mjs +69 -0
- package/scripts/copy-napi.mjs +48 -0
- package/scripts/generate-metrics.py +150 -0
- package/scripts/generate-napi-types.mjs +156 -0
- package/src/Vellum.ts +829 -0
- package/src/VellumProvider.ts +51 -0
- package/src/augmentations.ts +25 -0
- package/src/config.ts +47 -0
- package/src/configure.ts +92 -0
- package/src/errors.ts +20 -0
- package/src/index.ts +74 -0
- package/src/native/generated.ts +375 -0
- package/src/native.ts +358 -0
- package/src/responder.ts +116 -0
- package/src/services/main.ts +48 -0
- package/src/signers.ts +149 -0
package/dist/native.js
ADDED
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Loader for the Rust PDF engine.
|
|
3
|
+
*
|
|
4
|
+
* The engine is not optional. Parsing, authoring and rendering PDF have no
|
|
5
|
+
* JavaScript implementation in this package, so a missing binary is a hard
|
|
6
|
+
* failure with an actionable message — never a silent degradation that lets
|
|
7
|
+
* one deployment behave differently from another.
|
|
8
|
+
*/
|
|
9
|
+
import { createRequire } from "node:module";
|
|
10
|
+
import { dirname, join } from "node:path";
|
|
11
|
+
import { arch, platform } from "node:process";
|
|
12
|
+
import { fileURLToPath } from "node:url";
|
|
13
|
+
import { VellumError } from "./errors.js";
|
|
14
|
+
const requireNative = createRequire(import.meta.url);
|
|
15
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
16
|
+
const platformMap = {
|
|
17
|
+
"linux-x64": "linux-x64-gnu",
|
|
18
|
+
"linux-arm64": "linux-arm64-gnu",
|
|
19
|
+
"darwin-x64": "darwin-x64",
|
|
20
|
+
"darwin-arm64": "darwin-arm64",
|
|
21
|
+
"win32-x64": "win32-x64-msvc",
|
|
22
|
+
};
|
|
23
|
+
let native;
|
|
24
|
+
let loadError;
|
|
25
|
+
try {
|
|
26
|
+
const suffix = platformMap[`${platform}-${arch}`];
|
|
27
|
+
if (suffix) {
|
|
28
|
+
native = requireNative(join(here, `../index.${suffix}.node`));
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
32
|
+
loadError = error;
|
|
33
|
+
}
|
|
34
|
+
export function isNativeAvailable() {
|
|
35
|
+
return native !== undefined;
|
|
36
|
+
}
|
|
37
|
+
/** Why the engine could not be loaded, phrased for whoever has to fix it. */
|
|
38
|
+
function unavailableReason() {
|
|
39
|
+
const target = `${platform}-${arch}`;
|
|
40
|
+
if (loadError !== undefined) {
|
|
41
|
+
return `failed to load (${loadError instanceof Error ? loadError.message : String(loadError)})`;
|
|
42
|
+
}
|
|
43
|
+
return platformMap[target] !== undefined
|
|
44
|
+
? "binary not found"
|
|
45
|
+
: `no prebuilt binary for ${target}`;
|
|
46
|
+
}
|
|
47
|
+
/** Raised when an operation needs the Rust engine and it is not there. */
|
|
48
|
+
export class VellumNativeRequiredError extends VellumError {
|
|
49
|
+
constructor() {
|
|
50
|
+
super("NAPI_REQUIRED", `The Rust PDF engine is required but not loaded — ${unavailableReason()}.\n` +
|
|
51
|
+
"Install the prebuilt binary for this platform, or build it with `pnpm build:napi`.");
|
|
52
|
+
this.name = "VellumNativeRequiredError";
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
/** The engine, or a refusal explaining how to get one. */
|
|
56
|
+
function engine() {
|
|
57
|
+
if (native === undefined)
|
|
58
|
+
throw new VellumNativeRequiredError();
|
|
59
|
+
return native;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Run engine work, translating its failures into coded errors.
|
|
63
|
+
*
|
|
64
|
+
* The engine reports failures as plain reasons. Without this, a caller
|
|
65
|
+
* catching a corrupt upload would get an `Error` it cannot branch on.
|
|
66
|
+
*/
|
|
67
|
+
function run(code, work) {
|
|
68
|
+
const loaded = engine();
|
|
69
|
+
try {
|
|
70
|
+
return work(loaded);
|
|
71
|
+
}
|
|
72
|
+
catch (error) {
|
|
73
|
+
throw new VellumError(code, error instanceof Error ? error.message : String(error), { cause: error });
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
export function inspectNative(pdf) {
|
|
77
|
+
return run("INVALID_PDF", (loaded) => loaded.inspect(pdf));
|
|
78
|
+
}
|
|
79
|
+
export function createBlankNative(pages) {
|
|
80
|
+
return run("WRITE_FAILED", (loaded) => loaded.createBlank([...pages]));
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Rasterise one page, addressed from zero.
|
|
84
|
+
*
|
|
85
|
+
* The engine hands this to the libuv thread pool, so the returned promise
|
|
86
|
+
* settles without the calling thread ever blocking.
|
|
87
|
+
*/
|
|
88
|
+
export async function renderPageNative(pdf, pageIndex, options) {
|
|
89
|
+
const loaded = engine();
|
|
90
|
+
try {
|
|
91
|
+
return await loaded.renderPage(pdf, pageIndex, options);
|
|
92
|
+
}
|
|
93
|
+
catch (error) {
|
|
94
|
+
throw new VellumError("RENDER_FAILED", error instanceof Error ? error.message : String(error), { cause: error });
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
/** Rasterise every page, in document order. */
|
|
98
|
+
export async function renderAllNative(pdf, options) {
|
|
99
|
+
const loaded = engine();
|
|
100
|
+
try {
|
|
101
|
+
return await loaded.renderAll(pdf, options);
|
|
102
|
+
}
|
|
103
|
+
catch (error) {
|
|
104
|
+
throw new VellumError("RENDER_FAILED", error instanceof Error ? error.message : String(error), { cause: error });
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
export function pageDimensionsNative(pdf) {
|
|
108
|
+
return run("INVALID_PDF", (loaded) => loaded.pageDimensions(pdf));
|
|
109
|
+
}
|
|
110
|
+
export function metadataNative(pdf) {
|
|
111
|
+
return run("INVALID_PDF", (loaded) => loaded.metadata(pdf));
|
|
112
|
+
}
|
|
113
|
+
export async function extractTextNative(pdf, pageIndex) {
|
|
114
|
+
const loaded = engine();
|
|
115
|
+
try {
|
|
116
|
+
return await loaded.extractText(pdf, pageIndex);
|
|
117
|
+
}
|
|
118
|
+
catch (error) {
|
|
119
|
+
throw new VellumError("EXTRACT_FAILED", error instanceof Error ? error.message : String(error), { cause: error });
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
export async function extractTextAllNative(pdf) {
|
|
123
|
+
const loaded = engine();
|
|
124
|
+
try {
|
|
125
|
+
return await loaded.extractTextAll(pdf);
|
|
126
|
+
}
|
|
127
|
+
catch (error) {
|
|
128
|
+
throw new VellumError("EXTRACT_FAILED", error instanceof Error ? error.message : String(error), { cause: error });
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
async function edit(code, work) {
|
|
132
|
+
const loaded = engine();
|
|
133
|
+
try {
|
|
134
|
+
return await work(loaded);
|
|
135
|
+
}
|
|
136
|
+
catch (error) {
|
|
137
|
+
throw new VellumError(code, error instanceof Error ? error.message : String(error), { cause: error });
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
export function mergeNative(documents) {
|
|
141
|
+
return edit("MERGE_FAILED", (loaded) => loaded.merge([...documents]));
|
|
142
|
+
}
|
|
143
|
+
export function selectPagesNative(pdf, pageIndexes) {
|
|
144
|
+
return edit("SELECT_FAILED", (loaded) => loaded.selectPages(pdf, [...pageIndexes]));
|
|
145
|
+
}
|
|
146
|
+
export function splitNative(pdf) {
|
|
147
|
+
return edit("SPLIT_FAILED", (loaded) => loaded.split(pdf));
|
|
148
|
+
}
|
|
149
|
+
export function rotateNative(pdf, degrees, pageIndexes) {
|
|
150
|
+
return edit("ROTATE_FAILED", (loaded) => loaded.rotate(pdf, degrees, pageIndexes ? [...pageIndexes] : undefined));
|
|
151
|
+
}
|
|
152
|
+
export function stampNative(pdf, image, options) {
|
|
153
|
+
return edit("STAMP_FAILED", (loaded) => loaded.stamp(pdf, image, options));
|
|
154
|
+
}
|
|
155
|
+
export function stampTextNative(pdf, text, options) {
|
|
156
|
+
return edit("STAMP_TEXT_FAILED", (loaded) => loaded.stampText(pdf, text, options));
|
|
157
|
+
}
|
|
158
|
+
export function formFieldsNative(pdf) {
|
|
159
|
+
return run("INVALID_PDF", (loaded) => loaded.formFields(pdf));
|
|
160
|
+
}
|
|
161
|
+
export function fillFormNative(pdf, values) {
|
|
162
|
+
return edit("FILL_FAILED", (loaded) => loaded.fillForm(pdf, values));
|
|
163
|
+
}
|
|
164
|
+
export function flattenFormNative(pdf) {
|
|
165
|
+
return edit("FLATTEN_FAILED", (loaded) => loaded.flattenForm(pdf));
|
|
166
|
+
}
|
|
167
|
+
export function prepareSignatureNative(pdf, options) {
|
|
168
|
+
return edit("SIGN_FAILED", (loaded) => loaded.prepareSignature(pdf, options));
|
|
169
|
+
}
|
|
170
|
+
export function embedSignatureNative(prepared, value) {
|
|
171
|
+
return edit("SIGN_FAILED", (loaded) => loaded.embedSignature(prepared, value));
|
|
172
|
+
}
|
|
173
|
+
export function signCmsNative(digest, key, certificates, signedAt) {
|
|
174
|
+
return edit("SIGN_FAILED", (loaded) => loaded.signCms(digest, key, [...certificates], signedAt));
|
|
175
|
+
}
|
|
176
|
+
export function timestampQueryNative(cms) {
|
|
177
|
+
return run("TIMESTAMP_FAILED", (loaded) => loaded.timestampQuery(cms));
|
|
178
|
+
}
|
|
179
|
+
export function attachTimestampNative(cms, response, nonce) {
|
|
180
|
+
return run("TIMESTAMP_FAILED", (loaded) => loaded.attachTimestamp(cms, response, nonce));
|
|
181
|
+
}
|
|
182
|
+
export function verifySignaturesNative(pdf, trust) {
|
|
183
|
+
return edit("VERIFY_FAILED", (loaded) => loaded.verifySignatures(pdf, trust));
|
|
184
|
+
}
|
|
185
|
+
export function responderUrlNative(certificate) {
|
|
186
|
+
return run("VERIFY_FAILED", (loaded) => loaded.responderUrl(certificate));
|
|
187
|
+
}
|
|
188
|
+
export function revocationQueryNative(certificate, issuer) {
|
|
189
|
+
return run("VERIFY_FAILED", (loaded) => loaded.revocationQuery(certificate, issuer));
|
|
190
|
+
}
|
|
191
|
+
export function readRevocationNative(response, certificate, issuer, at) {
|
|
192
|
+
return run("VERIFY_FAILED", (loaded) => loaded.readRevocation(response, certificate, issuer, at ?? undefined));
|
|
193
|
+
}
|
|
194
|
+
//# sourceMappingURL=native.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"native.js","sourceRoot":"","sources":["../src/native.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deciding whether a revocation responder may be contacted.
|
|
3
|
+
*
|
|
4
|
+
* The address comes out of the certificate inside the document being checked,
|
|
5
|
+
* which is to say: from whoever sent the document. Following it unconditionally
|
|
6
|
+
* turns "check whether this signature is still valid" into "make my server
|
|
7
|
+
* issue an HTTP request wherever this stranger points" — the cloud metadata
|
|
8
|
+
* endpoint, a Redis on loopback, or simply a URL that confirms the document was
|
|
9
|
+
* opened and reveals the address it was opened from.
|
|
10
|
+
*
|
|
11
|
+
* So the default is a public responder over HTTP, and an application whose
|
|
12
|
+
* authority answers on the internal network says so by name. Everything
|
|
13
|
+
* refused answers `"unknown"`, never `"good"`: a responder that was not asked
|
|
14
|
+
* has told us nothing.
|
|
15
|
+
*/
|
|
16
|
+
/** Who an application is willing to ask about revocation. */
|
|
17
|
+
export type ResponderPolicy = ReadonlyArray<string> | ((url: URL) => boolean | Promise<boolean>);
|
|
18
|
+
/** Why a responder was refused, in a sentence a caller can act on. */
|
|
19
|
+
export interface ResponderRefusal {
|
|
20
|
+
refused: string;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Decide whether `url` may be asked.
|
|
24
|
+
*
|
|
25
|
+
* @param policy What the application allows: a list of hostnames, or a
|
|
26
|
+
* predicate. Absent, only public hosts over http/https are contacted.
|
|
27
|
+
*/
|
|
28
|
+
export declare function mayAsk(url: string, policy?: ResponderPolicy): Promise<URL | ResponderRefusal>;
|
|
29
|
+
//# sourceMappingURL=responder.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"responder.d.ts","sourceRoot":"","sources":["../src/responder.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,6DAA6D;AAC7D,MAAM,MAAM,eAAe,GACxB,aAAa,CAAC,MAAM,CAAC,GACrB,CAAC,CAAC,GAAG,EAAE,GAAG,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC;AA2C9C,sEAAsE;AACtE,MAAM,WAAW,gBAAgB;IAChC,OAAO,EAAE,MAAM,CAAC;CAChB;AAED;;;;;GAKG;AACH,wBAAsB,MAAM,CAC3B,GAAG,EAAE,MAAM,EACX,MAAM,CAAC,EAAE,eAAe,GACtB,OAAO,CAAC,GAAG,GAAG,gBAAgB,CAAC,CAuCjC"}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deciding whether a revocation responder may be contacted.
|
|
3
|
+
*
|
|
4
|
+
* The address comes out of the certificate inside the document being checked,
|
|
5
|
+
* which is to say: from whoever sent the document. Following it unconditionally
|
|
6
|
+
* turns "check whether this signature is still valid" into "make my server
|
|
7
|
+
* issue an HTTP request wherever this stranger points" — the cloud metadata
|
|
8
|
+
* endpoint, a Redis on loopback, or simply a URL that confirms the document was
|
|
9
|
+
* opened and reveals the address it was opened from.
|
|
10
|
+
*
|
|
11
|
+
* So the default is a public responder over HTTP, and an application whose
|
|
12
|
+
* authority answers on the internal network says so by name. Everything
|
|
13
|
+
* refused answers `"unknown"`, never `"good"`: a responder that was not asked
|
|
14
|
+
* has told us nothing.
|
|
15
|
+
*/
|
|
16
|
+
/** Hosts that are never a public certificate authority. */
|
|
17
|
+
function isPrivateHost(hostname) {
|
|
18
|
+
const host = hostname.replace(/^\[|\]$/g, "").toLowerCase();
|
|
19
|
+
if (host === "localhost" || host.endsWith(".localhost"))
|
|
20
|
+
return true;
|
|
21
|
+
// IPv6 loopback, unspecified, unique-local (fc00::/7) and link-local (fe80::/10).
|
|
22
|
+
if (host === "::1" || host === "::")
|
|
23
|
+
return true;
|
|
24
|
+
if (/^f[cd][0-9a-f]{2}:/.test(host))
|
|
25
|
+
return true;
|
|
26
|
+
if (/^fe[89ab][0-9a-f]:/.test(host))
|
|
27
|
+
return true;
|
|
28
|
+
// An IPv4-mapped IPv6 address is the same machine under another spelling,
|
|
29
|
+
// and the URL parser rewrites `::ffff:127.0.0.1` as `::ffff:7f00:1` — so a
|
|
30
|
+
// check that only reads dotted quads waves loopback straight through.
|
|
31
|
+
const mapped = /^::ffff:(\d+\.\d+\.\d+\.\d+)$/.exec(host) ??
|
|
32
|
+
(() => {
|
|
33
|
+
const hex = /^::ffff:([0-9a-f]{1,4}):([0-9a-f]{1,4})$/.exec(host);
|
|
34
|
+
if (hex === null)
|
|
35
|
+
return null;
|
|
36
|
+
const packed = (Number.parseInt(hex[1], 16) << 16) |
|
|
37
|
+
Number.parseInt(hex[2], 16);
|
|
38
|
+
return [
|
|
39
|
+
host,
|
|
40
|
+
[24, 16, 8, 0].map((shift) => (packed >>> shift) & 0xff).join("."),
|
|
41
|
+
];
|
|
42
|
+
})();
|
|
43
|
+
const literal = mapped?.[1] ?? host;
|
|
44
|
+
const parts = literal.split(".");
|
|
45
|
+
if (parts.length !== 4 || !parts.every((p) => /^\d{1,3}$/.test(p)))
|
|
46
|
+
return false;
|
|
47
|
+
const [a, b] = parts.map(Number);
|
|
48
|
+
return (a === 0 || // this network
|
|
49
|
+
a === 127 || // loopback
|
|
50
|
+
a === 10 || // private
|
|
51
|
+
(a === 172 && b >= 16 && b <= 31) || // private
|
|
52
|
+
(a === 192 && b === 168) || // private
|
|
53
|
+
(a === 169 && b === 254) || // link-local, and the cloud metadata address
|
|
54
|
+
(a === 100 && b >= 64 && b <= 127) || // carrier-grade NAT
|
|
55
|
+
a >= 224 // multicast and reserved
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Decide whether `url` may be asked.
|
|
60
|
+
*
|
|
61
|
+
* @param policy What the application allows: a list of hostnames, or a
|
|
62
|
+
* predicate. Absent, only public hosts over http/https are contacted.
|
|
63
|
+
*/
|
|
64
|
+
export async function mayAsk(url, policy) {
|
|
65
|
+
let parsed;
|
|
66
|
+
try {
|
|
67
|
+
parsed = new URL(url);
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
return { refused: `the certificate names "${url}", which is not a URL` };
|
|
71
|
+
}
|
|
72
|
+
if (typeof policy === "function") {
|
|
73
|
+
return (await policy(parsed))
|
|
74
|
+
? parsed
|
|
75
|
+
: { refused: `the configured policy refused ${parsed.origin}` };
|
|
76
|
+
}
|
|
77
|
+
if (policy !== undefined) {
|
|
78
|
+
return policy.includes(parsed.hostname)
|
|
79
|
+
? parsed
|
|
80
|
+
: {
|
|
81
|
+
refused: `${parsed.hostname} is not among the responders this application allows` +
|
|
82
|
+
` (${policy.join(", ") || "none"})`,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
// OCSP is defined over HTTP. Anything else is a scheme somebody wanted the
|
|
86
|
+
// server to speak, not a way to ask about a certificate.
|
|
87
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
|
88
|
+
return {
|
|
89
|
+
refused: `the certificate names a ${parsed.protocol.replace(":", "")} responder, which is not asked`,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
if (isPrivateHost(parsed.hostname)) {
|
|
93
|
+
return {
|
|
94
|
+
refused: `the certificate names ${parsed.hostname}, an address on this network — ` +
|
|
95
|
+
"a document does not get to choose which of your own services is contacted. " +
|
|
96
|
+
"Name it in `allowedResponders` if that really is your authority.",
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
return parsed;
|
|
100
|
+
}
|
|
101
|
+
//# sourceMappingURL=responder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"responder.js","sourceRoot":"","sources":["../src/responder.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Container service accessor — `import vellum from '@c9up/vellum/services/main'`.
|
|
3
|
+
*
|
|
4
|
+
* Populated by `VellumProvider.boot()`. Reading it before the provider has
|
|
5
|
+
* booted throws rather than answering with an unconfigured service, which
|
|
6
|
+
* would render with defaults nobody chose.
|
|
7
|
+
*/
|
|
8
|
+
import type { Vellum } from "../Vellum.js";
|
|
9
|
+
/** @internal Called by the provider once the service exists. */
|
|
10
|
+
export declare function setVellum(service: Vellum): void;
|
|
11
|
+
/** @internal Test helper — forget the service between cases. */
|
|
12
|
+
export declare function clearVellum(): void;
|
|
13
|
+
declare const vellum: Vellum;
|
|
14
|
+
export default vellum;
|
|
15
|
+
//# sourceMappingURL=main.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../src/services/main.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAI3C,gEAAgE;AAChE,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAE/C;AAED,gEAAgE;AAChE,wBAAgB,WAAW,IAAI,IAAI,CAElC;AAYD,QAAA,MAAM,MAAM,EAAE,MAaZ,CAAC;AAEH,eAAe,MAAM,CAAC"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Container service accessor — `import vellum from '@c9up/vellum/services/main'`.
|
|
3
|
+
*
|
|
4
|
+
* Populated by `VellumProvider.boot()`. Reading it before the provider has
|
|
5
|
+
* booted throws rather than answering with an unconfigured service, which
|
|
6
|
+
* would render with defaults nobody chose.
|
|
7
|
+
*/
|
|
8
|
+
let instance;
|
|
9
|
+
/** @internal Called by the provider once the service exists. */
|
|
10
|
+
export function setVellum(service) {
|
|
11
|
+
instance = service;
|
|
12
|
+
}
|
|
13
|
+
/** @internal Test helper — forget the service between cases. */
|
|
14
|
+
export function clearVellum() {
|
|
15
|
+
instance = undefined;
|
|
16
|
+
}
|
|
17
|
+
function resolve() {
|
|
18
|
+
if (!instance) {
|
|
19
|
+
throw new Error("[vellum] Vellum service accessed before VellumProvider.boot() ran. " +
|
|
20
|
+
"Check that `@c9up/vellum/provider` is listed in your reamrc.ts providers.");
|
|
21
|
+
}
|
|
22
|
+
return instance;
|
|
23
|
+
}
|
|
24
|
+
const vellum = new Proxy(Object.create(null), {
|
|
25
|
+
get(_target, property) {
|
|
26
|
+
// A module loader probes an imported binding for `then` to see whether
|
|
27
|
+
// it is thenable, and for well-known symbols. Answering those by
|
|
28
|
+
// resolving the service would throw during the import itself, before
|
|
29
|
+
// any provider had a chance to boot.
|
|
30
|
+
if (property === "then" || typeof property === "symbol") {
|
|
31
|
+
return undefined;
|
|
32
|
+
}
|
|
33
|
+
const service = resolve();
|
|
34
|
+
const value = Reflect.get(service, property, service);
|
|
35
|
+
return typeof value === "function" ? value.bind(service) : value;
|
|
36
|
+
},
|
|
37
|
+
});
|
|
38
|
+
export default vellum;
|
|
39
|
+
//# sourceMappingURL=main.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"main.js","sourceRoot":"","sources":["../../src/services/main.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,IAAI,QAA4B,CAAC;AAEjC,gEAAgE;AAChE,MAAM,UAAU,SAAS,CAAC,OAAe;IACxC,QAAQ,GAAG,OAAO,CAAC;AACpB,CAAC;AAED,gEAAgE;AAChE,MAAM,UAAU,WAAW;IAC1B,QAAQ,GAAG,SAAS,CAAC;AACtB,CAAC;AAED,SAAS,OAAO;IACf,IAAI,CAAC,QAAQ,EAAE,CAAC;QACf,MAAM,IAAI,KAAK,CACd,qEAAqE;YACpE,2EAA2E,CAC5E,CAAC;IACH,CAAC;IACD,OAAO,QAAQ,CAAC;AACjB,CAAC;AAED,MAAM,MAAM,GAAW,IAAI,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE;IACrD,GAAG,CAAC,OAAO,EAAE,QAAQ;QACpB,uEAAuE;QACvE,iEAAiE;QACjE,qEAAqE;QACrE,qCAAqC;QACrC,IAAI,QAAQ,KAAK,MAAM,IAAI,OAAO,QAAQ,KAAK,QAAQ,EAAE,CAAC;YACzD,OAAO,SAAS,CAAC;QAClB,CAAC;QACD,MAAM,OAAO,GAAG,OAAO,EAAE,CAAC;QAC1B,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,OAAO,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;QACtD,OAAO,OAAO,KAAK,KAAK,UAAU,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;IAClE,CAAC;CACD,CAAC,CAAC;AAEH,eAAe,MAAM,CAAC"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Signers that need nothing but a key you hold.
|
|
3
|
+
*
|
|
4
|
+
* A signer bound to a certified provider is not here and should not be: it
|
|
5
|
+
* belongs to whoever has the account, as an adapter of its own. This package
|
|
6
|
+
* ships the contract and the one implementation that has no vendor behind it.
|
|
7
|
+
*/
|
|
8
|
+
import type { Signer } from "./Vellum.js";
|
|
9
|
+
/** A key you hold, and the certificate that vouches for it. */
|
|
10
|
+
export interface Pkcs8SignerOptions {
|
|
11
|
+
/** The private key, PKCS#8 DER. */
|
|
12
|
+
key: Buffer;
|
|
13
|
+
/** The signer's certificate, DER. */
|
|
14
|
+
certificate: Buffer;
|
|
15
|
+
/**
|
|
16
|
+
* The rest of the chain, DER, nearest issuer first.
|
|
17
|
+
*
|
|
18
|
+
* Worth supplying: a verifier that has to go and find the issuers itself
|
|
19
|
+
* will often decide it cannot.
|
|
20
|
+
*/
|
|
21
|
+
chain?: ReadonlyArray<Buffer>;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Sign with a key held by the application.
|
|
25
|
+
*
|
|
26
|
+
* ```ts
|
|
27
|
+
* // config/vellum.ts
|
|
28
|
+
* signers: {
|
|
29
|
+
* internal: pkcs8Signer({
|
|
30
|
+
* key: readFileSync(app.makePath('storage/signing.key.der')),
|
|
31
|
+
* certificate: readFileSync(app.makePath('storage/signing.crt.der')),
|
|
32
|
+
* }),
|
|
33
|
+
* }
|
|
34
|
+
* ```
|
|
35
|
+
*
|
|
36
|
+
* This is an *advanced* signature: it proves the document has not changed
|
|
37
|
+
* since a particular key signed it. Whether that is enough is a question about
|
|
38
|
+
* the document, not about the code — where the law requires a qualified
|
|
39
|
+
* signature, the key has to live with a certified provider, and that is an
|
|
40
|
+
* adapter rather than this.
|
|
41
|
+
*
|
|
42
|
+
* PKCS#8 and DER rather than a `.p12` bundle: reading PKCS#12 in Rust is not
|
|
43
|
+
* something to put underneath a signature, and
|
|
44
|
+
* `openssl pkcs12 -in bundle.p12 -nodes` gets you here in one command.
|
|
45
|
+
*/
|
|
46
|
+
export declare function pkcs8Signer(options: Pkcs8SignerOptions): Signer;
|
|
47
|
+
/** Where to ask for a timestamp, and how. */
|
|
48
|
+
export interface TimestampOptions {
|
|
49
|
+
/** The authority's RFC 3161 endpoint. */
|
|
50
|
+
url: string;
|
|
51
|
+
/** Anything the authority needs, such as an Authorization header. */
|
|
52
|
+
headers?: Record<string, string>;
|
|
53
|
+
/** How long to wait before giving up. Default 10 seconds. */
|
|
54
|
+
timeoutMs?: number;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Add a trusted timestamp to whatever `inner` signs.
|
|
58
|
+
*
|
|
59
|
+
* ```ts
|
|
60
|
+
* signers: {
|
|
61
|
+
* internal: timestamped(pkcs8Signer({ key, certificate }), {
|
|
62
|
+
* url: 'https://freetsa.org/tsr',
|
|
63
|
+
* }),
|
|
64
|
+
* }
|
|
65
|
+
* ```
|
|
66
|
+
*
|
|
67
|
+
* A signature proves a document has not changed since a key signed it, not
|
|
68
|
+
* *when*. Once the signing certificate expires, a verifier cannot tell a
|
|
69
|
+
* signature made while it was valid from one forged afterwards, and stops
|
|
70
|
+
* accepting it. For a document kept for years — which is most of the documents
|
|
71
|
+
* anyone bothers to sign — a timestamp is what keeps it verifiable.
|
|
72
|
+
*
|
|
73
|
+
* It wraps any signer, so it works over a provider's as well as the local one.
|
|
74
|
+
* The token goes on as an unsigned attribute, which is what lets it be added
|
|
75
|
+
* without disturbing the signature.
|
|
76
|
+
*
|
|
77
|
+
* The signature grows by a few kilobytes, so a document prepared with a tight
|
|
78
|
+
* `capacity` may need a larger one.
|
|
79
|
+
*/
|
|
80
|
+
export declare function timestamped(inner: Signer, options: TimestampOptions): Signer;
|
|
81
|
+
//# sourceMappingURL=signers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"signers.d.ts","sourceRoot":"","sources":["../src/signers.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAQH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAE1C,+DAA+D;AAC/D,MAAM,WAAW,kBAAkB;IAClC,mCAAmC;IACnC,GAAG,EAAE,MAAM,CAAC;IACZ,qCAAqC;IACrC,WAAW,EAAE,MAAM,CAAC;IACpB;;;;;OAKG;IACH,KAAK,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;CAC9B;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,kBAAkB,GAAG,MAAM,CAmB/D;AAED,6CAA6C;AAC7C,MAAM,WAAW,gBAAgB;IAChC,yCAAyC;IACzC,GAAG,EAAE,MAAM,CAAC;IACZ,qEAAqE;IACrE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,6DAA6D;IAC7D,SAAS,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,gBAAgB,GAAG,MAAM,CAuC5E"}
|
package/dist/signers.js
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Signers that need nothing but a key you hold.
|
|
3
|
+
*
|
|
4
|
+
* A signer bound to a certified provider is not here and should not be: it
|
|
5
|
+
* belongs to whoever has the account, as an adapter of its own. This package
|
|
6
|
+
* ships the contract and the one implementation that has no vendor behind it.
|
|
7
|
+
*/
|
|
8
|
+
import { VellumError } from "./errors.js";
|
|
9
|
+
import { attachTimestampNative, signCmsNative, timestampQueryNative, } from "./native.js";
|
|
10
|
+
/**
|
|
11
|
+
* Sign with a key held by the application.
|
|
12
|
+
*
|
|
13
|
+
* ```ts
|
|
14
|
+
* // config/vellum.ts
|
|
15
|
+
* signers: {
|
|
16
|
+
* internal: pkcs8Signer({
|
|
17
|
+
* key: readFileSync(app.makePath('storage/signing.key.der')),
|
|
18
|
+
* certificate: readFileSync(app.makePath('storage/signing.crt.der')),
|
|
19
|
+
* }),
|
|
20
|
+
* }
|
|
21
|
+
* ```
|
|
22
|
+
*
|
|
23
|
+
* This is an *advanced* signature: it proves the document has not changed
|
|
24
|
+
* since a particular key signed it. Whether that is enough is a question about
|
|
25
|
+
* the document, not about the code — where the law requires a qualified
|
|
26
|
+
* signature, the key has to live with a certified provider, and that is an
|
|
27
|
+
* adapter rather than this.
|
|
28
|
+
*
|
|
29
|
+
* PKCS#8 and DER rather than a `.p12` bundle: reading PKCS#12 in Rust is not
|
|
30
|
+
* something to put underneath a signature, and
|
|
31
|
+
* `openssl pkcs12 -in bundle.p12 -nodes` gets you here in one command.
|
|
32
|
+
*/
|
|
33
|
+
export function pkcs8Signer(options) {
|
|
34
|
+
if (options.key.length === 0) {
|
|
35
|
+
throw new VellumError("SIGNER_INVALID", "The signing key is empty.");
|
|
36
|
+
}
|
|
37
|
+
if (options.certificate.length === 0) {
|
|
38
|
+
throw new VellumError("SIGNER_INVALID", "The certificate is empty.");
|
|
39
|
+
}
|
|
40
|
+
const certificates = [options.certificate, ...(options.chain ?? [])];
|
|
41
|
+
return {
|
|
42
|
+
async sign(digest) {
|
|
43
|
+
return signCmsNative(digest, options.key, certificates, new Date().toISOString());
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Add a trusted timestamp to whatever `inner` signs.
|
|
49
|
+
*
|
|
50
|
+
* ```ts
|
|
51
|
+
* signers: {
|
|
52
|
+
* internal: timestamped(pkcs8Signer({ key, certificate }), {
|
|
53
|
+
* url: 'https://freetsa.org/tsr',
|
|
54
|
+
* }),
|
|
55
|
+
* }
|
|
56
|
+
* ```
|
|
57
|
+
*
|
|
58
|
+
* A signature proves a document has not changed since a key signed it, not
|
|
59
|
+
* *when*. Once the signing certificate expires, a verifier cannot tell a
|
|
60
|
+
* signature made while it was valid from one forged afterwards, and stops
|
|
61
|
+
* accepting it. For a document kept for years — which is most of the documents
|
|
62
|
+
* anyone bothers to sign — a timestamp is what keeps it verifiable.
|
|
63
|
+
*
|
|
64
|
+
* It wraps any signer, so it works over a provider's as well as the local one.
|
|
65
|
+
* The token goes on as an unsigned attribute, which is what lets it be added
|
|
66
|
+
* without disturbing the signature.
|
|
67
|
+
*
|
|
68
|
+
* The signature grows by a few kilobytes, so a document prepared with a tight
|
|
69
|
+
* `capacity` may need a larger one.
|
|
70
|
+
*/
|
|
71
|
+
export function timestamped(inner, options) {
|
|
72
|
+
return {
|
|
73
|
+
async sign(digest) {
|
|
74
|
+
const cms = await inner.sign(digest);
|
|
75
|
+
const { query, nonce } = timestampQueryNative(cms);
|
|
76
|
+
let answer;
|
|
77
|
+
try {
|
|
78
|
+
answer = await fetch(options.url, {
|
|
79
|
+
method: "POST",
|
|
80
|
+
headers: {
|
|
81
|
+
"content-type": "application/timestamp-query",
|
|
82
|
+
...options.headers,
|
|
83
|
+
},
|
|
84
|
+
body: new Uint8Array(query),
|
|
85
|
+
signal: AbortSignal.timeout(options.timeoutMs ?? 10_000),
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
catch (error) {
|
|
89
|
+
throw new VellumError("TIMESTAMP_UNREACHABLE", `The timestamp authority at ${options.url} could not be reached.`, { cause: error });
|
|
90
|
+
}
|
|
91
|
+
if (!answer.ok) {
|
|
92
|
+
throw new VellumError("TIMESTAMP_REFUSED", `The timestamp authority at ${options.url} answered ${answer.status}.`);
|
|
93
|
+
}
|
|
94
|
+
return attachTimestampNative(cms, Buffer.from(await answer.arrayBuffer()), nonce);
|
|
95
|
+
},
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
//# sourceMappingURL=signers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"signers.js","sourceRoot":"","sources":["../src/signers.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EACN,qBAAqB,EACrB,aAAa,EACb,oBAAoB,GACpB,MAAM,aAAa,CAAC;AAkBrB;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,UAAU,WAAW,CAAC,OAA2B;IACtD,IAAI,OAAO,CAAC,GAAG,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC9B,MAAM,IAAI,WAAW,CAAC,gBAAgB,EAAE,2BAA2B,CAAC,CAAC;IACtE,CAAC;IACD,IAAI,OAAO,CAAC,WAAW,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACtC,MAAM,IAAI,WAAW,CAAC,gBAAgB,EAAE,2BAA2B,CAAC,CAAC;IACtE,CAAC;IAED,MAAM,YAAY,GAAG,CAAC,OAAO,CAAC,WAAW,EAAE,GAAG,CAAC,OAAO,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,CAAC;IACrE,OAAO;QACN,KAAK,CAAC,IAAI,CAAC,MAAc;YACxB,OAAO,aAAa,CACnB,MAAM,EACN,OAAO,CAAC,GAAG,EACX,YAAY,EACZ,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CACxB,CAAC;QACH,CAAC;KACD,CAAC;AACH,CAAC;AAYD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,UAAU,WAAW,CAAC,KAAa,EAAE,OAAyB;IACnE,OAAO;QACN,KAAK,CAAC,IAAI,CAAC,MAAc;YACxB,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YACrC,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,oBAAoB,CAAC,GAAG,CAAC,CAAC;YAEnD,IAAI,MAAgB,CAAC;YACrB,IAAI,CAAC;gBACJ,MAAM,GAAG,MAAM,KAAK,CAAC,OAAO,CAAC,GAAG,EAAE;oBACjC,MAAM,EAAE,MAAM;oBACd,OAAO,EAAE;wBACR,cAAc,EAAE,6BAA6B;wBAC7C,GAAG,OAAO,CAAC,OAAO;qBAClB;oBACD,IAAI,EAAE,IAAI,UAAU,CAAC,KAAK,CAAC;oBAC3B,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,OAAO,CAAC,SAAS,IAAI,MAAM,CAAC;iBACxD,CAAC,CAAC;YACJ,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBAChB,MAAM,IAAI,WAAW,CACpB,uBAAuB,EACvB,8BAA8B,OAAO,CAAC,GAAG,wBAAwB,EACjE,EAAE,KAAK,EAAE,KAAK,EAAE,CAChB,CAAC;YACH,CAAC;YAED,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC;gBAChB,MAAM,IAAI,WAAW,CACpB,mBAAmB,EACnB,8BAA8B,OAAO,CAAC,GAAG,aAAa,MAAM,CAAC,MAAM,GAAG,CACtE,CAAC;YACH,CAAC;YAED,OAAO,qBAAqB,CAC3B,GAAG,EACH,MAAM,CAAC,IAAI,CAAC,MAAM,MAAM,CAAC,WAAW,EAAE,CAAC,EACvC,KAAK,CACL,CAAC;QACH,CAAC;KACD,CAAC;AACH,CAAC"}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|