char/sorcery
static-files based git repo viewer
git clone https://git.t4t.associates/char/sorcery
a580f9a
main
1// deno's dependency cache is non-reproducible in newer versions, but since 2// we use it in a Nix derivation we need it to have a fixed output hash. 3// we remove a bunch of http metadata and restrict indices to locked versions 4// and serialize metadata canonically 5 6const [ cache , lockPath ] = Deno . args ; 7const lock = JSON . parse ( Deno . readTextFileSync ( lockPath )); 8const versions :Record < string , Record < string , Record < string , unknown >>> = { jsr :{}, npm :{} }; 9for ( const registry in versions ) { 10for ( const pkg of Object . keys ( lock [ registry ])) { 11const [, name , version ] = pkg . match ( / ^(.+?)@([^_]+)(?:_.*)?$ / ) ! ; 12( versions [ registry ][ name ] ??= {})[ version ] = {}; 13} 14} 15 16function encode ( value :unknown ) :string { 17return JSON . stringify ( value , ( _ , value ) => { 18if ( value === null || typeof value !== "object" || Array . isArray ( value )) return value ; 19return Object . fromEntries ( Object . keys ( value ). sort (). map ( key => [ key , value [ key ]])); 20}); 21} 22 23function pick ( value :Record < string , unknown >, keys :string []) :Record < string , unknown > { 24return Object . fromEntries ( keys . filter ( key => Object . hasOwn ( value , key )). map ( key => [ key , value [ key ]])); 25} 26 27function * files ( dir :string ) :Generator < string > { 28for ( const entry of Deno . readDirSync ( dir )) { 29const path = ` ${ dir } / ${ entry . name } ` ; 30if ( entry . isDirectory ) yield * files ( path ); 31else if ( entry . isFile ) yield path ; 32} 33} 34 35const utf8 = new TextDecoder (); 36const encoder = new TextEncoder (); 37const marker = "\n// denoCacheMetadata=" ; 38 39// Deno appends HTTP metadata to the source, including cookies and fetch times. 40for ( const path of files ( ` ${ cache } /remote` )) { 41const bytes = Deno . readFileSync ( path ); 42const offset = bytes . lastIndexOf ( 10 ); 43const trailer = utf8 . decode ( bytes . subarray ( offset )); 44if ( ! trailer . startsWith ( marker )) throw new Error ( `Missing Deno cache metadata: ${ path } ` ); 45const metadata = JSON . parse ( trailer . slice ( marker . length )); 46let source = bytes . subarray ( 0 , offset ); 47delete metadata . time ; 48metadata . headers = pick ( metadata . headers , [ "content-type" , "location" , "x-typescript-types" ]); 49if ( metadata . url . startsWith ( "https://jsr.io/" ) && metadata . url . endsWith ( "/meta.json" )) { 50const manifest = JSON . parse ( utf8 . decode ( source )); 51const name = `@ ${ manifest . scope } / ${ manifest . name } ` ; 52source = encoder . encode ( encode ({ 53scope :manifest . scope , 54name :manifest . name , 55versions :versions . jsr [ name ], 56})); 57} 58await Deno . writeFile ( path , new Blob ([ source , marker , encode ( metadata )]). stream ()); 59} 60 61// Registry indexes change when unrelated versions are published or tags move. 62const npm = ` ${ cache } /npm` ; 63for ( const path of files ( npm )) { 64if ( ! /^[^/]+\/(?:@[^/]+\/)?[^/]+\/registry\.json$ / . test ( path . slice ( npm . length + 1 ))) continue ; 65const manifest = JSON . parse ( Deno . readTextFileSync ( path )); 66const pinned :Record < string , unknown > = {}; 67for ( const version of Object . keys ( versions . npm [ manifest . name ])) { 68const info = manifest . versions [ version ]; 69pinned [ version ] = { 70 ...pick ( info , [ 71"version" , "bin" , "dependencies" , "optionalDependencies" , 72"peerDependencies" , "peerDependenciesMeta" , "bundleDependencies" , 73"bundledDependencies" , "os" , "cpu" , "scripts" , "hasInstallScript" , 74]), 75dist :pick ( info . dist , [ "tarball" , "shasum" , "integrity" ]), 76}; 77} 78Deno . writeTextFileSync ( path , encode ({ 79name :manifest . name , 80versions :pinned , 81// Publish dates are stable; Deno needs them for its minimum-age check. 82time :pick ( manifest . time ?? {}, Object . keys ( pinned )), 83"_deno.packumentFormat" :manifest [ "_deno.packumentFormat" ], 84})); 85}