@slepp/requisite
v0.1.0
Published
Validated confidence gates and monotonic freshness checks for TypeScript.
Maintainers
Readme
Requisite for TypeScript
Validated confidence gates and monotonic freshness checks for TypeScript, with an optional no-dependency trust-transition wrapper.
npm install @slepp/requisiteThe package ships ESM, CommonJS, declarations, source maps, and subpath exports. It has no runtime dependencies and requires Node.js 20 or later.
Confidence gates
Confident<T> keeps its value behind a private field until gate() classifies
the validated probability.
import {
Confident,
Thresholds,
type Certain,
} from "@slepp/requisite";
function execute(_proof: Certain, value: string): void {
console.log(value);
}
const forecast = new Confident("wake operator", 0.98);
const thresholds = new Thresholds(0.7, 0.97);
const decision = forecast.gate(thresholds);
switch (decision.kind) {
case "certain":
execute(decision.proof, decision.value);
break;
case "likely":
console.log("request review", decision.value);
break;
case "unsure":
console.log("record only", decision.value);
}Probabilities must be finite numbers in 0..=1. The default thresholds are
0.60 and 0.95. Custom certain thresholds may be raised but not lowered
below Thresholds.MIN_CERTAIN, so every Certain token has the same minimum
meaning.
Certain has a module-private type brand and runtime identity. It is issued
only by the highest gate branch. isCertain(value) checks whether this copy of
the module issued a token.
A token proves only that some gate in that runtime identity classified a
probability at or above a valid certain threshold. It is reusable and is not
bound to the gated value, the chosen threshold object, the gate invocation, or
a point in time. Keep the proof and value together in application control flow.
String(new Confidence(0.75)) returns Confidence(0.75). Errors expose stable
code fields for recognition across package runtime identities.
Freshness
Each Fresh<T> stores its own fetch time, TTL, and monotonic clock. A value is
fresh through the exact TTL boundary. Monotonic elapsed time may exclude time
while the process or machine is suspended; it is not a wall-clock expiry
deadline.
import { Fresh, type MonotonicClock } from "@slepp/requisite";
let now = 100;
const clock: MonotonicClock = { now: () => now };
const quote = Fresh.capture(499, { ttlMs: 30, clock });
now = 131;
const checked = quote.read();
if (checked.status === "stale") {
console.log(checked.stale.ageMs, checked.stale.overdueByMs);
}read() withholds an expired value. recover() is the explicit stale-value
path:
const recovered = quote.recover();
if (recovered.status === "stale") {
persistForAudit(recovered.value, recovered.stale);
}Fresh.at(value, { fetchedAt, ttlMs, clock }) accepts an existing timestamp
from the same clock epoch and rejects future timestamps. Invalid clock readings
and clock regressions have dedicated error classes.
The default clock is globalThis.performance.now(). Inject a clock for tests
and for systems with another monotonic time source. A clock whose now method
is directly Date.now is rejected because wall time can move backward. A
wrapper such as () => Date.now() cannot be identified at runtime; do not use
one.
Clock regression remains fail-closed: read(), recover(), and
remainingMs() throw ClockRegressionError rather than treating a negative
age as fresh.
Freshness throw paths
| operation | throws |
|---|---|
| Fresh.capture | invalid TTL or clock, non-finite clock reading, or the clock's own exception |
| Fresh.at | the above, non-finite fetchedAt, or future fetchedAt |
| read, recover, remainingMs | invalid/mutated clock, non-finite reading, clock regression, or the clock's own exception |
The concrete classes are InvalidTtlError, InvalidMonotonicClockError,
InvalidClockReadingError, InvalidFetchedAtError,
InvalidFetchTimeError, MonotonicClockUnavailableError, and
ClockRegressionError.
Trust transitions
The trust API is intentionally small. Its value beside schema-library brands is
runtime identity: wrapper implementations and fields are module-private, and
isTrusted checks provenance at runtime.
import {
sanitize,
untrusted,
type Trusted,
} from "@slepp/requisite";
function loadCustomer(id: Trusted<number>): void {
databaseLookup(id.unwrap());
}
const raw = untrusted(" 42 ");
const id = sanitize(raw, (value) => {
const parsed = Number(value.trim());
if (!Number.isSafeInteger(parsed)) throw new Error("invalid id");
return parsed;
});
loadCustomer(id);trySanitize accepts a typed { ok: true, value } | { ok: false, error }
result. That error branch represents expected policy failure. Exceptions thrown
by either sanitize or trySanitize policies propagate unchanged; they are not
converted into result values. Forged or foreign wrappers raise
InvalidTrustInputError whose operation is "sanitize" or "trySanitize".
Trusted<T>.downgrade() returns Untrusted<T>.
The callback defines the destination's policy. An identity callback proves only that an identity callback ran. If a project already uses Zod or Effect schemas and brands, those usually provide richer validation and error reporting.
Enforcement limits
TypeScript is not a security boundary.
any, type assertions,@ts-ignore, and untyped JavaScript can bypass static contracts.- The package's ESM and CommonJS builds are separate runtime identities.
Package-issued
Confidence,Thresholds,Certain,Trusted, andUntrustedvalues or tokens cannot cross between them. Root and subpath imports within one module system share an identity. Use one module system throughout a process. instanceofalso does not cross ESM/CommonJS identities. UsehasRequisiteErrorCode(error, REQUISITE_ERROR_CODES.invalidConfidence)when an error may cross that boundary. Error codes are discriminators, not authenticity proofs, and plain objects can spoof them.- Duplicate physical installations likewise have separate runtime identities.
Certaintokens are reusable andgate()can be called more than once; TypeScript has no affine or linear values.- JavaScript has no move or borrow checking.
recover()cannot consume a wrapper or revoke references obtained earlier. - Freshness is checked only when
read(),recover(), orremainingMs()is called. A previously returned object reference can later become stale. - There is no
Live/withLiveAPI: TypeScript callbacks cannot prevent a value from escaping through outer mutable state.
API surface
The root export and these subpaths are public:
@slepp/requisite/confidence@slepp/requisite/errors@slepp/requisite/freshness@slepp/requisite/trust
Declarations document each exported type and function. See
examples/payment.ts
for a combined flow and
CHANGELOG.md for release notes.
Development
npm ci
npm audit --audit-level=high
npm run typecheck
npm test
npm run checkRuntime tests use Vitest. test-d/ contains positive and negative compile-time
contracts; removing an expected error makes tsc fail. The prepack hook
rebuilds dist, so npm pack does not depend on ignored build artifacts being
present.
License
Licensed under either Apache-2.0 or MIT, at
your option. The package metadata uses the SPDX compound expression
MIT OR Apache-2.0; see the packed LICENSE summary.
