@redue/cli 0.1.0-alpha.16 → 0.1.0-alpha.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,4 @@
1
- // Pinned pnpm 12 node-modules installation provider. The engine sees only
1
+ // pnpm 9-12 node-modules installation provider. The engine sees only
2
2
  // repository-relative input sets and resolution relationships, never pnpm
3
3
  // virtual-store names as semantic identities.
4
4
  import {probeTimeLeft} from './probe-deadline.mjs';
@@ -7,16 +7,18 @@ import fs from 'node:fs';
7
7
  import path from 'node:path';
8
8
  import {createRequire} from 'node:module';
9
9
  import {execFileSync,spawnSync} from 'node:child_process';
10
- import micromatch from './glob.mjs';
10
+ import micromatch,{patternMatcher} from './glob.mjs';
11
11
  import YAML from 'yaml';
12
12
  import {resolveLinks} from './installed-inputs.mjs';
13
13
  import {withinPath as within,realObservedPath,samePath} from './path-identity.mjs';
14
14
  import {timing} from './decision-profile.mjs';
15
+ import {npmrc} from './npmrc.mjs';
16
+ import {runConfig,tscRun,tscRuns} from './typescript-invocation.mjs';
15
17
 
16
18
  const rel=(root,file)=>path.relative(root,file).split(path.sep).join('/');
17
19
  const read=file=>JSON.parse(fs.readFileSync(file,'utf8'));
18
20
  const exists=file=>{try{fs.lstatSync(file);return true;}catch{return false;}};
