@vzn/vx 0.0.354 → 0.0.356

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.
@@ -8,6 +8,7 @@ import { hasEsmExport, unprovidedBareImports } from './config-imports.js'
8
8
  import {
9
9
  configEvalKey,
10
10
  configEvalKeyFromClosure,
11
+ configEvalKeyFromIdentities,
11
12
  configImports,
12
13
  type ConfigEvalStore,
13
14
  } from './config-cache.js'
@@ -297,7 +298,7 @@ export interface LoadProjectConfigOptions {
297
298
  * order given, so a failure names the first broken file the way a
298
299
  * one-by-one load did). A single-path load is the one-element case.
299
300
  */
300
- const LOAD_WIDTH = 64
301
+ const LOAD_WIDTH = 128
301
302
 
302
303
  export async function loadProjectConfigs(
303
304
  configPaths: readonly string[],
@@ -323,49 +324,69 @@ export async function loadProjectConfigs(
323
324
  // the map; a file the batch could not stat has no identity, which makes
324
325
  // that config's fast key miss and sends it down the slow path exactly as
325
326
  // a throwing per-file `hashFile` did.
326
- let fastHashFile = hashFile
327
+ let identities: Map<string, string> | undefined
327
328
  if (closures.size > 0 && store?.hashFiles !== undefined) {
328
329
  const files = new Set<string>()
329
330
  for (const closure of closures.values()) for (const f of closure) files.add(f)
330
- const identities = await store.hashFiles([...files])
331
- fastHashFile = (file: string): Promise<string> => {
332
- const id = identities.get(file)
333
- return id === undefined
334
- ? Promise.reject(new Error(`no identity for ${file}`))
335
- : Promise.resolve(id)
336
- }
331
+ identities = await store.hashFiles([...files])
332
+ }
333
+ // With the batch in hand a fast key is synchronous: an async key per
334
+ // config, awaiting identities already in the map, was ~5 ms of a
335
+ // 1,000-config warm load. Only a config off the fast path is awaited.
336
+ const fastKeyOf = (configPath: string): string | null => {
337
+ const closure = closures.get(configPath)
338
+ if (closure === undefined || evalCache === undefined || identities === undefined) return null
339
+ return configEvalKeyFromIdentities({
340
+ closure,
341
+ identities,
342
+ workspaceFingerprint: evalCache.workspaceFingerprint,
343
+ })
344
+ }
345
+ const fastHashFile = identities === undefined ? hashFile : undefined
346
+ interface Prepared {
347
+ configPath: string
348
+ bytes: Uint8Array | null
349
+ cacheKey: string | null
350
+ indexed: boolean
351
+ closure?: string[] | undefined
337
352
  }
338
353
  const prepared = await Promise.all(
339
- configPaths.map(async (configPath) => {
340
- const closure = closures.get(configPath)
341
- if (closure !== undefined && evalCache !== undefined && fastHashFile !== undefined) {
342
- const fastKey = await configEvalKeyFromClosure({
343
- closure,
344
- hashFile: fastHashFile,
345
- workspaceFingerprint: evalCache.workspaceFingerprint,
346
- })
347
- if (fastKey !== null) return { configPath, bytes: null, cacheKey: fastKey, indexed: true }
348
- }
349
- const bytes = await Bun.file(configPath).bytes()
350
- const keyed =
351
- evalCache === undefined
352
- ? null
353
- : await configEvalKey({
354
- configPath,
355
- bytes,
356
- workspaceRoot: evalCache.workspaceRoot,
357
- workspaceFingerprint: evalCache.workspaceFingerprint,
358
- ...slowKeyHash,
359
- })
360
- return {
361
- configPath,
362
- bytes,
363
- cacheKey: keyed?.key ?? null,
364
- indexed: false,
365
- closure: keyed !== null && keyed.indexable ? keyed.closure : undefined,
366
- }
354
+ configPaths.map((configPath): Prepared | Promise<Prepared> => {
355
+ const fastKey = fastKeyOf(configPath)
356
+ return fastKey !== null
357
+ ? { configPath, bytes: null, cacheKey: fastKey, indexed: true }
358
+ : slowPrepared(configPath)
367
359
  }),
368
360
  )
361
+ async function slowPrepared(configPath: string): Promise<Prepared> {
362
+ const closure = closures.get(configPath)
363
+ if (closure !== undefined && evalCache !== undefined && fastHashFile !== undefined) {
364
+ const fastKey = await configEvalKeyFromClosure({
365
+ closure,
366
+ hashFile: fastHashFile,
367
+ workspaceFingerprint: evalCache.workspaceFingerprint,
368
+ })
369
+ if (fastKey !== null) return { configPath, bytes: null, cacheKey: fastKey, indexed: true }
370
+ }
371
+ const bytes = await Bun.file(configPath).bytes()
372
+ const keyed =
373
+ evalCache === undefined
374
+ ? null
375
+ : await configEvalKey({
376
+ configPath,
377
+ bytes,
378
+ workspaceRoot: evalCache.workspaceRoot,
379
+ workspaceFingerprint: evalCache.workspaceFingerprint,
380
+ ...slowKeyHash,
381
+ })
382
+ return {
383
+ configPath,
384
+ bytes,
385
+ cacheKey: keyed?.key ?? null,
386
+ indexed: false,
387
+ closure: keyed !== null && keyed.indexable ? keyed.closure : undefined,
388
+ }
389
+ }
369
390
  let hits = new Map<string, string>()
370
391
  if (evalCache !== undefined) {
371
392
  const keys = prepared.map((p) => p.cacheKey).filter((k): k is string => k !== null)
@@ -462,7 +483,9 @@ export async function loadProjectConfigs(
462
483
  try {
463
484
  // Loaded LOAD_WIDTH at a time: one import after another put 1,000 cold
464
485
  // configs at ~160 ms of imports where 64 at once take ~60 (D-68), and
465
- // the width bounds the files a module load may hold open. A failure
486
+ // 128 took `load configs` from 432 to 411 ms more; the width bounds the
487
+ // files a module load may hold open, so it stays under macOS's default
488
+ // 256-descriptor limit. A failure
466
489
  // stops nothing (the rest are evaluated and stored for the next
467
490
  // attempt); the error thrown is the first in order, as the serial
468
491
  // loop's was.