Skip to content

Commit 5c184fb

Browse files
committed
refactor: delete camelToKebab + dead types file; fix doc drift
Org-scan R4 #1: camelToKebab was a subset of toKebabCase (snake_case support + more comprehensive acronym handling). Zero internal + zero sibling-repo callers confirmed via grep. Deleted the function and its tests (~130 LOC across src + test); updated toKebabCase JSDoc to drop the now-defunct cross-reference. Org-scan R4 #4: src/types/external-modules.d.ts was an ambient module declaration for cacache/pacote/make-fetch-happen that shadowed the actual typings already provided via tsconfig paths mapping to src/external/*.d.ts. Deleted the file + empty src/types/ dir. One caller (provenance.ts) was using the old ambient's shape for make-fetch-happen — switched its _fetcher type annotation to ReturnType<typeof makeFetchHappen.defaults>. Docs scan R4: - README.md: setupIPC() doesn't exist; replaced with real exports (processLock.lock/unlock, writeIpcStub/getIpcStubPath) - docs/process-utilities.md: rewrote ProcessLock class-style docs to match the actual processLock singleton API (acquire/release/withLock); replaced fictional setupIPC() section with docs for the two real IPC surfaces (ipc stub, ipc-cli env vars) - docs/http-utilities.md: User-Agent default is SOCKET_LIB_USER_AGENT (socketsecurity-lib/<version>), not socket-registry/1.0; httpDownload options type is HttpDownloadOptions not HttpDownloadResult - docs/visual-effects.md: Spinner({ spinner: 'dots' }) was wrong (SpinnerStyle is an object) — replaced with a getSpinner('dots') hint
1 parent a274c99 commit 5c184fb

8 files changed

Lines changed: 69 additions & 328 deletions

File tree

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,8 +97,8 @@ Spawn child processes safely with cross-platform support.
9797
- `spawnSync()` - Synchronous version for blocking operations
9898
- Array-based arguments prevent command injection
9999
- Automatic Windows `.cmd`/`.bat` handling
100-
- `ProcessLock` - Ensure only one instance runs at a time
101-
- `setupIPC()` - Inter-process communication
100+
- `processLock.lock()` / `processLock.unlock()` - Ensure only one instance runs at a time
101+
- `writeIpcStub()` / `getIpcStubPath()` - Filesystem-based inter-process data handoff
102102

103103
### Environment Detection
104104

docs/http-utilities.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,7 @@ console.log(response.headers['location']) // Redirect target
219219

220220
- `url` (string): The URL to download from
221221
- `destPath` (string): Path where file should be saved (absolute path recommended)
222-
- `options` (HttpDownloadResult): Download configuration
222+
- `options` (HttpDownloadOptions): Download configuration
223223

224224
**Returns:** `Promise<HttpDownloadResult>` with `headers`, `ok`, `path`, `size`, `status`, and `statusText`
225225

@@ -309,7 +309,7 @@ await httpJson('https://api.example.com/data', {
309309
})
310310
```
311311

312-
**Default:** Includes `User-Agent: socket-registry/1.0`
312+
**Default:** Includes `User-Agent: socketsecurity-lib/<version> (<url>)` (see `SOCKET_LIB_USER_AGENT` in `constants/socket`).
313313

314314
### body
315315

docs/process-utilities.md

Lines changed: 58 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -14,21 +14,16 @@ Spawn child processes, manage inter-process communication (IPC), handle process
1414

1515
```typescript
1616
import { spawn } from '@socketsecurity/lib/spawn'
17-
import { ProcessLock } from '@socketsecurity/lib/process-lock'
17+
import { processLock } from '@socketsecurity/lib/process-lock'
1818

1919
// Run a command
2020
const result = await spawn('git', ['status'])
2121
console.log(result.stdout)
2222

2323
// Ensure only one instance runs
24-
const lock = new ProcessLock('my-operation')
25-
if (await lock.acquire()) {
26-
try {
27-
await doWork()
28-
} finally {
29-
await lock.release()
30-
}
31-
}
24+
await processLock.withLock('/tmp/my-operation.lock', async () => {
25+
await doWork()
26+
})
3227
```
3328

3429
## Spawning Processes
@@ -334,114 +329,109 @@ try {
334329

335330
## Process Locks
336331

337-
### ProcessLock
332+
### processLock
338333

339-
**What it does:** Ensures only one instance of an operation runs at a time.
334+
**What it does:** Filesystem-based advisory lock for ensuring only one process runs a critical section at a time.
340335

341336
**When to use:** Preventing duplicate builds, ensuring atomic operations, coordinating between processes.
342337

338+
The module exports a singleton `processLock` (type `ProcessLockManager`). There is NO `ProcessLock` constructor — use the singleton.
339+
343340
**Example:**
344341

345342
```typescript
346-
import { ProcessLock } from '@socketsecurity/lib/process-lock'
343+
import { processLock } from '@socketsecurity/lib/process-lock'
347344