19
- function yaml(file){
21
+ export function yaml(file){
20
22
  const doc=YAML.parseDocument(fs.readFileSync(file,'utf8'),{uniqueKeys:true});
21
23
  if(doc.errors.length)throw Error(`${path.basename(file)} cannot be interpreted safely`);
22
24
  return doc.toJS()||{};
@@ -27,40 +29,67 @@ function safePatterns(values){
27
29
  throw Error('pnpm workspace patterns are unsupported');
28
30
  return values;
29
31
  }
32
+ // The supported pnpm majors. pnpm 9 reads settings from .npmrc, pnpm 10 from
33
+ // pnpm-workspace.yaml first and then .npmrc, pnpm 11 and 12 from
34
+ // pnpm-workspace.yaml only.
35
+ export const pnpmMajors=[9,10,11,12];
36
+ // Settings that move or copy installed files, or run code during install,
37
+ // where the contract does not look; by pnpm-workspace.yaml and .npmrc name.
38
+ // Other settings change only what gets installed, and the installed files are
39
+ // what the contract observes. These are refused when either file sets them,
40
+ // whichever file the installed pnpm reads. Only the project's files are read:
41
+ // user or global rc files and npm_config_* variables at install time are not.
42
+ const refused=[['modulesDir','modules-dir'],['virtualStoreDir','virtual-store-dir'],['lockfileDir','lockfile-dir'],
43
+ ['enableGlobalVirtualStore','enable-global-virtual-store'],['injectWorkspacePackages','inject-workspace-packages'],
44
+ ['sharedWorkspaceLockfile','shared-workspace-lockfile'],['pnpmfile','pnpmfile'],['globalPnpmfile','global-pnpmfile'],
45
+ ['configDependencies','config-dependencies']];
46
+ // Whether a refused setting is set: present at all, except that only
47
+ // sharedWorkspaceLockfile=false and injectWorkspacePackages=true move files.
48
+ const setting=(key,value)=>value!==undefined&&
49
+ (key==='sharedWorkspaceLockfile'?value===false:key==='injectWorkspacePackages'?value===true:true);
30
50
  export function pnpmProjectInfo(root){
31
51
  root=realObservedPath(root);
32
52
  const pkg=read(path.join(root,'package.json'));
33
- const pin=/^pnpm@(12\.\d+\.\d+)(?:\+[^\s]+)?$/.exec(pkg.packageManager||'');
34
- if(!pin)throw Error('an exact pnpm 12 packageManager pin is required');
53
+ // A pin is optional; when present it names pnpm 9-12, and the install must match it.
54
+ const pin=pkg.packageManager===undefined?null:/^pnpm@((\d+)\.\d+\.\d+)(?:\+[^\s]+)?$/.exec(pkg.packageManager);
55
+ if(pkg.packageManager!==undefined&&(!pin||!pnpmMajors.includes(Number(pin[2]))))
56
+ throw Error(`packageManager ${JSON.stringify(String(pkg.packageManager).split('+')[0])} is outside pnpm ${pnpmMajors[0]}-${pnpmMajors.at(-1)}`);
35
57
  const workspaceFile=path.join(root,'pnpm-workspace.yaml');
36
- const settings=exists(workspaceFile)?yaml(workspaceFile):{};
37
- const patterns=safePatterns(settings.packages||[]);
38
- const linker=settings.nodeLinker||'isolated';
39
- return {version:pin[1],linker,patterns,settings};
58
+ const workspace=exists(workspaceFile)?yaml(workspaceFile):{},rc=npmrc(path.join(root,'.npmrc'));
59
+ const patterns=safePatterns(workspace.packages||[]);
60
+ // The linker the given pnpm major reads; without a major, the one any reads.
61
+ const linkerFor=major=>(major===9?rc['node-linker']:major>=11?workspace.nodeLinker:
62
+ workspace.nodeLinker??rc['node-linker'])||'isolated';
63
+ const major=pin?Number(pin[2]):null;
64
+ return {version:pin?.[1]??null,linker:linkerFor(major),linkerFor,patterns,settings:workspace,
65
+ refused:refused.filter(([key,name])=>setting(key,workspace[key])||setting(key,rc[name])).map(([key])=>key)};
40
66
  }
41
67
  export function pnpmInstallInfo(root){
42
68
  const started=performance.now();
43
69
  root=realObservedPath(root);
44
- const pkg=read(path.join(root,'package.json'));
45
- const {version,linker,patterns,settings}=pnpmProjectInfo(root);
70
+ const {version,linkerFor,patterns,settings,refused}=pnpmProjectInfo(root);
46
71
  const workspaceFile=path.join(root,'pnpm-workspace.yaml');
47
- if(!['isolated','hoisted'].includes(linker))throw Error(`pnpm ${linker} linker is recording-only`);
48
- if(settings.modulesDir||settings.virtualStoreDir||settings.enableGlobalVirtualStore)
49
- throw Error('custom pnpm modules or virtual-store location is recording-only');
50
- if(settings.injectWorkspacePackages)
51
- throw Error('injected workspace dependencies are recording-only');
52
- if(settings.sharedWorkspaceLockfile===false)
53
- throw Error('separate workspace lockfiles are recording-only');
54
- if(exists(path.join(root,'.pnpmfile.cjs'))||exists(path.join(root,'.pnpmfile.mjs')))
72
+ if(refused.some(key=>['pnpmfile','globalPnpmfile','configDependencies'].includes(key))||
73
+ exists(path.join(root,'.pnpmfile.cjs'))||exists(path.join(root,'.pnpmfile.mjs')))
55
74
  throw Error('custom pnpm installation hooks need review');
75
+ if(refused.some(key=>['modulesDir','virtualStoreDir','lockfileDir','enableGlobalVirtualStore'].includes(key)))
76
+ throw Error('custom pnpm modules or virtual-store location is recording-only');
77
+ if(refused.includes('injectWorkspacePackages'))throw Error('injected workspace dependencies are recording-only');
78
+ if(refused.includes('sharedWorkspaceLockfile'))throw Error('separate workspace lockfiles are recording-only');
56
79
  const modulesFile=path.join(root,'node_modules','.modules.yaml');
57
80
  if(!exists(modulesFile))throw Error('installed pnpm module metadata is missing');
58
81
  const modules=yaml(modulesFile);
59
- if(modules.nodeLinker!==linker||modules.packageManager!==pkg.packageManager)
82
+ const installed=/^pnpm@((\d+)\.\d+\.\d+)$/.exec(String(modules.packageManager));
83
+ if(!installed||!pnpmMajors.includes(Number(installed[2])))
84
+ throw Error(`installed by ${JSON.stringify(String(modules.packageManager))}, outside pnpm ${pnpmMajors[0]}-${pnpmMajors.at(-1)}`);
85
+ const linker=linkerFor(Number(installed[2]));
86
+ if(!['isolated','hoisted'].includes(linker))throw Error(`pnpm ${linker} linker is recording-only`);
87
+ if(modules.nodeLinker!==linker||version!==null&&installed[1]!==version)
60
88
  throw Error('installed pnpm linker or version differs from project configuration');
61
89
  const virtualStore=path.join(root,'node_modules','.pnpm');
62
- // pnpm 12 on Windows can omit virtualStoreDir when using its project-local
63
- // default. Accept only the observed, ordinary local directory in that case.
90
+ // pnpm can omit virtualStoreDir when using its project-local default
91
+ // (seen with pnpm 12 on Windows). Accept only the observed, ordinary local
92
+ // directory in that case.
64
93
  const declared=modules.virtualStoreDir;
65
94
  const selected=declared===undefined?virtualStore:
66
95
  path.resolve(path.dirname(modulesFile),declared);
@@ -72,18 +101,8 @@ export function pnpmInstallInfo(root){
72
101
  throw Error('nonstandard or incomplete project-local pnpm virtual store');
73
102
  if(!exists(path.join(root,'pnpm-lock.yaml')))
74
103
  throw Error('pnpm lockfile is missing');
75
- if(exists(path.join(root,'.npmrc'))){
76
- // pnpm 12 reads project settings from pnpm-workspace.yaml. Registry/auth
77
- // entries affect acquisition, not a completed local TypeScript invocation.
78
- for(const line of fs.readFileSync(path.join(root,'.npmrc'),'utf8').split(/\r?\n/)){
79
- const trimmed=line.trim();if(!trimmed||/^[#;]/.test(trimmed))continue;
80
- const key=trimmed.split('=')[0].trim();
81
- if(key!=='registry'&&!/^\/\/[^\s]+\/:_authToken$/.test(key))
82
- throw Error('project .npmrc has execution-relevant pnpm settings requiring review');
83
- }
84
- }
85
104
  timing('pnpm.install_metadata',started);
86
- return {version,linker,patterns,settings,modulesFile,
105
+ return {version:installed[1],linker,patterns,settings,modulesFile,
87
106
  configurationInputs:['package.json','pnpm-lock.yaml',
88
107
  ...(exists(workspaceFile)?['pnpm-workspace.yaml']:[]),
89
108
  ...(exists(path.join(root,'.npmrc'))?['.npmrc']:[]),
@@ -173,16 +192,37 @@ function packageAt(root,requester,name){
173
192
  }
174
193
  return {physical:null,candidates};
175
194
  }
176
- function parsedTypeScript(root,dir,ts){
177
- const config=path.join(root,dir,'tsconfig.json'),reads=new Set();
195
+ // Files TypeScript may consume from its own include: each directory its
196
+ // include patterns watch for new files (TypeScript's own
197
+ // wildcardDirectories), and each file it lists. node_modules below them is
198
+ // left out, as TypeScript's wildcards leave it out; installed files a program
199
+ // reads are installed inputs. `exclude` entries stay inputs: an import can
200
+ // still reach an excluded file.
201
+ const sourceFiles='*.{ts,tsx,mts,cts,js,jsx,mjs,cjs,json}';
202
+ function sourcePatterns(root,parsed){
203
+ const patterns=new Set(),excluded=new Set();
204
+ for(const [folder,flags] of Object.entries(parsed.wildcardDirectories||{})){
205
+ // TypeScript 4 lowercases these keys on case-insensitive file systems;
206
+ // the native realpath gives the directory's own spelling.
207
+ let at;try{at=fs.realpathSync.native(folder);}catch{at=path.resolve(folder);}
208
+ if(!within(at,root))throw Error('TypeScript source membership escapes the checkout');
209
+ const prefix=rel(root,at)?rel(root,at)+'/':'';
210
+ if(flags&1){patterns.add(`${prefix}**/${sourceFiles}`);excluded.add(`!${prefix}**/node_modules/**`);}
211
+ else patterns.add(prefix+sourceFiles);
212
+ }
213
+ const included=patternMatcher([...patterns,...excluded]);
214
+ for(const file of parsed.fileNames.map(name=>path.resolve(name))){
215
+ if(!within(file,root))throw Error('TypeScript source membership escapes the checkout');
216
+ if(!included(rel(root,file)))patterns.add(rel(root,file));
217
+ }
218
+ return [...patterns,...excluded];
219
+ }
220
+ function parsedTypeScript(root,dir,ts,configName='tsconfig.json'){
221
+ const config=path.join(root,dir,configName),reads=new Set();
178
222
  const host={...ts.sys,readFile(file){reads.add(path.resolve(file));return ts.sys.readFile(file);},
179
223
  onUnRecoverableConfigFileDiagnostic(){}};
180
224
  const parsed=ts.getParsedCommandLineOfConfigFile(config,{},host);
181
225
  if(!parsed||parsed.errors?.length)throw Error('TypeScript configuration unavailable');
182
- const raw=ts.readConfigFile(config,ts.sys.readFile);
183
- if(raw.error||!Array.isArray(raw.config?.include)||raw.config.include.length!==1||
184
- !/^src(?:$|\/)/.test(raw.config.include[0]))
185
- throw Error('TypeScript source file membership is outside the supported src include');
186
226
  if(parsed.options.plugins?.length||parsed.options.incremental||parsed.options.composite||
187
227
  parsed.projectReferences?.length||parsed.options.paths||parsed.options.baseUrl||
188
228
  parsed.options.rootDirs?.length||parsed.options.typeRoots?.length||
@@ -194,12 +234,13 @@ function parsedTypeScript(root,dir,ts){
194
234
  throw Error('TypeScript module resolver is outside the supported Node contract');
195
235
  for(const file of reads)if(!within(realObservedPath(file),root))
196
236
  throw Error('TypeScript configuration escapes the checkout');
197
- return {parsed,configs:[...reads].map(file=>rel(root,file)).sort()};
237
+ return {parsed,configs:[...reads].map(file=>rel(root,file)).sort(),
238
+ sourcePatterns:sourcePatterns(root,parsed)};
198
239
  }
199
- function listedTypeScriptFiles(root,selected,tsc){
240
+ function listedTypeScriptFiles(root,selected,tsc,configName){
200
241
  // --listFilesOnly resolves the compiler's effective module/file set but
201
242
  // performs no typecheck and emits no project outputs.
202
- const out=spawnSync(process.execPath,[tsc,'-p','tsconfig.json','--listFilesOnly'],
243
+ const out=spawnSync(process.execPath,[tsc,'-p',configName,'--listFilesOnly'],
203
244
  {cwd:path.join(root,selected.dir),env:process.env,encoding:'utf8',
204
245
  // The native NTFS public workspace takes 3-5 s to list ~770 inputs;
205
246
  // 5 s sometimes expired despite bounded, progressing compiler work.
@@ -236,17 +277,23 @@ function sharedHardlinkIn(folder){
236
277
  }
237
278
  return false;
238
279
  }
239
- export function pnpmTypecheckInputs(root,workspace,script,{captureQueries=false}={}){
280
+ // `run`: the check's {script, part, args}, or a script name for its only run.
281
+ export function pnpmTypecheckInputs(root,workspace,run,{captureQueries=false}={}){
240
282
  const totalStarted=performance.now();
241
283
  root=realObservedPath(root);
242
284
  const install=pnpmInstallInfo(root),members=pnpmWorkspacePackages(root,install.patterns);
243
285
  const selected=workspace==='.'?{name:'.',dir:'.',manifest:'package.json',
244
286
  pkg:read(path.join(root,'package.json'))}:members.get(workspace);
245
287
  if(!selected)throw Error(`workspace ${workspace} is not declared`);
246
- if(!/^(?:tsc --noEmit|tsc -p (?:\.|tsconfig\.json)(?: --noEmit)?)$/.test(
247
- selected.pkg.scripts?.[script]?.trim()||'')||
248
- selected.pkg.scripts?.['pre'+script]||selected.pkg.scripts?.['post'+script])
249
- throw Error('typecheck must be one standalone TypeScript invocation');
288
+ // A script name stands for its only run.
289
+ if(typeof run==='string'){const runs=tscRuns(selected.pkg.scripts?.[run]);run={script:run,args:runs?.length===1?runs[0].args:[]};}
290
+ const {run:own,issue}=tscRun(selected.pkg.scripts,run);
291
+ if(issue)throw Error(`typecheck must be standalone TypeScript runs: ${issue}`);
292
+ // `tsc -p .` without --noEmit predates the other forms.
293
+ if(!own.noEmit&&!['-p .','-p tsconfig.json'].includes(run.args.join(' ')))
294
+ throw Error('typecheck must pass --noEmit');
295
+ const configName=runConfig(path.join(root,selected.dir),run.args);
296
+ if(!configName)throw Error('TypeScript project is outside the package');
250
297
  const selectedDir=path.join(root,selected.dir),lookup=createRequire(path.join(selectedDir,'package.json'));
251
298
  let tsPackage;try{tsPackage=realObservedPath(lookup.resolve('typescript/package.json'));}
252
299
  catch{throw Error('installed TypeScript compiler cannot be resolved');}
@@ -255,7 +302,8 @@ export function pnpmTypecheckInputs(root,workspace,script,{captureQueries=false}
255
302
  const tsc=path.join(tsRoot,'bin','tsc');
256
303
  if(!exists(tsc))throw Error('installed TypeScript executable is missing');
257
304
  const configStarted=performance.now();
258
- const {configs}=parsedTypeScript(root,selected.dir,ts);
305
+ const {configs,sourcePatterns:sources}=parsedTypeScript(root,selected.dir,ts,configName);
306
+ const isSource=patternMatcher(sources);
259
307
  timing('pnpm.ts_config',configStarted,{configs:configs.length});
260
308
  const closure=new Map(),installed=new Set(),generated=new Set(),
261
309
  links=new Set(),candidates=new Set(),absences=[],instances=new Map();
@@ -305,12 +353,12 @@ export function pnpmTypecheckInputs(root,workspace,script,{captureQueries=false}
305
353
  resolutionMs:Math.round(resolutionMs),workspaceLinks:links.size,
306
354
  candidates:candidates.size});
307
355
  const listingStarted=performance.now();
308
- const captured=captureQueries?compilerFiles(tsRoot,path.join(root,selected.dir)):null;
309
- const files=captured?captured.files.map(realObservedPath):listedTypeScriptFiles(root,selected,tsc);
356
+ const captured=captureQueries?compilerFiles(tsRoot,path.join(root,selected.dir),configName):null;
357
+ const files=captured?captured.files.map(realObservedPath):listedTypeScriptFiles(root,selected,tsc,configName);
310
358
  timing('pnpm.ts_input_listing',listingStarted,{files:files.length});
311
359
  const mappingStarted=performance.now();
312
360
  for(const file of files){
313
- if(within(file,path.join(selectedDir,'src'))||
361
+ if(isSource(rel(root,file))||
314
362
  [...closure.values()].some(member=>within(file,path.join(root,member.dir))))continue;
315
363
  const packageRoot=installedPackageRoot(root,file);
316
364
  if(!packageRoot)throw Error(`TypeScript consumes a file outside the observed pnpm contract: ${rel(root,file)}`);
@@ -321,6 +369,16 @@ export function pnpmTypecheckInputs(root,workspace,script,{captureQueries=false}
321
369
  }
322
370
  timing('pnpm.input_mapping',mappingStarted,{instances:instances.size,
323
371
  installedPatterns:installed.size});
372
+ // A config extended from an installed package is an installed input too:
373
+ // source patterns leave node_modules out.
374
+ for(const config of configs){
375
+ const file=realObservedPath(path.join(root,config));
376
+ if(!within(file,path.join(root,'node_modules')))continue;
377
+ const packageRoot=installedPackageRoot(root,file);
378
+ if(!packageRoot)throw Error(`TypeScript configuration ${config} is outside an installed package`);
379
+ installed.add(`${rel(root,packageRoot)}/**`);
380
+ installed.add(`!${rel(root,packageRoot)}/node_modules/**`);
381
+ }
324
382
  installed.add(`${rel(root,tsRoot)}/**`);
325
383
  installed.add(`!${rel(root,tsRoot)}/node_modules/**`);
326
384
  if(!instances.has(tsRoot))instances.set(tsRoot,{name:'typescript',physicalRoot:rel(root,tsRoot)});
@@ -329,11 +387,12 @@ export function pnpmTypecheckInputs(root,workspace,script,{captureQueries=false}
329
387
  timing('pnpm.hardlink_inspection',sharedStarted,{instances:instances.size,
330
388
  shared:shared.length});
331
389
  const source=[...new Set([...install.configurationInputs,selected.manifest,
332
- ...configs,`${selected.dir==='.'?'':selected.dir+'/'}src/**`,
390
+ ...configs,...sources,
333
391
  ...closure.values().map(member=>member.manifest),...generated])];
334
392
  timing('pnpm.total_discovery',totalStarted,{instances:instances.size,
335
393
  inputFiles:files.length});
336
394
  return {install,workspace:selected,closure:[...closure.values()],source,
395
+ sourcePatterns:sources,args:run.args,
337
396
  installed:[...installed],generated:[...generated],
338
397
  // The read-only state probe consumes this same resolved compiler file set.
339
398
  // It is deliberately omitted from the persisted user-facing input plan.
@@ -7,14 +7,17 @@ import fs from 'node:fs';
7
7
  import path from 'node:path';
8
8
  import {createHash} from 'node:crypto';
9
9
  import {pnpmTypecheckInputs} from './pnpm-inputs.mjs';
10
+ import {patternMatcher} from './glob.mjs';
10
11
  import {withinPath as within,realObservedPath} from './path-identity.mjs';
12
+ import {runFromArgv} from './typescript-invocation.mjs';
11
13
 
12
- const [rootArg,workspace,script]=process.argv.slice(2);
14
+ const [rootArg,workspace,...rest]=process.argv.slice(2).filter(value=>!value.startsWith('--redue-'));
13
15
  const hash=value=>createHash('sha256').update(value).digest('hex');
14
16
  const digest=file=>hash(fs.readFileSync(file));
15
17
  const fail=code=>{throw Object.assign(Error(code),{code});};
16
18
  try{
17
- if(!rootArg||!workspace||!script)fail('probe-arguments');
19
+ if(!rootArg||!workspace)fail('probe-arguments');
20
+ const run=runFromArgv(rest);
18
21
  const root=realObservedPath(rootArg);
19
22
  if(process.env.NODE_OPTIONS||process.env.NODE_PATH||process.env.BASH_ENV||
20
23
  process.env.ENV||Object.entries(process.env).some(([key,value])=>{
@@ -24,23 +27,25 @@ try{
24
27
  return /^(PNPM_|COREPACK_|npm_config_|DYLD_|TSGO_)/i.test(key);
25
28
  }))
26
29
  fail('pnpm-execution-environment-unsupported');
27
- const contextHash=hash(JSON.stringify([root,workspace,script,process.version]));
30
+ const contextHash=hash(JSON.stringify([root,workspace,run,process.version]));
28
31
  if(contextOnly(contextHash))process.exit(0);
29
- const contract=pnpmTypecheckInputs(root,workspace,script,{captureQueries:process.argv.includes('--redue-checkpoint')});
32
+ const contract=pnpmTypecheckInputs(root,workspace,run,{captureQueries:process.argv.includes('--redue-checkpoint')});
30
33
  if(contract.limitations.length)fail('pnpm-input-coverage-unavailable');
31
34
  const selected=path.join(root,contract.workspace.dir),tsc=contract.tsc;
32
35
  const inputs=contract.listedFiles;
33
36
  if(!inputs.length)fail('typescript-input-list-unavailable');
34
- const inputRoots=[path.join(selected,'src'),...contract.generated.map(pattern=>
37
+ const inputRoots=[...contract.generated.map(pattern=>
35
38
  path.join(root,pattern.slice(0,-3))),...contract.installed.filter(p=>!p.startsWith('!'))
36
39
  .map(pattern=>path.join(root,pattern.slice(0,-3)))];
40
+ const isSource=patternMatcher(contract.sourcePatterns);
37
41
  for(const file of inputs)if(!within(file,root)||
42
+ !isSource(path.relative(root,file).split(path.sep).join('/'))&&
38
43
  !inputRoots.some(boundary=>within(file,boundary)))
39
44
  fail('typescript-input-outside-pnpm-contract');
40
45
  const files=[...new Set(inputs)].sort().map(file=>
41
46
  [hash(path.relative(root,file)),digest(file)]);
42
47
  const configs=contract.configs.map(file=>[hash(file),digest(path.join(root,file))]);
43
- probeResult(hash(JSON.stringify({schema:1,workspace,script,
48
+ probeResult(hash(JSON.stringify({schema:1,workspace,run,
44
49
  files,configs,node:process.version,tsc:digest(tsc),
45
50
  packageManager:contract.install.version,linker:contract.install.linker})),contextHash,contract.discoveryQueries);
46
51
  }catch(e){console.error(`VSTATE_REASON:${e.code&&/^[a-z-]+$/.test(e.code)?e.code: