first commit

This commit is contained in:
2026-05-29 08:20:56 +01:00
commit 949299f58d
6368 changed files with 527176 additions and 0 deletions

View File

@@ -0,0 +1,81 @@
# `@suaveplan/license` — constants
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
Wire-format constants for the license package. Every value here
is part of the binary contract: the per-field bit widths
(`FIELD_BITS`), the Base32 alphabet, the signature lengths, the version
byte, and the day-to-millisecond conversion factor. Changing any
constant breaks every existing license key, so they live in their own
module and are imported by every code path that touches the format.
## Features
- `LICENSE_VERSION` (currently `2`) — pinned by the 3-bit version field;
`validateLicense` rejects any payload with a higher version.
- `FULL_SIGNATURE_BYTES` (64) / `SIGNATURE_BYTES` (20) /
`MIN_SIGNATURE_BYTES` (16) — the three signature-length thresholds
honoured by generation and validation.
- `BASE32_ALPHABET` — 32-char Crockford-style alphabet (no I/L/O/U) used
by both encode and decode.
- `LICENSE_SCHEMA` — per-field maximum values, used by Zod schemas and
by the bounds checks in `packPayload`.
- `FIELD_BITS` — per-field bit widths summing to 72, consumed by the
bitpacker.
- `PAYLOAD_BYTES` (9), `OBFUSCATION_PAYLOAD_CHARS` (15), and
`MS_PER_DAY` (86 400 000) — derived constants pulled out so callers
don't have to recompute them.
## Usage: Basic
```ts
import { LICENSE_VERSION, BASE32_ALPHABET } from "@suaveplan/license/constants/constants";
console.log(`Targeting wire format v${LICENSE_VERSION}`);
console.log(`Alphabet size: ${BASE32_ALPHABET.length}`); // 32
```
## Usage: Advanced
Bounds-checking a payload field at the application layer before
constructing it:
```ts
import { LICENSE_SCHEMA } from "@suaveplan/license/constants/constants";
import type { LicensePayload } from "@suaveplan/license/types/license";
function clampPayload(p: LicensePayload): LicensePayload {
return {
...p,
productId: Math.min(p.productId, LICENSE_SCHEMA.productId),
timeValue: Math.min(p.timeValue, LICENSE_SCHEMA.timeValue),
};
}
```
## API Reference
| Symbol | Type | Value |
|---|---|---|
| `LICENSE_VERSION` | `2` | Current format version |
| `FULL_SIGNATURE_BYTES` | `64` | secp256k1 compact signature length |
| `SIGNATURE_BYTES` | `20` | Default truncated-signature length |
| `MIN_SIGNATURE_BYTES` | `16` | Lowest acceptable truncation |
| `BASE32_ALPHABET` | `string` | `0-9A-HJKMNP-TV-Z` (32 chars) |
| `LICENSE_SCHEMA` | `Readonly<Record<field, max>>` | Per-field max values |
| `FIELD_BITS` | `Readonly<Record<field, bits>>` | Per-field bit widths |
| `PAYLOAD_BYTES` | `9` | Packed payload size |
| `OBFUSCATION_PAYLOAD_CHARS` | `15` | Chars rotated by obfuscation |
| `MS_PER_DAY` | `86_400_000` | UTC day in milliseconds |
## Implementation Notes
Every constant is declared `as const`, so its TypeScript type is the
literal value (e.g. `LICENSE_VERSION: 2`), giving exhaustiveness benefits
when compared in `switch` statements. The two records (`LICENSE_SCHEMA`
and `FIELD_BITS`) are also frozen at the type level via `as const`,
preventing accidental mutation through their `Readonly` view.