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.
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.
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.
| 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.
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.
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.
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.
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.
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.