@broadpaper/license 0.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/LICENSE ADDED
@@ -0,0 +1,143 @@
1
+ BroadPaper — Software Licence
2
+
3
+ Copyright (c) 2026 Codingbadger Creative Limited, trading as BroadPaper.
4
+ Registered in England and Wales, company number 16896863.
5
+ hello@broadpaper.com
6
+
7
+ This is proprietary software, not open source. It is free to evaluate, with
8
+ every feature enabled and no trial clock, and requires a paid Licence for
9
+ production use. The full agreement is the Terms and Conditions at
10
+ https://www.broadpaper.com/legal/terms/ ; this notice is the part of it you need
11
+ in the package, and where the two differ, those Terms prevail.
12
+
13
+ Version of 9 September 2026.
14
+
15
+
16
+ 1. DEFINITIONS
17
+
18
+ "Software" means the BroadPaper packages published by us — @broadpaper/core,
19
+ @broadpaper/blocks, @broadpaper/renderer, @broadpaper/forme, @broadpaper/pdf,
20
+ @broadpaper/server, @broadpaper/license, @broadpaper/editor, @broadpaper/react,
21
+ @broadpaper/angular and BroadPaper.Client — together with their documentation
22
+ and updates.
23
+
24
+ "Licence" means a signed licence certificate we have issued to your
25
+ Organisation, and the rights granted with it. A Licence belongs to the
26
+ Organisation, not to an individual.
27
+
28
+ "Product" means an application your Organisation builds that incorporates the
29
+ Software.
30
+
31
+ "Internal User" means someone who works for your Organisation: an employee, an
32
+ officer, or a contractor engaged by you and working under your direction.
33
+ "External User" means anyone else, including your customers, their staff, your
34
+ partners and the public.
35
+
36
+ "Licence Year" means the twelve months from the date a paid Licence is issued
37
+ or renewed.
38
+
39
+
40
+ 2. EVALUATION
41
+
42
+ You may install and use the Software free of charge, worldwide and
43
+ non-exclusively, for development, testing and evaluation. Every feature is
44
+ enabled, nothing expires, and there is no limit on how long you take.
45
+
46
+ Documents generated without a paid Licence carry a visible BroadPaper
47
+ evaluation mark. This grant does not permit production use, whether or not you
48
+ charge for it and whether or not the mark is present in the output. Removing,
49
+ suppressing or circumventing the mark does not make unlicensed production use
50
+ permitted.
51
+
52
+
53
+ 3. PAID LICENCES
54
+
55
+ Subject to payment, we grant your Organisation a non-exclusive,
56
+ non-transferable, worldwide licence to install and use the Software in
57
+ Products, in any number of Products and for an unlimited number of users,
58
+ developers, documents and renders. Two types are offered, unlocking identical
59
+ software and differing only in who the resulting Product may be made available
60
+ to:
61
+
62
+ Internal — Products for your Organisation's Internal Users only.
63
+ SaaS / OEM — the above, and Products made available to External Users:
64
+ software you sell, host, embed, ship or otherwise make
65
+ available outside your Organisation.
66
+
67
+ Because the Software cannot and does not detect how it is deployed, which type
68
+ you need is determined by the use you actually make of it, not by what the
69
+ software permits.
70
+
71
+ A Licence runs for the Licence Year. When it ends you may continue to use any
72
+ version of the Software released during a Licence Year you paid for,
73
+ indefinitely and under this notice, but you will receive no further updates or
74
+ support unless you renew, and the Software stops rendering without the
75
+ evaluation mark once the certificate's grace period has passed.
76
+
77
+
78
+ 4. REDISTRIBUTION
79
+
80
+ You may redistribute the Software only as a compiled, integrated part of a
81
+ Product. You may not distribute it on its own, offer it as a component for
82
+ others to build with, or make its functionality available as a stand-alone
83
+ report-design service. The end users of your Product do not need their own
84
+ Licence.
85
+
86
+
87
+ 5. LICENCE CERTIFICATES
88
+
89
+ A Licence is a cryptographically signed certificate recording your
90
+ Organisation, its Licence type and its expiry date. The Software verifies it
91
+ locally, against a public key compiled into the package, and makes no network
92
+ call to do so.
93
+
94
+ It is not a secret, and we do not ask you to treat it as one: it may appear in
95
+ your application's configuration, its build and its distributed code. You may
96
+ not share it with anyone outside your Organisation for their own use, alter it,
97
+ or interfere with the Software's licence checking or its evaluation mark.
98
+
99
+
100
+ 6. RESTRICTIONS
101
+
102
+ Except as expressly permitted above, you may not: copy, modify, translate or
103
+ create derivative works of the Software beyond what integrating it into your
104
+ Product requires; reverse engineer it beyond what applicable law allows; remove
105
+ any proprietary notice; use it to build a competing report designer; or use it
106
+ in breach of any law, including to generate documents intended to deceive.
107
+
108
+ We own the Software and everything in it that is not third-party material.
109
+ Nothing here transfers ownership. Your templates, your data, your themes and
110
+ the documents your Product produces are yours; we claim no rights in them and,
111
+ because the Software makes no calls to us, we never receive them.
112
+
113
+
114
+ 7. THIRD-PARTY COMPONENTS
115
+
116
+ The Software incorporates third-party components under their own licences.
117
+ These are listed in the THIRD-PARTY-NOTICES.md file distributed with each
118
+ package — including the fonts and Rust crates statically linked into the Forme
119
+ engine, which no dependency manifest reveals. Those licences govern those
120
+ components.
121
+
122
+
123
+ 8. NO WARRANTY, AND LIABILITY
124
+
125
+ Except for the limited ninety-day warranty in the Terms, the Software is
126
+ provided "AS IS", without warranty of any kind, express or implied, including
127
+ merchantability, fitness for a particular purpose and non-infringement. We do
128
+ not warrant that the Software is error-free, or that a document it produces
129
+ meets any regulatory or legal requirement that applies to you: that remains
130
+ your responsibility.
131
+
132
+ To the extent the law allows, we are not liable for any loss of profit,
133
+ revenue, business, data or goodwill, or for any indirect or consequential
134
+ loss. Our total liability in any twelve-month period is limited to the amount
135
+ you paid us in that period, or $100 if you paid nothing. Nothing here limits
136
+ liability for death or personal injury caused by negligence, for fraud, or for
137
+ anything else that cannot be limited by law.
138
+
139
+ This notice is governed by the law of England and Wales.
140
+
141
+
142
+ Questions, and licence purchases: hello@broadpaper.com
143
+ Pricing and the full Terms: https://www.broadpaper.com/legal/terms/
@@ -0,0 +1,255 @@
1
+ # Third-party notices
2
+
3
+ BroadPaper is built on the work below. This file is generated by
4
+ `scripts/third-party-notices.mjs` from what is actually installed — including
5
+ the contents of the WebAssembly engine, which carries its own dependencies and
6
+ its own fonts inside a single binary. Do not edit it by hand.
7
+
8
+ It ships inside every published package and beside the demo, because two of the
9
+ licences here require their notice to travel with the software.
10
+
11
+ ## The PDF engine
12
+
13
+ `@formepdf/core` and `@formepdf/shared` — the browserless layout and PDF engine,
14
+ Rust compiled to WebAssembly. Used unmodified, as an npm dependency.
15
+
16
+ MIT License
17
+
18
+ Copyright (c) 2026 Daniel Molitor
19
+
20
+ Permission is hereby granted, free of charge, to any person obtaining a copy
21
+ of this software and associated documentation files (the "Software"), to deal
22
+ in the Software without restriction, including without limitation the rights
23
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
24
+ copies of the Software, and to permit persons to whom the Software is
25
+ furnished to do so, subject to the following conditions:
26
+
27
+ The above copyright notice and this permission notice shall be included in all
28
+ copies or substantial portions of the Software.
29
+
30
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
31
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
32
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
33
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
34
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
35
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
36
+ SOFTWARE.
37
+
38
+ ## Fonts inside the engine
39
+
40
+ The engine binary carries complete font files, and subsets of them are embedded
41
+ into any PDF containing a character the standard PDF faces cannot represent — a
42
+ Greek or Cyrillic name, a symbol. Documents produced this way are unrestricted:
43
+ the licence below exempts anything created *with* the font. The notice is
44
+ required because the font software itself travels inside the engine.
45
+
46
+ ### Noto Sans Regular
47
+
48
+ Copyright 2022 The Noto Project Authors (https://github.com/notofonts/latin-greek-cyrillic)
49
+
50
+ Licensed under the SIL Open Font License, Version 1.1.
51
+
52
+ Its own declaration, verbatim from the font's name table:
53
+
54
+ Copyright 2022 The Noto Project Authors (https://github.com/notofonts/latin-
55
+ greek-cyrillic)Noto SansRegular2.015;GOOG;NotoSans-RegularNoto Sans RegularV
56
+ ersion 2.015; ttfautohint (v1.8.4.7-5d5b)NotoSans-RegularNoto is a trademark
57
+ of Google LLC.Monotype Imaging Inc.Monotype Design TeamDesigned by Monotype
58
+ design team, Irene Vlachou.http://www.google.com/get/noto/http://www.monoty
59
+ pe.com/studioThis Font Software is licensed under the SIL Open Font License,
60
+ Version 1.1. This license is available with a FAQ at: https://scripts.sil.o
61
+ rg/OFLhttps://scripts.sil.org/OFLflorin symbolTitling Alternates I and J for
62
+ titling and all cap settingsAccented Greek SCiota adscrip
63
+ t
64
+
65
+ ### Noto Sans Bold
66
+
67
+ Copyright 2022 The Noto Project Authors (https://github.com/notofonts/latin-greek-cyrillic)
68
+
69
+ Licensed under the SIL Open Font License, Version 1.1.
70
+
71
+ Its own declaration, verbatim from the font's name table:
72
+
73
+ Copyright 2022 The Noto Project Authors (https://github.com/notofonts/latin-
74
+ greek-cyrillic)Noto SansBold2.015;GOOG;NotoSans-BoldNoto Sans BoldVersion 2.
75
+ 015; ttfautohint (v1.8.4.7-5d5b)NotoSans-BoldNoto is a trademark of Google L
76
+ LC.Monotype Imaging Inc.Monotype Design TeamDesigned by Monotype design team
77
+ , Irene Vlachou.http://www.google.com/get/noto/http://www.monotype.com/studi
78
+ oThis Font Software is licensed under the SIL Open Font License, Version 1.1
79
+ . This license is available with a FAQ at: https://scripts.sil.org/OFLhttps:
80
+ //scripts.sil.org/OFLflorin symbolTitling Alternates I and J for titling and
81
+ all cap settingsAccented Greek SCiota adscrip
82
+ t
83
+
84
+ -----------------------------------------------------------
85
+ SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
86
+ -----------------------------------------------------------
87
+
88
+ PREAMBLE
89
+ The goals of the Open Font License (OFL) are to stimulate worldwide
90
+ development of collaborative font projects, to support the font creation
91
+ efforts of academic and linguistic communities, and to provide a free and
92
+ open framework in which fonts may be shared and improved in partnership
93
+ with others.
94
+
95
+ The OFL allows the licensed fonts to be used, studied, modified and
96
+ redistributed freely as long as they are not sold by themselves. The
97
+ fonts, including any derivative works, can be bundled, embedded,
98
+ redistributed and/or sold with any software provided that any reserved
99
+ names are not used by derivative works. The fonts and derivatives,
100
+ however, cannot be released under any other type of license. The
101
+ requirement for fonts to remain under this license does not apply
102
+ to any document created using the fonts or their derivatives.
103
+
104
+ DEFINITIONS
105
+ "Font Software" refers to the set of files released by the Copyright
106
+ Holder(s) under this license and clearly marked as such. This may
107
+ include source files, build scripts and documentation.
108
+
109
+ "Reserved Font Name" refers to any names specified as such after the
110
+ copyright statement(s).
111
+
112
+ "Original Version" refers to the collection of Font Software components as
113
+ distributed by the Copyright Holder(s).
114
+
115
+ "Modified Version" refers to any derivative made by adding to, deleting,
116
+ or substituting -- in part or in whole -- any of the components of the
117
+ Original Version, by changing formats or by porting the Font Software to a
118
+ new environment.
119
+
120
+ "Author" refers to any designer, engineer, programmer, technical
121
+ writer or other person who contributed to the Font Software.
122
+
123
+ PERMISSION & CONDITIONS
124
+ Permission is hereby granted, free of charge, to any person obtaining
125
+ a copy of the Font Software, to use, study, copy, merge, embed, modify,
126
+ redistribute, and sell modified and unmodified copies of the Font
127
+ Software, subject to the following conditions:
128
+
129
+ 1) Neither the Font Software nor any of its individual components,
130
+ in Original or Modified Versions, may be sold by itself.
131
+
132
+ 2) Original or Modified Versions of the Font Software may be bundled,
133
+ redistributed and/or sold with any software, provided that each copy
134
+ contains the above copyright notice and this license. These can be
135
+ included either as stand-alone text files, human-readable headers or
136
+ in the appropriate machine-readable metadata fields within text or
137
+ binary files as long as those fields can be easily viewed by the user.
138
+
139
+ 3) No Modified Version of the Font Software may use the Reserved Font
140
+ Name(s) unless explicit written permission is granted by the corresponding
141
+ Copyright Holder. This restriction only applies to the primary font name as
142
+ presented to the users.
143
+
144
+ 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
145
+ Software shall not be used to promote, endorse or advertise any
146
+ Modified Version, except to acknowledge the contribution(s) of the
147
+ Copyright Holder(s) and the Author(s) or with their explicit written
148
+ permission.
149
+
150
+ 5) The Font Software, modified or unmodified, in part or in whole,
151
+ must be distributed entirely under this license, and must not be
152
+ distributed under any other license. The requirement for fonts to
153
+ remain under this license does not apply to any document created
154
+ using the Font Software.
155
+
156
+ TERMINATION
157
+ This license becomes null and void if any of the above conditions are
158
+ not met.
159
+
160
+ DISCLAIMER
161
+ THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
162
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
163
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
164
+ OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
165
+ COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
166
+ INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
167
+ DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
168
+ FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
169
+ OTHER DEALINGS IN THE FONT SOFTWARE.
170
+
171
+ ## Rust crates inside the engine
172
+
173
+ These 38 crates are statically linked into the engine binary and are
174
+ therefore redistributed with it. The list is read out of the compiled artefact
175
+ rather than from a manifest, so it reflects what is genuinely in there.
176
+
177
+ - aho-corasick-1.1.4
178
+ - barcoders-2.0.0
179
+ - base64-0.22.1
180
+ - base64ct-1.8.3
181
+ - console_error_panic_hook-0.1.7
182
+ - const-oid-0.9.6
183
+ - der-0.7.10
184
+ - fdeflate-0.3.7
185
+ - hypher-0.1.6
186
+ - image-0.24.9
187
+ - itoa-1.0.17
188
+ - jpeg-decoder-0.3.2
189
+ - memchr-2.8.0
190
+ - miniz_oxide-0.7.4
191
+ - num-bigint-dig-0.8.6
192
+ - num-integer-0.1.46
193
+ - pem-rfc7468-0.7.0
194
+ - png-0.17.16
195
+ - qrcode-0.14.1
196
+ - quick-xml-0.37.5
197
+ - regex-1.12.3
198
+ - regex-automata-0.4.14
199
+ - regex-syntax-0.8.10
200
+ - rsa-0.9.10
201
+ - rustybuzz-0.20.1
202
+ - serde-1.0.228
203
+ - serde-wasm-bindgen-0.6.5
204
+ - serde_core-1.0.228
205
+ - serde_json-1.0.149
206
+ - signature-2.2.0
207
+ - smallvec-1.15.1
208
+ - ttf-parser-0.25.1
209
+ - unicode-bidi-0.3.18
210
+ - unicode-bidi-mirroring-0.4.0
211
+ - unicode-linebreak-0.1.5
212
+ - unicode-properties-0.1.4
213
+ - unicode-script-0.5.8
214
+ - wasm-bindgen-0.2.108
215
+
216
+ ## npm dependencies
217
+
218
+ Runtime dependencies of the published packages. A consumer installs these from
219
+ npm themselves, under the licences their own packages carry.
220
+
221
+ | Package | Version | Licence |
222
+ | --- | --- | --- |
223
+ | `@floating-ui/react` | 0.27.20 | MIT |
224
+ | `@formepdf/core` | 0.20.0 | MIT |
225
+ | `@formepdf/shared` | 0.20.0 | MIT |
226
+ | `@noble/ed25519` | 3.2.0 | MIT |
227
+ | `d3-scale` | 4.0.2 | ISC |
228
+ | `d3-shape` | 3.2.0 | ISC |
229
+ | `immer` | 10.2.0 | MIT |
230
+ | `lucide-react` | 0.469.0 | ISC |
231
+ | `pdf-lib` | 1.17.1 | MIT |
232
+ | `prosemirror-commands` | 1.7.2 | MIT |
233
+ | `prosemirror-history` | 1.5.0 | MIT |
234
+ | `prosemirror-keymap` | 1.2.3 | MIT |
235
+ | `prosemirror-model` | 1.25.11 | MIT |
236
+ | `prosemirror-schema-list` | 1.5.1 | MIT |
237
+ | `prosemirror-state` | 1.4.4 | MIT |
238
+ | `prosemirror-view` | 1.42.3 | MIT |
239
+ | `react` | 19.2.8 | MIT |
240
+ | `react-dom` | 19.2.8 | MIT |
241
+ | `tslib` | 2.8.1 | 0BSD |
242
+ | `zod` | 3.25.76 | MIT |
243
+
244
+ ### A note on the Chromium backend
245
+
246
+ `@broadpaper/pdf` drives headless Chromium through Playwright, which is an
247
+ optional peer dependency: it is absent from the table above because the host
248
+ installs it deliberately, not because the backend stopped needing it.
249
+ `playwright-core` is Apache-2.0, but it *downloads a browser build separately
250
+ at install time*. That browser is covered by nothing here: it carries its own
251
+ licences, several of them, and they are not ours to restate. Anyone shipping an
252
+ image built on that backend needs to carry Chromium's notices too. The
253
+ browserless engine named at the top of this file has no such tail, which is one
254
+ of the reasons it is the recommended backend.
255
+
@@ -0,0 +1,372 @@
1
+ /**
2
+ * The licence certificate and the states it can be in.
3
+ *
4
+ * A BroadPaper licence is a signed certificate, not a secret. It says who
5
+ * bought what and until when, BroadPaper signs it, and the SDK checks that
6
+ * signature with a public key compiled into the package. Nothing here talks to
7
+ * a network, reads a machine identifier or writes a file: a licence that is
8
+ * valid on a laptop with no route to the internet is valid, full stop.
9
+ */
10
+ /**
11
+ * What the licence permits. Not a feature gate — both entitlements unlock
12
+ * exactly the same software. The difference is who the resulting application
13
+ * may be put in front of, which is a contractual matter and is presented as
14
+ * one in the account area and the licence agreement.
15
+ */
16
+ type Entitlement = "internal" | "saas";
17
+ /** The product a licence is for. Present so one signing key can serve more later. */
18
+ declare const PRODUCT = "broadpaper-sdk";
19
+ /**
20
+ * The signed payload.
21
+ *
22
+ * `customerId` is the durable identifier of the buying organisation — a Clerk
23
+ * organisation id — rather than an email address, because people change
24
+ * addresses and organisations outlive their founders.
25
+ *
26
+ * Times are epoch **seconds**, not milliseconds: the payload is base64'd into
27
+ * a string a developer will paste into a config file, and three characters per
28
+ * timestamp is three characters.
29
+ */
30
+ interface LicensePayload {
31
+ /** Payload schema version. Bumped only when the meaning of a field changes. */
32
+ version: 1;
33
+ /** Which signing key signed this. Lets keys be rotated without reissuing everything. */
34
+ keyId: string;
35
+ /**
36
+ * BroadPaper's own identifier for *this certificate*, `lic_…`.
37
+ *
38
+ * One per issuance, so a renewal or an upgrade carries a new one and the
39
+ * document it produced can be dated from it. The customer is identified by
40
+ * `customerId`, which does not change.
41
+ */
42
+ licenseId: string;
43
+ /** The licensed organisation's durable id. */
44
+ customerId: string;
45
+ /** The organisation's name, for display in warnings and PDF metadata. */
46
+ customerName: string;
47
+ product: typeof PRODUCT;
48
+ entitlement: Entitlement;
49
+ /** Epoch seconds. */
50
+ issuedAt: number;
51
+ /** Epoch seconds. */
52
+ expiresAt: number;
53
+ }
54
+ /**
55
+ * Why a licence is not being honoured, or how it is being honoured.
56
+ *
57
+ * These are deliberately coarse. A developer needs to know whether their PDFs
58
+ * will carry the evaluation mark and, if so, what to do about it; a taxonomy of
59
+ * eleven parse failures helps nobody.
60
+ */
61
+ type LicenseState =
62
+ /** No licence was configured. The SDK works; PDFs carry the evaluation mark. */
63
+ "unlicensed"
64
+ /** A licence was configured but could not be read, or its signature is wrong. */
65
+ | "invalid"
66
+ /** Signed by a key this build of the SDK does not trust. */
67
+ | "untrusted"
68
+ /** For a different product, or a payload version this SDK is older than. */
69
+ | "unsupported"
70
+ /** Valid, signed, and inside its term. */
71
+ | "active"
72
+ /** Past `expiresAt` but inside the grace period: honoured, and warned about. */
73
+ | "grace"
74
+ /** Past `expiresAt` and past the grace period. */
75
+ | "expired";
76
+ /** The outcome of checking a licence. Everything the SDK needs to decide. */
77
+ interface LicenseStatus {
78
+ state: LicenseState;
79
+ /** True when PDFs render clean. `active` and `grace` only. */
80
+ licensed: boolean;
81
+ /** The payload, when one could be read and verified. */
82
+ payload: LicensePayload | null;
83
+ /** What the licence permits, when it is being honoured. */
84
+ entitlement: Entitlement | null;
85
+ /** Expiry as a Date, when known. */
86
+ expiresAt: Date | null;
87
+ /** Days until expiry; negative once expired. Null when there is no licence. */
88
+ daysRemaining: number | null;
89
+ /**
90
+ * A developer-facing sentence. Always present for a state that is not
91
+ * `active`, so callers can log it without composing their own prose.
92
+ */
93
+ message: string | null;
94
+ }
95
+ /** A public key this build of the SDK will accept signatures from. */
96
+ interface TrustedKey {
97
+ /** Matches `keyId` in the payload. */
98
+ keyId: string;
99
+ /** The raw 32-byte Ed25519 public key, base64url or base64. */
100
+ publicKey: string;
101
+ /** Free text for the key's provenance. Never displayed to end users. */
102
+ note?: string;
103
+ }
104
+
105
+ /**
106
+ * How long an expired licence keeps working.
107
+ *
108
+ * A renewal that is a week late is an accounts payable problem, not a reason to
109
+ * stamp a customer's month-end reports. Thirty days is long enough to cover a
110
+ * card expiring over a holiday and short enough that it is not a second licence
111
+ * term.
112
+ */
113
+ declare const DEFAULT_GRACE_PERIOD_DAYS = 30;
114
+ interface VerifyOptions {
115
+ /** Fixed clock. Tests and deterministic renders pass one. */
116
+ now?: Date;
117
+ /** Extra keys to trust, on top of the ones built into this SDK. */
118
+ trustedKeys?: readonly TrustedKey[];
119
+ /** Overrides {@link DEFAULT_GRACE_PERIOD_DAYS}. */
120
+ gracePeriodDays?: number;
121
+ }
122
+ /** The status of a caller that supplied no licence at all. */
123
+ declare function unlicensed(): LicenseStatus;
124
+ /**
125
+ * Checks a licence string and reports what it entitles the caller to.
126
+ *
127
+ * Never throws for a bad licence: a malformed string, a forged signature and an
128
+ * expired term are all ordinary answers, and a render is not the place to
129
+ * discover that a config file has a typo in it.
130
+ */
131
+ declare function verifyLicense(license: string | null | undefined, options?: VerifyOptions): Promise<LicenseStatus>;
132
+
133
+ /**
134
+ * Configuring BroadPaper once, for the life of the process.
135
+ *
136
+ * import { BroadPaper } from "@broadpaper/forme";
137
+ * BroadPaper.configure({ license: process.env.BROADPAPER_LICENSE });
138
+ *
139
+ * The licence is not a credential, so this is not a secrets API: it is the same
140
+ * shape of thing as setting a locale. A browser bundle that carries the licence
141
+ * string in plain text is fine — the string is a signed certificate, and
142
+ * possessing it grants nothing beyond what its payload already says.
143
+ *
144
+ * Configuration is deliberately process-global rather than an argument to every
145
+ * render. Threading a licence through `renderPdf`, the render service, the
146
+ * batch endpoint, the .NET client and every future export path is exactly the
147
+ * kind of plumbing that ends with one path quietly forgetting it, and a
148
+ * customer who has paid discovering a watermark in production.
149
+ *
150
+ * The store hangs off a `Symbol.for` key rather than a module-level variable so
151
+ * that two copies of this package in one dependency tree — the usual result of
152
+ * a mixed-version install — still agree about the licence.
153
+ */
154
+
155
+ interface BroadPaperConfig {
156
+ /**
157
+ * The licence string, `BP1.…`. Read from `BROADPAPER_LICENSE` when this is
158
+ * not set and the runtime has environment variables.
159
+ */
160
+ license?: string | null;
161
+ /**
162
+ * Public keys to trust in addition to the ones built into the SDK. This is
163
+ * how a development licence is honoured on a developer's machine without a
164
+ * development key ever being trusted by a production build.
165
+ */
166
+ trustedKeys?: readonly TrustedKey[];
167
+ /** Overrides the 30-day post-expiry grace period. Mostly for tests. */
168
+ gracePeriodDays?: number;
169
+ /** Where licence warnings go. Defaults to `console.warn`. */
170
+ logger?: ((message: string) => void) | null;
171
+ }
172
+ /**
173
+ * Sets the licence and any other SDK-wide options. Call it once, at startup.
174
+ *
175
+ * Successive calls merge, so a host can set the licence in one place and the
176
+ * grace period in another; passing `license: null` clears it.
177
+ */
178
+ declare function configure(config: BroadPaperConfig): void;
179
+ /** What {@link configure} has been given, plus whatever the environment supplies. */
180
+ declare function getConfig(): BroadPaperConfig;
181
+ /** Forgets the configuration entirely. Exists for tests. */
182
+ declare function resetConfig(): void;
183
+ /**
184
+ * `BroadPaper.configure({ … })`, for hosts that prefer a namespace to a bare
185
+ * function. Both are the same store.
186
+ */
187
+ declare const BroadPaper: {
188
+ configure: typeof configure;
189
+ getConfig: typeof getConfig;
190
+ };
191
+ /** Logs a message once, whatever it takes to reach the developer. */
192
+ declare function warn(message: string): void;
193
+ interface ResolveOptions extends VerifyOptions {
194
+ /** A licence for this call only, overriding the configured one. */
195
+ license?: string | null;
196
+ }
197
+ /**
198
+ * The licence status for the current configuration.
199
+ *
200
+ * Memoised on the licence string, because every PDF asks and a signature check
201
+ * per page of a five-hundred-page batch is waste. `configure` clears it, and a
202
+ * per-call `license` bypasses it.
203
+ *
204
+ * Whatever the answer, any developer-facing message is logged once. That is the
205
+ * only thing the SDK does that a caller did not ask for, and it is the
206
+ * difference between a developer seeing "your licence expired" and a developer
207
+ * seeing an unexplained watermark.
208
+ */
209
+ declare function resolveLicenseStatus(options?: ResolveOptions): Promise<LicenseStatus>;
210
+
211
+ /** The only string format this SDK can read. */
212
+ declare const FORMAT_PREFIX = "BP1";
213
+ /** A licence string split into its parts, before any signature check. */
214
+ interface ParsedLicense {
215
+ prefix: string;
216
+ /** The exact text that was signed. */
217
+ signedText: string;
218
+ encodedPayload: string;
219
+ signature: Uint8Array;
220
+ payload: LicensePayload;
221
+ }
222
+ declare class LicenseFormatError extends Error {
223
+ /** `unsupported` when the licence is well-formed but newer than this SDK. */
224
+ readonly kind: "invalid" | "unsupported";
225
+ constructor(message: string,
226
+ /** `unsupported` when the licence is well-formed but newer than this SDK. */
227
+ kind?: "invalid" | "unsupported");
228
+ }
229
+ /** Whitespace is stripped: licences get pasted, wrapped and indented. */
230
+ declare function normalizeLicense(license: string): string;
231
+ /**
232
+ * Reads a licence string without verifying it.
233
+ *
234
+ * Nothing here decides whether a licence is *honoured* — that needs the
235
+ * signature — but a structurally broken string can be rejected before any
236
+ * cryptography happens, and the account UI can display a licence's contents
237
+ * without holding a public key.
238
+ */
239
+ declare function parseLicense(license: string): ParsedLicense;
240
+ /**
241
+ * Encodes a payload into the text that gets signed.
242
+ *
243
+ * Exported because BroadPaper's own licence issuer must produce byte-identical
244
+ * input to what the SDK verifies, and the only way to guarantee that is for
245
+ * both to call this function.
246
+ */
247
+ declare function encodeSigningInput(payload: LicensePayload): {
248
+ signedText: string;
249
+ encodedPayload: string;
250
+ };
251
+ /** Assembles the final licence string from a payload and its signature. */
252
+ declare function formatLicense(payload: LicensePayload, signature: Uint8Array): string;
253
+
254
+ /**
255
+ * The evaluation mark.
256
+ *
257
+ * This is the only thing an unlicensed BroadPaper does differently. Every
258
+ * block, both PDF backends, the designer, data binding, pagination and the
259
+ * render service behave identically with and without a licence; what changes is
260
+ * that the file says, on every page, that it was produced with an evaluation
261
+ * copy.
262
+ *
263
+ * It is drawn to be unmistakable in a document review and harmless to a
264
+ * developer evaluating the product: a light diagonal line across the page,
265
+ * behind nothing and over everything, at an opacity that leaves the report
266
+ * entirely readable. Making the output illegible would only push people towards
267
+ * evaluating something else.
268
+ *
269
+ * Each backend draws the spec with its own primitives — the Forme engine has a
270
+ * page-native watermark node, Chromium gets a printed overlay — because a
271
+ * single drawing implementation would mean one of them post-processing a file
272
+ * the other had already finished, and a stamping pass that can be skipped is a
273
+ * stamping pass that eventually is.
274
+ */
275
+
276
+ interface WatermarkSpec {
277
+ text: string;
278
+ /** Points. The engines size the mark from this, not from the page. */
279
+ fontSize: number;
280
+ /** Degrees, anticlockwise from horizontal. */
281
+ angle: number;
282
+ /** Hex, without alpha. */
283
+ color: string;
284
+ /** 0–1. */
285
+ opacity: number;
286
+ }
287
+ /**
288
+ * The mark itself.
289
+ *
290
+ * No dashes or typographic punctuation: the standard PDF faces carry no em
291
+ * dash, and a theme that names a font the engine does not have falls back to
292
+ * them, which turned "BroadPaper — Evaluation Copy" into "BroadPaper
293
+ * Evaluation Copy" in half the documents and a question mark in the rest.
294
+ */
295
+ declare const EVALUATION_WATERMARK: WatermarkSpec;
296
+ /**
297
+ * The mark a render should carry, or null when the licence says none.
298
+ *
299
+ * Backends call this rather than reading the licence status themselves, so
300
+ * "licensed" and "no watermark" cannot drift apart.
301
+ */
302
+ declare function resolveWatermark(options?: ResolveOptions): Promise<WatermarkSpec | null>;
303
+ /** The pure half of {@link resolveWatermark}, for callers that already have a status. */
304
+ declare function watermarkFor(status: LicenseStatus): WatermarkSpec | null;
305
+ /**
306
+ * CSS that paints the mark on every `.bp-page` of a rendered document.
307
+ *
308
+ * Used by the Chromium backend and by the designer's print path — the two
309
+ * places a PDF is produced by a browser rather than by the engine. `position:
310
+ * fixed` would put it on the viewport rather than on each page, so it is an
311
+ * absolutely positioned pseudo-element on the page box, which prints once per
312
+ * sheet.
313
+ *
314
+ * `print-color-adjust` matters: without it Chromium drops the mark from the
315
+ * printed file exactly as it drops any other background colour.
316
+ */
317
+ declare function watermarkCss(spec?: WatermarkSpec): string;
318
+ /**
319
+ * The PDF Producer string for a render.
320
+ *
321
+ * Licensed output records the licence id. It is not a secret — it identifies a
322
+ * customer's licence, not their account — and having it in the file means a
323
+ * document that turns up somewhere it should not can be traced back to the
324
+ * licence that produced it without any of the tracking a phone-home would need.
325
+ */
326
+ declare function producerString(status: LicenseStatus): string;
327
+
328
+ /**
329
+ * The public keys this build of the SDK will accept licence signatures from.
330
+ *
331
+ * **This file contains public keys only.** BroadPaper's private signing key
332
+ * lives in the licence issuer's secret store and is never in this repository,
333
+ * never in the npm package and never in a browser bundle. Publishing the
334
+ * public half is the point: it is what lets a licence be checked with no
335
+ * network call.
336
+ *
337
+ * Add a production key with:
338
+ *
339
+ * node scripts/licence-keys.mjs --production --key-id bp-1
340
+ *
341
+ * which prints the private key once, for the operator to paste into the
342
+ * issuer's configuration, and writes the public half into this array.
343
+ *
344
+ * The list is an allowlist, so it is also the rotation mechanism: issue with a
345
+ * new `keyId`, ship an SDK release trusting both, and drop the old entry once
346
+ * every licence signed with it has expired. Licences carry their `keyId` inside
347
+ * the signed payload, so an attacker cannot point one at a different key.
348
+ *
349
+ * It starts empty on purpose. A key here is trusted by every copy of the SDK in
350
+ * the world, including production installs, which is exactly why a development
351
+ * key must never be added to it — development keys are supplied at runtime
352
+ * instead, through `configure({ trustedKeys })` or `BROADPAPER_TRUSTED_KEYS`,
353
+ * so a licence signed in development cannot be honoured by a production build.
354
+ */
355
+
356
+ declare const TRUSTED_KEYS: readonly TrustedKey[];
357
+
358
+ /**
359
+ * base64url and UTF-8, without depending on Node or on the DOM.
360
+ *
361
+ * The SDK runs in Node, in edge runtimes, in a worker and in a browser, and the
362
+ * licence string has to decode identically in all four. `atob`/`btoa` exist
363
+ * everywhere modern but are byte-oriented and mangle anything above U+00FF, so
364
+ * text goes through `TextEncoder`/`TextDecoder` first and only bytes are
365
+ * base64'd. The tables below are the whole implementation, which is smaller
366
+ * than the runtime detection needed to use `Buffer` where it happens to exist.
367
+ */
368
+ declare function bytesToBase64Url(bytes: Uint8Array): string;
369
+ /** Throws on anything that is not base64/base64url. Padding is optional. */
370
+ declare function base64UrlToBytes(text: string): Uint8Array;
371
+
372
+ export { BroadPaper, type BroadPaperConfig, DEFAULT_GRACE_PERIOD_DAYS, EVALUATION_WATERMARK, type Entitlement, FORMAT_PREFIX, LicenseFormatError, type LicensePayload, type LicenseState, type LicenseStatus, PRODUCT, type ParsedLicense, type ResolveOptions, TRUSTED_KEYS, type TrustedKey, type VerifyOptions, type WatermarkSpec, base64UrlToBytes, bytesToBase64Url, configure, encodeSigningInput, formatLicense, getConfig, normalizeLicense, parseLicense, producerString, resetConfig, resolveLicenseStatus, resolveWatermark, unlicensed, verifyLicense, warn, watermarkCss, watermarkFor };
package/dist/index.js ADDED
@@ -0,0 +1,412 @@
1
+ // src/verify.ts
2
+ import { verifyAsync } from "@noble/ed25519";
3
+
4
+ // src/encoding.ts
5
+ var ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
6
+ var LOOKUP = /* @__PURE__ */ (() => {
7
+ const t = new Int8Array(128).fill(-1);
8
+ for (let i = 0; i < ALPHABET.length; i++) t[ALPHABET.charCodeAt(i)] = i;
9
+ t["+".charCodeAt(0)] = 62;
10
+ t["/".charCodeAt(0)] = 63;
11
+ return t;
12
+ })();
13
+ function bytesToBase64Url(bytes) {
14
+ let out = "";
15
+ let i = 0;
16
+ for (; i + 2 < bytes.length; i += 3) {
17
+ const n = bytes[i] << 16 | bytes[i + 1] << 8 | bytes[i + 2];
18
+ out += ALPHABET[n >>> 18 & 63] + ALPHABET[n >>> 12 & 63] + ALPHABET[n >>> 6 & 63] + ALPHABET[n & 63];
19
+ }
20
+ const rest = bytes.length - i;
21
+ if (rest === 1) {
22
+ const n = bytes[i] << 16;
23
+ out += ALPHABET[n >>> 18 & 63] + ALPHABET[n >>> 12 & 63];
24
+ } else if (rest === 2) {
25
+ const n = bytes[i] << 16 | bytes[i + 1] << 8;
26
+ out += ALPHABET[n >>> 18 & 63] + ALPHABET[n >>> 12 & 63] + ALPHABET[n >>> 6 & 63];
27
+ }
28
+ return out;
29
+ }
30
+ function base64UrlToBytes(text) {
31
+ let end = text.length;
32
+ while (end > 0 && text[end - 1] === "=") end--;
33
+ const chars = [];
34
+ for (let i2 = 0; i2 < end; i2++) {
35
+ const code = text.charCodeAt(i2);
36
+ const value = code < 128 ? LOOKUP[code] : -1;
37
+ if (value < 0) throw new Error(`not base64: ${JSON.stringify(text[i2])} at ${i2}`);
38
+ chars.push(value);
39
+ }
40
+ if (chars.length % 4 === 1) throw new Error("not base64: truncated");
41
+ const out = new Uint8Array(Math.floor(chars.length * 3 / 4));
42
+ let o = 0;
43
+ let i = 0;
44
+ for (; i + 3 < chars.length; i += 4) {
45
+ const n = chars[i] << 18 | chars[i + 1] << 12 | chars[i + 2] << 6 | chars[i + 3];
46
+ out[o++] = n >>> 16 & 255;
47
+ out[o++] = n >>> 8 & 255;
48
+ out[o++] = n & 255;
49
+ }
50
+ const rest = chars.length - i;
51
+ if (rest === 2) {
52
+ out[o++] = (chars[i] << 18 | chars[i + 1] << 12) >>> 16;
53
+ } else if (rest === 3) {
54
+ const n = chars[i] << 18 | chars[i + 1] << 12 | chars[i + 2] << 6;
55
+ out[o++] = n >>> 16 & 255;
56
+ out[o++] = n >>> 8 & 255;
57
+ }
58
+ return out;
59
+ }
60
+ var encoder = /* @__PURE__ */ new TextEncoder();
61
+ var decoder = /* @__PURE__ */ new TextDecoder("utf-8", { fatal: true });
62
+ var utf8ToBytes = (text) => encoder.encode(text);
63
+ var bytesToUtf8 = (bytes) => decoder.decode(bytes);
64
+
65
+ // src/format.ts
66
+ var FORMAT_PREFIX = "BP1";
67
+ var LicenseFormatError = class extends Error {
68
+ constructor(message, kind = "invalid") {
69
+ super(message);
70
+ this.kind = kind;
71
+ this.name = "LicenseFormatError";
72
+ }
73
+ kind;
74
+ };
75
+ var ENTITLEMENTS = ["internal", "saas"];
76
+ function normalizeLicense(license) {
77
+ return license.replace(/\s+/g, "");
78
+ }
79
+ function parseLicense(license) {
80
+ const text = normalizeLicense(license);
81
+ if (!text) throw new LicenseFormatError("the licence string is empty");
82
+ const parts = text.split(".");
83
+ if (parts.length !== 3) throw new LicenseFormatError(`expected three dot-separated parts, found ${parts.length}`);
84
+ const [prefix, encodedPayload, encodedSignature] = parts;
85
+ if (prefix !== FORMAT_PREFIX) {
86
+ const known = /^BP\d+$/.test(prefix);
87
+ throw new LicenseFormatError(
88
+ known ? `licence format ${prefix} is newer than this version of BroadPaper, which reads ${FORMAT_PREFIX}` : `unrecognised licence prefix ${JSON.stringify(prefix)}`,
89
+ known ? "unsupported" : "invalid"
90
+ );
91
+ }
92
+ if (!encodedPayload || !encodedSignature) throw new LicenseFormatError("the licence string has an empty part");
93
+ let json;
94
+ let signature;
95
+ try {
96
+ json = bytesToUtf8(base64UrlToBytes(encodedPayload));
97
+ signature = base64UrlToBytes(encodedSignature);
98
+ } catch (e) {
99
+ throw new LicenseFormatError(`the licence string is not valid base64 (${e.message})`);
100
+ }
101
+ if (signature.length !== 64) throw new LicenseFormatError(`an Ed25519 signature is 64 bytes, this one is ${signature.length}`);
102
+ let raw;
103
+ try {
104
+ raw = JSON.parse(json);
105
+ } catch {
106
+ throw new LicenseFormatError("the licence payload is not valid JSON");
107
+ }
108
+ return {
109
+ prefix,
110
+ signedText: `${prefix}.${encodedPayload}`,
111
+ encodedPayload,
112
+ signature,
113
+ payload: readPayload(raw)
114
+ };
115
+ }
116
+ function readPayload(raw) {
117
+ if (typeof raw !== "object" || raw === null || Array.isArray(raw)) throw new LicenseFormatError("the licence payload is not an object");
118
+ const o = raw;
119
+ const version = o.version;
120
+ if (version !== 1) {
121
+ const numeric = typeof version === "number" && Number.isInteger(version) && version > 1;
122
+ throw new LicenseFormatError(
123
+ numeric ? `licence payload version ${version} is newer than this version of BroadPaper, which reads version 1` : "the licence payload has no usable version",
124
+ numeric ? "unsupported" : "invalid"
125
+ );
126
+ }
127
+ const text = (key) => {
128
+ const value = o[key];
129
+ if (typeof value !== "string" || value === "") throw new LicenseFormatError(`the licence payload is missing ${key}`);
130
+ return value;
131
+ };
132
+ const seconds = (key) => {
133
+ const value = o[key];
134
+ if (typeof value !== "number" || !Number.isFinite(value)) throw new LicenseFormatError(`the licence payload is missing ${key}`);
135
+ return value;
136
+ };
137
+ const entitlement = text("entitlement");
138
+ if (!ENTITLEMENTS.includes(entitlement)) {
139
+ throw new LicenseFormatError(`this version of BroadPaper does not know the entitlement ${JSON.stringify(entitlement)}`, "unsupported");
140
+ }
141
+ return {
142
+ ...o,
143
+ version: 1,
144
+ keyId: text("keyId"),
145
+ licenseId: text("licenseId"),
146
+ customerId: text("customerId"),
147
+ customerName: text("customerName"),
148
+ product: text("product"),
149
+ entitlement,
150
+ issuedAt: seconds("issuedAt"),
151
+ expiresAt: seconds("expiresAt")
152
+ };
153
+ }
154
+ function encodeSigningInput(payload) {
155
+ const encodedPayload = bytesToBase64Url(utf8ToBytes(JSON.stringify(payload)));
156
+ return { encodedPayload, signedText: `${FORMAT_PREFIX}.${encodedPayload}` };
157
+ }
158
+ function formatLicense(payload, signature) {
159
+ if (signature.length !== 64) throw new LicenseFormatError(`an Ed25519 signature is 64 bytes, this one is ${signature.length}`);
160
+ const { encodedPayload } = encodeSigningInput(payload);
161
+ return `${FORMAT_PREFIX}.${encodedPayload}.${bytesToBase64Url(signature)}`;
162
+ }
163
+
164
+ // src/trusted-keys.ts
165
+ var TRUSTED_KEYS = [
166
+ { keyId: "bp-2026", publicKey: "woKmWyIazae7SpGhi4dwA7zNK1lWdboq88Rl5xscTs4", note: "production, generated 2026-09-08" }
167
+ ];
168
+
169
+ // src/types.ts
170
+ var PRODUCT = "broadpaper-sdk";
171
+
172
+ // src/verify.ts
173
+ var DEFAULT_GRACE_PERIOD_DAYS = 30;
174
+ var DAY_MS = 864e5;
175
+ function unlicensed() {
176
+ return {
177
+ state: "unlicensed",
178
+ licensed: false,
179
+ payload: null,
180
+ entitlement: null,
181
+ expiresAt: null,
182
+ daysRemaining: null,
183
+ message: "No BroadPaper licence is configured, so PDFs carry the evaluation watermark. Everything else works; see https://www.broadpaper.com/docs/guide/licensing."
184
+ };
185
+ }
186
+ function failure(state, message, payload = null) {
187
+ return {
188
+ state,
189
+ licensed: false,
190
+ payload,
191
+ entitlement: null,
192
+ expiresAt: payload ? new Date(payload.expiresAt * 1e3) : null,
193
+ daysRemaining: null,
194
+ message
195
+ };
196
+ }
197
+ async function verifyLicense(license, options = {}) {
198
+ if (!license || !license.trim()) return unlicensed();
199
+ let parsed;
200
+ try {
201
+ parsed = parseLicense(license);
202
+ } catch (e) {
203
+ if (e instanceof LicenseFormatError) {
204
+ return failure(
205
+ e.kind,
206
+ e.kind === "unsupported" ? `This BroadPaper licence needs a newer version of the SDK (${e.message}). Update @broadpaper/* or the PDF will carry the evaluation watermark.` : `The BroadPaper licence could not be read (${e.message}), so PDFs carry the evaluation watermark.`
207
+ );
208
+ }
209
+ throw e;
210
+ }
211
+ const { payload } = parsed;
212
+ if (payload.product !== PRODUCT) {
213
+ return failure(
214
+ "invalid",
215
+ `This licence is for ${JSON.stringify(payload.product)}, not the BroadPaper SDK, so PDFs carry the evaluation watermark.`,
216
+ payload
217
+ );
218
+ }
219
+ const key = findKey(payload.keyId, options.trustedKeys);
220
+ if (!key) {
221
+ return failure(
222
+ "untrusted",
223
+ `This BroadPaper licence was signed with key ${JSON.stringify(payload.keyId)}, which this build does not trust. A licence issued for development is not honoured by a production build; check BROADPAPER_LICENSE points at the right one.`,
224
+ payload
225
+ );
226
+ }
227
+ let signatureOk = false;
228
+ try {
229
+ signatureOk = await verifyAsync(parsed.signature, utf8ToBytes(parsed.signedText), base64UrlToBytes(key.publicKey));
230
+ } catch {
231
+ signatureOk = false;
232
+ }
233
+ if (!signatureOk) {
234
+ return failure("invalid", "This BroadPaper licence failed its signature check \u2014 it has been altered or truncated. PDFs carry the evaluation watermark.", payload);
235
+ }
236
+ const now = options.now ?? /* @__PURE__ */ new Date();
237
+ const expiresAt = new Date(payload.expiresAt * 1e3);
238
+ const graceDays = options.gracePeriodDays ?? DEFAULT_GRACE_PERIOD_DAYS;
239
+ const msRemaining = expiresAt.getTime() - now.getTime();
240
+ const daysRemaining = Math.floor(msRemaining / DAY_MS);
241
+ if (msRemaining > 0) {
242
+ return {
243
+ state: "active",
244
+ licensed: true,
245
+ payload,
246
+ entitlement: payload.entitlement,
247
+ expiresAt,
248
+ daysRemaining,
249
+ message: null
250
+ };
251
+ }
252
+ if (msRemaining > -graceDays * DAY_MS) {
253
+ const daysOver = Math.max(1, Math.ceil(-msRemaining / DAY_MS));
254
+ return {
255
+ state: "grace",
256
+ licensed: true,
257
+ payload,
258
+ entitlement: payload.entitlement,
259
+ expiresAt,
260
+ daysRemaining,
261
+ message: `The BroadPaper licence for ${payload.customerName} expired ${daysOver} day${daysOver === 1 ? "" : "s"} ago and is inside its ${graceDays}-day grace period. Renew it and copy the new licence from your account, or PDFs will start carrying the evaluation watermark.`
262
+ };
263
+ }
264
+ return failure(
265
+ "expired",
266
+ `The BroadPaper licence for ${payload.customerName} expired on ${expiresAt.toISOString().slice(0, 10)} and its ${graceDays}-day grace period has passed, so PDFs carry the evaluation watermark. Renewing issues a new licence in your BroadPaper account.`,
267
+ payload
268
+ );
269
+ }
270
+ function findKey(keyId, extra) {
271
+ return extra?.find((k) => k.keyId === keyId) ?? TRUSTED_KEYS.find((k) => k.keyId === keyId);
272
+ }
273
+
274
+ // src/config.ts
275
+ var STORE_KEY = /* @__PURE__ */ Symbol.for("broadpaper.license.store");
276
+ function store() {
277
+ const g = globalThis;
278
+ let s = g[STORE_KEY];
279
+ if (!s) {
280
+ s = { config: {}, cache: null, logged: /* @__PURE__ */ new Set() };
281
+ g[STORE_KEY] = s;
282
+ }
283
+ return s;
284
+ }
285
+ function configure(config) {
286
+ const s = store();
287
+ s.config = { ...s.config, ...config };
288
+ s.cache = null;
289
+ s.logged.clear();
290
+ }
291
+ function getConfig() {
292
+ return { ...store().config };
293
+ }
294
+ function resetConfig() {
295
+ const g = globalThis;
296
+ delete g[STORE_KEY];
297
+ }
298
+ var BroadPaper = { configure, getConfig };
299
+ function env(name) {
300
+ const p = globalThis.process;
301
+ const value = p?.env?.[name];
302
+ return value && value.trim() ? value : void 0;
303
+ }
304
+ function trustedKeysFromEnv() {
305
+ const raw = env("BROADPAPER_TRUSTED_KEYS");
306
+ if (!raw) return [];
307
+ try {
308
+ const parsed = JSON.parse(raw);
309
+ if (!Array.isArray(parsed)) throw new Error("expected a JSON array");
310
+ return parsed.map((entry, i) => {
311
+ const k = entry;
312
+ if (typeof k?.keyId !== "string" || typeof k?.publicKey !== "string") throw new Error(`entry ${i} needs keyId and publicKey`);
313
+ return { keyId: k.keyId, publicKey: k.publicKey };
314
+ });
315
+ } catch (e) {
316
+ warn(`BROADPAPER_TRUSTED_KEYS could not be read (${e.message}); it is being ignored.`);
317
+ return [];
318
+ }
319
+ }
320
+ function warn(message) {
321
+ const s = store();
322
+ if (s.logged.has(message)) return;
323
+ s.logged.add(message);
324
+ const logger = s.config.logger;
325
+ if (logger === null) return;
326
+ const line = `[BroadPaper] ${message}`;
327
+ if (logger) logger(line);
328
+ else console.warn(line);
329
+ }
330
+ async function resolveLicenseStatus(options = {}) {
331
+ const s = store();
332
+ const config = s.config;
333
+ const license = options.license !== void 0 ? options.license : config.license ?? env("BROADPAPER_LICENSE") ?? null;
334
+ const trustedKeys = [...options.trustedKeys ?? [], ...config.trustedKeys ?? [], ...trustedKeysFromEnv()];
335
+ const gracePeriodDays = options.gracePeriodDays ?? config.gracePeriodDays;
336
+ if (!license) {
337
+ const status = unlicensed();
338
+ if (status.message) warn(status.message);
339
+ return status;
340
+ }
341
+ const verify = () => verifyLicense(license, { ...options.now ? { now: options.now } : {}, trustedKeys, ...gracePeriodDays !== void 0 ? { gracePeriodDays } : {} }).then((status) => {
342
+ if (status.message) warn(status.message);
343
+ return status;
344
+ });
345
+ if (options.now || options.license !== void 0) return verify();
346
+ const key = `${license}|${trustedKeys.map((k) => k.keyId).join(",")}|${gracePeriodDays ?? ""}`;
347
+ if (s.cache?.key !== key) s.cache = { key, status: verify() };
348
+ return s.cache.status;
349
+ }
350
+
351
+ // src/watermark.ts
352
+ var EVALUATION_WATERMARK = {
353
+ text: "BroadPaper Evaluation Copy",
354
+ fontSize: 44,
355
+ angle: -35,
356
+ color: "#c8102e",
357
+ opacity: 0.12
358
+ };
359
+ async function resolveWatermark(options = {}) {
360
+ const status = await resolveLicenseStatus(options);
361
+ return watermarkFor(status);
362
+ }
363
+ function watermarkFor(status) {
364
+ return status.licensed ? null : EVALUATION_WATERMARK;
365
+ }
366
+ function watermarkCss(spec = EVALUATION_WATERMARK) {
367
+ const text = spec.text.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
368
+ return [
369
+ ".bp-page{position:relative;}",
370
+ ".bp-page::after{",
371
+ `content:"${text}";`,
372
+ "position:absolute;inset:0;",
373
+ "display:flex;align-items:center;justify-content:center;",
374
+ "pointer-events:none;z-index:2147483647;",
375
+ `transform:rotate(${spec.angle}deg);`,
376
+ "font-family:Helvetica,Arial,sans-serif;font-weight:700;letter-spacing:0.04em;white-space:nowrap;",
377
+ `font-size:${spec.fontSize}pt;color:${spec.color};opacity:${spec.opacity};`,
378
+ "-webkit-print-color-adjust:exact;print-color-adjust:exact;",
379
+ "}"
380
+ ].join("");
381
+ }
382
+ function producerString(status) {
383
+ if (!status.licensed || !status.payload) return "BroadPaper (evaluation)";
384
+ return `BroadPaper (${status.payload.licenseId})`;
385
+ }
386
+ export {
387
+ BroadPaper,
388
+ DEFAULT_GRACE_PERIOD_DAYS,
389
+ EVALUATION_WATERMARK,
390
+ FORMAT_PREFIX,
391
+ LicenseFormatError,
392
+ PRODUCT,
393
+ TRUSTED_KEYS,
394
+ base64UrlToBytes,
395
+ bytesToBase64Url,
396
+ configure,
397
+ encodeSigningInput,
398
+ formatLicense,
399
+ getConfig,
400
+ normalizeLicense,
401
+ parseLicense,
402
+ producerString,
403
+ resetConfig,
404
+ resolveLicenseStatus,
405
+ resolveWatermark,
406
+ unlicensed,
407
+ verifyLicense,
408
+ warn,
409
+ watermarkCss,
410
+ watermarkFor
411
+ };
412
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/verify.ts","../src/encoding.ts","../src/format.ts","../src/trusted-keys.ts","../src/types.ts","../src/config.ts","../src/watermark.ts"],"sourcesContent":["/**\n * Offline licence verification.\n *\n * Everything here is local: a signature check against a compiled-in public key\n * and a comparison against the clock. There is no activation call, no licence\n * server and nothing to be offline from. That is a deliberate product decision\n * as much as a technical one — a report renderer that stops working because a\n * key server is down is a report renderer nobody puts in a payroll run.\n *\n * The failure modes are all *soft*. An unreadable licence never throws out of a\n * render; it produces a status whose `message` says what is wrong, and the PDF\n * comes out carrying the evaluation mark.\n */\nimport { verifyAsync } from \"@noble/ed25519\";\nimport { base64UrlToBytes, utf8ToBytes } from \"./encoding.js\";\nimport { LicenseFormatError, parseLicense } from \"./format.js\";\nimport { TRUSTED_KEYS } from \"./trusted-keys.js\";\nimport { PRODUCT, type LicensePayload, type LicenseStatus, type TrustedKey } from \"./types.js\";\n\n/**\n * How long an expired licence keeps working.\n *\n * A renewal that is a week late is an accounts payable problem, not a reason to\n * stamp a customer's month-end reports. Thirty days is long enough to cover a\n * card expiring over a holiday and short enough that it is not a second licence\n * term.\n */\nexport const DEFAULT_GRACE_PERIOD_DAYS = 30;\n\nconst DAY_MS = 86_400_000;\n\nexport interface VerifyOptions {\n /** Fixed clock. Tests and deterministic renders pass one. */\n now?: Date;\n /** Extra keys to trust, on top of the ones built into this SDK. */\n trustedKeys?: readonly TrustedKey[];\n /** Overrides {@link DEFAULT_GRACE_PERIOD_DAYS}. */\n gracePeriodDays?: number;\n}\n\n/** The status of a caller that supplied no licence at all. */\nexport function unlicensed(): LicenseStatus {\n return {\n state: \"unlicensed\",\n licensed: false,\n payload: null,\n entitlement: null,\n expiresAt: null,\n daysRemaining: null,\n message:\n \"No BroadPaper licence is configured, so PDFs carry the evaluation watermark. Everything else works; see https://www.broadpaper.com/docs/guide/licensing.\"\n };\n}\n\nfunction failure(state: LicenseStatus[\"state\"], message: string, payload: LicensePayload | null = null): LicenseStatus {\n return {\n state,\n licensed: false,\n payload,\n entitlement: null,\n expiresAt: payload ? new Date(payload.expiresAt * 1000) : null,\n daysRemaining: null,\n message\n };\n}\n\n/**\n * Checks a licence string and reports what it entitles the caller to.\n *\n * Never throws for a bad licence: a malformed string, a forged signature and an\n * expired term are all ordinary answers, and a render is not the place to\n * discover that a config file has a typo in it.\n */\nexport async function verifyLicense(license: string | null | undefined, options: VerifyOptions = {}): Promise<LicenseStatus> {\n if (!license || !license.trim()) return unlicensed();\n\n let parsed;\n try {\n parsed = parseLicense(license);\n } catch (e) {\n if (e instanceof LicenseFormatError) {\n return failure(\n e.kind,\n e.kind === \"unsupported\"\n ? `This BroadPaper licence needs a newer version of the SDK (${e.message}). Update @broadpaper/* or the PDF will carry the evaluation watermark.`\n : `The BroadPaper licence could not be read (${e.message}), so PDFs carry the evaluation watermark.`\n );\n }\n /* c8 ignore next -- parseLicense only throws LicenseFormatError */\n throw e;\n }\n\n const { payload } = parsed;\n if (payload.product !== PRODUCT) {\n return failure(\n \"invalid\",\n `This licence is for ${JSON.stringify(payload.product)}, not the BroadPaper SDK, so PDFs carry the evaluation watermark.`,\n payload\n );\n }\n\n const key = findKey(payload.keyId, options.trustedKeys);\n if (!key) {\n return failure(\n \"untrusted\",\n `This BroadPaper licence was signed with key ${JSON.stringify(payload.keyId)}, which this build does not trust. ` +\n \"A licence issued for development is not honoured by a production build; check BROADPAPER_LICENSE points at the right one.\",\n payload\n );\n }\n\n let signatureOk = false;\n try {\n signatureOk = await verifyAsync(parsed.signature, utf8ToBytes(parsed.signedText), base64UrlToBytes(key.publicKey));\n } catch {\n // A malformed public key or a point that is not on the curve. Either way\n // this licence is not going to verify, and a render must not fail over it.\n signatureOk = false;\n }\n if (!signatureOk) {\n return failure(\"invalid\", \"This BroadPaper licence failed its signature check — it has been altered or truncated. PDFs carry the evaluation watermark.\", payload);\n }\n\n const now = options.now ?? new Date();\n const expiresAt = new Date(payload.expiresAt * 1000);\n const graceDays = options.gracePeriodDays ?? DEFAULT_GRACE_PERIOD_DAYS;\n const msRemaining = expiresAt.getTime() - now.getTime();\n const daysRemaining = Math.floor(msRemaining / DAY_MS);\n\n if (msRemaining > 0) {\n return {\n state: \"active\",\n licensed: true,\n payload,\n entitlement: payload.entitlement,\n expiresAt,\n daysRemaining,\n message: null\n };\n }\n\n if (msRemaining > -graceDays * DAY_MS) {\n const daysOver = Math.max(1, Math.ceil(-msRemaining / DAY_MS));\n return {\n state: \"grace\",\n licensed: true,\n payload,\n entitlement: payload.entitlement,\n expiresAt,\n daysRemaining,\n message:\n `The BroadPaper licence for ${payload.customerName} expired ${daysOver} day${daysOver === 1 ? \"\" : \"s\"} ago and is inside its ${graceDays}-day grace period. ` +\n \"Renew it and copy the new licence from your account, or PDFs will start carrying the evaluation watermark.\"\n };\n }\n\n return failure(\n \"expired\",\n `The BroadPaper licence for ${payload.customerName} expired on ${expiresAt.toISOString().slice(0, 10)} and its ${graceDays}-day grace period has passed, ` +\n \"so PDFs carry the evaluation watermark. Renewing issues a new licence in your BroadPaper account.\",\n payload\n );\n}\n\nfunction findKey(keyId: string, extra: readonly TrustedKey[] | undefined): TrustedKey | undefined {\n // Runtime keys are searched first so a development key can shadow a built-in\n // one of the same name without a rebuild.\n return extra?.find((k) => k.keyId === keyId) ?? TRUSTED_KEYS.find((k) => k.keyId === keyId);\n}\n","/**\n * base64url and UTF-8, without depending on Node or on the DOM.\n *\n * The SDK runs in Node, in edge runtimes, in a worker and in a browser, and the\n * licence string has to decode identically in all four. `atob`/`btoa` exist\n * everywhere modern but are byte-oriented and mangle anything above U+00FF, so\n * text goes through `TextEncoder`/`TextDecoder` first and only bytes are\n * base64'd. The tables below are the whole implementation, which is smaller\n * than the runtime detection needed to use `Buffer` where it happens to exist.\n */\n\nconst ALPHABET = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_\";\nconst LOOKUP = /* @__PURE__ */ (() => {\n const t = new Int8Array(128).fill(-1);\n for (let i = 0; i < ALPHABET.length; i++) t[ALPHABET.charCodeAt(i)] = i;\n // Accept standard base64 as well, so a licence that survived a round trip\n // through a system that rewrote the alphabet still reads.\n t[\"+\".charCodeAt(0)] = 62;\n t[\"/\".charCodeAt(0)] = 63;\n return t;\n})();\n\nexport function bytesToBase64Url(bytes: Uint8Array): string {\n let out = \"\";\n let i = 0;\n for (; i + 2 < bytes.length; i += 3) {\n const n = (bytes[i]! << 16) | (bytes[i + 1]! << 8) | bytes[i + 2]!;\n out += ALPHABET[(n >>> 18) & 63]! + ALPHABET[(n >>> 12) & 63]! + ALPHABET[(n >>> 6) & 63]! + ALPHABET[n & 63]!;\n }\n const rest = bytes.length - i;\n if (rest === 1) {\n const n = bytes[i]! << 16;\n out += ALPHABET[(n >>> 18) & 63]! + ALPHABET[(n >>> 12) & 63]!;\n } else if (rest === 2) {\n const n = (bytes[i]! << 16) | (bytes[i + 1]! << 8);\n out += ALPHABET[(n >>> 18) & 63]! + ALPHABET[(n >>> 12) & 63]! + ALPHABET[(n >>> 6) & 63]!;\n }\n return out;\n}\n\n/** Throws on anything that is not base64/base64url. Padding is optional. */\nexport function base64UrlToBytes(text: string): Uint8Array {\n let end = text.length;\n while (end > 0 && text[end - 1] === \"=\") end--;\n const chars: number[] = [];\n for (let i = 0; i < end; i++) {\n const code = text.charCodeAt(i);\n const value = code < 128 ? LOOKUP[code]! : -1;\n if (value < 0) throw new Error(`not base64: ${JSON.stringify(text[i])} at ${i}`);\n chars.push(value);\n }\n // 4 base64 characters carry 3 bytes; a group of 1 is never valid.\n if (chars.length % 4 === 1) throw new Error(\"not base64: truncated\");\n const out = new Uint8Array(Math.floor((chars.length * 3) / 4));\n let o = 0;\n let i = 0;\n for (; i + 3 < chars.length; i += 4) {\n const n = (chars[i]! << 18) | (chars[i + 1]! << 12) | (chars[i + 2]! << 6) | chars[i + 3]!;\n out[o++] = (n >>> 16) & 255;\n out[o++] = (n >>> 8) & 255;\n out[o++] = n & 255;\n }\n const rest = chars.length - i;\n if (rest === 2) {\n out[o++] = ((chars[i]! << 18) | (chars[i + 1]! << 12)) >>> 16;\n } else if (rest === 3) {\n const n = (chars[i]! << 18) | (chars[i + 1]! << 12) | (chars[i + 2]! << 6);\n out[o++] = (n >>> 16) & 255;\n out[o++] = (n >>> 8) & 255;\n }\n return out;\n}\n\nconst encoder = /* @__PURE__ */ new TextEncoder();\nconst decoder = /* @__PURE__ */ new TextDecoder(\"utf-8\", { fatal: true });\n\nexport const utf8ToBytes = (text: string): Uint8Array => encoder.encode(text);\nexport const bytesToUtf8 = (bytes: Uint8Array): string => decoder.decode(bytes);\n","/**\n * The licence string.\n *\n * BP1.<base64url payload>.<base64url signature>\n *\n * Three parts, dot-separated, safe in an environment variable, a YAML file, a\n * URL and a shell argument without quoting. The leading `BP1` is the *string\n * format* version and `payload.version` is the *payload schema* version; they\n * move independently, and both are checked, so a licence issued for a later\n * format is reported as unsupported rather than as corrupt.\n *\n * What is signed is the bytes of `BP1.<payload>` — prefix included. Signing the\n * encoded text rather than the decoded object means verification never depends\n * on two JSON serialisers agreeing about key order or number formatting, and\n * covering the prefix stops a payload being lifted into a future format that\n * might read a field differently.\n */\nimport { base64UrlToBytes, bytesToBase64Url, bytesToUtf8, utf8ToBytes } from \"./encoding.js\";\nimport { PRODUCT, type Entitlement, type LicensePayload } from \"./types.js\";\n\n/** The only string format this SDK can read. */\nexport const FORMAT_PREFIX = \"BP1\";\n\n/** A licence string split into its parts, before any signature check. */\nexport interface ParsedLicense {\n prefix: string;\n /** The exact text that was signed. */\n signedText: string;\n encodedPayload: string;\n signature: Uint8Array;\n payload: LicensePayload;\n}\n\nexport class LicenseFormatError extends Error {\n constructor(\n message: string,\n /** `unsupported` when the licence is well-formed but newer than this SDK. */\n readonly kind: \"invalid\" | \"unsupported\" = \"invalid\"\n ) {\n super(message);\n this.name = \"LicenseFormatError\";\n }\n}\n\nconst ENTITLEMENTS: readonly Entitlement[] = [\"internal\", \"saas\"];\n\n/** Whitespace is stripped: licences get pasted, wrapped and indented. */\nexport function normalizeLicense(license: string): string {\n return license.replace(/\\s+/g, \"\");\n}\n\n/**\n * Reads a licence string without verifying it.\n *\n * Nothing here decides whether a licence is *honoured* — that needs the\n * signature — but a structurally broken string can be rejected before any\n * cryptography happens, and the account UI can display a licence's contents\n * without holding a public key.\n */\nexport function parseLicense(license: string): ParsedLicense {\n const text = normalizeLicense(license);\n if (!text) throw new LicenseFormatError(\"the licence string is empty\");\n\n const parts = text.split(\".\");\n if (parts.length !== 3) throw new LicenseFormatError(`expected three dot-separated parts, found ${parts.length}`);\n const [prefix, encodedPayload, encodedSignature] = parts as [string, string, string];\n\n if (prefix !== FORMAT_PREFIX) {\n // A licence issued in a format this SDK predates is a version problem, not\n // a corrupt string, and the fix is to upgrade rather than to re-copy it.\n const known = /^BP\\d+$/.test(prefix);\n throw new LicenseFormatError(\n known\n ? `licence format ${prefix} is newer than this version of BroadPaper, which reads ${FORMAT_PREFIX}`\n : `unrecognised licence prefix ${JSON.stringify(prefix)}`,\n known ? \"unsupported\" : \"invalid\"\n );\n }\n if (!encodedPayload || !encodedSignature) throw new LicenseFormatError(\"the licence string has an empty part\");\n\n let json: string;\n let signature: Uint8Array;\n try {\n json = bytesToUtf8(base64UrlToBytes(encodedPayload));\n signature = base64UrlToBytes(encodedSignature);\n } catch (e) {\n throw new LicenseFormatError(`the licence string is not valid base64 (${(e as Error).message})`);\n }\n if (signature.length !== 64) throw new LicenseFormatError(`an Ed25519 signature is 64 bytes, this one is ${signature.length}`);\n\n let raw: unknown;\n try {\n raw = JSON.parse(json);\n } catch {\n throw new LicenseFormatError(\"the licence payload is not valid JSON\");\n }\n\n return {\n prefix,\n signedText: `${prefix}.${encodedPayload}`,\n encodedPayload,\n signature,\n payload: readPayload(raw)\n };\n}\n\n/**\n * Validates the payload's shape.\n *\n * Unknown properties are kept, not rejected: they are inside the signature, so\n * a licence issued by a later BroadPaper carrying a field this SDK has never\n * heard of is still a licence this SDK can honour.\n */\nfunction readPayload(raw: unknown): LicensePayload {\n if (typeof raw !== \"object\" || raw === null || Array.isArray(raw)) throw new LicenseFormatError(\"the licence payload is not an object\");\n const o = raw as Record<string, unknown>;\n\n const version = o.version;\n if (version !== 1) {\n const numeric = typeof version === \"number\" && Number.isInteger(version) && version > 1;\n throw new LicenseFormatError(\n numeric ? `licence payload version ${version} is newer than this version of BroadPaper, which reads version 1` : \"the licence payload has no usable version\",\n numeric ? \"unsupported\" : \"invalid\"\n );\n }\n\n const text = (key: string): string => {\n const value = o[key];\n if (typeof value !== \"string\" || value === \"\") throw new LicenseFormatError(`the licence payload is missing ${key}`);\n return value;\n };\n const seconds = (key: string): number => {\n const value = o[key];\n if (typeof value !== \"number\" || !Number.isFinite(value)) throw new LicenseFormatError(`the licence payload is missing ${key}`);\n return value;\n };\n\n const entitlement = text(\"entitlement\");\n if (!ENTITLEMENTS.includes(entitlement as Entitlement)) {\n // An entitlement this SDK does not know is a newer commercial model, and\n // refusing to honour a licence we cannot classify would punish the customer\n // for buying something new. It is reported as unsupported so the developer\n // is told to upgrade.\n throw new LicenseFormatError(`this version of BroadPaper does not know the entitlement ${JSON.stringify(entitlement)}`, \"unsupported\");\n }\n\n return {\n ...o,\n version: 1,\n keyId: text(\"keyId\"),\n licenseId: text(\"licenseId\"),\n customerId: text(\"customerId\"),\n customerName: text(\"customerName\"),\n product: text(\"product\") as typeof PRODUCT,\n entitlement: entitlement as Entitlement,\n issuedAt: seconds(\"issuedAt\"),\n expiresAt: seconds(\"expiresAt\")\n };\n}\n\n/**\n * Encodes a payload into the text that gets signed.\n *\n * Exported because BroadPaper's own licence issuer must produce byte-identical\n * input to what the SDK verifies, and the only way to guarantee that is for\n * both to call this function.\n */\nexport function encodeSigningInput(payload: LicensePayload): { signedText: string; encodedPayload: string } {\n const encodedPayload = bytesToBase64Url(utf8ToBytes(JSON.stringify(payload)));\n return { encodedPayload, signedText: `${FORMAT_PREFIX}.${encodedPayload}` };\n}\n\n/** Assembles the final licence string from a payload and its signature. */\nexport function formatLicense(payload: LicensePayload, signature: Uint8Array): string {\n if (signature.length !== 64) throw new LicenseFormatError(`an Ed25519 signature is 64 bytes, this one is ${signature.length}`);\n const { encodedPayload } = encodeSigningInput(payload);\n return `${FORMAT_PREFIX}.${encodedPayload}.${bytesToBase64Url(signature)}`;\n}\n","/**\n * The public keys this build of the SDK will accept licence signatures from.\n *\n * **This file contains public keys only.** BroadPaper's private signing key\n * lives in the licence issuer's secret store and is never in this repository,\n * never in the npm package and never in a browser bundle. Publishing the\n * public half is the point: it is what lets a licence be checked with no\n * network call.\n *\n * Add a production key with:\n *\n * node scripts/licence-keys.mjs --production --key-id bp-1\n *\n * which prints the private key once, for the operator to paste into the\n * issuer's configuration, and writes the public half into this array.\n *\n * The list is an allowlist, so it is also the rotation mechanism: issue with a\n * new `keyId`, ship an SDK release trusting both, and drop the old entry once\n * every licence signed with it has expired. Licences carry their `keyId` inside\n * the signed payload, so an attacker cannot point one at a different key.\n *\n * It starts empty on purpose. A key here is trusted by every copy of the SDK in\n * the world, including production installs, which is exactly why a development\n * key must never be added to it — development keys are supplied at runtime\n * instead, through `configure({ trustedKeys })` or `BROADPAPER_TRUSTED_KEYS`,\n * so a licence signed in development cannot be honoured by a production build.\n */\nimport type { TrustedKey } from \"./types.js\";\n\nexport const TRUSTED_KEYS: readonly TrustedKey[] = [\n { keyId: \"bp-2026\", publicKey: \"woKmWyIazae7SpGhi4dwA7zNK1lWdboq88Rl5xscTs4\", note: \"production, generated 2026-09-08\" }\n];\n","/**\n * The licence certificate and the states it can be in.\n *\n * A BroadPaper licence is a signed certificate, not a secret. It says who\n * bought what and until when, BroadPaper signs it, and the SDK checks that\n * signature with a public key compiled into the package. Nothing here talks to\n * a network, reads a machine identifier or writes a file: a licence that is\n * valid on a laptop with no route to the internet is valid, full stop.\n */\n\n/**\n * What the licence permits. Not a feature gate — both entitlements unlock\n * exactly the same software. The difference is who the resulting application\n * may be put in front of, which is a contractual matter and is presented as\n * one in the account area and the licence agreement.\n */\nexport type Entitlement = \"internal\" | \"saas\";\n\n/** The product a licence is for. Present so one signing key can serve more later. */\nexport const PRODUCT = \"broadpaper-sdk\";\n\n/**\n * The signed payload.\n *\n * `customerId` is the durable identifier of the buying organisation — a Clerk\n * organisation id — rather than an email address, because people change\n * addresses and organisations outlive their founders.\n *\n * Times are epoch **seconds**, not milliseconds: the payload is base64'd into\n * a string a developer will paste into a config file, and three characters per\n * timestamp is three characters.\n */\nexport interface LicensePayload {\n /** Payload schema version. Bumped only when the meaning of a field changes. */\n version: 1;\n /** Which signing key signed this. Lets keys be rotated without reissuing everything. */\n keyId: string;\n /**\n * BroadPaper's own identifier for *this certificate*, `lic_…`.\n *\n * One per issuance, so a renewal or an upgrade carries a new one and the\n * document it produced can be dated from it. The customer is identified by\n * `customerId`, which does not change.\n */\n licenseId: string;\n /** The licensed organisation's durable id. */\n customerId: string;\n /** The organisation's name, for display in warnings and PDF metadata. */\n customerName: string;\n product: typeof PRODUCT;\n entitlement: Entitlement;\n /** Epoch seconds. */\n issuedAt: number;\n /** Epoch seconds. */\n expiresAt: number;\n}\n\n/**\n * Why a licence is not being honoured, or how it is being honoured.\n *\n * These are deliberately coarse. A developer needs to know whether their PDFs\n * will carry the evaluation mark and, if so, what to do about it; a taxonomy of\n * eleven parse failures helps nobody.\n */\nexport type LicenseState =\n /** No licence was configured. The SDK works; PDFs carry the evaluation mark. */\n | \"unlicensed\"\n /** A licence was configured but could not be read, or its signature is wrong. */\n | \"invalid\"\n /** Signed by a key this build of the SDK does not trust. */\n | \"untrusted\"\n /** For a different product, or a payload version this SDK is older than. */\n | \"unsupported\"\n /** Valid, signed, and inside its term. */\n | \"active\"\n /** Past `expiresAt` but inside the grace period: honoured, and warned about. */\n | \"grace\"\n /** Past `expiresAt` and past the grace period. */\n | \"expired\";\n\n/** The outcome of checking a licence. Everything the SDK needs to decide. */\nexport interface LicenseStatus {\n state: LicenseState;\n /** True when PDFs render clean. `active` and `grace` only. */\n licensed: boolean;\n /** The payload, when one could be read and verified. */\n payload: LicensePayload | null;\n /** What the licence permits, when it is being honoured. */\n entitlement: Entitlement | null;\n /** Expiry as a Date, when known. */\n expiresAt: Date | null;\n /** Days until expiry; negative once expired. Null when there is no licence. */\n daysRemaining: number | null;\n /**\n * A developer-facing sentence. Always present for a state that is not\n * `active`, so callers can log it without composing their own prose.\n */\n message: string | null;\n}\n\n/** A public key this build of the SDK will accept signatures from. */\nexport interface TrustedKey {\n /** Matches `keyId` in the payload. */\n keyId: string;\n /** The raw 32-byte Ed25519 public key, base64url or base64. */\n publicKey: string;\n /** Free text for the key's provenance. Never displayed to end users. */\n note?: string;\n}\n","/**\n * Configuring BroadPaper once, for the life of the process.\n *\n * import { BroadPaper } from \"@broadpaper/forme\";\n * BroadPaper.configure({ license: process.env.BROADPAPER_LICENSE });\n *\n * The licence is not a credential, so this is not a secrets API: it is the same\n * shape of thing as setting a locale. A browser bundle that carries the licence\n * string in plain text is fine — the string is a signed certificate, and\n * possessing it grants nothing beyond what its payload already says.\n *\n * Configuration is deliberately process-global rather than an argument to every\n * render. Threading a licence through `renderPdf`, the render service, the\n * batch endpoint, the .NET client and every future export path is exactly the\n * kind of plumbing that ends with one path quietly forgetting it, and a\n * customer who has paid discovering a watermark in production.\n *\n * The store hangs off a `Symbol.for` key rather than a module-level variable so\n * that two copies of this package in one dependency tree — the usual result of\n * a mixed-version install — still agree about the licence.\n */\nimport { unlicensed, verifyLicense, type VerifyOptions } from \"./verify.js\";\nimport type { LicenseStatus, TrustedKey } from \"./types.js\";\n\nexport interface BroadPaperConfig {\n /**\n * The licence string, `BP1.…`. Read from `BROADPAPER_LICENSE` when this is\n * not set and the runtime has environment variables.\n */\n license?: string | null;\n /**\n * Public keys to trust in addition to the ones built into the SDK. This is\n * how a development licence is honoured on a developer's machine without a\n * development key ever being trusted by a production build.\n */\n trustedKeys?: readonly TrustedKey[];\n /** Overrides the 30-day post-expiry grace period. Mostly for tests. */\n gracePeriodDays?: number;\n /** Where licence warnings go. Defaults to `console.warn`. */\n logger?: ((message: string) => void) | null;\n}\n\ninterface Store {\n config: BroadPaperConfig;\n /** Memoised by the licence string that produced it. */\n cache: { key: string; status: Promise<LicenseStatus> } | null;\n /** Messages already logged, so a hundred renders do not print a hundred lines. */\n logged: Set<string>;\n}\n\nconst STORE_KEY = Symbol.for(\"broadpaper.license.store\");\n\nfunction store(): Store {\n const g = globalThis as Record<symbol, unknown>;\n let s = g[STORE_KEY] as Store | undefined;\n if (!s) {\n s = { config: {}, cache: null, logged: new Set() };\n g[STORE_KEY] = s;\n }\n return s;\n}\n\n/**\n * Sets the licence and any other SDK-wide options. Call it once, at startup.\n *\n * Successive calls merge, so a host can set the licence in one place and the\n * grace period in another; passing `license: null` clears it.\n */\nexport function configure(config: BroadPaperConfig): void {\n const s = store();\n s.config = { ...s.config, ...config };\n s.cache = null;\n s.logged.clear();\n}\n\n/** What {@link configure} has been given, plus whatever the environment supplies. */\nexport function getConfig(): BroadPaperConfig {\n return { ...store().config };\n}\n\n/** Forgets the configuration entirely. Exists for tests. */\nexport function resetConfig(): void {\n const g = globalThis as Record<symbol, unknown>;\n delete g[STORE_KEY];\n}\n\n/**\n * `BroadPaper.configure({ … })`, for hosts that prefer a namespace to a bare\n * function. Both are the same store.\n */\nexport const BroadPaper = { configure, getConfig };\n\nfunction env(name: string): string | undefined {\n // Node, Deno and bundlers that inline `process.env` all satisfy this; a\n // browser without a shim simply has no environment, which is not an error.\n const p = (globalThis as { process?: { env?: Record<string, string | undefined> } }).process;\n const value = p?.env?.[name];\n return value && value.trim() ? value : undefined;\n}\n\n/**\n * Keys trusted in addition to the built-in ones, from configuration and from\n * `BROADPAPER_TRUSTED_KEYS` — a JSON array of `{ keyId, publicKey }`.\n *\n * A syntax error there is reported rather than swallowed: someone setting that\n * variable is doing so on purpose, and silently ignoring it would present as\n * \"my development licence is not being honoured\" with nothing to go on.\n */\nfunction trustedKeysFromEnv(): readonly TrustedKey[] {\n const raw = env(\"BROADPAPER_TRUSTED_KEYS\");\n if (!raw) return [];\n try {\n const parsed: unknown = JSON.parse(raw);\n if (!Array.isArray(parsed)) throw new Error(\"expected a JSON array\");\n return parsed.map((entry, i) => {\n const k = entry as Record<string, unknown>;\n if (typeof k?.keyId !== \"string\" || typeof k?.publicKey !== \"string\") throw new Error(`entry ${i} needs keyId and publicKey`);\n return { keyId: k.keyId, publicKey: k.publicKey };\n });\n } catch (e) {\n warn(`BROADPAPER_TRUSTED_KEYS could not be read (${(e as Error).message}); it is being ignored.`);\n return [];\n }\n}\n\n/** Logs a message once, whatever it takes to reach the developer. */\nexport function warn(message: string): void {\n const s = store();\n if (s.logged.has(message)) return;\n s.logged.add(message);\n const logger = s.config.logger;\n if (logger === null) return;\n const line = `[BroadPaper] ${message}`;\n if (logger) logger(line);\n else console.warn(line);\n}\n\nexport interface ResolveOptions extends VerifyOptions {\n /** A licence for this call only, overriding the configured one. */\n license?: string | null;\n}\n\n/**\n * The licence status for the current configuration.\n *\n * Memoised on the licence string, because every PDF asks and a signature check\n * per page of a five-hundred-page batch is waste. `configure` clears it, and a\n * per-call `license` bypasses it.\n *\n * Whatever the answer, any developer-facing message is logged once. That is the\n * only thing the SDK does that a caller did not ask for, and it is the\n * difference between a developer seeing \"your licence expired\" and a developer\n * seeing an unexplained watermark.\n */\nexport async function resolveLicenseStatus(options: ResolveOptions = {}): Promise<LicenseStatus> {\n const s = store();\n const config = s.config;\n const license = options.license !== undefined ? options.license : config.license ?? env(\"BROADPAPER_LICENSE\") ?? null;\n const trustedKeys = [...(options.trustedKeys ?? []), ...(config.trustedKeys ?? []), ...trustedKeysFromEnv()];\n const gracePeriodDays = options.gracePeriodDays ?? config.gracePeriodDays;\n\n if (!license) {\n const status = unlicensed();\n if (status.message) warn(status.message);\n return status;\n }\n\n const verify = (): Promise<LicenseStatus> =>\n verifyLicense(license, { ...(options.now ? { now: options.now } : {}), trustedKeys, ...(gracePeriodDays !== undefined ? { gracePeriodDays } : {}) }).then((status) => {\n if (status.message) warn(status.message);\n return status;\n });\n\n // A fixed clock is a test or a deterministic render; caching across different\n // clocks would make the grace-period boundary depend on call order.\n if (options.now || options.license !== undefined) return verify();\n\n const key = `${license}|${trustedKeys.map((k) => k.keyId).join(\",\")}|${gracePeriodDays ?? \"\"}`;\n if (s.cache?.key !== key) s.cache = { key, status: verify() };\n return s.cache.status;\n}\n","/**\n * The evaluation mark.\n *\n * This is the only thing an unlicensed BroadPaper does differently. Every\n * block, both PDF backends, the designer, data binding, pagination and the\n * render service behave identically with and without a licence; what changes is\n * that the file says, on every page, that it was produced with an evaluation\n * copy.\n *\n * It is drawn to be unmistakable in a document review and harmless to a\n * developer evaluating the product: a light diagonal line across the page,\n * behind nothing and over everything, at an opacity that leaves the report\n * entirely readable. Making the output illegible would only push people towards\n * evaluating something else.\n *\n * Each backend draws the spec with its own primitives — the Forme engine has a\n * page-native watermark node, Chromium gets a printed overlay — because a\n * single drawing implementation would mean one of them post-processing a file\n * the other had already finished, and a stamping pass that can be skipped is a\n * stamping pass that eventually is.\n */\nimport { resolveLicenseStatus, type ResolveOptions } from \"./config.js\";\nimport type { LicenseStatus } from \"./types.js\";\n\nexport interface WatermarkSpec {\n text: string;\n /** Points. The engines size the mark from this, not from the page. */\n fontSize: number;\n /** Degrees, anticlockwise from horizontal. */\n angle: number;\n /** Hex, without alpha. */\n color: string;\n /** 0–1. */\n opacity: number;\n}\n\n/**\n * The mark itself.\n *\n * No dashes or typographic punctuation: the standard PDF faces carry no em\n * dash, and a theme that names a font the engine does not have falls back to\n * them, which turned \"BroadPaper — Evaluation Copy\" into \"BroadPaper\n * Evaluation Copy\" in half the documents and a question mark in the rest.\n */\nexport const EVALUATION_WATERMARK: WatermarkSpec = {\n text: \"BroadPaper Evaluation Copy\",\n fontSize: 44,\n angle: -35,\n color: \"#c8102e\",\n opacity: 0.12\n};\n\n/**\n * The mark a render should carry, or null when the licence says none.\n *\n * Backends call this rather than reading the licence status themselves, so\n * \"licensed\" and \"no watermark\" cannot drift apart.\n */\nexport async function resolveWatermark(options: ResolveOptions = {}): Promise<WatermarkSpec | null> {\n const status = await resolveLicenseStatus(options);\n return watermarkFor(status);\n}\n\n/** The pure half of {@link resolveWatermark}, for callers that already have a status. */\nexport function watermarkFor(status: LicenseStatus): WatermarkSpec | null {\n return status.licensed ? null : EVALUATION_WATERMARK;\n}\n\n/**\n * CSS that paints the mark on every `.bp-page` of a rendered document.\n *\n * Used by the Chromium backend and by the designer's print path — the two\n * places a PDF is produced by a browser rather than by the engine. `position:\n * fixed` would put it on the viewport rather than on each page, so it is an\n * absolutely positioned pseudo-element on the page box, which prints once per\n * sheet.\n *\n * `print-color-adjust` matters: without it Chromium drops the mark from the\n * printed file exactly as it drops any other background colour.\n */\nexport function watermarkCss(spec: WatermarkSpec = EVALUATION_WATERMARK): string {\n const text = spec.text.replace(/\\\\/g, \"\\\\\\\\\").replace(/\"/g, '\\\\\"');\n return [\n \".bp-page{position:relative;}\",\n \".bp-page::after{\",\n `content:\"${text}\";`,\n \"position:absolute;inset:0;\",\n \"display:flex;align-items:center;justify-content:center;\",\n \"pointer-events:none;z-index:2147483647;\",\n `transform:rotate(${spec.angle}deg);`,\n \"font-family:Helvetica,Arial,sans-serif;font-weight:700;letter-spacing:0.04em;white-space:nowrap;\",\n `font-size:${spec.fontSize}pt;color:${spec.color};opacity:${spec.opacity};`,\n \"-webkit-print-color-adjust:exact;print-color-adjust:exact;\",\n \"}\"\n ].join(\"\");\n}\n\n/**\n * The PDF Producer string for a render.\n *\n * Licensed output records the licence id. It is not a secret — it identifies a\n * customer's licence, not their account — and having it in the file means a\n * document that turns up somewhere it should not can be traced back to the\n * licence that produced it without any of the tracking a phone-home would need.\n */\nexport function producerString(status: LicenseStatus): string {\n if (!status.licensed || !status.payload) return \"BroadPaper (evaluation)\";\n return `BroadPaper (${status.payload.licenseId})`;\n}\n"],"mappings":"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","names":["i"]}
package/package.json ADDED
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "@broadpaper/license",
3
+ "version": "0.1.1",
4
+ "description": "BroadPaper licence certificates: offline Ed25519 verification, entitlement states and the evaluation watermark. Contains only the public verification keys.",
5
+ "license": "SEE LICENSE IN LICENSE",
6
+ "type": "module",
7
+ "main": "./dist/index.js",
8
+ "module": "./dist/index.js",
9
+ "types": "./dist/index.d.ts",
10
+ "exports": {
11
+ ".": {
12
+ "types": "./dist/index.d.ts",
13
+ "import": "./dist/index.js"
14
+ }
15
+ },
16
+ "files": [
17
+ "dist",
18
+ "LICENSE",
19
+ "THIRD-PARTY-NOTICES.md"
20
+ ],
21
+ "sideEffects": false,
22
+ "dependencies": {
23
+ "@noble/ed25519": "^3.2.0"
24
+ },
25
+ "scripts": {
26
+ "build": "tsup src/index.ts --format esm --dts --sourcemap --clean",
27
+ "typecheck": "tsc --noEmit -p tsconfig.json"
28
+ }
29
+ }