npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@sema-agent/primitives

v7.68.3

Published

Shared low-level definitions of the sema engine, importable by any layer: the credential reference (closed set of kinds, one reader) and the secret-shape detection definition (one table, its matcher and its renderer, no layer's policy). Zero dependencies.

Readme

@sema-agent/primitives

Low-level definitions of the sema engine that every layer may import: no policy of any layer, no state, no dependencies. It holds two members: the credential reference, and the secret-shape detection definition (the one table of credential shapes, its matcher and its renderer). The engine depends on this package and reads it by name, so in a project that installs both there is one copy: an acceptance gate, a settings validator or an admin form reads the same closed set through the same function object as the engine does. Runs in Node and in browsers; ships its own types.

Install

npm install @sema-agent/primitives

ES modules only ("type": "module"), Node >= 20.18.1 where Node is used. One entry, .; there are no deep paths.

Version

@sema-agent/[email protected] is cut with @sema-agent/[email protected]: the same version number, built from the same commit. A package version moves with every core release whether or not the primitives changed.

@sema-agent/[email protected] depends on @sema-agent/[email protected] exactly. A project that also imports this package names the same version as its core (or a range npm can resolve to it); then npm ls @sema-agent/primitives shows one copy. Naming another version installs two — npm keeps core's own beside it — and the two answer for different versions.

The credential reference

A declaration names a credential by reference instead of carrying its value. The reference is one of a closed set of kinds; whoever resolves it (a deployment's seat, not this package) decides what the name means and whether this declaration may use it.

| kind | Members | Request surface (CREDENTIAL_REF_TRUST[kind].request) | | --- | --- | --- | | env | headers?: header name → variable name · env?: child variable → variable name (at least one) | refused | | vault | entry: a stored credential entry | own_entry — only an entry that belongs to the submitting principal | | handle | id: an opaque id the deployment minted | refused | | assertion | audience: what a short-lived assertion is requested for | refused |

readCredentialRef(value) answers { ok: true, ref } with a fresh frozen copy (forward the copy, never the value you read), or { ok: false, problem }. Each fault is its own reason, and the first one found is the one reported, in this order: not a record; kind absent, not text or unknown; an extra member; a missing member; a bad value. The reader does not throw: a value whose members cannot be read (a revoked or throwing Proxy) is not_a_record (or, inside a name map, that map's bad_value). With no stack left a valid value may be refused, under any reason, and never an invalid one accepted; the call itself may throw a RangeError before it is entered, which no function can catch.

| reason | Meaning | | --- | --- | | not_a_record | not an ordinary record (a primitive, null, an array, a class instance, a Map, a Date), or one whose members cannot be read; an object whose prototype is none or itself has none — the Object.prototype of any realm is one — is a record | | unknown_kind | kind is text outside CREDENTIAL_REF_KINDS (every spelling the set does not hold: another case, padded, zero-width, full-width, a look-alike letter) | | extra_field | a member the kind does not have (a member spelled in another case is one) | | missing_field | kind, or a member the kind needs, is absent (for env: neither headers nor env, field headers\|env) | | bad_value | a member of the wrong form (below) |

A member is an own enumerable string-keyed data property. A getter is refused and never called, nothing is read through a prototype, a key present with the value undefined is a bad value (not an absent member), and a non-enumerable or symbol-keyed member is not read and not copied.

bad_value is: for kind, anything but text; for a text member (entry, id, audience), anything but 1 to CREDENTIAL_REF_TEXT_MAX (2048) UTF-16 code units with no control, format or line-separator character (C0 and C1, zero-width, bidi override, soft hyphen, BOM, U+2028 / U+2029), no default-ignorable, noncharacter or private-use character, no lone surrogate and no leading or trailing white space; for a name map (headers, env), anything but a record of 1 to CREDENTIAL_REF_MAP_MAX (64) entries whose names are within the grammar (a header name is an HTTP token; a child variable name and every variable name is [A-Za-z_][A-Za-z0-9_]*) and within the text bound, and — for headers, whose names are case-insensitive — in which no two names differ only by case. The maps come back frozen and without a prototype: an absent name reads undefined, and an own __proto__ name is a plain key. Text is compared as written; nothing is normalised (two spellings of one visible string are two entries; visually confusable text — homoglyphs, a braille blank, stacked combining marks — is not judged, and an emoji presentation selector or a zero-width joiner is refused, because these members are identifiers, not prose). The text bound counts UTF-16 code units (up to 6144 UTF-8 bytes), and the character classes are the host's Unicode tables, so two surfaces on engines with different Unicode versions can differ for a recently added character.

import { readCredentialRef } from "@sema-agent/primitives";

readCredentialRef({ kind: "vault", entry: "github-main" }); // { ok: true, ref: { kind: "vault", entry: "github-main" } }
readCredentialRef({ kind: "vault2", entry: "x" });          // { ok: false, problem: { reason: "unknown_kind", kind: "vault2" } }
readCredentialRef({ kind: "handle" });                      // { ok: false, problem: { reason: "missing_field", kind: "handle", field: "id" } }
readCredentialRef({ kind: "handle", id: "h1", note: "x" }); // { ok: false, problem: { reason: "extra_field", kind: "handle", field: "note" } }

A kind or field the reader echoes back is the caller's text, cut to 64 characters with control, format and line-separator characters and lone surrogates replaced; fence it before rendering it where markup is interpreted. A Proxy's own traps (on the value, its maps or its prototype) are the caller's code and run as they would for any property read.

The reader judges shape only: it does not know whether a variable exists, whether an entry belongs to anyone, which header names a deployment forbids (the protocol's own names, a principal header), which child variables a declaration may set, or whether a reference may reach a destination. Those are the policy of the layer that reads the reference.

CREDENTIAL_REF_TRUST has a row for each kind and for nothing else (it has no prototype, so no inherited member answers); look a kind up in it only after readCredentialRef has accepted the value.

Secret-shape detection

CREDENTIAL_VOCABULARY is the one table of what a credential looks like: value families (each row with a wide form, and for some a strict form, a fragment form — what is left of the credential when a text was cut inside its body — and the form and token head an environment value is read by), the credential words a name is read by, the names that look like credentials but are none, and names: the names that mark a credential in an assignment (places: ["assignment"]) or as a URL query or fragment parameter ("url-param"). Every layer that looks for a secret reads this table, so a shape added to it is found everywhere at once. The table holds no layer's policy: which form is read, how a text is read first and how a URL's username is treated are the caller's, passed in whole on every call. Nothing here has a default. Each value family row names the mark its hit is written as (mark, below).

interface SecretScanPolicy {
  grade: "strict" | "wide" | "fragment";         // the strict forms (narrow: a hit may refuse a write), the wide ones (broad: a hit is withheld) or the fragment ones (a cut residue)
  readings: SecretReading[];                     // at least one of { kind: "raw" } (as written) / { kind: "format-free" } (format characters removed),
                                                 // and any of { kind: "percent-layers", depth: 1..8 } / { kind: "percent-boundary" } / { kind: "form-plus" }
  urlUserinfo: "keep-username" | "whole";        // a URL userinfo: keep the user and withhold the password, or withhold all of it
  onUnresolvedReading: "secret" | "ignore";      // what a reading that cannot read a text to its end answers
}

| Function | Answers | | --- | --- | | scanSecrets(text, policy) | { outcome: "spans", spans } — every hit as { start, end, family, kind } in text's own coordinates, sorted (empty = none found) — or { outcome: "whole", reason } with reason one of scan-budget (more than 64 places where format characters stood), reading-unresolved (the percent layers ran out, under onUnresolvedReading: "secret"), mapping-failed; a hit in a derived text is reported over the written units it was read from | | isSecretShaped(text, policy) | scanSecrets found a span or answered the whole text | | userinfoHolds(userinfo) | a URL userinfo (no trailing @) holds a user or a password — an empty one and the bare separator : hold neither (the reading the keep-username rendering withholds by; a mark is one token, its : splits nothing) | | renderSecrets(text, policy, { passes?, report? }?) | text with every hit replaced by its row's mark (the fragment grade's by that mark's :partial member; a text no reading can afford, whole, by «redacted:oversized»); passes are the caller's own rules, run after the table's in the same pipeline, each marker a member of REDACTION_MARKS; report.findings receives one finding per hit (report.preexistingMarkers is the caller's to set) | | renderWithheld(text, spans) | text with each { start, end, mark } span withheld by the same law renderSecrets writes its own hits by — for a layer that finds its regions itself | | isSensitiveRecordKey(key) / isSensitiveHeaderName(name) | a record member / an HTTP header name names a credential | | envNameView() | the readings of environment variable names (exact names, credential words, a namespace's names anySegmentUnder(namespace)) | | envValueView() | the readings of environment values, off the rows: forms (each row's env value form, in table order), tokenHead (each row's token head, one anchored alternation), flagWords | | secretReadingViews(text, readings) | the texts the readings derive — the written text first, each once — each with toOriginalSpan(start, end) (the whole written units a span was read from), and unresolved (the percent layers ran out while one more would still decode) |

A policy that is not whole (a missing or unknown member), a caller's pass whose marker is no member of the marks table, or whose string replace writes anything but that member and $n references, throws a TypeError; so does a renderWithheld span that is not a range of the text or whose mark is no member. The policy is read on every call; what it reads as picks a compilation kept across calls. scanSecrets answers where its forms match, a mark the text holds included (password=«redacted:key» is a span); renderSecrets leaves a hit whose withheld part is exactly a mark the text already holds (below). URL_OF_ANY_SCHEME is the URL grammar (a global regex: read it with replace / matchAll), for a layer that withholds whole URLs. CREDENTIAL_MIN_CHARS is the fewest characters a string can have and still be read as a credential.

Redaction marks

REDACTION_MARKS is the one table of what stands in a text where a layer withheld part of it — fifteen members, frozen:

| Name | Member | Written for | | --- | --- | --- | | bare | «redacted» | a value whose place in the text already says what it was: a credential name's value, an assignment, a header value, a scheme word's credential (Authorization: «redacted»), the password after a kept username (https://alice:«redacted»@host), a URL query or fragment value | | key | «redacted:key» | a credential read by its shape alone (sk-…, AIza…, a JWT, …) | | gh-token / aws-key / slack-token / private-key | «redacted:gh-token» … | a credential read by its shape alone, of that vendor or kind | | <kind>:partial | «redacted:key:partial» … | the same five kinds, found by the fragment grade (a cut residue) | | userinfo | «redacted:userinfo» | a URL userinfo withheld whole | | url / path | «redacted:url» / «redacted:path» | a whole URL / an absolute path, withheld by a layer that hides where things are | | oversized | «redacted:oversized» | a stretch withheld because it could not be read |

isRedactionMark(s), redactionMarkerSpans(s) and eachRedactionMarkerSpan(s) (lazy) are the one reader: they recognize the members exactly — «redacted:evil» and square-bracket spellings ([redacted], [redacted-url]) are ordinary text. A mark is one token to the renderer: a hit whose withheld part is a mark the text already holds is no hit (password=«redacted:key» stays as written), a hit reaching into or inside a mark takes the whole mark, and hits that overlap or touch write one mark — the widest hit's (of two over one region, the later pass's). Rendering a rendered text again changes nothing.

Every count in the table follows one character or one character class and is closed — "n or more" is written as n of them and then a plain loop, and a group's count is written out — so a reading keeps no regex backtrack state per character of one value run, and a run of millions of characters is scanned and rendered to its end (pinned at 8 million code units) instead of throwing RangeError. A credential header value made of millions of = / , / quoted-parameter pieces is still read one group pass per piece and throws from about two million code units on.

The table and its rows are frozen; its regexes are not (replace writes a global regex's lastIndex), and the functions here never run them: the package runs its own copies, made as it loads, so a regex a caller recompiles or moves on the exported table changes only that caller's reading.

import { renderSecrets, scanSecrets } from "@sema-agent/primitives";

const policy = Object.freeze({ grade: "wide", readings: Object.freeze([Object.freeze({ kind: "raw" })]), urlUserinfo: "keep-username", onUnresolvedReading: "secret" });
renderSecrets("clone https://me:[email protected]/r", policy);           // "clone https://me:«redacted»@git.example/r"
scanSecrets("clone https://me:[email protected]/r", policy);             // { outcome: "spans", spans: [{ start: 17, end: 25, family: "url-userinfo", kind: "url-credentials" }] }

Detection is best effort over known shapes: it never proves a text holds no credential.

Exports

Each name below is a runtime value or a type exported from the package root. The root's declarations are the contract; a removed or re-kinded export is named as breaking in the @sema-agent/core release notes.

| Group | Names | | --- | --- | | Credential reference | readCredentialRef CREDENTIAL_REF_KINDS CREDENTIAL_REF_TRUST CREDENTIAL_REF_TEXT_MAX CREDENTIAL_REF_MAP_MAX CREDENTIAL_REF_PROBLEM_REASONS | | Types | CredentialRef CredentialRefKind CredentialRefRequestAdmission CredentialRefProblem CredentialRefProblemReason CredentialRefReading | | Secret-shape detection | CREDENTIAL_VOCABULARY CREDENTIAL_MIN_CHARS URL_OF_ANY_SCHEME SECRET_READING_KINDS scanSecrets isSecretShaped renderSecrets renderWithheld isSensitiveRecordKey isSensitiveHeaderName envNameView envValueView secretReadingViews splitCamelCase | | Redaction marks | REDACTION_MARKS isRedactionMark redactionMarkerSpans eachRedactionMarkerSpan | | Types | CredentialVocabulary ValueFamilyRow CredentialWordRow CredentialWordPosition CredentialNameRow CredentialNamePlace EnvNameView EnvValueView RecordKeyView RedactionConfidence RedactionSource SecretGrade SecretReading SecretReadingKind SecretReadingView SecretReadingViews SecretScanPolicy SecretScan SecretSpan RenderOptions RedactionPass RedactionFinding RedactionReport SummarizableFinding WithheldSpan RedactionMark RedactionMarkName |