Skip to content

Commit 66e4e5d

Browse files
committed
fix: large results on small streams
Signed-off-by: Jan Kowalleck <jan.kowalleck@gmail.com>
1 parent 1b0f30c commit 66e4e5d

File tree

5 files changed

+43
-6
lines changed

5 files changed

+43
-6
lines changed

HISTORY.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,13 @@ All notable changes to this project will be documented in this file.
44

55
## unreleased
66

7+
* Fixed
8+
* Writing large results to streams no longer drops data, but retries (via [#])
9+
* Docs
10+
* Showcase programmatic CLI usage ([#1142] via [#])
11+
12+
[#1142]: https://github.com/CycloneDX/cyclonedx-node-npm/issues/1142
13+
714
## 1.16.0 - 2023-12-11
815

916
* Change

bin/cyclonedx-npm-cli.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#!/usr/bin/env node
2+
/* !!! do not remove/rename this file, it is public CLI in replacement for an API !!! */
23
require('../dist/cli.js').run(process).catch(e => {
34
process.stderr.write(`\n${e}\n`)
45
return Math.max(1, Math.floor(Number(e?.code)) || 254)

src/_helpers.ts

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,28 @@ SPDX-License-Identifier: Apache-2.0
1717
Copyright (c) OWASP Foundation. All Rights Reserved.
1818
*/
1919

20-
import { readFileSync } from 'fs'
20+
import { readFileSync, writeSync } from 'fs'
2121

2222
export function loadJsonFile (path: string): any {
2323
return JSON.parse(readFileSync(path, 'utf8'))
2424
// may be replaced by `require(f, { with: { type: "json" } })`
2525
// as soon as this spec is properly implemented.
2626
// see https://github.com/tc39/proposal-import-attributes
2727
}
28+
29+
export async function writeAllSync (fd: number, data: string): Promise<number> {
30+
const b = Buffer.from(data)
31+
const l = b.byteLength
32+
let w = 0
33+
while (w < l) {
34+
try {
35+
w += writeSync(fd, b, w)
36+
} catch (error: any) {
37+
if (error.code !== 'EAGAIN') {
38+
throw error
39+
}
40+
await new Promise((resolve) => setTimeout(resolve, 100))
41+
}
42+
}
43+
return w
44+
}

src/cli.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,10 @@ Copyright (c) OWASP Foundation. All Rights Reserved.
1919

2020
import { Builders, Enums, Factories, Serialize, Spec, Validation } from '@cyclonedx/cyclonedx-library'
2121
import { Argument, Command, Option } from 'commander'
22-
import { existsSync, openSync, writeSync } from 'fs'
22+
import { existsSync, openSync } from 'fs'
2323
import { dirname, resolve } from 'path'
2424

25-
import { loadJsonFile } from './_helpers'
25+
import { loadJsonFile, writeAllSync } from './_helpers'
2626
import { BomBuilder, TreeBuilder } from './builders'
2727
import { makeConsoleLogger } from './logger'
2828

@@ -309,7 +309,7 @@ export async function run (process: NodeJS.Process): Promise<number> {
309309
}
310310

311311
myConsole.log('LOG | writing BOM to', options.outputFile)
312-
const written = writeSync(
312+
const written = await writeAllSync(
313313
options.outputFile === OutputStdOut
314314
? process.stdout.fd
315315
: openSync(resolve(process.cwd(), options.outputFile), 'w'),

src/index.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,5 +17,17 @@ SPDX-License-Identifier: Apache-2.0
1717
Copyright (c) OWASP Foundation. All Rights Reserved.
1818
*/
1919

20-
// not publishing anything at the moment
21-
export { }
20+
console.warn(`
21+
There is no public API. Instead, there is a well-thought, stable CLI.
22+
Call it programmatically like so:
23+
const { execFileSync } = require('child_process')
24+
const BUFFER_MAX_LENGTH = require('buffer').constants.MAX_LENGTH
25+
const sbom = JSON.parse(execFileSync(process.execPath, [
26+
'../path/to/this/module/bin/cyclonedx-npm-cli.js',
27+
'--output-format', 'JSON',
28+
'--output-file', '-'
29+
// additional CLI args
30+
], { stdio: ['ignore', 'pipe', 'ignore'], encoding: 'buffer', maxBuffer: BUFFER_MAX_LENGTH }))
31+
`)
32+
33+
export {/* See above! */}

0 commit comments

Comments
 (0)