Skip to content

Commit be7bfa6

Browse files
committed
feat(dlx): unify .dlx-metadata.json schema across implementations
Standardize metadata format for cached binaries to be consistent across TypeScript dlxBinary and C++ socket_macho_decompress implementations. Changes: - Add DlxMetadata interface with comprehensive documentation - Update writeMetadata() to use unified schema with core fields - Add support for source tracking (download vs decompression) - Include cache_key, size, and checksum_algorithm fields - Maintain backward compatibility in listDlxCache() reader - Export DlxMetadata interface as canonical schema reference Schema structure: - Core fields: version, cache_key, timestamp, checksum, checksum_algorithm, platform, arch, size, source - Extra fields: Reserved for implementation-specific data (e.g., compression metrics for C++ decompressor) Note: Uses SHA-256 for checksums (C++ uses SHA-512)
1 parent 5b4bc3d commit be7bfa6

File tree

1 file changed

+118
-8
lines changed

1 file changed

+118
-8
lines changed

src/dlx-binary.ts

Lines changed: 118 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,90 @@ export interface DlxBinaryResult {
4141
spawnPromise: ReturnType<typeof spawn>
4242
}
4343

44+
/**
45+
* Metadata structure for cached binaries (.dlx-metadata.json).
46+
* Unified schema shared across TypeScript (dlxBinary) and C++ (socket_macho_decompress).
47+
*
48+
* Core Fields (present in all implementations):
49+
* - version: Schema version (currently "1.0.0")
50+
* - cache_key: First 16 chars of SHA-512 hash (matches directory name)
51+
* - timestamp: Unix timestamp in milliseconds
52+
* - checksum: Full hash of cached binary (SHA-512 for C++, SHA-256 for TypeScript)
53+
* - checksum_algorithm: "sha512" or "sha256"
54+
* - platform: "darwin" | "linux" | "win32"
55+
* - arch: "x64" | "arm64"
56+
* - size: Size of cached binary in bytes
57+
* - source: Origin information
58+
* - type: "download" (from URL) or "decompression" (from embedded binary)
59+
* - url: Download URL (if type is "download")
60+
* - path: Source binary path (if type is "decompression")
61+
*
62+
* Extra Fields (implementation-specific):
63+
* - For C++ decompression:
64+
* - compressed_size: Size of compressed data in bytes
65+
* - compression_algorithm: Brotli level (numeric)
66+
* - compression_ratio: original_size / compressed_size
67+
*
68+
* Example (TypeScript download):
69+
* ```json
70+
* {
71+
* "version": "1.0.0",
72+
* "cache_key": "a1b2c3d4e5f67890",
73+
* "timestamp": 1730332800000,
74+
* "checksum": "sha256-abc123...",
75+
* "checksum_algorithm": "sha256",
76+
* "platform": "darwin",
77+
* "arch": "arm64",
78+
* "size": 15000000,
79+
* "source": {
80+
* "type": "download",
81+
* "url": "https://example.com/binary"
82+
* }
83+
* }
84+
* ```
85+
*
86+
* Example (C++ decompression):
87+
* ```json
88+
* {
89+
* "version": "1.0.0",
90+
* "cache_key": "0123456789abcdef",
91+
* "timestamp": 1730332800000,
92+
* "checksum": "sha512-def456...",
93+
* "checksum_algorithm": "sha512",
94+
* "platform": "darwin",
95+
* "arch": "arm64",
96+
* "size": 13000000,
97+
* "source": {
98+
* "type": "decompression",
99+
* "path": "/usr/local/bin/socket"
100+
* },
101+
* "extra": {
102+
* "compressed_size": 1700000,
103+
* "compression_algorithm": 3,
104+
* "compression_ratio": 7.647
105+
* }
106+
* }
107+
* ```
108+
*
109+
* @internal This interface documents the metadata file format.
110+
*/
111+
export interface DlxMetadata {
112+
version: string
113+
cache_key: string
114+
timestamp: number
115+
checksum: string
116+
checksum_algorithm: string
117+
platform: string
118+
arch: string
119+
size: number
120+
source?: {
121+
type: 'download' | 'decompression'
122+
url?: string
123+
path?: string
124+
}
125+
extra?: Record<string, unknown>
126+
}
127+
44128
/**
45129
* Get metadata file path for a cached binary.
46130
*/
@@ -153,20 +237,32 @@ async function downloadBinaryFile(
153237

154238
/**
155239
* Write metadata for a cached binary.
240+
* Uses unified schema shared with C++ decompressor and CLI dlxBinary.
241+
* Schema documentation: See DlxMetadata interface in this file (exported).
242+
* Core fields: version, cache_key, timestamp, checksum, checksum_algorithm, platform, arch, size, source
243+
* Note: This implementation uses SHA-256 checksums instead of SHA-512.
156244
*/
157245
async function writeMetadata(
158246
cacheEntryPath: string,
247+
cacheKey: string,
159248
url: string,
160249
checksum: string,
250+
size: number,
161251
): Promise<void> {
162252
const metaPath = getMetadataPath(cacheEntryPath)
163253
const metadata = {
164-
arch: os.arch(),
254+
version: '1.0.0',
255+
cache_key: cacheKey,
256+
timestamp: Date.now(),
165257
checksum,
258+
checksum_algorithm: 'sha256',
166259
platform: os.platform(),
167-
timestamp: Date.now(),
168-
url,
169-
version: '1.0.0',
260+
arch: os.arch(),
261+
size,
262+
source: {
263+
type: 'download',
264+
url,
265+
},
170266
}
171267
await fs.writeFile(metaPath, JSON.stringify(metadata, null, 2))
172268
}
@@ -325,7 +421,10 @@ export async function dlxBinary(
325421

326422
// Download the binary.
327423
computedChecksum = await downloadBinaryFile(url, binaryPath, checksum)
328-
await writeMetadata(cacheEntryDir, url, computedChecksum || '')
424+
425+
// Get file size for metadata.
426+
const stats = await fs.stat(binaryPath)
427+
await writeMetadata(cacheEntryDir, cacheKey, url, computedChecksum || '', stats.size)
329428
}
330429

331430
// Execute the binary.
@@ -431,7 +530,10 @@ export async function downloadBinary(
431530

432531
// Download the binary.
433532
const computedChecksum = await downloadBinaryFile(url, binaryPath, checksum)
434-
await writeMetadata(cacheEntryDir, url, computedChecksum || '')
533+
534+
// Get file size for metadata.
535+
const stats = await fs.stat(binaryPath)
536+
await writeMetadata(cacheEntryDir, cacheKey, url, computedChecksum || '', stats.size)
435537
downloaded = true
436538
}
437539

@@ -537,6 +639,15 @@ export async function listDlxCache(): Promise<
537639
continue
538640
}
539641

642+
const metaObj = metadata as Record<string, unknown>
643+
644+
// Get URL from unified schema (source.url) or legacy schema (url).
645+
const source = metaObj['source'] as Record<string, unknown> | undefined
646+
const url = (source?.['url'] as string) || (metaObj['url'] as string) || ''
647+
if (!url) {
648+
continue
649+
}
650+
540651
// Find the binary file in the directory.
541652
// eslint-disable-next-line no-await-in-loop
542653
const files = await fs.readdir(entryPath)
@@ -547,15 +658,14 @@ export async function listDlxCache(): Promise<
547658
// eslint-disable-next-line no-await-in-loop
548659
const binaryStats = await fs.stat(binaryPath)
549660

550-
const metaObj = metadata as Record<string, unknown>
551661
results.push({
552662
age: now - ((metaObj['timestamp'] as number) || 0),
553663
arch: (metaObj['arch'] as string) || 'unknown',
554664
checksum: (metaObj['checksum'] as string) || '',
555665
name: binaryFile,
556666
platform: (metaObj['platform'] as string) || 'unknown',
557667
size: binaryStats.size,
558-
url: (metaObj['url'] as string) || '',
668+
url,
559669
})
560670
}
561671
} catch {}

0 commit comments

Comments
 (0)