Initial Commit
This commit is contained in:
@@ -0,0 +1,87 @@
|
||||
# `commands/files/packet-io`
|
||||
|
||||
## Purpose
|
||||
|
||||
Shared on-disk format helpers for the file-transport commands. Holds
|
||||
the canonical packet envelope `{ data, signature, checksum,
|
||||
originalSize }` (all hex-encoded scalars in JSON), the canonical
|
||||
packet filename convention (`packet.<6-digit-index>.bin`), the manifest
|
||||
filename (`manifest.json`), and the read/write helpers used by both
|
||||
`prepareFileCommand` and `serveFileCommand`.
|
||||
|
||||
Lives in its own module so the format is the contract: any tool that
|
||||
agrees on the envelope shape can interop with a server-keys-prepared
|
||||
directory without depending on the full file-command surface. Also keeps
|
||||
`files.ts` and `serve-file.ts` under the 300-line hard limit by lifting
|
||||
out the duplicated serialisation code.
|
||||
|
||||
## Features
|
||||
|
||||
- `packetFilename(index)` — zero-padded 6-digit filename for
|
||||
deterministic sort order.
|
||||
- `bytesToHex` / `hexToBytes` — lowercase hex, strict input validation.
|
||||
- `serializePacket(packed, originalSize)` — JSON-encode a `PackedData`
|
||||
envelope.
|
||||
- `deserializePacket(buffer)` — parse a JSON envelope back into the
|
||||
shape `Unpacker.unpack` expects, with a typed `PACKET_INTEGRITY_FAILED`
|
||||
failure on malformed JSON.
|
||||
- `readManifest(dir)` / `writeManifest(dir, manifest)` — single-file
|
||||
JSON manifest I/O with directory creation.
|
||||
|
||||
## Usage: Basic
|
||||
|
||||
```ts
|
||||
import {
|
||||
packetFilename,
|
||||
serializePacket,
|
||||
writeManifest,
|
||||
} from "@suaveplan/server-keys/commands/files/packet-io";
|
||||
|
||||
const filename = packetFilename(7); // "packet.000007.bin"
|
||||
const text = serializePacket(packed, 1024);
|
||||
await fs.writeFile(`/tmp/prepared/${filename}`, text);
|
||||
await writeManifest("/tmp/prepared", manifest);
|
||||
```
|
||||
|
||||
## Usage: Advanced
|
||||
|
||||
Round-trip a packet envelope without touching disk:
|
||||
|
||||
```ts
|
||||
import {
|
||||
deserializePacket,
|
||||
serializePacket,
|
||||
} from "@suaveplan/server-keys/commands/files/packet-io";
|
||||
|
||||
const text = serializePacket(packed, slice.byteLength);
|
||||
const buf = new TextEncoder().encode(text);
|
||||
const { packed: rehydrated, envelope } = deserializePacket(buf);
|
||||
expect(envelope.checksum).toBe(packed.metadata.checksum);
|
||||
expect(Array.from(rehydrated.data)).toEqual(Array.from(packed.data));
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `MANIFEST_FILENAME = "manifest.json"`
|
||||
- `packetFilename(index: number) => string`
|
||||
- `bytesToHex(bytes: Uint8Array) => string`
|
||||
- `hexToBytes(hex: string) => Uint8Array` (throws on malformed input)
|
||||
- `interface SerializedPacket { data, signature, checksum, originalSize }`
|
||||
- `serializePacket(packed, originalSize) => string`
|
||||
- `deserializePacket(buffer) => { packed, envelope }`
|
||||
- `writeManifest(dir, manifest) => Promise<void>`
|
||||
- `readManifest(dir) => Promise<FileManifest>`
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
`readManifest` translates a missing file into
|
||||
`SERVER_KEYS_FILE_NOT_FOUND`; this is the closest match in the
|
||||
registry for "the directory exists but the manifest entry is absent"
|
||||
and aligns with how the keys commands report missing key files.
|
||||
`deserializePacket` raises `PACKET_INTEGRITY_FAILED` on malformed JSON
|
||||
rather than `INVALID_INPUT` because the input is a file the caller
|
||||
already trusted enough to read — the failure is on the storage side,
|
||||
not on a CLI option-bag boundary. The packet envelope deliberately
|
||||
hex-encodes both `data` and `signature` (rather than base64) to match
|
||||
the rest of the package's binary-on-disk convention and to keep the
|
||||
file readable by hand.
|
||||
Reference in New Issue
Block a user