Source identity
| Field | Recorded value |
|---|---|
| Source type | GitHub source |
| Original record | Janady13/llms-txt-hub / packages/cli/src/lib/lockfile.ts |
| Date | Not specified in source |
| Repository | Janady13/llms-txt-hub |
| Revision | e0f4bf978d4df168369c7601d9c5f5ea89e84377 |
| Branch | main |
| Path or archive member | packages/cli/src/lib/lockfile.ts |
| Original SHA-256 | 5eba2a332bd01294bc9e582e66602fdba642bfa40cede9ca264a2562f362afbe |
| Git blob SHA | 40162dc210dde4dd14d4435fb25c433638696479 |
| Reading-copy SHA-256 | 5eba2a332bd01294bc9e582e66602fdba642bfa40cede9ca264a2562f362afbe |
Recovered source transcript
Download complete source text ↓121 lines · 3.1 KB
import { mkdirSync, readFileSync, renameSync, writeFileSync } from 'node:fs'
import { dirname, join } from 'node:path'
import type { Lockfile, LockfileEntry } from '../types/index.js'
import * as logger from './logger.js'
const LOCKFILE_NAME = 'llms.lock.json'
/**
* Type guard for Node.js errors that include a code property.
*/
function isNodeError(err: unknown): err is NodeJS.ErrnoException {
return err instanceof Error && 'code' in err
}
/**
* Return a fresh empty lockfile object.
*/
function emptyLockfile(): Lockfile {
return {
version: 1,
updatedAt: new Date().toISOString(),
entries: {}
}
}
/**
* Get the absolute path to the lockfile for a project directory.
*/
export function getLockfilePath(projectDir: string): string {
return join(projectDir, '.llms', LOCKFILE_NAME)
}
/**
* Read and parse the lockfile, returning an empty one on missing or corrupt file.
*/
export function readLockfile(projectDir: string): Lockfile {
const lockfilePath = getLockfilePath(projectDir)
try {
const raw = readFileSync(lockfilePath, 'utf-8')
const data: Lockfile = JSON.parse(raw)
if (data.version !== 1 || !data.entries) {
throw new Error('Invalid lockfile format')
}
return data
} catch (err) {
if (isNodeError(err) && err.code === 'ENOENT') {
return emptyLockfile()
}
// Corrupt lockfile — back it up and recreate
try {
const backupPath = `${lockfilePath}.backup`
renameSync(lockfilePath, backupPath)
logger.warn(`Corrupt lockfile backed up to ${backupPath}`)
} catch {
// backup rename failed, ignore
}
return emptyLockfile()
}
}
export interface WriteLockfileInput {
projectDir: string
lockfile: Lockfile
}
/**
* Write the lockfile to disk atomically via a temp file.
*/
export function writeLockfile({ projectDir, lockfile }: WriteLockfileInput): void {
const lockfilePath = getLockfilePath(projectDir)
const dir = dirname(lockfilePath)
mkdirSync(dir, { recursive: true })
lockfile.updatedAt = new Date().toISOString()
const tmpPath = `${lockfilePath}.tmp`
writeFileSync(tmpPath, `${JSON.stringify(lockfile, null, 2)}\n`, 'utf-8')
renameSync(tmpPath, lockfilePath)
}
export interface AddEntryInput {
projectDir: string
entry: LockfileEntry
}
/**
* Add or update a single entry in the lockfile.
*/
export function addEntry({ projectDir, entry }: AddEntryInput): void {
const lockfile = readLockfile(projectDir)
lockfile.entries[entry.slug] = entry
writeLockfile({ projectDir, lockfile })
}
export interface RemoveEntryInput {
projectDir: string
slug: string
}
/**
* Remove an entry from the lockfile by slug.
*/
export function removeEntry({ projectDir, slug }: RemoveEntryInput): void {
const lockfile = readLockfile(projectDir)
delete lockfile.entries[slug]
writeLockfile({ projectDir, lockfile })
}
export interface GetEntryInput {
projectDir: string
slug: string
}
/**
* Look up a single lockfile entry by slug.
*/
export function getEntry({ projectDir, slug }: GetEntryInput): LockfileEntry | undefined {
const lockfile = readLockfile(projectDir)
return lockfile.entries[slug]
}