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,85 @@
# `@suaveplan/license` — time
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
Time-encoding helpers for the four `TimeEncodingType` variants. The
72-bit payload encodes time in 18 bits with an additional 2-bit
discriminator (`NONE` / `ABSOLUTE_DAYS` / `RELATIVE_DAYS` /
`ABSOLUTE_TIMESTAMP`), giving four distinct expiration strategies. This
module turns those numeric fields into concrete `{ isExpired,
expirationDate }` records and lets callers inject a `Clock` (from
`@suaveplan/types`) for deterministic test runs.
## Features
- Four expiration strategies, one switch — no separate code paths per
call site.
- Injectable `Clock` parameter; defaults to `Date.now()` so production
callers don't have to thread it through.
- `getDayNumber` / `dayNumberToDate` round-trip helpers used by
generation when computing absolute-day expirations.
- Default-arm fall-through returns `{ isExpired: false }` for unknown
encodings, matching upstream's permissive behaviour (so a future
encoding variant never bricks an older validator).
## Usage: Basic
```ts
import { checkExpiration } from "@suaveplan/license/time/time";
import { TimeEncodingType } from "@suaveplan/license/types/license";
const r = checkExpiration(TimeEncodingType.NONE, 0);
console.log(r.isExpired); // false (perpetual)
```
## Usage: Advanced
Threading a deterministic `Clock` through to relative-day expiry:
```ts
import type { Clock } from "@suaveplan/types";
import { checkExpiration } from "@suaveplan/license/time/time";
import { TimeEncodingType } from "@suaveplan/license/types/license";
const clock: Clock = () => Date.UTC(2026, 5, 17, 12, 0, 0);
const creation = new Date("2026-06-01T00:00:00Z");
const r = checkExpiration(
TimeEncodingType.RELATIVE_DAYS,
14,
undefined, // use clock
creation,
clock
);
```
## API Reference
### `getDayNumber(date?: Date, clock?: Clock): number`
Days since the Unix epoch. Uses `date.getTime()` when provided, otherwise
the injected `clock` (default `Date.now()`).
### `dayNumberToDate(dayNumber: number): Date`
Days since Unix epoch → `Date`.
### `checkExpiration(timeEncodingType, timeValue, referenceDate?, creationDate?, clock?): { isExpired, expirationDate? }`
Determine expiration for a license. `referenceDate` defaults to
`new Date(clock())`. `creationDate` is consulted only for
`RELATIVE_DAYS` (falls back to `referenceDate`).
## Implementation Notes
The unknown-encoding default arm (`return { isExpired: false }`) is
intentional — it makes adding a new encoding variant a backward-
compatible change for existing validators, which is important because
license keys are long-lived and may outlive the validator that
originally minted them. The `clock` parameter sits last in the
argument list and defaults to `Date.now()`, so the public
`checkExpiration` call site stays compact in normal usage and only
expands when a test needs determinism.