@broadpaper/license 0.2.0 → 0.3.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/README.md +64 -0
- package/dist/index.d.ts +29 -0
- package/dist/index.js.map +1 -1
- package/package.json +21 -1
package/README.md
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# @broadpaper/license
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/@broadpaper/license)
|
|
4
|
+
[](https://www.broadpaper.com/legal/terms/)
|
|
5
|
+
[](https://www.broadpaper.com/docs/guide/licensing/)
|
|
6
|
+
|
|
7
|
+
Offline licence verification for BroadPaper. Public keys only — nothing here can sign a licence.
|
|
8
|
+
|
|
9
|
+
Part of [**BroadPaper**](https://www.broadpaper.com/developers/) — an embeddable visual report designer and PDF renderer.
|
|
10
|
+
Your users design the report in your application; BroadPaper owns the layout, the branding,
|
|
11
|
+
the pagination and the output.
|
|
12
|
+
|
|
13
|
+
## Install
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install @broadpaper/license
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
A BroadPaper licence is an Ed25519-signed certificate, not an API key. This package verifies
|
|
20
|
+
one against a public key compiled into it, locally, with no network call of any kind — not on
|
|
21
|
+
render, not at start-up, not ever. A licence that is valid on a laptop with no route to the
|
|
22
|
+
internet is valid.
|
|
23
|
+
|
|
24
|
+
That also means the licence is **not a secret**. It says what was bought and until when,
|
|
25
|
+
editing it invalidates the signature, and it is safe in a browser bundle, a container image or
|
|
26
|
+
a repository.
|
|
27
|
+
|
|
28
|
+
```ts
|
|
29
|
+
import { BroadPaper } from "@broadpaper/license";
|
|
30
|
+
|
|
31
|
+
BroadPaper.configure({ license: process.env.BROADPAPER_LICENSE });
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
`configure` is process-wide and idempotent, and is re-exported from the backends, so you can
|
|
35
|
+
import it from whichever one you already depend on.
|
|
36
|
+
|
|
37
|
+
## Licensing
|
|
38
|
+
|
|
39
|
+
BroadPaper is **free to evaluate, with no trial clock and nothing behind a feature gate**.
|
|
40
|
+
Install it, use every feature and evaluate it against your real data for as long as you like.
|
|
41
|
+
Production use needs a licence.
|
|
42
|
+
|
|
43
|
+
In the software, the only difference without a licence is that generated PDFs carry an
|
|
44
|
+
evaluation mark, and a licence removes it. Contractually, the licence is also what permits
|
|
45
|
+
production use. No seats, no activation, no machine binding, no telemetry, and no call home at
|
|
46
|
+
any point.
|
|
47
|
+
|
|
48
|
+
- [How licensing works](https://www.broadpaper.com/docs/guide/licensing/)
|
|
49
|
+
- [Pricing](https://www.broadpaper.com/developers/pricing/)
|
|
50
|
+
- [Licence agreement](https://www.broadpaper.com/legal/terms/)
|
|
51
|
+
|
|
52
|
+
## Documentation
|
|
53
|
+
|
|
54
|
+
- [Licensing](https://www.broadpaper.com/docs/guide/licensing)
|
|
55
|
+
- [Full documentation](https://www.broadpaper.com/docs/)
|
|
56
|
+
|
|
57
|
+
## Support
|
|
58
|
+
|
|
59
|
+
Paid licences include email support at [support@broadpaper.com](mailto:support@broadpaper.com).
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
Copyright © 2026 Codingbadger Creative Limited. See [LICENSE](./LICENSE) in this package, and
|
|
64
|
+
[THIRD-PARTY-NOTICES.md](./THIRD-PARTY-NOTICES.md) for the components it ships with.
|
package/dist/index.d.ts
CHANGED
|
@@ -334,6 +334,35 @@ interface WatermarkSpec {
|
|
|
334
334
|
color: string;
|
|
335
335
|
/** 0–1. */
|
|
336
336
|
opacity: number;
|
|
337
|
+
/**
|
|
338
|
+
* Where on the page it goes.
|
|
339
|
+
*
|
|
340
|
+
* `center` is the evaluation mark's diagonal stamp: sized from `fontSize`,
|
|
341
|
+
* rotated by `angle`, across the middle of every page. It is the default and
|
|
342
|
+
* the only thing `watermarkFor` ever asks for.
|
|
343
|
+
*
|
|
344
|
+
* `bottom` is a credit line along the foot of the page, in the bottom margin —
|
|
345
|
+
* the shape a host's own commercial model usually wants. BroadPaper Cloud's
|
|
346
|
+
* free plan started as a `center` mark at eight points, which is a small grey
|
|
347
|
+
* sentence floating in the middle of an otherwise finished invoice: it reads
|
|
348
|
+
* as a mistake rather than as a credit, and the whole point of that mark is
|
|
349
|
+
* that a free user's document stays sendable.
|
|
350
|
+
*
|
|
351
|
+
* It is drawn in the margin rather than in the footer band deliberately, so it
|
|
352
|
+
* costs no layout: a mark that changed the content height would make the
|
|
353
|
+
* finished file paginate differently from the canvas the user approved.
|
|
354
|
+
*/
|
|
355
|
+
placement?: "center" | "bottom";
|
|
356
|
+
/**
|
|
357
|
+
* A small mark drawn before the text, as an `image/svg+xml` data URI. `bottom`
|
|
358
|
+
* only.
|
|
359
|
+
*
|
|
360
|
+
* Vector rather than a bitmap because it is drawn at about nine points and a
|
|
361
|
+
* raster at that size is a smudge. Both backends parse the URI themselves —
|
|
362
|
+
* the Forme engine's image decoder cannot read an SVG, so it is re-emitted as
|
|
363
|
+
* the drawing it is.
|
|
364
|
+
*/
|
|
365
|
+
mark?: string;
|
|
337
366
|
}
|
|
338
367
|
/**
|
|
339
368
|
* The mark itself.
|
package/dist/index.js.map
CHANGED
|
@@ -1 +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 plan: null,\n validUntil: 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 plan: null,\n validUntil: payload?.validUntil != null ? new Date(payload.validUntil * 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\n // A licence with no end date never expires, so there is no clock to consult.\n // Nothing sold today issues one — `readPayload` refuses the only licence type\n // that would — but the field is nullable, and a null that fell through to the\n // arithmetic below would come out as 1970 and watermark a valid licence.\n if (payload.validUntil === null) {\n return { state: \"active\", licensed: true, payload, plan: payload.plan, validUntil: null, daysRemaining: null, message: null };\n }\n\n const validUntil = new Date(payload.validUntil * 1000);\n const graceDays = options.gracePeriodDays ?? DEFAULT_GRACE_PERIOD_DAYS;\n const msRemaining = validUntil.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 plan: payload.plan,\n validUntil,\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 plan: payload.plan,\n validUntil,\n daysRemaining,\n message:\n `The BroadPaper licence for ${payload.organisationName} 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.organisationName} expired on ${validUntil.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 LicensePayload, type LicenseType, type Plan } 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 PLANS: readonly Plan[] = [\"internal\", \"saas\"];\n\n/**\n * The licence types this SDK can actually honour.\n *\n * Only `subscription`. A perpetual licence's whole meaning is that the right to\n * use outlives the right to updates, which needs the running build's release\n * date to enforce — a fact a certificate cannot carry, because it is issued\n * before the versions it covers exist. A build with no such date could only\n * honour a perpetual licence by honouring *every* future version forever, which\n * is not what was sold. So it is refused, and refused as `unsupported` rather\n * than `invalid`: the fix is to upgrade, and saying so is more use than calling\n * a valid certificate corrupt.\n */\nconst LICENSE_TYPES: readonly LicenseType[] = [\"subscription\"];\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 schemaVersion = o.schemaVersion;\n if (schemaVersion !== 1) {\n const numeric = typeof schemaVersion === \"number\" && Number.isInteger(schemaVersion) && schemaVersion > 1;\n throw new LicenseFormatError(\n numeric\n ? `licence payload schema version ${schemaVersion} is newer than this version of BroadPaper, which reads version 1`\n : \"the licence payload has no usable schemaVersion\",\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 // `null` is the honest encoding of \"no end date\" — a sentinel year 9999 would\n // be a date somebody eventually compares against. Absent is still an error;\n // only an explicit null means never.\n const secondsOrNever = (key: string): number | null => {\n const value = o[key];\n if (value === null) return null;\n return seconds(key);\n };\n\n const plan = text(\"plan\");\n if (!PLANS.includes(plan as Plan)) {\n // A plan this SDK does not know is a newer commercial model, and refusing\n // to honour a licence we cannot classify would punish the customer for\n // buying something new. It is reported as unsupported so the developer is\n // told to upgrade.\n throw new LicenseFormatError(`this version of BroadPaper does not know the plan ${JSON.stringify(plan)}`, \"unsupported\");\n }\n\n const licenseType = text(\"licenseType\");\n if (!LICENSE_TYPES.includes(licenseType as LicenseType)) {\n throw new LicenseFormatError(`this version of BroadPaper cannot honour a ${JSON.stringify(licenseType)} licence`, \"unsupported\");\n }\n\n return {\n ...o,\n schemaVersion: 1,\n keyId: text(\"keyId\"),\n licenseId: text(\"licenseId\"),\n organisationId: text(\"organisationId\"),\n organisationName: text(\"organisationName\"),\n product: text(\"product\") as typeof PRODUCT,\n plan: plan as Plan,\n licenseType: licenseType as LicenseType,\n issuedAt: seconds(\"issuedAt\"),\n validUntil: secondsOrNever(\"validUntil\"),\n updatesUntil: seconds(\"updatesUntil\")\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 * Which licence was bought. Not a feature gate — both plans unlock exactly the\n * same software. The difference is who the resulting application may be put in\n * front of, which is a contractual matter and is presented as one in the\n * account area and the licence agreement.\n *\n * These are the `id`s in `shared/plans.json`, deliberately: that file is the\n * one machine-readable statement of the commercial model, and a certificate\n * that quotes a different vocabulary from the thing that priced it is a\n * certificate somebody eventually has to write a mapping table for.\n */\nexport type Plan = \"internal\" | \"saas\";\n\n/**\n * How the licence is held.\n *\n * `subscription` is a term: the right to use the software ends with the term,\n * and so does the right to updates. It is the only thing sold today.\n *\n * `perpetual` is reserved. It would mean the right to use never ends, while\n * the right to updates still does — `validUntil` null, `updatesUntil` set. The\n * field exists now so that offering one later is a change to what the issuer\n * writes rather than a change to the certificate format, which would mean\n * reissuing every licence in the field. **This SDK does not honour one**: see\n * `readPayload` in `format.ts` for why refusing is the only safe answer.\n */\nexport type LicenseType = \"subscription\" | \"perpetual\";\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 * `organisationId` is the durable identifier of the buying organisation — a\n * Clerk organisation id — rather than an email address, because people change\n * addresses and organisations outlive their founders. It is *not* the Creem\n * customer id, which identifies the same buyer to the payment processor and is\n * a different string; the API keeps both, and naming this one for the\n * organisation is what stops the two being confused at a glance.\n *\n * Times are epoch **seconds**, not milliseconds and not ISO strings. The\n * certificate is written by `Utf8JsonWriter` in C# and read by `JSON.parse` in\n * TypeScript, and an integer cannot disagree about a timezone suffix or a\n * fractional second the way two date serialisers can — which matters more here\n * than anywhere, because the two halves only find out they disagree when a\n * customer's licence is rejected in the field.\n *\n * Field order is load-bearing on the issuing side only: `encodeSigningInput`\n * serialises this object, and `LicenceSigner.Payload` writes the same keys in\n * the same order so a certificate is byte-identical whichever issued it.\n * Verification never re-serialises — it checks the signature over the text as\n * received — so an unknown field from a later BroadPaper is carried through\n * rather than rejected.\n */\nexport interface LicensePayload {\n /**\n * Payload schema version. Bumped only when the meaning of a field changes,\n * never to add one: `readPayload` keeps fields it does not know about, so a\n * later BroadPaper can extend the certificate without older SDKs refusing it.\n */\n schemaVersion: 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 buyer is identified by\n * `organisationId`, which does not change.\n */\n licenseId: string;\n /** The licensed organisation's durable id. A Clerk organisation id. */\n organisationId: string;\n /** The organisation's name, for display in warnings and PDF metadata. */\n organisationName: string;\n product: typeof PRODUCT;\n plan: Plan;\n licenseType: LicenseType;\n /** Epoch seconds. */\n issuedAt: number;\n /**\n * When the right to *use* the software ends, in epoch seconds.\n *\n * Null means never, which only a perpetual licence says. A subscription\n * always carries a date.\n */\n validUntil: number | null;\n /**\n * When the right to *updates* ends, in epoch seconds.\n *\n * For a subscription this equals `validUntil` and nothing reads it: the term\n * ends, and both rights end with it. It is separate from `validUntil` because\n * for a perpetual licence the two genuinely differ, and that is the whole\n * reason the field is here before anything consults it.\n */\n updatesUntil: 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 certificate this SDK is too old to honour. */\n | \"unsupported\"\n /** Valid, signed, and inside its term. */\n | \"active\"\n /** Past `validUntil` but inside the grace period: honoured, and warned about. */\n | \"grace\"\n /** Past `validUntil` 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 /** Which licence was bought, when it is being honoured. */\n plan: Plan | null;\n /** When the right to use ends, when known. Null for a licence with no end. */\n validUntil: Date | null;\n /** Days until `validUntil`; negative once expired. Null when there is no end date. */\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"]}
|
|
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 plan: null,\n validUntil: 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 plan: null,\n validUntil: payload?.validUntil != null ? new Date(payload.validUntil * 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\n // A licence with no end date never expires, so there is no clock to consult.\n // Nothing sold today issues one — `readPayload` refuses the only licence type\n // that would — but the field is nullable, and a null that fell through to the\n // arithmetic below would come out as 1970 and watermark a valid licence.\n if (payload.validUntil === null) {\n return { state: \"active\", licensed: true, payload, plan: payload.plan, validUntil: null, daysRemaining: null, message: null };\n }\n\n const validUntil = new Date(payload.validUntil * 1000);\n const graceDays = options.gracePeriodDays ?? DEFAULT_GRACE_PERIOD_DAYS;\n const msRemaining = validUntil.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 plan: payload.plan,\n validUntil,\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 plan: payload.plan,\n validUntil,\n daysRemaining,\n message:\n `The BroadPaper licence for ${payload.organisationName} 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.organisationName} expired on ${validUntil.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 LicensePayload, type LicenseType, type Plan } 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 PLANS: readonly Plan[] = [\"internal\", \"saas\"];\n\n/**\n * The licence types this SDK can actually honour.\n *\n * Only `subscription`. A perpetual licence's whole meaning is that the right to\n * use outlives the right to updates, which needs the running build's release\n * date to enforce — a fact a certificate cannot carry, because it is issued\n * before the versions it covers exist. A build with no such date could only\n * honour a perpetual licence by honouring *every* future version forever, which\n * is not what was sold. So it is refused, and refused as `unsupported` rather\n * than `invalid`: the fix is to upgrade, and saying so is more use than calling\n * a valid certificate corrupt.\n */\nconst LICENSE_TYPES: readonly LicenseType[] = [\"subscription\"];\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 schemaVersion = o.schemaVersion;\n if (schemaVersion !== 1) {\n const numeric = typeof schemaVersion === \"number\" && Number.isInteger(schemaVersion) && schemaVersion > 1;\n throw new LicenseFormatError(\n numeric\n ? `licence payload schema version ${schemaVersion} is newer than this version of BroadPaper, which reads version 1`\n : \"the licence payload has no usable schemaVersion\",\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 // `null` is the honest encoding of \"no end date\" — a sentinel year 9999 would\n // be a date somebody eventually compares against. Absent is still an error;\n // only an explicit null means never.\n const secondsOrNever = (key: string): number | null => {\n const value = o[key];\n if (value === null) return null;\n return seconds(key);\n };\n\n const plan = text(\"plan\");\n if (!PLANS.includes(plan as Plan)) {\n // A plan this SDK does not know is a newer commercial model, and refusing\n // to honour a licence we cannot classify would punish the customer for\n // buying something new. It is reported as unsupported so the developer is\n // told to upgrade.\n throw new LicenseFormatError(`this version of BroadPaper does not know the plan ${JSON.stringify(plan)}`, \"unsupported\");\n }\n\n const licenseType = text(\"licenseType\");\n if (!LICENSE_TYPES.includes(licenseType as LicenseType)) {\n throw new LicenseFormatError(`this version of BroadPaper cannot honour a ${JSON.stringify(licenseType)} licence`, \"unsupported\");\n }\n\n return {\n ...o,\n schemaVersion: 1,\n keyId: text(\"keyId\"),\n licenseId: text(\"licenseId\"),\n organisationId: text(\"organisationId\"),\n organisationName: text(\"organisationName\"),\n product: text(\"product\") as typeof PRODUCT,\n plan: plan as Plan,\n licenseType: licenseType as LicenseType,\n issuedAt: seconds(\"issuedAt\"),\n validUntil: secondsOrNever(\"validUntil\"),\n updatesUntil: seconds(\"updatesUntil\")\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 * Which licence was bought. Not a feature gate — both plans unlock exactly the\n * same software. The difference is who the resulting application may be put in\n * front of, which is a contractual matter and is presented as one in the\n * account area and the licence agreement.\n *\n * These are the `id`s in `shared/plans.json`, deliberately: that file is the\n * one machine-readable statement of the commercial model, and a certificate\n * that quotes a different vocabulary from the thing that priced it is a\n * certificate somebody eventually has to write a mapping table for.\n */\nexport type Plan = \"internal\" | \"saas\";\n\n/**\n * How the licence is held.\n *\n * `subscription` is a term: the right to use the software ends with the term,\n * and so does the right to updates. It is the only thing sold today.\n *\n * `perpetual` is reserved. It would mean the right to use never ends, while\n * the right to updates still does — `validUntil` null, `updatesUntil` set. The\n * field exists now so that offering one later is a change to what the issuer\n * writes rather than a change to the certificate format, which would mean\n * reissuing every licence in the field. **This SDK does not honour one**: see\n * `readPayload` in `format.ts` for why refusing is the only safe answer.\n */\nexport type LicenseType = \"subscription\" | \"perpetual\";\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 * `organisationId` is the durable identifier of the buying organisation — a\n * Clerk organisation id — rather than an email address, because people change\n * addresses and organisations outlive their founders. It is *not* the Creem\n * customer id, which identifies the same buyer to the payment processor and is\n * a different string; the API keeps both, and naming this one for the\n * organisation is what stops the two being confused at a glance.\n *\n * Times are epoch **seconds**, not milliseconds and not ISO strings. The\n * certificate is written by `Utf8JsonWriter` in C# and read by `JSON.parse` in\n * TypeScript, and an integer cannot disagree about a timezone suffix or a\n * fractional second the way two date serialisers can — which matters more here\n * than anywhere, because the two halves only find out they disagree when a\n * customer's licence is rejected in the field.\n *\n * Field order is load-bearing on the issuing side only: `encodeSigningInput`\n * serialises this object, and `LicenceSigner.Payload` writes the same keys in\n * the same order so a certificate is byte-identical whichever issued it.\n * Verification never re-serialises — it checks the signature over the text as\n * received — so an unknown field from a later BroadPaper is carried through\n * rather than rejected.\n */\nexport interface LicensePayload {\n /**\n * Payload schema version. Bumped only when the meaning of a field changes,\n * never to add one: `readPayload` keeps fields it does not know about, so a\n * later BroadPaper can extend the certificate without older SDKs refusing it.\n */\n schemaVersion: 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 buyer is identified by\n * `organisationId`, which does not change.\n */\n licenseId: string;\n /** The licensed organisation's durable id. A Clerk organisation id. */\n organisationId: string;\n /** The organisation's name, for display in warnings and PDF metadata. */\n organisationName: string;\n product: typeof PRODUCT;\n plan: Plan;\n licenseType: LicenseType;\n /** Epoch seconds. */\n issuedAt: number;\n /**\n * When the right to *use* the software ends, in epoch seconds.\n *\n * Null means never, which only a perpetual licence says. A subscription\n * always carries a date.\n */\n validUntil: number | null;\n /**\n * When the right to *updates* ends, in epoch seconds.\n *\n * For a subscription this equals `validUntil` and nothing reads it: the term\n * ends, and both rights end with it. It is separate from `validUntil` because\n * for a perpetual licence the two genuinely differ, and that is the whole\n * reason the field is here before anything consults it.\n */\n updatesUntil: 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 certificate this SDK is too old to honour. */\n | \"unsupported\"\n /** Valid, signed, and inside its term. */\n | \"active\"\n /** Past `validUntil` but inside the grace period: honoured, and warned about. */\n | \"grace\"\n /** Past `validUntil` 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 /** Which licence was bought, when it is being honoured. */\n plan: Plan | null;\n /** When the right to use ends, when known. Null for a licence with no end. */\n validUntil: Date | null;\n /** Days until `validUntil`; negative once expired. Null when there is no end date. */\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 * Where on the page it goes.\n *\n * `center` is the evaluation mark's diagonal stamp: sized from `fontSize`,\n * rotated by `angle`, across the middle of every page. It is the default and\n * the only thing `watermarkFor` ever asks for.\n *\n * `bottom` is a credit line along the foot of the page, in the bottom margin —\n * the shape a host's own commercial model usually wants. BroadPaper Cloud's\n * free plan started as a `center` mark at eight points, which is a small grey\n * sentence floating in the middle of an otherwise finished invoice: it reads\n * as a mistake rather than as a credit, and the whole point of that mark is\n * that a free user's document stays sendable.\n *\n * It is drawn in the margin rather than in the footer band deliberately, so it\n * costs no layout: a mark that changed the content height would make the\n * finished file paginate differently from the canvas the user approved.\n */\n placement?: \"center\" | \"bottom\";\n /**\n * A small mark drawn before the text, as an `image/svg+xml` data URI. `bottom`\n * only.\n *\n * Vector rather than a bitmap because it is drawn at about nine points and a\n * raster at that size is a smudge. Both backends parse the URI themselves —\n * the Forme engine's image decoder cannot read an SVG, so it is re-emitted as\n * the drawing it is.\n */\n mark?: string;\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
CHANGED
|
@@ -1,8 +1,28 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@broadpaper/license",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "BroadPaper licence certificates: offline Ed25519 verification, entitlement states and the evaluation watermark. Contains only the public verification keys.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"pdf",
|
|
7
|
+
"pdf-generation",
|
|
8
|
+
"report",
|
|
9
|
+
"reporting",
|
|
10
|
+
"report-designer",
|
|
11
|
+
"document-generation",
|
|
12
|
+
"broadpaper",
|
|
13
|
+
"licensing",
|
|
14
|
+
"ed25519",
|
|
15
|
+
"offline-verification"
|
|
16
|
+
],
|
|
5
17
|
"license": "SEE LICENSE IN LICENSE",
|
|
18
|
+
"homepage": "https://www.broadpaper.com",
|
|
19
|
+
"bugs": {
|
|
20
|
+
"email": "support@broadpaper.com"
|
|
21
|
+
},
|
|
22
|
+
"author": {
|
|
23
|
+
"name": "Codingbadger Creative Limited",
|
|
24
|
+
"url": "https://www.broadpaper.com"
|
|
25
|
+
},
|
|
6
26
|
"type": "module",
|
|
7
27
|
"main": "./dist/index.js",
|
|
8
28
|
"module": "./dist/index.js",
|