🎩 You're Invited:Meet the Socket team at Black Hat in Las Vegas, August 3-6.RSVP
Sign In

mendapi

Package Overview
Dependencies
Maintainers
1
Versions
8
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

mendapi - npm Package Compare versions

Comparing version
0.5.6
to
0.5.7
+86
-3
CHANGELOG.md

@@ -8,7 +8,90 @@ # Changelog

## [0.5.7] - 2026-08-06
Four scanner fixes, all shipping together. The headline one is the worst class of
bug this tool can have: a repository that used a common import spelling was
scanned, found clean, and told it had nothing to worry about.
### Fixed
- **A repository that loads an SDK dynamically is no longer scanned as clean.**
The JavaScript import matcher enumerated `require(`, `from '...'` and
`import '...'`, but not `import(...)`. A repo that reaches for a provider SDK
through a dynamic import was therefore never considered for that provider at
all: the scan finished, found nothing, and reported no impacted usage. That is
a silent false negative, not a low-confidence verdict — and it is the worst
possible failure for a tool whose entire job is to tell you whether an upstream
breaking change touches your code. Measured on the same dependency: 0 impacts
for the dynamic spelling versus 1762 for the static and CommonJS spellings.
Two further spellings measured at zero for the same reason. A module specifier
may be a template literal — ``import(`stripe`)`` is legal and common in
lazy-loading code — but the quote class held only `'` and `"`. And a call may
span a newline: the matcher accepts it, but the per-line pass that attributes a
line number could not re-match a multiline hit, so a confirmed whole-file match
was dropped. Line attribution now runs as a single whole-text pass, so a
multi-line usage survives even when the same file also contains a single-line
one. Adding the dynamic form exposed a pre-existing twin: neither parenthesised
form carried a left boundary, so `myrequire('stripe')` already false-matched.
Both now carry a negative lookbehind, which also excludes member calls such as
`obj.import(...)`. Evidence requirements are unchanged — the path-level
evidence bar is not relaxed, and a substituted template specifier is still
treated as a variable reference that a text matcher must never claim to
resolve.
- **A partly failed `sync` no longer looks like a successful one.** `sync` is the
only networked command and the first thing every user runs. Previously only a
total wipeout (every feed erroring) exited non-zero; if some feeds failed and
others succeeded, sync exited `0` and said nothing. Because GitHub rate-limits
anonymous feed requests (60/hour, and sync fetches 27 feeds), a second run
inside the same hour could leave most providers unfetched. The resulting change
database is thin, and the next `mendapi scan` truthfully reports no impacted
usage — which is indistinguishable from a genuinely clean repository. Sync now
ends with an explicit verdict: `sync complete:` on success, or
`sync incomplete:` on stderr naming every provider left stale plus the coverage
percentage, and warning that a scan run now can report no impact simply because
those feeds were never fetched. Exit code stays `0` for a partial failure (the
database did get fresher, and one dead upstream feed should not fail CI) and
remains `1` when every feed fails. `mendapi sync --help` now documents both
exit codes.
- **A value-taking flag given without its value no longer falls back to a
default.** `mendapi scan --repo` (flag present, value missing) exited `0` and
reported on the current directory instead — the common shape being
`--repo $REPO` in CI with `REPO` unset. The user gets a complete, plausible
report about a tree they did not ask about, with no warning and no non-zero
exit. All five flag-parsing subcommands (`scan`, `fix`, `llmfix`, `pr`,
`deps`) now declare which flags require a value and exit `2` with
`Missing value for --<flag>` when one is missing. Boolean flags
(`--json`, `--apply`, `--push`, `--run-checks`, `--match`, ...) are unaffected.
This completes the fix started in 0.5.6, which covered discarded positional
arguments but not half-given flags.
- **Endpoint-level evidence is now held to a path-level bar, not a host-level
one.** An impact used to qualify on the strength of the host alone, so any
usage of a provider's domain could carry a finding about one specific
endpoint. Evidence must now match at path level for the endpoint it claims.
### Changed
- **Changelog wording for 0.5.6 corrected.** The published 0.5.6 tarball ships a
preamble describing the release as "landed in the repository but not yet
published to npm" — text written while the release was still pending and left
in place when it went out. It now states what the release contains. No code
behaviour changed; this entry exists so the correction reaches users with the
next publish rather than silently.
- **The "30 seconds" claim now states the repo size it holds for.** Every surface
that made the claim said "in under 30 seconds" with no qualification. Measured
wall-clock scan time is roughly 8 ms per file, so the budget is exhausted at
around 3,200-3,900 files depending on machine load — true for a typical service
repository, false for a large monorepo, and nothing said which was which. The
README, home page, how-it-works page and docs index now publish a conservative
ceiling (~3,000 files, below the lowest measurement observed) and state in prose
that large monorepos take proportionally longer. The ceiling is a single source
in the build and is asserted against the live measurement by the test suite, so
the published number cannot outrun what the scanner actually does.
## [0.5.6] - 2026-08-05
Changes landed in the repository but not yet published to npm. The set of files
that differ from the published tarball is derived mechanically — see
`loop/release-drift.mjs` and the release-drift gate.
Two correctness fixes that could damage or misreport a user's repository.

