Initial Commit
This commit is contained in:
@@ -0,0 +1,135 @@
|
||||
/**
|
||||
* Zod boundary schemas for every wire-format payload exchanged by
|
||||
* `@suaveplan/drive-mesh-protocol`. The codec modules invoke
|
||||
* `safeParse` on the decoded JSON before any logic touches the
|
||||
* payload, satisfying DMP-21's "schema validation before payload
|
||||
* processing" mandate.
|
||||
*/
|
||||
|
||||
import * as z from "zod";
|
||||
|
||||
/** Hex-encoded 32-byte file id; matches the format produced by drive-plugin-chunks. */
|
||||
export const FileIdSchema = z
|
||||
.string()
|
||||
.min(1)
|
||||
.max(128)
|
||||
.regex(/^[0-9a-f]+$/i, "fileId must be lowercase hex");
|
||||
|
||||
/** Five-priority tier (DMP-06). */
|
||||
export const ChunkRequestPrioritySchema = z.enum([
|
||||
"critical",
|
||||
"high",
|
||||
"normal",
|
||||
"low",
|
||||
"bulk",
|
||||
]);
|
||||
|
||||
/** Discriminated union for the three request forms (DMP-01). */
|
||||
export const ChunkRequestSchema = z.discriminatedUnion("kind", [
|
||||
z.object({ kind: z.literal("whole") }),
|
||||
z.object({
|
||||
kind: z.literal("specific"),
|
||||
// Empty arrays are caught by the explicit DMP-03 / DMP-22 gates in
|
||||
// the codec; keeping the schema permissive lets those throws fire.
|
||||
chunkIds: z.array(z.number().int().nonnegative()),
|
||||
}),
|
||||
z.object({
|
||||
kind: z.literal("bitset"),
|
||||
bytes: z.instanceof(Uint8Array),
|
||||
}),
|
||||
]);
|
||||
|
||||
export const ChunkRequestPayloadSchema = z
|
||||
.object({
|
||||
fileId: FileIdSchema,
|
||||
request: ChunkRequestSchema,
|
||||
priority: ChunkRequestPrioritySchema,
|
||||
// The hard cap is enforced by the codec with `MAX_CHUNKS_EXCEEDED`
|
||||
// so consumers see the spec-mandated DMP-07 code; Zod only checks
|
||||
// that maxChunks is a positive integer.
|
||||
maxChunks: z.number().int().min(1).optional(),
|
||||
})
|
||||
.strict();
|
||||
|
||||
/** Hard cap for `maxChunks` per DMP-07. */
|
||||
export const MAX_CHUNKS_HARD_CAP = 1024 as const;
|
||||
|
||||
/** Default `maxChunks` per DMP-07. */
|
||||
export const MAX_CHUNKS_DEFAULT = 64 as const;
|
||||
|
||||
/** Threshold above which a `bitset` request is more compact than `specific` (DMP-05). */
|
||||
export const SELECT_FORM_BITSET_THRESHOLD = 64 as const;
|
||||
|
||||
/** Default 1-in-100 sampling for healthy peers (DMP-17). */
|
||||
export const SAMPLE_RATE_HEALTHY = 100 as const;
|
||||
|
||||
/** Default 1-in-10 sampling for suspect peers (DMP-17). */
|
||||
export const SAMPLE_RATE_SUSPECT = 10 as const;
|
||||
|
||||
/** Trust threshold below which a peer is "suspect" (DMP-17). */
|
||||
export const TRUST_SUSPECT_THRESHOLD = 0.95 as const;
|
||||
|
||||
/** Miss-rate floor below which trust decay is dormant (DMP-15). */
|
||||
export const TRUST_MISS_RATE_FLOOR = 0.05 as const;
|
||||
|
||||
/** Multiplier on (missRate - floor) to compute P(skip) (DMP-15). */
|
||||
export const TRUST_DECAY_SLOPE = 20 as const;
|
||||
|
||||
/** Default throttle window between outgoing advertisements (DMP-10). */
|
||||
export const ADVERTISE_THROTTLE_MS_DEFAULT = 1_000 as const;
|
||||
|
||||
/** Hard upper bound on the throttle window. */
|
||||
export const ADVERTISE_THROTTLE_MS_MAX = 60_000 as const;
|
||||
|
||||
/** Generation wrap detection threshold (DMP-09). */
|
||||
export const GENERATION_WRAP_THRESHOLD = 2 ** 31;
|
||||
|
||||
export const ChunkAdvertisePayloadSchema = z
|
||||
.object({
|
||||
fileId: FileIdSchema,
|
||||
presence: z.instanceof(Uint8Array),
|
||||
totalChunks: z.number().int().min(1),
|
||||
generation: z.number().int().min(0).max(0xff_ff_ff_ff),
|
||||
timestamp: z.number().int().min(0),
|
||||
})
|
||||
.strict();
|
||||
|
||||
export const ChunkBytesSchema = z
|
||||
.object({
|
||||
chunkId: z.number().int().nonnegative(),
|
||||
bytes: z.instanceof(Uint8Array),
|
||||
})
|
||||
.strict();
|
||||
|
||||
export const ChunkResponsePayloadSchema = z
|
||||
.object({
|
||||
fileId: FileIdSchema,
|
||||
chunks: z.array(ChunkBytesSchema),
|
||||
presence: z.instanceof(Uint8Array),
|
||||
generation: z.number().int().min(0).max(0xff_ff_ff_ff),
|
||||
})
|
||||
.strict();
|
||||
|
||||
export const ChunkErrorCodeSchema = z.enum([
|
||||
"NOT_FOUND",
|
||||
"ACCESS_DENIED",
|
||||
"BANDWIDTH_EXCEEDED",
|
||||
"GENERATION_STALE",
|
||||
"INTERNAL",
|
||||
]);
|
||||
|
||||
export const ChunkErrorPayloadSchema = z
|
||||
.object({
|
||||
fileId: FileIdSchema,
|
||||
correlationId: z.string().min(1),
|
||||
code: ChunkErrorCodeSchema,
|
||||
chunkId: z.number().int().nonnegative().optional(),
|
||||
retryAfterMs: z.number().int().nonnegative().optional(),
|
||||
responderGeneration: z
|
||||
.number()
|
||||
.int()
|
||||
.min(0)
|
||||
.max(0xff_ff_ff_ff)
|
||||
.optional(),
|
||||
})
|
||||
.strict();
|
||||
Reference in New Issue
Block a user