348-
const lock = new ProcessLock('my-critical-operation')
345+
const lockPath = '/tmp/my-critical-operation.lock'
349346

350-
if (await lock.acquire()) {
347+
const acquired = await processLock.acquire(lockPath)
348+
if (acquired) {
351349
try {
352350
// Do critical work that shouldn't run concurrently
353351
await buildProject()
354352
} finally {
355-
// Always release in finally block
356-
await lock.release()
353+
processLock.release(lockPath)
357354
}
358-
} else {
359-
console.log('Another process is running this operation')
360355
}
361356
```
362357

363-
### ProcessLock Methods
358+
### withLock (scoped helper)
364359

365-
#### constructor(name, options?)
366-
367-
Creates a new process lock.
360+
Prefer `withLock` for automatic release:
368361

369362
```typescript
370-
const lock = new ProcessLock('build-process', {
371-
lockDir: '/tmp/locks', // Custom lock directory
372-
timeout: 30000, // Timeout in ms
363+
import { processLock } from '@socketsecurity/lib/process-lock'
364+
365+
await processLock.withLock('/tmp/build.lock', async () => {
366+
await buildProject()
373367
})
374368
```
375369

376-
#### acquire()
370+
### processLock Methods
377371

378-
Attempts to acquire the lock.
372+
#### acquire(lockPath, options?)
373+
374+
Attempts to acquire the lock at `lockPath`. Returns `true` if acquired, `false` if another process holds it.
379375

380376
```typescript
381-
const acquired = await lock.acquire()
382-
if (acquired) {
383-
// Lock acquired, do work
384-
}
377+
const acquired = await processLock.acquire('/tmp/my.lock', {
378+
retries: 10, // retry up to 10 times
379+
baseDelayMs: 100, // start retrying with 100ms backoff
380+
staleMs: 60_000, // consider lock stale after 60s
381+
})
385382
```
386383

387-
Returns `true` if lock was acquired, `false` if another process holds it.
388-
389-
#### release()
384+
#### release(lockPath)
390385

391-
Releases the lock.
386+
Releases a lock previously acquired by this process. Always pair with `acquire` in a `finally` block, or use `withLock`.
392387

393388
```typescript
394-
await lock.release()
389+
processLock.release('/tmp/my.lock')
395390
```
396391

397-
Always call this in a `finally` block to ensure cleanup.
392+
#### withLock(lockPath, fn, options?)
398393

399-
#### isLocked()
400-
401-
Checks if the lock is currently held.
394+
Scoped helper — acquires the lock, runs `fn`, releases the lock even on error. Returns the value of `fn`.
402395

403396
```typescript
404-
if (await lock.isLocked()) {
405-
console.log('Lock is held by another process')
406-
}
397+
const result = await processLock.withLock('/tmp/my.lock', async () => {
398+
return await doWork()
399+
})
407400
```
408401

409402
## Inter-Process Communication (IPC)
410403

411-
### setupIPC()
412-
413-
**What it does:** Sets up IPC channel for communication between parent and child processes.
404+
Two complementary IPC modules are provided:
414405

415-
**When to use:** Sending messages between processes, coordinating work, passing data.
406+
### Filesystem stub IPC (`@socketsecurity/lib/ipc`)
416407

417-
**Example:**
408+
For passing data between parent and child processes when the payload may exceed environment-variable size limits or needs restricted-perm (0o600) storage.
418409

419410
```typescript
420-
import { setupIPC } from '@socketsecurity/lib/ipc'
411+
import { writeIpcStub, getIpcStubPath } from '@socketsecurity/lib/ipc'
421412

422-
// In parent process
423-
const child = spawn('node', ['worker.js'], {
424-
stdio: ['ignore', 'pipe', 'pipe', 'ipc'],
425-
})
426-
427-
setupIPC(child.process, {
428-
onMessage: message => {
429-
console.log('Received from child:', message)
413+
// Parent: write payload to stub file and pass path to child
414+
const stubPath = await writeIpcStub('socket-cli', {
415+
apiToken: 'secret',
416+
config: {
417+
/* ... */
430418
},
431419
})
420+
spawn('node', ['child.js', stubPath])
432421

433-
// Send to child
434-
child.process.send({ type: 'work', data: 'foo' })
422+
// Child: read the stub path from argv and load the JSON
423+
import { readFile } from 'node:fs/promises'
424+
const data = JSON.parse(await readFile(process.argv[2], 'utf8'))
425+
```
435426

436-
// In child (worker.js)
437-
setupIPC(process, {
438-
onMessage: message => {
439-
if (message.type === 'work') {
440-
const result = doWork(message.data)
441-
process.send({ type: 'result', value: result })
442-
}
443-
},
444-
})
427+
### CLI env-var IPC (`@socketsecurity/lib/ipc-cli`)
428+
429+
For reading `SOCKET_CLI_*` environment variables forwarded by a parent Socket CLI.
430+
431+
```typescript
432+
import { getIpc } from '@socketsecurity/lib/ipc-cli'
433+
434+
const { SOCKET_CLI_FIX, SOCKET_CLI_OPTIMIZE } = await getIpc()
445435
```
446436

447437
## Real-World Examples

docs/visual-effects.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,9 @@ import { Spinner } from '@socketsecurity/lib/spinner'
3535
const spinner = Spinner({
3636
text: 'Loading data...',
3737
color: [140, 82, 255], // Socket purple RGB
38-
spinner: 'dots', // Animation style
3938
})
39+
// Use a preset animation by name via getSpinner('dots'):
40+
// const spinner = Spinner({ spinner: getSpinner('dots') })
4041
```
4142

4243
**What it does:** Creates an animated CLI spinner with custom text, colors, and animation style.

src/packages/provenance.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ const ArrayIsArray = Array.isArray
2121
const SLSA_PROVENANCE_V0_2 = 'https://slsa.dev/provenance/v0.2'
2222
const SLSA_PROVENANCE_V1_0 = 'https://slsa.dev/provenance/v1'
2323

24-
let _fetcher: typeof import('make-fetch-happen') | undefined
24+
let _fetcher: ReturnType<typeof makeFetchHappen.defaults> | undefined
2525

2626
/**
2727
* Find the first attestation with valid provenance data.
@@ -98,7 +98,7 @@ function getFetcher() {
9898
cache: 'force-cache',
9999
})
100100
}
101-
return _fetcher as typeof import('make-fetch-happen')
101+
return _fetcher
102102
}
103103

104104
/**

src/strings.ts

Lines changed: 2 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -82,84 +82,6 @@ export function applyLinePrefix(
8282
: str
8383
}
8484

85-
/**
86-
* Convert a camelCase string to kebab-case.
87-
*
88-
* Transforms camelCase strings by converting uppercase letters to lowercase
89-
* and inserting hyphens before uppercase sequences. Handles consecutive
90-
* uppercase letters (like "XMLHttpRequest") by treating them as a single word.
91-
* Returns empty string for empty input.
92-
*
93-
* Note: This function only handles camelCase. For mixed formats including
94-
* snake_case, use `toKebabCase()` instead.
95-
*
96-
* @param str - The camelCase string to convert
97-
* @returns The kebab-case string
98-
*
99-
* @example
100-
* ```ts
101-
* camelToKebab('helloWorld')
102-
* // Returns: 'hello-world'
103-
*
104-
* camelToKebab('XMLHttpRequest')
105-
* // Returns: 'xmlhttp-request'
106-
*
107-
* camelToKebab('iOS')
108-
* // Returns: 'i-os'
109-
*
110-
* camelToKebab('')
111-
* // Returns: ''
112-
* ```
113-
*/
114-
/*@__NO_SIDE_EFFECTS__*/
115-
export function camelToKebab(str: string): string {
116-
const { length } = str
117-
if (!length) {
118-
return ''
119-
}
120-
let result = ''
121-
let i = 0
122-
while (i < length) {
123-
const char = str[i]
124-
if (!char) {
125-
break
126-
}
127-
const charCode = char.charCodeAt(0)
128-
// Check if current character is uppercase letter.
129-
// A = 65, Z = 90
130-
const isUpperCase = charCode >= 65 /*'A'*/ && charCode <= 90 /*'Z'*/
131-
if (isUpperCase) {
132-
// Add dash before uppercase sequence (except at start).
133-
if (result.length > 0) {
134-
result += '-'
135-
}
136-
// Collect all consecutive uppercase letters.
137-
while (i < length) {
138-
const currChar = str[i]
139-
if (!currChar) {
140-
break
141-
}
142-
const currCharCode = currChar.charCodeAt(0)
143-
const isCurrUpper =
144-
currCharCode >= 65 /*'A'*/ && currCharCode <= 90 /*'Z'*/
145-
if (isCurrUpper) {
146-
// Convert uppercase to lowercase: subtract 32 (A=65 -> a=97, diff=32)
147-
result += fromCharCode(currCharCode + 32 /*'a'-'A'*/)
148-
i += 1
149-
} else {
150-
// Stop when we hit non-uppercase.
151-
break
152-
}
153-
}
154-
} else {
155-
// Handle lowercase letters, digits, and other characters.
156-
result += char
157-
i += 1
158-
}
159-
}
160-
return result
161-
}
162-
16385
/**
16486
* Center text within a given width.
16587
*
@@ -800,8 +722,8 @@ export function stripBom(str: string): string {
800722
* - Inserting hyphens before uppercase letters (for camelCase)
801723
* - Replacing underscores with hyphens (for snake_case)
802724
*
803-
* This is more comprehensive than `camelToKebab()` as it handles mixed
804-
* formats including snake_case. Returns empty string for empty input.
725+
* Handles mixed formats (camelCase, snake_case, acronyms) in one pass.
726+
* Returns empty string for empty input.
805727
*
806728
* @param str - The string to convert
807729
* @returns The kebab-case string

0 commit comments

Comments
 (0)