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

@scientific-method/standard-checker

v6.0.0

Published

Checks a restoration's spec/, parity/ and deviations/ against version 1 of the dinorefurb documentation standard.

Readme

@scientific-method/standard-checker

Checks a game restoration's spec/, parity/ and deviations/ against version 1 of the dinorefurb documentation standard, and writes what the standard says is generated: the four indexes in spec/index/, the totals in PARITY.md, and on request a validation run file in validation/. It has no dependencies and needs Node 22 or later.

pnpm add -D @scientific-method/standard-checker
pnpm exec standard-checker            # check, then rewrite stale indexes and PARITY.md
pnpm exec standard-checker --check    # check, and fail on a stale index or PARITY.md

It exits with 0 when the repository passes, 1 when it reports problems (one line per problem, starting with the file's path), and 2 when the options are invalid or --record-validation cannot write the record. --record-validation cannot be combined with --check, nor --require-ksc with --no-ksy, nor --message with either --check or --record-validation.

A pass ends with spec check passed: and the counts of entries, parity rows and deviations. When a step of the check did not run, it ends with spec check passed with skipped steps:, the counts, and Skipped: followed by each step and why, such as Skipped: Kaitai compilation of 2 definitions (--no-ksy). A failing run lists the skipped steps after its problems. A call or emit whose arguments cannot be counted, because the Parameters section it is counted against is missing or is not None. or a list of parameters, or because its argument list is never closed, is a skipped step as well. For a section in another form, the skipped step names the first thing that stops the count, such as item 2 names more than one parameter or it holds text and no list.

Without --base, the checker compares the spec with where HEAD forked from origin/$GITHUB_BASE_REF, or from origin/main when that variable is unset. Outside a git repository, in a shallow clone, or when that branch was never fetched, the fork point does not resolve and the comparison does not run. The result line then names it, such as Skipped: comparison with the base branch (HEAD has no merge-base with origin/main, fetch it with enough history or pass --base). Without git on PATH the skipped step names the missing git instead. With --require-base, either case is a problem and the run fails.

The same comparison finds an ID taken twice before the two branches meet. A spec ID or deviation whose file this change adds since the fork point, committed or not, fails when the tip of the base branch (--base, or origin/$GITHUB_BASE_REF or origin/main) holds a file under that ID that matches neither the working tree, the index, nor the file at any commit of this branch since the fork point: another change took the ID first, and IDENTIFIERS-6 has the branch merged second renumber its entry before it is merged, as in spec/findings/FND-SCORE-012.md: FND-SCORE-012 also exists at origin/main with content this branch never held; renumber this one before it is merged [IDENTIFIERS-6]. A tip that holds the branch's own entry, merged or cherry-picked there in an earlier version, passes, and so does one the base branch edited after taking it. Run the check before committing an entry, for example from a pre-commit hook, so the renumbering happens before commit messages and other files cite the ID. Pass the branch a change merges into as --base when it is not the default, such as a long-lived branch that several working branches merge into. Only the tip of that one branch is read: an ID taken on another branch, or in a draft no one has committed, is not seen until it reaches it.

A problem that breaks a numbered rule of the standard ends with the rule's label in brackets, such as [STATUS-14]. The standard opens that rule with the heading ###### STATUS-14, anchored at #status-14 on the site and in the copies restorations vendor, so the rule can be read on its own. Rules are numbered in Identifiers, Status and Entry types up to the end of Builds so far, and problems under other sections carry no label yet.

A build that keeps a listing record, spec/builds/<ID>.listing.yaml named by the entry's listing field, has it checked against its manifest and its list of other files (ENTRY-TYPES-18): every file the record lists is in the manifest with the manifest's size, in the list of other files, or under a directory exclusion; every manifest path and every listed other file is in the record; and every link or stopped path is in the list of other files by its own path. A disc read from an image names that image in source by a path the manifest or the list of other files gives (ENTRY-TYPES-16). Each problem names the path. Agreement shows only that the three name the same paths. It does not show that the game uses a file or that the listing missed nothing. Where the list of other files is prose in the Other files section, or <ID>.other-files.yaml could not be read or does not exist, the comparison with it is a skipped step on the result line that gives the reason, and only a readable <ID>.other-files.yaml is compared.

Each pattern in a format entry's files matches a file of the manifest of each build it lists. A superseded format entry keeps the files it had when it was replaced (IDENTIFIERS-7), so its pattern also passes when it matches a path that <ID>.other-files.yaml gives by its own path. A directory exclusion, a list written as prose and a list that could not be read or does not exist do not let it pass, and the problem names which of them stopped it.

Options

| Option | Meaning | Default | |---|---|---| | --root <dir> | The repository to check. | the current directory | | --check | Fail when an index or PARITY.md is stale, instead of rewriting it. | rewrite | | --scheduled-generation | For a restoration that updates the indexes and PARITY.md on its main branch only, such as from a scheduled job. Neither write nor compare them with the spec, and fail when the change since the base (where HEAD forked from --base, or the fork point) edits, adds or removes one, untracked files git does not ignore included. A file that matches its copy at the base branch's tip (--base, or origin/$GITHUB_BASE_REF or origin/main) passes, as does a change that only regenerates them and touches nothing else. The result line names the comparison with the spec as skipped. Without a base the change is not compared either, and the base comparison is named as skipped or, with --require-base, fails. | write or compare | | --base <ref> | The branch this one merges into. Fail when a spec ID, area or deviation that exists where HEAD forked from <ref> is gone, when a superseded format entry has no layout table although it had one there, or when an ID this change adds exists at <ref> with content this branch never held (see below). | origin/$GITHUB_BASE_REF or origin/main; when HEAD's fork point with it does not resolve, the comparison is named as skipped | | --squashed <list> | Comma-separated items OLD=NEW, or OLD=NEW+NEW for an entry replaced by several: superseded entries this change deleted, squashed into the replacements that keep the final content. Without it, a deleted ID fails IDENTIFIERS-6. A listed deletion passes when the entry's superseded_by at the base names exactly the listed replacements and each replacement exists now and is not superseded, or is listed too, so A=B,B=C squashes a chain. An OLD that still exists fails, so keeping the option after the squash reaches the main branch stops a squashed ID from being used again. One the base does not have is named as a skipped step. A squashed ID still cited in the spec, the glossary, the --code and --references directories, parity/ or deviations/ fails, a build or source alias included, and the message names the replacements to cite. The indexes and PARITY.md are written by the check, not searched for citations, so with --scheduled-generation they may name a squashed ID until the main branch regenerates them. Keep the list where the reference check does not read it, such as a workflow file or a text file a wrapper script reads: a .js, .mjs, .ts, .cs or .ps1 file under a checked directory that spells the list out cites every squashed ID in it and fails. | none | | --require-base | Fail when no --base is given and the fork point does not resolve, instead of passing with the comparison skipped. | pass with the comparison skipped | | --no-ksy | Skip compiling the Kaitai definitions in spec/formats/. The result line names the skipped compilation. | compile | | --require-ksc | Fail when spec/formats/ holds Kaitai definitions and no compiler is found, instead of passing with the compilation skipped. | pass with the compilation skipped | | --glossary <path> | Also accept the terms of a draft glossary file, or of a directory of them. | none | | --code <dirs> | Comma-separated directories whose files may cite spec and deviation IDs and hold PLACEHOLDER comments. | src,tests,tools | | --references <dirs> | Comma-separated directories whose files may cite IDs but whose PLACEHOLDER comments do not count against parity. | none | | --images <ranges> | Comma-separated half-open address ranges of the original's flat 32-bit images, such as 0x00400000..0x004C9000. A 0x value inside one that a code comment gives, or that the code uses under a comment, must be recorded in an entry the comment cites. | none, so only fn_ and g_ names are checked | | --max-range <bytes> | The largest address range an entry can record an address by. A larger one, such as a whole section, records only its two ends. | 0x10000 | | --data-dirs <dirs> | Comma-separated top-level directories of the original's data. A path into one must name, with its exact case, a file of some build's manifest or a path of some build's <ID>.other-files.yaml. A path under a directory exclusion counts only when the list gives it by its own path. A path that names nothing known is reported only when its last part holds a digit and no placeholder such as nn. | the top-level directories of the files the manifests list | | --rebuild <dirs> | Comma-separated directories that hold the rebuild. No Markdown file in spec/ may name a path in them, or a source file found in them by its file name. In a source entry in spec/sources/, a path counts only when it exists, in any case, so the entry can cite the external source's own paths. An empty value turns the check off. | src,tests | | --rebuild-source <dirs> | Comma-separated directories that hold the rebuild's source, which cites the rules, formats, screens and bugs it implements (see below). Only files that a --code directory also holds are read. An empty value turns the check off, and the address hint goes back to suggesting a finding. | src | | --message <file> | Check only the commit message in the file: every address it gives must be recorded in an entry it cites, as for a code comment. Everything from the scissors line of git commit --verbose on is left out; comment lines before it are checked, since git keeps them under git commit -m. Exits with 0 or 1, or 2 when the file cannot be read. For a commit-msg hook. | not checked | | --record-validation <builds> | Write validation/<date>-<commit>.md for the marked test files of validated parity rows, naming the comma-separated build IDs the run used and HEAD as the commit the run tested, and delete every other run file, except one that lists a test file git tracks but a sparse checkout does not hold. Run it only after every test in those files passed with none skipped, against HEAD as committed: it refuses, with exit code 2, when the working tree differs from HEAD in anything outside validation/, counting untracked files that git does not ignore. | not written | | --help | Print the options. | |

The work protocol's Implementation batches section has code in src/ cite the rules, formats, screens and bugs it implements and no findings or experiments. Each FND- or EXP- ID that a file under a --rebuild-source directory cites fails, once per file with the lines that cite it. The message names the candidates the protocol moves the citation to: the rules, formats, screens and bugs that are not superseded and list the finding or experiment in their evidence, or, for a superseded one, those its superseded_by leads to. For example: src/Game.cs: line 3 cites FND-SCORE-001, a finding, and code in the rebuild's source cites only the rules, formats, screens and bugs it implements; RULE-SCORE-001 lists it in its evidence: move the citation to it where the code implements it and the comment does not cite it yet, and delete it otherwise. Where there is no candidate, the message says so: the citation is deleted, and the code goes with its tests unless an entry or a deviation that is still live describes what it does. The check cannot tell which entries the code implements, so the choice among the candidates is the session's, and so is the row that each new citation sends back to Code partial. A superseded finding or experiment cited there is reported by this check alone, with its replacements, in place of the citation check's which is superseded; cite what replaced it. A finding or experiment that alone replaced a superseded rule, format, screen or bug is not reported: the protocol's research batch leaves the code written for that entry citing it, with the check passing, until an implementation session removes the code.

The address check's hint follows the same rule. For an address in a file of the rebuild's source that no entry the comment cites records, the message names the rules, formats, screens and bugs that record it or cite evidence that does (RULE-SCORE-001 records it or cites evidence that does: cite the one the code implements). Where none does, it says to leave the address out of the comment and to treat code that needs it as a gap in the spec, and suggests no finding. In other code directories, such as tests and tools, the hint still suggests the finding that records the address.

The KSC environment variable names the Kaitai Struct compiler. Without it, the checker looks for kaitai-struct-compiler or ksc on PATH. When it finds neither, it skips the compilation and names it in the result line, or with --require-ksc reports the missing compiler as a problem.

On Windows the search tries each name with the PATHEXT extensions, as cmd.exe does, so it finds the .bat launcher of the official release and skips the extensionless Unix script beside it. The checker then runs the launcher by its full path. A KSC that holds a bare name, with no directory, is looked up on PATH the same way.

On every platform, a launcher found on PATH whose --version fails is not used, and the checker prints a warning naming its path, with its output.

Coverage

standard-coverage measures how much of each analysed file of code the spec cites and has read completely, against the function inventories in coverage/, and how much of each file the inventory maps, as the work protocol's Measuring progress section describes. It keeps citation coverage, complete reading and mapping coverage apart, and gives no figure that adds files together.

pnpm exec standard-coverage                       # per-file figures and the uncited functions
pnpm exec standard-coverage --list                # also every function with the entries citing it
pnpm exec standard-coverage --json                # the same figures as JSON
pnpm exec standard-coverage --require-complete    # fail while an in-scope function is uncited

An inventory is coverage/<build>/<file>.tsv, with <file> the manifest's path and a CD: prefix written as a directory @CD. Its columns are start and size, then optionally name, out_of_scope and ranges. start is in the standard's notation for the file's format: an address, or for MZ overlay code an offset inside a row of the build's Code ranges. When the body is not one range from start, ranges lists its half-open ranges as start..end in the same notation, separated by spaces: 0x00401000..0x00401010 0x00401200..0x00401210. size is then their total, one of them holds start, and they do not overlap. In an NE file each range, and a body without ranges, ends in the segment it starts in.

Every file a manifest lists whose format, or for a packed file whose unpacked format, holds code and that has no inventory is printed as <build>: <file> has no inventory and is not measured, followed at the end by how many there are, and --json lists each under withoutInventory with build, file and format. No figure covers those files, so an empty unmeasured says only that every inventory present was read. Whether a file without an inventory is outside the work's scope is for the report that uses the figures to say: the manifest lists every file the game uses, including those of builds nobody has analysed. --require-complete measures the inventories that exist and does not fail on a file without one. Members of an archive are not listed, since the checker does not read a manifest's members yet.

An entry cites a function when one of its locations names the same build and file and its address, offset or half-open range overlaps the function's body: its ranges, or size bytes from start when the row gives none. Locations with kind: file-data, into the unpacked form of a packed file, or of superseded entries cite nothing, and neither does an address written in an entry's body. Real-mode segmented addresses (MZ, COM) are compared by the linear address they name, and each NE segment is a space of its own. A row without ranges is measured as if its body were contiguous. The shares count functions and bytes of the functions not out of scope.

Two rows may list the same bytes, as an analyzer that gives one chunk to several functions (a shared function tail) writes them. A location in those bytes cites every function that lists them, and the byte figures count each byte once: bytes and citedBytes are over the union of the bodies. A byte is cited when a cited function lists it, so citedBytes can equal bytes while a function whose whole body lies in a cited one's is still uncited; the function figures and --require-complete count that function. The bytes that two or more in-scope functions list are sharedBytes in --json, and the text line ends with ; <n> bytes listed by more than one in-scope function when there are any.

A cited function has been looked at, not read completely. A function counts as read completely when the locations of the static findings that entries list in complete_reading cover every byte of its body, together when several entries list them; a finding located only by the function's start covers one byte. Only the complete readings the standard accepts count (STATUS-4): static findings the entry lists in evidence and that list its first build, of an entry that is neither superseded nor disputed and whose procedure has no # may run: comment, and that are not superseded themselves. An entry's status counts for nothing here. The text prints complete reading: <n> of <m> functions, <b> of <B> bytes under each inventory, --json gives completeReading as functions and bytes, and --list marks each function read completely, with read in --json. The count is 0 when no complete reading covers a function. It is unavailable, <reason>, and completeReading is { "unavailable": "<reason>" } with no read in the list, when loading the spec had problems, since an entry that could not be read may hold a complete reading, and for a file where a location of a counted finding does not parse. ADR 0043 gives the reasons.

The .provenance.tsv and .regions.tsv files the work protocol puts beside an inventory are read when they exist; a missing one is no problem. The provenance file holds one name and value per line, separated by a tab, each name once, and must give xxh3. An inventory whose provenance gives another xxh3 than the manifest gives for the file, or for a packed file its unpacked form, lists another file and is not measured: it goes under unmeasured with the two hashes as the reason. Otherwise provenance in --json holds the names and values, and the text prints them without the hashes.

The regions file has the columns kind, start, size, instructions, instructions_outside, data, data_outside, undefined and undefined_outside, as ExportFunctionInventory writes it (Exporting a function inventory). Each row's three totals add up to its size and each outside count is at most its total; region rows do not overlap, outside rows overlap neither a region nor each other and have outside counts of 0, and an entry row is the one byte at a start the inventory lists, which can lie in an instruction that starts before it. The file must agree with the inventory, row by row: the bytes each region row puts in bodies must be the bytes of the inventory's bodies in that region, and its outside rows must hold the rest of the bodies, so a regions file from another export of the analysis fails. When it agrees, mapping in --json gives regions, regionBytes, bodyBytes, outsideBodies (instructions, data and undefined), bodyBytesOutsideRegions and startsWithoutInstruction, and the text prints them in one mapping: line. Without the file, or when it has a problem, mapping is { "unavailable": "<reason>" }. The citation figures do not depend on either file.

An inventory that cannot be read, or that has a row that cannot be read, gets no figures. It is printed as <path>: not measured, <reason> (for a bad row, 2 of 40 rows are invalid), each problem is printed after the figures, and --json lists it under unmeasured with path and reason instead of under inventories. Figures over the readable rows alone would leave the other functions out of both counts, and an inventory whose rows are all invalid would read as a file with no functions.

It exits with 0, with 1 when an inventory, a provenance file or a regions file is invalid, when a provenance file names another file, or when --require-complete finds an uncited function or no inventory at all, and with 2 when the options are invalid. Problems with the spec itself are left to standard-checker; a warning gives how many there were while loading it and how many locations in files of code did not parse, since those cite nothing.

standard-checker reads the same inventories. Ranges are half-open, so a range whose end is an inventoried function's last byte (start plus size minus one, or for a row with ranges the last byte of each range, with ranges that touch taken as one) stops a byte short, the usual slip when a range is copied from an analyzer that gives last bytes, and the check fails it with the end it should have, naming every function whose range ends on that byte. An end where an inventoried function starts passes, as the standard's Notation says: a correct range that stops before that function ends there, and a one-byte function's last byte is its first. An end where a later range of a function's body starts passes for the same reason. So the slip in a range whose last item is a one-byte function is not caught. It checks every range a location of a current entry gives in that build and file, by address or by offset, and the address ranges written in the body of an entry whose locations all name that one build and file. Without inventories it checks nothing and reports no skipped step.

A range that ends on a last byte on purpose, such as a function body cited without its one-byte return, passes when the entry lists it in ends_on_last_byte (ENTRY-TYPES-22), written as the entry writes it: ends_on_last_byte: [0x00401000..0x0040103F]. Backticks and spaces around .. are ignored, so 0x00401000 .. 0x0040103F in the text matches that listing. The listing covers every place the entry gives that range, its locations, text and tables. With inventories, the check fails a listed range that the entry does not give, one that no inventory checks anywhere the entry gives it (its file has no inventory, or it is in the text of an entry whose locations name several files), and one it would pass without the listing everywhere the entry gives it, so the list holds only the ranges that need it. It fails a field that is not a list of text items, or an empty one, with or without inventories. It does not compare a listed range with the base branch, so a listed range corrected in place, which the standard requires to be superseded instead, is not caught.

In GitHub Actions

The toolkit's actions/check-documentation composite action runs this checker with --check, installs a pinned Kaitai compiler when the repository has .ksy files (and then passes --require-ksc), and exposes every option above as an input, apart from --message and --record-validation. On a pull request with no base input, it fetches the base branch with enough history for the fork point and passes --require-base. Pin the action to the toolkit commit whose packages/standard-checker matches the version installed here, so CI and local runs apply the same checks.

The documentation standard check guide covers setting up the action, every check the tool makes, and moving an older restoration onto it.