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,112 @@
# `@suaveplan/license` — flags
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
Bitmask helpers for the 5-bit `flags` field (`LicenseFlags`) and the
19-bit `featureMask` field. The flags field stores up to 5 boolean
license states (`TRIAL`, `UPGRADE`, `MACHINE_LOCKED`, `SUBSCRIPTION`,
`EDUCATIONAL`); the feature mask stores up to 19 product-specific
feature toggles. This module gives type-safe accessors for both: the
flag helpers are typed against the `LicenseFlags` enum; the feature
helpers validate that the supplied index is within `[0, 18]` and raise
`LicenseError(LICENSE.FEATURE_INDEX_OUT_OF_RANGE)` otherwise.
## Features
- `hasFlag` / `combineFlags` — flag-bit accessors typed against the
`LicenseFlags` enum so misuses surface at compile time.
- `hasFeature` / `combineFeatures` / `getEnabledFeatures` — feature-bit
accessors with runtime index-range checks.
- Out-of-range indices throw a typed `LicenseError`, never a generic
`Error` — callers can `instanceof LicenseError` and dispatch on
`error.code`.
- `getEnabledFeatures` walks only the 19 declared bits, so callers
feeding in stray high bits get a clean answer.
## Usage: Basic
```ts
import {
combineFlags,
hasFlag,
hasFeature,
} from "@suaveplan/license/flags/flags";
import { LicenseFlags } from "@suaveplan/license/types/license";
const flags = combineFlags(LicenseFlags.TRIAL, LicenseFlags.SUBSCRIPTION);
hasFlag(flags, LicenseFlags.TRIAL); // true
hasFeature(0b1010, 1); // true
```
## Usage: Advanced
Building a payload-shaped feature mask from a domain-specific feature
enum, with overflow guard:
```ts
import {
combineFeatures,
getEnabledFeatures,
} from "@suaveplan/license/flags/flags";
import { LicenseError } from "@suaveplan/license";
const enum Feature {
Export = 0,
BulkOps = 1,
AdvancedSearch = 4,
AiAssistant = 18,
}
try {
const mask = combineFeatures(
Feature.Export,
Feature.BulkOps,
Feature.AdvancedSearch,
Feature.AiAssistant
);
const enabled = getEnabledFeatures(mask); // [0, 1, 4, 18]
} catch (e) {
if (e instanceof LicenseError && e.code === "LICENSE.FEATURE_INDEX_OUT_OF_RANGE") {
// upstream produced an out-of-range index
}
}
```
## API Reference
### `hasFlag(flags: number, flag: LicenseFlags): boolean`
Test whether `flag` is set in `flags`.
### `combineFlags(...flags: LicenseFlags[]): number`
OR-merge multiple flags into a single bitmask.
### `hasFeature(featureMask: number, featureIndex: number): boolean`
Test whether feature index `featureIndex` is set. Throws
`LicenseError(LICENSE.FEATURE_INDEX_OUT_OF_RANGE)` when `featureIndex`
is outside `[0, 18]`.
### `combineFeatures(...features: number[]): number`
OR-merge multiple feature indices into a single bitmask. Same range
check as `hasFeature`.
### `getEnabledFeatures(featureMask: number): number[]`
Enumerate every enabled feature index in ascending order. Bits past
index 18 are ignored (consistent with the 19-bit field width).
## Implementation Notes
The 19-feature ceiling matches the 19-bit `featureMask` field in the
license payload. The shared `ensureFeatureIndexInRange` helper means
the two range-checking call sites stay in lockstep; if the bit budget
ever changes, only one constant needs to move. The flag enum uses
power-of-two values (`1 << 0`, `1 << 1`, ...) so `combineFlags` is a
straight `reduce(|)`.