@@ -15,0 +98,0 @@ ### Fixed

@@ -21,3 +21,3 @@ #!/usr/bin/env node

// node app/llmfix.js --from-report <impact.json> [--repo path]
// [--max N] [--out-dir dir] [--list]
// [--max <n>] [--out-dir dir] [--list]
//

@@ -40,3 +40,3 @@ // --list print the candidate changes (those with a code-fixable knowledge

function usage() {
console.error('Usage: mendapi llmfix --from-report <impact.json> [--repo <path>] [--max N] [--out-dir <dir>] [--list]');
console.error('Usage: mendapi llmfix --from-report <impact.json> [--repo <path>] [--max <n>] [--out-dir <dir>] [--list]');
console.error('Requires MENDAPI_LLM_* config (BYO compute; see the BYO LLM docs at https://mendapi.com/docs/byo-llm.html). Emits DRAFT patches only; never modifies the repo.');

@@ -53,2 +53,8 @@ process.exit(2);

// flag, so anything not starting with `--` can only be a mistake. Fail loud.
// Flags that require a value. A flag listed here that arrives without one is a
// half-finished instruction (`--repo $REPO` where REPO is unset is the common
// shape), not an absent one -- so it must fail loud rather than fall back to a
// default and answer a different question than the one the user asked.
const VALUE_FLAGS = new Set(['repo', 'from-report', 'out-dir', 'max']);
function parseArgs(argv) {

@@ -58,3 +64,13 @@ const args = {};

const a = argv[i];
if (a.startsWith('--')) { args[a.slice(2)] = argv[i + 1] && !argv[i + 1].startsWith('--') ? argv[++i] : true; continue; }
if (a.startsWith('--')) {
const name = a.slice(2);
const value = argv[i + 1] && !argv[i + 1].startsWith('--') ? argv[++i] : true;
if (value === true && VALUE_FLAGS.has(name)) {
console.error(`Missing value for --${name}`);
console.error(`Usage: --${name} <value>. Run with --help for usage.`);
process.exit(2);
}
args[name] = value;
continue;
}
console.error(`Unexpected argument: ${a}`);

@@ -61,0 +77,0 @@ console.error('This command takes flags only (for example: --repo <path>). Run with --help for usage.');

+1
-1
{
"name": "mendapi",
"version": "0.5.6",
"version": "0.5.7",
"license": "AGPL-3.0-only",

@@ -5,0 +5,0 @@ "type": "module",

+17
-1

@@ -34,2 +34,8 @@ #!/usr/bin/env node

// flag, so anything not starting with `--` can only be a mistake. Fail loud.
// Flags that require a value. A flag listed here that arrives without one is a
// half-finished instruction (`--repo $REPO` where REPO is unset is the common
// shape), not an absent one -- so it must fail loud rather than fall back to a
// default and answer a different question than the one the user asked.
const VALUE_FLAGS = new Set(['repo', 'migration', 'from-report', 'out-dir']);
function parseArgs(argv) {

@@ -39,3 +45,13 @@ const args = {};

const a = argv[i];
if (a.startsWith('--')) { args[a.slice(2)] = argv[i + 1] && !argv[i + 1].startsWith('--') ? argv[++i] : true; continue; }
if (a.startsWith('--')) {
const name = a.slice(2);
const value = argv[i + 1] && !argv[i + 1].startsWith('--') ? argv[++i] : true;
if (value === true && VALUE_FLAGS.has(name)) {
console.error(`Missing value for --${name}`);
console.error(`Usage: --${name} <value>. Run with --help for usage.`);
process.exit(2);
}
args[name] = value;
continue;
}
console.error(`Unexpected argument: ${a}`);

@@ -42,0 +58,0 @@ console.error('This command takes flags only (for example: --repo <path>). Run with --help for usage.');

@@ -15,2 +15,4 @@ # mendapi

That budget is measured, not aspirational: a 3,000-file repo is about where 30 seconds runs out. Large monorepos take proportionally longer.
`sync` pulls the upstream API change feed into a local SQLite database. It is the only command that touches the network, and you run it once (then whenever you want fresher data).

@@ -168,3 +170,3 @@

Published on npm as [`mendapi`](https://www.npmjs.com/package/mendapi) (v0.5.6). Early release — the change database and migration pack registry grow daily; interfaces may still shift before 1.0.
Published on npm as [`mendapi`](https://www.npmjs.com/package/mendapi) (v0.5.7). Early release — the change database and migration pack registry grow daily; interfaces may still shift before 1.0.

@@ -171,0 +173,0 @@ ## License

@@ -206,2 +206,47 @@ #!/usr/bin/env node

// syncVerdict — pure, importable, no I/O. Decides what a sync run means for the
// user, given how many feeds were attempted and how many failed.
//
// Why this exists (Loop 680): `sync` is the only networked command and it is the
// FIRST thing every user runs. Before this, only a TOTAL wipeout (errors > 0 &&
// inserted === 0) exited non-zero. A PARTIAL failure — GitHub's anonymous rate
// limit is 60 req/h and we fetch 24 feeds, so a second run within the hour can
// fail most of them — exited 0 while quietly leaving the change database thin.
// `scan` then truthfully reports "no impacted usage", and the user concludes the
// product found nothing. That is the silent-wrong-answer family (Loop 665/675/676)
// landing on the one command whose failure mode is invisible by construction:
// a report of zero findings looks exactly like a clean repo.
//
// The verdict is deliberately not "any error is fatal". Upstream feeds go down
// individually all the time, and failing the whole run for one dead feed would
// make `sync` useless in CI. Three states:
// ok — every feed answered.
// degraded — some feeds failed. exit 0 (the DB did get fresher), but say so
// loudly and name the missing providers, because the next `scan`
// is now blind to exactly those.
// failed — every feed failed, or none succeeded. exit 1.
export function syncVerdict({ attempted, errors, inserted, failedProviders = [] }) {
if (attempted === 0) {
return { status: 'failed', exitCode: 1, coverage: 0,
message: 'sync attempted no feeds — nothing was fetched.' };
}
const okFeeds = attempted - errors;
const coverage = Math.round((okFeeds / attempted) * 100);
const uniq = [...new Set(failedProviders)].sort();
if (okFeeds === 0) {
return { status: 'failed', exitCode: 1, coverage,
message: `sync failed: all ${attempted} feeds errored. The change database was not updated.` +
' Check network access, then re-run `mendapi sync`.' };
}
if (errors > 0) {
return { status: 'degraded', exitCode: 0, coverage,
message: `sync incomplete: ${errors} of ${attempted} feeds failed (${coverage}% coverage).` +
` Changes for these providers may be missing or stale: ${uniq.join(', ')}.` +
' A scan run now can report no impact simply because those feeds were not fetched.' +
' GitHub rate-limits anonymous feed requests; wait and re-run `mendapi sync`.' };
}
return { status: 'ok', exitCode: 0, coverage,
message: `sync complete: ${attempted}/${attempted} feeds fetched, ${inserted} new change(s).` };
}
async function main() {

@@ -214,5 +259,8 @@ const db = openDb();

let inserted = 0, errors = 0;
let attempted = 0;
const failedProviders = [];
const jobs = [];
for (const [provider, repos] of Object.entries(SOURCES)) {
for (const repo of repos) {
attempted++;
jobs.push(

@@ -231,2 +279,3 @@ fetchFeed(repo)

errors++;
failedProviders.push(provider);
console.error(`error ${provider.padEnd(11)} ${repo}: ${err.message}`);

@@ -244,2 +293,3 @@ })

for (const url of urls) {
attempted++;
clJobs.push(

@@ -258,2 +308,3 @@ fetchChangelog(url)

errors++;
failedProviders.push(provider);
console.error(`error ${provider.padEnd(11)} changelog ${url}: ${err.message}`);

@@ -271,3 +322,10 @@ })

db.close();
if (errors > 0 && inserted === 0) process.exit(1);
// Verdict last, so it is the final thing the user sees. A degraded run must not
// be indistinguishable from a clean one: the next `scan` is blind to exactly the
// providers named here, and "no impact found" would look identical either way.
const verdict = syncVerdict({ attempted, errors, inserted, failedProviders });
if (verdict.status === 'ok') console.log(verdict.message);
else console.error(verdict.message);
if (verdict.exitCode !== 0) process.exit(verdict.exitCode);
}

@@ -282,2 +340,7 @@

console.log('and it only runs when you invoke it without --help.');
console.log('');
console.log('Exit codes: 0 when every feed was fetched, or when some feeds failed');
console.log('but others succeeded (a "sync incomplete:" line on stderr names the');
console.log('providers left stale — a later scan is blind to exactly those).');
console.log('1 when every feed failed and the database was not updated.');
}

@@ -284,0 +347,0 @@

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display