Programmatic use
@joinmonolith/cli is also a library. The same fingerprint and perceptual
hash functions the monolith binary uses are exported from the package
root.
Install
bun add @joinmonolith/cli
# or
npm install @joinmonolith/cliFingerprints
import { fingerprintFile, fingerprintBytes, fingerprintStream } from '@joinmonolith/cli'
// From a path
const fp1 = await fingerprintFile('./image.png')
// → "0x20a7b5ff…"
// From an in-memory buffer
const fp2 = fingerprintBytes(new Uint8Array([1, 2, 3]))
// From any ReadableStream / AsyncIterable<Uint8Array>
const res = await fetch('https://example.com/track.mp3')
const fp3 = await fingerprintStream(res.body!)All three return the same 0x-prefixed lowercase-hex SHA-256 string the
API stores on an Artifact.
Perceptual hashes
import { detectKind, phashImage, chromaprintAudio } from '@joinmonolith/cli'
import type { FileKind, KindInfo } from '@joinmonolith/cli'
const bytes = new Uint8Array(await Bun.file('./image.png').bytes())
const info: KindInfo = await detectKind(bytes)
if (info.kind === 'image') {
const phash = await phashImage(bytes)
// → "918b8b916e4aee91"
}
if (info.kind === 'audio') {
const { fingerprint, sampleRate, channels, durationSec } =
await chromaprintAudio(bytes)
}detectKind sniffs magic bytes and returns
{ kind: 'image' | 'audio' | 'other', mime?, ext? }.
Perceptual hashes are deterministic across the CLI, the library, and the
API. The same bytes produce the same phash / chromaprint everywhere.
Build an Artifact payload
import { fingerprintFile } from '@joinmonolith/cli'
import { stat } from 'node:fs/promises'
async function artifactPayload(path: string, markId: string) {
const [fingerprint, st] = await Promise.all([
fingerprintFile(path),
stat(path)
])
return {
markId,
fingerprint,
filename: path.split('/').pop()!,
size: st.size
}
}
const body = await artifactPayload('./my-art.png', process.env.MARK_ID!)
await fetch('https://api.joinmonolith.com/api/v1/artifacts', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.MONOLITH_TOKEN}`,
'Content-Type': 'application/json'
},
body: JSON.stringify(body)
})Types
type FileKind = 'image' | 'audio' | 'other'
interface KindInfo {
kind: FileKind
mime?: string
ext?: string
}Last updated on