Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 12 additions & 19 deletions scripts/check-console-intercept-disarm.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -89,26 +89,19 @@ import { existsSync, mkdirSync, mkdtempSync, readdirSync, readFileSync, rmSync,
import { dirname, join, resolve } from 'node:path';
import { tmpdir } from 'node:os';
import { fileURLToPath } from 'node:url';
import { blank, scanSource } from './js-comment-mask.mjs';
import { maskCommentsAndLiterals } from './js-comment-mask.mjs';
import { isEntrypoint } from './invoked-as.mjs';

/**
* Comments AND string/template/regex content blanked, offsets kept. This gate
* looks for a bare code-position `disableConsoleIntercept: true`, so unlike
* the gates whose signal IS a string literal, a quoted spelling here is never
* the real setting — it is prose (an error message, a doc snippet) and
* blanking it keeps prose from satisfying the check. The boundary this
* accepts: a config spelling the KEY as a quoted property
* (`'disableConsoleIntercept': true`) reds the gate even though vitest would
* honour it — the failure is loud, names the file, and the remedy is the
* unquoted spelling every other config uses.
*/
function maskProse(source) {
const { comment, literal } = scanSource(source);
const flags = new Uint8Array(comment.length);
for (let i = 0; i < flags.length; i++) flags[i] = comment[i] | literal[i];
return blank(source, flags);
}
// Why this gate reads `maskCommentsAndLiterals` — the tree's one
// comments+literals projection, imported rather than re-derived here (#15776).
// This gate looks for a bare code-position `disableConsoleIntercept: true`, so
// unlike the gates whose signal IS a string literal, a quoted spelling here is
// never the real setting — it is prose (an error message, a doc snippet) and
// blanking it keeps prose from satisfying the check. The boundary this accepts:
// a config spelling the KEY as a quoted property
// (`'disableConsoleIntercept': true`) reds the gate even though vitest would
// honour it — the failure is loud, names the file, and the remedy is the
// unquoted spelling every other config uses.

const HERE = dirname(fileURLToPath(import.meta.url));
const REPO_ROOT = resolve(HERE, '..');
Expand Down Expand Up @@ -261,7 +254,7 @@ export function scan(root) {
);
continue;
}
const masked = maskProse(readFileSync(join(dir, configName), 'utf8'));
const masked = maskCommentsAndLiterals(readFileSync(join(dir, configName), 'utf8'));
if (REARM_RE.test(masked)) {
findings.push(
`${rel(root, dir)}/${configName}: sets disableConsoleIntercept: FALSE — this ` +
Expand Down
11 changes: 7 additions & 4 deletions scripts/check-docs-section-name.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ import YAML from 'yaml';

import { fencedBlocks } from './check-react-page-adapter-contract.mjs';
import { isEntrypoint } from './invoked-as.mjs';
import { blank, scanSource } from './js-comment-mask.mjs';
import { maskCommentsAndLiterals, scanSource } from './js-comment-mask.mjs';

// ── The self-test's own battery roster and floor (#13489) ──────────────────
//
Expand Down Expand Up @@ -444,14 +444,17 @@ export function docsFiles(root) {
/**
* The two projections of one scan. Both share byte offsets with `body`.
*
* `codeOnly` is `js-comment-mask.mjs`'s own `maskCommentsAndLiterals` (#15776),
* not a composition re-derived here; the raw `comment`/`literal` flags are what
* this gate still needs `scanSource` for (a rule below reads `literal[i] === 0`
* directly), and they address `body` at the same offsets the mask does.
*
* @param {string} body
* @returns {{ codeOnly: string, comment: Uint8Array, literal: Uint8Array }}
*/
export function project(body) {
const { comment, literal } = scanSource(body);
const both = new Uint8Array(body.length);
for (let i = 0; i < body.length; i++) both[i] = comment[i] || literal[i] ? 1 : 0;
return { codeOnly: blank(body, both), comment, literal };
return { codeOnly: maskCommentsAndLiterals(body), comment, literal };
}

/**
Expand Down
10 changes: 4 additions & 6 deletions scripts/check-error-status-conformance.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@
// `scripts/error-status-unpinned-baseline.json`; a NEW one fails the gate, and a
// row that becomes pinned fails it too (ratchet down with `--update`).
import { readdirSync, readFileSync, writeFileSync, statSync, existsSync } from 'node:fs';
import { maskComments, scanSource, blank } from './js-comment-mask.mjs';
import { maskComments, maskCommentsAndLiterals } from './js-comment-mask.mjs';
import { join, relative } from 'node:path';
import { isEntrypoint } from './invoked-as.mjs';

Expand Down Expand Up @@ -401,7 +401,8 @@ function classBodies(src) {
const lineOf = (src, idx) => src.slice(0, idx).split('\n').length;

/**
* The two projections a rule may read, from ONE scan of the source.
* The two projections a rule may read, both `js-comment-mask.mjs`'s own exports
* (#15776) rather than a composition re-derived here.
*
* `src` comments blanked, string/template/regex CONTENT intact — what
* every rule matches on, because a gate's signal (`code:
Expand All @@ -414,10 +415,7 @@ const lineOf = (src, idx) => src.slice(0, idx).split('\n').length;
* mask, so a line number read off either is true of the original.
*/
function projections(raw) {
const { comment, literal } = scanSource(raw);
const both = new Uint8Array(raw.length);
for (let k = 0; k < both.length; k++) both[k] = comment[k] || literal[k];
return { src: blank(raw, comment), structural: blank(raw, both) };
return { src: maskComments(raw), structural: maskCommentsAndLiterals(raw) };
}

/**
Expand Down
15 changes: 6 additions & 9 deletions scripts/check-parse-guard.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,12 @@ import { join, relative, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';

import { isEntrypoint } from './invoked-as.mjs';
import { blank, scanSource } from './js-comment-mask.mjs';
// This gate's `codeOnly` IS the tree's one comments+literals projection, not a
// local re-derivation of it (#15776): `js-comment-mask.mjs` owns the projections
// and its `--self-test` pins this one. The name stays because this gate's prose,
// its self-test rows and its findings all read `codeOnly`.
import { maskCommentsAndLiterals as codeOnly } from './js-comment-mask.mjs';
export { codeOnly };

// ── The self-test's own battery roster and floor (#13489) ──────────────────
//
Expand Down Expand Up @@ -382,14 +387,6 @@ function walkOutside(dir, out = []) {
return out;
}

/** Code only: comments, strings, templates and regex literals all blanked. */
export function codeOnly(source) {
const { comment, literal } = scanSource(source);
const both = new Uint8Array(comment.length);
for (let i = 0; i < both.length; i++) both[i] = comment[i] || literal[i];
return blank(source, both);
}

function lineOf(source, index) {
return source.slice(0, index).split('\n').length;
}
Expand Down
146 changes: 74 additions & 72 deletions scripts/check-stack-collection-maps.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -97,11 +97,80 @@ import { readFileSync, existsSync } from 'node:fs';
import { fileURLToPath } from 'node:url';
import { dirname, join, resolve } from 'node:path';
import { isEntrypoint } from './invoked-as.mjs';
import { blank, scanSource } from './js-comment-mask.mjs';
import { maskCommentsAndLiterals } from './js-comment-mask.mjs';

const here = dirname(fileURLToPath(import.meta.url));
const repoRoot = resolve(here, '..');

// ───────────────────────────────────────────────────────────────────────────
// The mask every scan below reads -- IMPORTED, not re-derived (#15776)
// ───────────────────────────────────────────────────────────────────────────
//
// `maskCommentsAndLiterals` is the tree's one comments+literals projection and
// `js-comment-mask.mjs` owns it; this gate used to spell a private `maskLiterals`
// that composed the same shared scanner by hand. What follows is the fact that
// belongs to THIS gate -- why a bracket counter here may read nothing else, and
// what the conversion onto the shared scanner measured when it happened.
//
// Blank out everything a bracket counter must not read: comment bodies, string
// and template contents, and regex literals. Returns a string of the SAME LENGTH
// as the input, so every index still addresses the original source — callers
// count structure on the mask and slice text from the original.
//
// Length-preserving masking rather than a `strip`: the first draft of this gate
// stripped comments and counted brackets over the rest, and one unbalanced paren
// inside PROSE — `.describe('Screen Flows (ADR-0019)')` — closed the
// `ObjectStackDefinitionSchema` literal 14 collections early. The gate then
// reconciled all seven sites against a truncated source of truth and reported
// 114 deviations, every one of them its own. A parser that fails toward "less
// schema" makes every consumer look wrong, which is the loudest possible way to
// be useless.
//
// String DELIMITERS survive (their contents do not), so a quoted object key and
// an array of string literals are both still locatable by index.
//
// ## CONVERTED onto the shared scanner (#13143)
//
// This body used to be a private left-to-right scanner written out here, and it
// was the one piece of comment-scanning code in this directory that no gate
// could see. `check-comment-mask-adoption.mjs` watches for exactly this shape
// and walks `packages` + `examples` only; `check-parse-guard.mjs` walks this
// directory for a different subject (the three TypeScript parser entry points).
// A private stripper here sits inside one gate's population and outside its
// subject, and inside the other's subject and outside its population, so
// nothing reds. Routing through the shared module is the half of that gap a
// caller can close on its own.
//
// A conversion is a MEASUREMENT rather than a mechanical edit, so here is the
// reading. The private scanner against `scanSource()`'s `comment | literal`
// projection, over THE POPULATION THIS GATE ACTUALLY READS (the seven SITE
// files plus `stack.zod.ts`, 1,028,984 chars): 3 of the 8 files disagree, 49
// spans, 247 characters. Two classes, and only one of them is a defect.
//
// - 18 spans are a PROJECTION difference and nothing else: the private copy
// blanked a regex literal's slash delimiters, the shared scanner keeps them
// as code. No bracket is a slash, so no caller here could ever see it.
// - 31 spans are the private scanner mis-reading a NESTED TEMPLATE. It closed
// an outer template at the first backtick inside a `${...}`, which flipped
// the parity of every backtick after it and handed the bracket counter 20
// bracket characters out of the interiors of string and template literals.
// Both files it happens in are live SITE files: `packages/objectql/src/
// engine.ts` and `packages/metadata/src/plugin.ts`. That is the same family
// as the `(ADR-0019)` incident above, arriving through a different door.
//
// Both directions of that defect are pinned in `--self-test` on synthetic
// bodies, because today's tree happens to punish neither: a nested template
// holding a `]` makes `stringArrayItems` DROP a real key, and one holding a
// quote makes it FABRICATE `${v}` as an enumerated key. The gate's verdict does
// NOT move on this tree -- `--list` is byte for byte identical before and after
// -- which is a fact about where this tree's nested templates sit, not a reason
// the private copy was safe.
//
// The instrument was shown able to fail before its empty results were read as
// agreement: the naive two-regex pair diffed against the shared scanner over
// the same eight files disagrees on 8 of 8, and the shared scanner diffed
// against itself returns nothing.

// ───────────────────────────────────────────────────────────────────────────
// Extraction -- pure, over source text
// ───────────────────────────────────────────────────────────────────────────
Expand All @@ -124,7 +193,7 @@ const repoRoot = resolve(here, '..');
export function sliceBody(source, anchor, from = 0) {
const at = source.indexOf(anchor, from);
if (at === -1) return null;
const mask = maskLiterals(source);
const mask = maskCommentsAndLiterals(source);
const openAt = at + anchor.length - 1;
const open = source[openAt];
const close = open === '{' ? '}' : ']';
Expand All @@ -140,79 +209,12 @@ export function sliceBody(source, anchor, from = 0) {
return null;
}

/**
* Blank out everything a bracket counter must not read: comment bodies, string
* and template contents, and regex literals. Returns a string of the SAME LENGTH
* as the input, so every index still addresses the original source — callers
* count structure on the mask and slice text from the original.
*
* Length-preserving masking rather than a `strip`: the first draft of this gate
* stripped comments and counted brackets over the rest, and one unbalanced paren
* inside PROSE — `.describe('Screen Flows (ADR-0019)')` — closed the
* `ObjectStackDefinitionSchema` literal 14 collections early. The gate then
* reconciled all seven sites against a truncated source of truth and reported
* 114 deviations, every one of them its own. A parser that fails toward "less
* schema" makes every consumer look wrong, which is the loudest possible way to
* be useless.
*
* String DELIMITERS survive (their contents do not), so a quoted object key and
* an array of string literals are both still locatable by index.
*
* ## CONVERTED onto the shared scanner (#13143)
*
* This body used to be a private left-to-right scanner written out here, and it
* was the one piece of comment-scanning code in this directory that no gate
* could see. `check-comment-mask-adoption.mjs` watches for exactly this shape
* and walks `packages` + `examples` only; `check-parse-guard.mjs` walks this
* directory for a different subject (the three TypeScript parser entry points).
* A private stripper here sits inside one gate's population and outside its
* subject, and inside the other's subject and outside its population, so
* nothing reds. Routing through the shared module is the half of that gap a
* caller can close on its own.
*
* A conversion is a MEASUREMENT rather than a mechanical edit, so here is the
* reading. The private scanner against `scanSource()`'s `comment | literal`
* projection, over THE POPULATION THIS GATE ACTUALLY READS (the seven SITE
* files plus `stack.zod.ts`, 1,028,984 chars): 3 of the 8 files disagree, 49
* spans, 247 characters. Two classes, and only one of them is a defect.
*
* - 18 spans are a PROJECTION difference and nothing else: the private copy
* blanked a regex literal's slash delimiters, the shared scanner keeps them
* as code. No bracket is a slash, so no caller here could ever see it.
* - 31 spans are the private scanner mis-reading a NESTED TEMPLATE. It closed
* an outer template at the first backtick inside a `${...}`, which flipped
* the parity of every backtick after it and handed the bracket counter 20
* bracket characters out of the interiors of string and template literals.
* Both files it happens in are live SITE files: `packages/objectql/src/
* engine.ts` and `packages/metadata/src/plugin.ts`. That is the same family
* as the `(ADR-0019)` incident above, arriving through a different door.
*
* Both directions of that defect are pinned in `--self-test` on synthetic
* bodies, because today's tree happens to punish neither: a nested template
* holding a `]` makes `stringArrayItems` DROP a real key, and one holding a
* quote makes it FABRICATE `${v}` as an enumerated key. The gate's verdict does
* NOT move on this tree -- `--list` is byte for byte identical before and after
* -- which is a fact about where this tree's nested templates sit, not a reason
* the private copy was safe.
*
* The instrument was shown able to fail before its empty results were read as
* agreement: the naive two-regex pair diffed against the shared scanner over
* the same eight files disagrees on 8 of 8, and the shared scanner diffed
* against itself returns nothing.
*/
export function maskLiterals(source) {
const { comment, literal } = scanSource(source);
const both = new Uint8Array(source.length);
for (let i = 0; i < source.length; i++) both[i] = comment[i] | literal[i];
return blank(source, both);
}

/**
* Top-level keys of an object-literal body, each with its value's source text.
* Depth-aware: a nested literal never contributes its own keys.
*/
export function objectEntries(body) {
const mask = maskLiterals(body);
const mask = maskCommentsAndLiterals(body);
const out = [];
let depth = 0;
let i = 0;
Expand Down Expand Up @@ -255,7 +257,7 @@ export function objectEntries(body) {

/** String literals at depth 0 of an array-literal body. */
export function stringArrayItems(body) {
const mask = maskLiterals(body);
const mask = maskCommentsAndLiterals(body);
const out = [];
let depth = 0;
for (let i = 0; i < body.length; i++) {
Expand Down Expand Up @@ -287,7 +289,7 @@ export function stringArrayItems(body) {
* answer at all, not to rescue the gate from a silent pass it never had.
*/
export function tupleFirstItems(body) {
const mask = maskLiterals(body);
const mask = maskCommentsAndLiterals(body);
const out = [];
let depth = 0;
let taken = false;
Expand Down
11 changes: 6 additions & 5 deletions scripts/check-test-source-alias.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,7 @@
// node scripts/check-test-source-alias.mjs --self-test

import { readFileSync, readdirSync, statSync, existsSync, mkdirSync, writeFileSync, rmSync } from 'node:fs';
import { stripComments, scanSource, blank } from './js-comment-mask.mjs';
import { stripComments, maskComments, maskCommentsAndLiterals } from './js-comment-mask.mjs';
import { join, resolve, relative, dirname } from 'node:path';
import { fileURLToPath } from 'node:url';
import {
Expand Down Expand Up @@ -809,12 +809,13 @@ const TYPE_QUERY_BEFORE = /\btypeof\s*$/;
* thing in both: `commentsOnly` keeps every string intact (the import regex has
* to read the specifier), `codeOnly` masks literal CONTENT as well (the brace
* scanner must not count a `{` inside a string or a template).
*
* Both are `js-comment-mask.mjs`'s own exports (#15776) rather than a projection
* re-derived here. They agree offset-for-offset because BOTH blank in place --
* that is the module's contract, not a property of deriving them from one scan.
*/
function maskedProjections(source) {
const { comment, literal } = scanSource(source);
const both = new Uint8Array(source.length);
for (let i = 0; i < source.length; i++) both[i] = comment[i] || literal[i] ? 1 : 0;
return { commentsOnly: blank(source, comment), codeOnly: blank(source, both) };
return { commentsOnly: maskComments(source), codeOnly: maskCommentsAndLiterals(source) };
}

/**
Expand Down
Loading
Loading