/** * PushEvent — the wire-shared payload exchanged between every link in the * event-driven-hq-cloud-sync pipeline. * * Producers (the watcher) emit one PushEvent per local content change or * delete tombstone. * Consumers (the push endpoint / receiver / coalescer) decode the payload, * validate it, and act on it. Because the same shape crosses a network * boundary, it has its own dedicated module. * * Conventions * ─────────── * - `kind` is `"upsert"` for content changes and `"delete"` for delete * tombstones. Missing `kind` defaults to `"upsert"` for older producers. * - `contentHash` is required for upserts and absent for delete tombstones. * It is `sha256:<64-lowercase-hex>`. The `:` prefix lets a * future hash migration ship without breaking the wire format — consumers * can branch on the prefix and fall back to refusing unknown algorithms. * - `mtime` and `eventTimestamp` are strict ISO-8601 datetime strings. * `mtime` is the filesystem modification time of the source file at the * moment of capture; `eventTimestamp` is when the watcher emitted the * event. They diverge whenever the watcher coalesces or retries. * - `sizeBytes` is the producer-declared byte size of the upsert content at * capture. It is optional; absence means "unknown", never 0. * - `sequenceNumber` is a non-negative safe integer. Monotonicity per * `originDeviceId` is a producer-side invariant — a single PushEvent * can't be self-monotonic, so the schema only validates the bounds. * - Unknown extra fields are dropped silently by `decodePushEvent` to * permit forwards-compatible additions on the producer side without * forcing every consumer to upgrade in lockstep. * * Ported from indigoai-us/hq-pro PR #112 (src/sync/push-event.ts) into * @indigoai-us/hq-cloud (Path B) per project event-driven-sync-menubar US-007. */ import { z } from "zod"; /** * `sha256:` + 64 lowercase hex chars. The algorithm prefix is mandatory so * future hash migrations stay non-breaking; consumers can switch on the * prefix and reject unknown algorithms explicitly. */ export declare const CONTENT_HASH_PATTERN: RegExp; /** * Strict ISO-8601 datetime regex matching what `z.iso.datetime()` produces. * * Format: `YYYY-MM-DDTHH:MM:SS(.fff)?(Z|±HH:MM)` */ export declare const ISO8601_DATETIME_PATTERN: RegExp; /** * Closed-vocabulary reason the server (`isSafeRelativePath` in hq-pro * push-handler) and this client share. Never include the path itself — that * is user content. */ export type RelativePathRejectionReason = "empty" | "leading_slash" | "leading_backslash" | "nul_byte" | "empty_segment" | "dot_segment" | "dotdot_segment" | "backslash_segment"; /** * Convert native path separators to POSIX `/`. Pass `path.sep` at the call * site. On win32 that rewrites `\\`; on POSIX a literal `\\` in a filename * is left intact so the schema can reject it instead of remapping the file. */ export declare function nativeSeparatorsToPosix(relativePath: string, sep: string): string; /** Why `relativePath` is not a safe vault-relative wire key, or null if it is. */ export declare function relativePathRejectionReason(relativePath: string): RelativePathRejectionReason | null; /** Same rule hq-pro `isSafeRelativePath` enforces on POST /v1/sync/push. */ export declare function isSafeRelativePath(relativePath: string): boolean; /** * Runtime schema for PushEvent. Unknown extra keys are dropped via the zod * default `.strip()` behavior — that is load-bearing for forwards * compatibility (see module JSDoc). */ export declare const PushEventSchema: z.ZodObject<{ relativePath: z.ZodString; kind: z.ZodDefault>>; contentHash: z.ZodOptional; mtime: z.ZodOptional; sizeBytes: z.ZodOptional; originDeviceId: z.ZodString; originTenantId: z.ZodString; sequenceNumber: z.ZodNumber; eventTimestamp: z.ZodString; }, z.core.$strip>; /** * The canonical decoded PushEvent type. `kind` is always present after decode; * `contentHash` and `mtime` are present for upserts and absent for deletes. */ export type PushEvent = z.output; /** * Input accepted by the schema. Kept public so encode callers can hand in * legacy upsert payloads where `kind` is absent. */ export type PushEventInput = z.input; /** * Public alias for a single decode issue. Aliased here (rather than re-exporting * the `$`-prefixed zod internal symbol directly) so consumers can annotate * against `PushEventDecodeIssue` without importing zod internals. The alias is * structurally identical to `z.core.$ZodIssue`, so this is a non-breaking * narrowing of the public surface. */ export type PushEventDecodeIssue = z.core.$ZodIssue; /** * Thrown by `decodePushEvent` (and `encodePushEvent` on invalid input) when * the payload fails validation. `.issues` carries the underlying zod issues * so callers can render structured diagnostics — see the test suite for an * example of asserting on the issue path. */ export declare class PushEventDecodeError extends Error { readonly issues: readonly PushEventDecodeIssue[]; readonly stage: "json-parse" | "schema-validation"; constructor(message: string, args: { issues: readonly PushEventDecodeIssue[]; stage: "json-parse" | "schema-validation"; cause?: unknown; }); } /** * Validate `event` against `PushEventSchema` and return the canonical JSON * serialization. Validating on encode catches producer-side mistakes early * (and ensures the output always round-trips through `decodePushEvent`). * * Extra keys on `event` are dropped — the returned JSON string contains * only the declared PushEvent fields. */ export declare function encodePushEvent(event: PushEventInput): string; /** * Parse and validate an incoming PushEvent. Accepts either a raw JSON string * (the on-the-wire form) or an already-parsed object (handy for in-process * wiring and tests). * * - Unknown extra fields are dropped silently — see module JSDoc. * - Missing required fields throw `PushEventDecodeError` whose `.issues` * exposes the underlying zod issues. * - Malformed JSON throws `PushEventDecodeError` with `stage:"json-parse"` * and a synthetic issue at the root path. * - A JSON string that parses to a non-object value (e.g. `'42'`, `'"text"'`, * `'null'`) surfaces as `stage: 'schema-validation'` — the JSON itself was * syntactically valid, so it clears the parse stage before failing the * object-shape check. */ export declare function decodePushEvent(input: unknown): PushEvent; //# sourceMappingURL=push-event.d.ts.map