112112import { readdirSync , readFileSync , writeFileSync , statSync , existsSync } from 'node:fs' ;
113113import { maskComments } from './js-comment-mask.mjs' ;
114114import { join , relative } from 'node:path' ;
115+ import { isEntrypoint } from './invoked-as.mjs' ;
115116
116117const SCAN_ROOT = 'packages' ;
117118const SKIP_DIRS = new Set ( [ 'node_modules' , 'dist' , '.git' , '.turbo' , 'coverage' , 'build' , 'fixtures' ] ) ;
@@ -1083,7 +1084,6 @@ function selfTest() {
10831084 process . exit ( 0 ) ;
10841085}
10851086
1086- if ( process . argv . includes ( '--self-test' ) ) selfTest ( ) ;
10871087
10881088// ───────────────────────────────────────────────────────────────────────────
10891089// The real check
@@ -1099,134 +1099,145 @@ function walk(dir, out) {
10991099 }
11001100}
11011101
1102- const update = process . argv . includes ( '--update' ) ;
1103-
1104- const files = [ ] ;
1105- walk ( SCAN_ROOT , files ) ;
1106- const sources = new Map ( ) ;
1107- for ( const f of files . sort ( ) ) sources . set ( relative ( '.' , f ) . replace ( / \\ / g, '/' ) , readFileSync ( f , 'utf8' ) ) ;
1108-
1109- const errorsZod = readFileSync ( ERRORS_ZOD , 'utf8' ) ;
1110- const members = parseStandardErrorCodes ( errorsZod ) ;
1111- const index = buildConstantIndex ( sources ) ;
1112- const derived = deriveRuntimeStatuses ( sources , index ) ;
1113- for ( const { code, status } of deriveDoorMap ( errorsZod ) ) {
1114- if ( ! derived . emitted . has ( code ) ) derived . emitted . set ( code , new Map ( ) ) ;
1115- const perStatus = derived . emitted . get ( code ) ;
1116- if ( ! perStatus . has ( status ) ) perStatus . set ( status , [ ] ) ;
1117- if ( ! perStatus . get ( status ) . includes ( `${ ERRORS_ZOD } : HttpStatusErrorCodeMap` ) ) {
1118- perStatus . get ( status ) . push ( `${ ERRORS_ZOD } : HttpStatusErrorCodeMap` ) ;
1102+ function main ( ) {
1103+ const update = process . argv . includes ( '--update' ) ;
1104+
1105+ const files = [ ] ;
1106+ walk ( SCAN_ROOT , files ) ;
1107+ const sources = new Map ( ) ;
1108+ for ( const f of files . sort ( ) ) sources . set ( relative ( '.' , f ) . replace ( / \\ / g, '/' ) , readFileSync ( f , 'utf8' ) ) ;
1109+
1110+ const errorsZod = readFileSync ( ERRORS_ZOD , 'utf8' ) ;
1111+ const members = parseStandardErrorCodes ( errorsZod ) ;
1112+ const index = buildConstantIndex ( sources ) ;
1113+ const derived = deriveRuntimeStatuses ( sources , index ) ;
1114+ for ( const { code, status } of deriveDoorMap ( errorsZod ) ) {
1115+ if ( ! derived . emitted . has ( code ) ) derived . emitted . set ( code , new Map ( ) ) ;
1116+ const perStatus = derived . emitted . get ( code ) ;
1117+ if ( ! perStatus . has ( status ) ) perStatus . set ( status , [ ] ) ;
1118+ if ( ! perStatus . get ( status ) . includes ( `${ ERRORS_ZOD } : HttpStatusErrorCodeMap` ) ) {
1119+ perStatus . get ( status ) . push ( `${ ERRORS_ZOD } : HttpStatusErrorCodeMap` ) ;
1120+ }
11191121 }
1120- }
11211122
1122- const doc = parseDocumentedStatuses ( {
1123- handling : readFileSync ( DOC_HANDLING , 'utf8' ) ,
1124- catalog : readFileSync ( DOC_CATALOG , 'utf8' ) ,
1125- } ) ;
1126- const { vocabulary, docPublishedBeyondStandard } = reconciledVocabulary ( { members, ...doc } ) ;
1127- const result = reconcile ( { vocabulary, emitted : derived . emitted , ...doc } ) ;
1128-
1129- const baseline = existsSync ( BASELINE_PATH )
1130- ? JSON . parse ( readFileSync ( BASELINE_PATH , 'utf8' ) )
1131- : { unpinned : [ ] } ;
1132- const baselined = new Set ( baseline . unpinned ?? [ ] ) ;
1133- const newlyUnpinned = result . unpinned . filter ( ( c ) => ! baselined . has ( c ) ) ;
1134- const nowPinnedFindings = nowPinned ( {
1135- baselined : [ ...baselined ] , unpinned : result . unpinned , vocabulary, documented : doc . documented ,
1136- } ) ;
1137-
1138- if ( update ) {
1139- writeFileSync (
1140- BASELINE_PATH ,
1141- `${ JSON . stringify ( {
1142- note :
1143- 'StandardErrorCode members documented with an HTTP status that NO producer this gate can read '
1144- + 'declares a status for — nothing pins the doc claim on either side. Shrink-only: a new entry is a '
1145- + 'gate failure, and a row that becomes pinned must be removed. Regenerate with '
1146- + '`node scripts/check-error-status-conformance.mjs --update`.' ,
1147- unpinned : result . unpinned ,
1148- } , null , 2 ) } \n`,
1149- ) ;
1150- console . log ( `Baseline rewritten: ${ result . unpinned . length } unpinned code(s).` ) ;
1151- process . exit ( 0 ) ;
1152- }
1123+ const doc = parseDocumentedStatuses ( {
1124+ handling : readFileSync ( DOC_HANDLING , 'utf8' ) ,
1125+ catalog : readFileSync ( DOC_CATALOG , 'utf8' ) ,
1126+ } ) ;
1127+ const { vocabulary, docPublishedBeyondStandard } = reconciledVocabulary ( { members, ...doc } ) ;
1128+ const result = reconcile ( { vocabulary, emitted : derived . emitted , ...doc } ) ;
11531129
1154- // The residual, as a SUBTRACTION from what the pages publish rather than a
1155- // claim about it: codes the deriver found that are neither a `StandardErrorCode`
1156- // member nor published with a status by a scanned page. Nothing here is a
1157- // literal, so registering a ledger code moves no number that has to be edited.
1158- const unreconciledLedger = [ ...derived . emitted . keys ( ) ] . filter ( ( c ) => ! vocabulary . includes ( c ) ) . sort ( ) ;
1159-
1160- // `--report` prints the whole derivation rather than only the disagreements.
1161- // A finding is only as trustworthy as the evidence behind it, and "which
1162- // producers did you actually see for this code?" is the first question anyone
1163- // reading a failure asks.
1164- if ( process . argv . includes ( '--report' ) ) {
1165- for ( const code of vocabulary ) {
1166- const runtime = derived . emitted . get ( code ) ;
1167- if ( ! runtime ) continue ;
1168- console . log ( `${ code } ` ) ;
1169- for ( const [ status , where ] of [ ...runtime ] . sort ( ( a , b ) => a [ 0 ] - b [ 0 ] ) ) {
1170- console . log ( ` ${ status } ${ where . join ( '\n ' ) } ` ) ;
1130+ const baseline = existsSync ( BASELINE_PATH )
1131+ ? JSON . parse ( readFileSync ( BASELINE_PATH , 'utf8' ) )
1132+ : { unpinned : [ ] } ;
1133+ const baselined = new Set ( baseline . unpinned ?? [ ] ) ;
1134+ const newlyUnpinned = result . unpinned . filter ( ( c ) => ! baselined . has ( c ) ) ;
1135+ const nowPinnedFindings = nowPinned ( {
1136+ baselined : [ ...baselined ] , unpinned : result . unpinned , vocabulary, documented : doc . documented ,
1137+ } ) ;
1138+
1139+ if ( update ) {
1140+ writeFileSync (
1141+ BASELINE_PATH ,
1142+ `${ JSON . stringify ( {
1143+ note :
1144+ 'StandardErrorCode members documented with an HTTP status that NO producer this gate can read '
1145+ + 'declares a status for — nothing pins the doc claim on either side. Shrink-only: a new entry is a '
1146+ + 'gate failure, and a row that becomes pinned must be removed. Regenerate with '
1147+ + '`node scripts/check-error-status-conformance.mjs --update`.' ,
1148+ unpinned : result . unpinned ,
1149+ } , null , 2 ) } \n`,
1150+ ) ;
1151+ console . log ( `Baseline rewritten: ${ result . unpinned . length } unpinned code(s).` ) ;
1152+ process . exit ( 0 ) ;
1153+ }
1154+
1155+ // The residual, as a SUBTRACTION from what the pages publish rather than a
1156+ // claim about it: codes the deriver found that are neither a `StandardErrorCode`
1157+ // member nor published with a status by a scanned page. Nothing here is a
1158+ // literal, so registering a ledger code moves no number that has to be edited.
1159+ const unreconciledLedger = [ ...derived . emitted . keys ( ) ] . filter ( ( c ) => ! vocabulary . includes ( c ) ) . sort ( ) ;
1160+
1161+ // `--report` prints the whole derivation rather than only the disagreements.
1162+ // A finding is only as trustworthy as the evidence behind it, and "which
1163+ // producers did you actually see for this code?" is the first question anyone
1164+ // reading a failure asks.
1165+ if ( process . argv . includes ( '--report' ) ) {
1166+ for ( const code of vocabulary ) {
1167+ const runtime = derived . emitted . get ( code ) ;
1168+ if ( ! runtime ) continue ;
1169+ console . log ( `${ code } ` ) ;
1170+ for ( const [ status , where ] of [ ...runtime ] . sort ( ( a , b ) => a [ 0 ] - b [ 0 ] ) ) {
1171+ console . log ( ` ${ status } ${ where . join ( '\n ' ) } ` ) ;
1172+ }
11711173 }
1174+ console . log ( `\nderived but NOT reconciled — no scanned page publishes a status for these ${ unreconciledLedger . length } :` ) ;
1175+ for ( const c of unreconciledLedger ) console . log ( ` ${ c } ` ) ;
11721176 }
1173- console . log ( `\nderived but NOT reconciled — no scanned page publishes a status for these ${ unreconciledLedger . length } :` ) ;
1174- for ( const c of unreconciledLedger ) console . log ( ` ${ c } ` ) ;
1175- }
11761177
1177- console . log ( 'check:error-status-conformance — documented HTTP status ⇄ runtime-emitted status' ) ;
1178- console . log (
1179- ` scope: ${ vocabulary . length } code(s) reconciled = ${ members . length } StandardErrorCode member(s) `
1180- + `+ ${ docPublishedBeyondStandard . length } ledger code(s) a doc page publishes a status for`
1181- + `${ docPublishedBeyondStandard . length ? ` (${ docPublishedBeyondStandard . join ( ', ' ) } )` : '' } ; `
1182- + `${ sources . size } source files scanned; ${ derived . sites } producer site(s) derived; `
1183- + `${ unreconciledLedger . length } further ledger code(s) derived but NOT reconciled — no scanned page publishes `
1184- + 'a status for them, so there is nothing to reconcile them against.' ,
1185- ) ;
1186- console . log (
1187- ` reconciled: ${ result . reconciledCodes } code(s) with a derived producer, `
1188- + `${ result . reconciledPairs } (code, status) pair(s) matched against the docs.` ,
1189- ) ;
1190- console . log ( ` unpinned: ${ result . unpinned . length } documented code(s) with no derivable producer (baselined: ${ baselined . size } ).` ) ;
1191- const ungraded = ungradedEntries ( doc ) ;
1192- if ( ungraded . length ) {
1178+ console . log ( 'check:error-status-conformance — documented HTTP status ⇄ runtime-emitted status' ) ;
11931179 console . log (
1194- ` ungraded: ${ ungraded . length } doc entr(y|ies) whose heading was read but for which no page publishes a `
1195- + 'status in a graded shape (reported, not failed — see `ungradedEntries`) —' ,
1180+ ` scope: ${ vocabulary . length } code(s) reconciled = ${ members . length } StandardErrorCode member(s) `
1181+ + `+ ${ docPublishedBeyondStandard . length } ledger code(s) a doc page publishes a status for`
1182+ + `${ docPublishedBeyondStandard . length ? ` (${ docPublishedBeyondStandard . join ( ', ' ) } )` : '' } ; `
1183+ + `${ sources . size } source files scanned; ${ derived . sites } producer site(s) derived; `
1184+ + `${ unreconciledLedger . length } further ledger code(s) derived but NOT reconciled — no scanned page publishes `
1185+ + 'a status for them, so there is nothing to reconcile them against.' ,
11961186 ) ;
1197- for ( const e of ungraded ) console . log ( ` ${ e . code } ${ e . where } ` ) ;
1198- }
1199- if ( doc . unreadableHeadings . length ) {
1200- console . log ( ` unreadable: ${ doc . unreadableHeadings . length } doc heading(s) naming a code in an unrecognised shape —` ) ;
1201- for ( const u of doc . unreadableHeadings ) console . log ( ` ${ u . path } :${ u . line } ${ JSON . stringify ( u . text ) } ` ) ;
1202- }
1203- if ( derived . unresolved . length ) {
1204- console . log ( ` unresolved: ${ derived . unresolved . length } declaration(s) the deriver could not read —` ) ;
1205- for ( const u of derived . unresolved ) console . log ( ` ${ u } ` ) ;
1206- }
1207-
1208- if ( result . reconciledPairs === 0 ) {
1209- console . error (
1210- '\n✗ the deriver matched ZERO (code, status) pairs. A green run with nothing reconciled is a blind run, '
1211- + 'not a clean one — the source anchors this gate reads have moved.\n' ,
1187+ console . log (
1188+ ` reconciled: ${ result . reconciledCodes } code(s) with a derived producer, `
1189+ + `${ result . reconciledPairs } (code, status) pair(s) matched against the docs.` ,
12121190 ) ;
1213- process . exit ( 1 ) ;
1214- }
1191+ console . log ( ` unpinned: ${ result . unpinned . length } documented code(s) with no derivable producer (baselined: ${ baselined . size } ).` ) ;
1192+ const ungraded = ungradedEntries ( doc ) ;
1193+ if ( ungraded . length ) {
1194+ console . log (
1195+ ` ungraded: ${ ungraded . length } doc entr(y|ies) whose heading was read but for which no page publishes a `
1196+ + 'status in a graded shape (reported, not failed — see `ungradedEntries`) —' ,
1197+ ) ;
1198+ for ( const e of ungraded ) console . log ( ` ${ e . code } ${ e . where } ` ) ;
1199+ }
1200+ if ( doc . unreadableHeadings . length ) {
1201+ console . log ( ` unreadable: ${ doc . unreadableHeadings . length } doc heading(s) naming a code in an unrecognised shape —` ) ;
1202+ for ( const u of doc . unreadableHeadings ) console . log ( ` ${ u . path } :${ u . line } ${ JSON . stringify ( u . text ) } ` ) ;
1203+ }
1204+ if ( derived . unresolved . length ) {
1205+ console . log ( ` unresolved: ${ derived . unresolved . length } declaration(s) the deriver could not read —` ) ;
1206+ for ( const u of derived . unresolved ) console . log ( ` ${ u } ` ) ;
1207+ }
12151208
1216- const failures = [ ] ;
1217- for ( const f of result . emittedNotDocumented ) failures . push ( emittedNotDocumentedMessage ( f ) ) ;
1218- for ( const f of result . documentedNotReachable ) failures . push ( documentedNotReachableMessage ( f ) ) ;
1219- for ( const u of doc . unreadableHeadings ) failures . push ( unreadableHeadingMessage ( u ) ) ;
1220- for ( const c of newlyUnpinned ) failures . push ( newUnpinnedMessage ( c ) ) ;
1221- for ( const f of nowPinnedFindings ) {
1222- failures . push ( f . reason === 'producer' ? nowPinnedProducerMessage ( f . code ) : nowPinnedDocRemovedMessage ( f . code ) ) ;
1223- }
1209+ if ( result . reconciledPairs === 0 ) {
1210+ console . error (
1211+ '\n✗ the deriver matched ZERO (code, status) pairs. A green run with nothing reconciled is a blind run, '
1212+ + 'not a clean one — the source anchors this gate reads have moved.\n' ,
1213+ ) ;
1214+ process . exit ( 1 ) ;
1215+ }
1216+
1217+ const failures = [ ] ;
1218+ for ( const f of result . emittedNotDocumented ) failures . push ( emittedNotDocumentedMessage ( f ) ) ;
1219+ for ( const f of result . documentedNotReachable ) failures . push ( documentedNotReachableMessage ( f ) ) ;
1220+ for ( const u of doc . unreadableHeadings ) failures . push ( unreadableHeadingMessage ( u ) ) ;
1221+ for ( const c of newlyUnpinned ) failures . push ( newUnpinnedMessage ( c ) ) ;
1222+ for ( const f of nowPinnedFindings ) {
1223+ failures . push ( f . reason === 'producer' ? nowPinnedProducerMessage ( f . code ) : nowPinnedDocRemovedMessage ( f . code ) ) ;
1224+ }
12241225
1225- if ( failures . length ) {
1226- console . error ( '' ) ;
1227- for ( const f of failures ) console . error ( ` ✗ ${ f } ` ) ;
1228- console . error ( `\n✗ check:error-status-conformance — ${ failures . length } finding(s).\n` ) ;
1229- process . exit ( 1 ) ;
1226+ if ( failures . length ) {
1227+ console . error ( '' ) ;
1228+ for ( const f of failures ) console . error ( ` ✗ ${ f } ` ) ;
1229+ console . error ( `\n✗ check:error-status-conformance — ${ failures . length } finding(s).\n` ) ;
1230+ process . exit ( 1 ) ;
1231+ }
1232+
1233+ console . log ( '\n✓ every derivable runtime status is documented, and every documented status is reachable.' ) ;
12301234}
12311235
1232- console . log ( '\n✓ every derivable runtime status is documented, and every documented status is reachable.' ) ;
1236+ // Guarded: this module exports the whole derivation (parseStandardErrorCodes,
1237+ // deriveRuntimeStatuses, reconcile, and the message builders), and unguarded an
1238+ // import of any of them walked the scan root, read every source file and printed
1239+ // this gate's full report into the importer's stdout before returning a binding.
1240+ if ( isEntrypoint ( import . meta. url ) ) {
1241+ if ( process . argv . includes ( '--self-test' ) ) selfTest ( ) ;
1242+ main ( ) ;
1243+ }
0 commit comments