/** Families we can mask. Anything else is returned untouched. */
export type CommentSyntax = "c-like" | "hash" | "none";
/**
* Which comment syntax a path uses.
*
* Two families are deliberately "none", and both are load-bearing:
*
* `.json` — JSON has no comments and JSONC does. Guessing wrong would hide a
* reference in the one format that actually carries dependency edges.
*
* `.jsx`/`.tsx` — JSX text is not lexable without a real parser, and guessing
* costs a FALSE NEGATIVE rather than a false positive. `src/*`
* opens what looks like a block comment, which then runs to the next `*/`
* anywhere in the file and masks every import in between. A bare URL in a text
* node does the same to one line. So JSX is scanned exactly as it was before
* comment masking existed: noisier, never blind.
*/
export declare function commentSyntaxForPath(path: string): CommentSyntax;
/**
* A span of the source that is not code: a comment, or a string/template
* literal.
*
* The masker needs the comments. The guard-test mention audit needs the
* literals and the bracket they sit inside, which is why one lexer produces
* both rather than two state machines drifting apart.
*/
export interface SourceToken {
kind: "comment" | "literal";
start: number;
/** Exclusive. */
end: number;
/**
* Callees of every unclosed call this token sits inside, innermost last.
* `null` entries are groupings and control heads, which call nothing.
*/
callees?: ReadonlyArray;
/** A template chunk that RESUMES after `${…}`, so its own text is spliced with computed values. */
interpolated?: true;
}
/**
* The text with comments replaced by spaces, same length, same line numbers.
*
* Fails open to the original text: an unparseable file is scanned exactly as
* it was before this module existed.
*/
export declare function maskComments(text: string, syntax: CommentSyntax): string;
/** Convenience: mask by file path. */
export declare function maskCommentsForPath(text: string, path: string): string;
/**
* Can this source only MENTION `moduleName`, never load it?
*
* Answers the one question the guard-test exemption rests on. The exemption
* exists because the mandated guard test must name the retired runtime in order
* to assert its absence; it must not become a way to load it. So every literal
* occurrence has to sit somewhere that provably cannot resolve a specifier:
* bound to a variable, an element of an array, a property value, or an argument
* to one of `INERT_CALLEES`.
*
* Fails CLOSED — `false` — on anything it cannot read: a JSX file, a lexer that
* lost the thread, an interpolated template chunk, an occurrence outside every
* literal. A false positive costs a repo one line; a false negative costs the
* gate its meaning.
*/
export declare function mentionsCannotLoad(text: string, path: string, moduleName: string): boolean;
/**
* A constant data structure spelled out in source: a literal array, a literal
* record, or a string.
*
* WHY THIS EXISTS. A bundler that inlines a dependency copies that
* dependency's constants into the consumer's output verbatim. The consumer did
* not write them and cannot delete them. Deciding what to do about that needs
* the STRUCTURE the names sit in, because that is the only place the answer
* lives: a filename cannot tell a copied constant from a hand-written one, and
* the presence or absence of import specifiers elsewhere in the file cannot
* either — a bundle that inlines `package.json` carries a whole `dependencies`
* map with no specifier anywhere near it.
*
* Deliberately NOT a JS parser. It reads one shape only: collections whose
* leaves are all string literals. That shape can hold data and nothing else —
* no call, no identifier, no computed member, no template with a substitution.
* Anything else makes the parse fail, and a failed parse yields no region, so
* the caller learns nothing and scans the text as it stands.
*/
export type InlineDataNode = {
kind: "string";
value: string;
start: number;
end: number;
} | {
kind: "array";
items: readonly InlineDataNode[];
start: number;
end: number;
} | {
kind: "record";
entries: ReadonlyMap;
start: number;
end: number;
};
/** An outermost inert collection, with the name it is bound to if it has one. */
export interface InlineDataRegion {
root: InlineDataNode;
/**
* The identifier this collection is assigned to — `RUNTIME_PATTERNS` in
* `var RUNTIME_PATTERNS = [...]`. `null` when the collection is not assigned
* to anything, which is also the answer when we could not read a name.
*/
boundName: string | null;
start: number;
end: number;
}
/**
* The outermost inert collections that contain any of `needles`.
*
* Driven from the occurrences rather than from every bracket in the file. A
* file that names none of them costs one substring search per needle and
* nothing else; a file that names one adds a single O(text) lex, which is what
* tells a stored constant from a call ARGUMENT. Everything after that is
* proportional to how many times the caller's names appear — a handful — and
* not to the size of a bundle.
*
* OUTERMOST matters for `boundName`: the record `{ pattern: "…" }` is an
* element of `RUNTIME_PATTERNS`, and it is the array that carries the name a
* later `require(NAME[0])` would have to use.
*/
export declare function inlineDataRegions(text: string, needles: readonly string[]): InlineDataRegion[];
/** Every node in a region, outermost first. */
export declare function inlineDataNodes(node: InlineDataNode): InlineDataNode[];
/**
* Does a load call in this text mention `name`?
*
* The companion to `isInertPosition`: that one refuses to explain away a
* collection something reads a member out of ON THE SPOT, and this one refuses
* when the collection was stored under a name and a load call names it —
* `var X = [...]; __require(X[0]);`.
*
* Bounded to the argument list, because a load call is the only place a
* specifier can arrive.
*/
export declare function loadCallMentions(text: string, name: string): boolean;
/**
* A span whose BYTES have been read and found to be one quoted constant.
*
* `constant` is not decoration — it is the claim the span carries, and
* `blankConstantSpans` re-checks it against the text before suppressing
* anything. So a span cannot be moved, widened, or synthesised somewhere else
* and still be blanked: it has to keep saying what it is, and be it.
*
* `quotedConstantSpan` is the only function that produces one. The TYPE is part of
* the mechanism, but it is worth being exact about how much it buys, because the
* first wording of this comment overstated it and a review measured the gap.
*
* WHAT IT DOES BUY: a plain `{start, end}` is not assignable here, so `tsc`
* rejects the specific edit that reverted this rule four times in a row —
* pushing the ENCLOSING node's span into the span list. That edit now needs an
* explicit cast, which is greppable.
*
* WHAT IT DOES NOT BUY, measured: widening the span array's own annotation back
* to `Array<{start: number; end: number}>` and calling an unchecked blanker
* restores the vulnerability with ZERO casts and `tsc` green. So the type is a
* guardrail against the obvious edit, NOT a proof. What actually catches a
* determined revert is `blankConstantSpans`'s runtime re-check, the duplicate-key
* forges, and the mutation audit. `blankSpans` is un-exported so that route needs
* a visible re-export rather than a one-word annotation change.
*/
export interface ConstantSpan {
readonly start: number;
readonly end: number;
/** The constant these bytes were compared against, quotes excluded. */
readonly constant: string;
}
/**
* The span of `node`, IF the bytes it covers are exactly one quoted `expected`.
*
* THIS IS THE WHOLE RULE, AND IT IS A BYTE COMPARISON ON PURPOSE.
*
* Four evasions reached a copy of this repo's own denylist through one gap:
* every one of them was caught, or not caught, by a check that read a PARSED
* structure while the caller suppressed RAW BYTES. The parse is lossy — most
* recently and most cheaply, `parseInlineData` stores a record's entries in a
* `Map`, so a duplicate key collapses last-wins and the shadowed value is a
* region of text nothing ever looked at. Repeating one key was the entire cost
* of getting a credential through a blanked span.
*
* So the comparison and the action are now on the SAME representation. What is
* compared is `text.slice(node.start, node.end)`; what gets blanked is
* `node.start .. node.end`. There is no third thing in between for a shape to
* hide in.
*
* WHY THIS DELETES CHECKS INSTEAD OF ADDING ONE, which is the reason to believe
* it is not another narrowing. Three separately-tested rules are strictly
* implied by this single line and were removed with it:
*
* - "the value must be a plain string, not a nested collection" — a record's
* span opens with `{` and an array's with `[`, so neither can equal
* `quote + expected + quote`;
* - "the value must equal the row's value" — that IS this comparison, on the
* stricter representation;
* - "the value's kind must be `string`" — subsumed by the opening quote.
*
* WHAT IT NEWLY REFUSES, and the refusal is deliberate: a literal that needs an
* escape to spell the constant. `"a\"b"` decodes to `a"b`, so the old check
* would call it equal — while the span it authorises is two bytes longer than
* the thing that was compared. No pattern in the table contains a quote or a
* backslash today, so this costs nothing measurable; it is here so that the
* guarantee does not quietly depend on that staying true. A row that one day
* needs an escape simply stops being attributed, which is the noisy direction.
*/
export declare function quotedConstantSpan(text: string, node: InlineDataNode | undefined, expected: string): ConstantSpan | null;
/**
* Blank verified spans, re-checking each one's claim against the text first.
*
* The check is not belt-and-braces on `quotedConstantSpan`; it is what makes a
* WRONG span observable instead of silent. A caller that casts its way past
* `ConstantSpan` — or that computes an offset from one text and blanks it in
* another — trips this, and it trips as a thrown error in the scanner's own
* test suite rather than as a quietly clean scan of somebody's artifact.
*
* It throws rather than dropping the span, because a span that does not match
* its own claim is a bug in this file, not something an input can cause. Every
* shape an INPUT can produce is answered by returning `null` above.
*/
export declare function blankConstantSpans(text: string, spans: readonly ConstantSpan[]): string;
/**
* Does this text import the module — in any of the four forms that create an
* edge — including deep imports like `@hasna/cloud/dist/adapter.js`?
*
* `from "x"` covers both `import ... from` and `export ... from`. The
* whitespace after a bare `import` is optional because `import"x/register";`
* is the same side-effect import with the space deleted.
*/
export declare function importsModule(maskedText: string, moduleName: string): boolean;
/**
* Local names bound by importing `moduleName`.
*
* This is what separates a fleet package's own `registerCloudTools` — defined
* in its own `src/mcp/cloud-tools.ts` and routed at its own service — from the
* retired shared runtime's export of the same name. A bare identifier says
* nothing about where it came from; the import statement says everything.
*/
export declare function importedBindings(maskedText: string, moduleName: string): Set;