Architektur: Craftvia-Domänenmodell, Verträge und Team-Schnitte
- Migration 0002_craftvia_domain: 27 Fachtabellen inkl. RLS (enable_tenant_rls) - TENANT_MODELS (db.ts, backup/topology.ts) um alle Fachmodelle ergänzt - moduleGuard liefert DB-autoritative Rechte; ServiceCtx für Domänen-Services - Verträge: Statusmaschine, Events, Nummernkreise, Sichtbarkeits-Scopes, Job-Queues + Worker, KI-Provider-Interfaces, Sync-Envelope - docs/craftvia/ARCHITEKTUR.md mit Lanes, Ownership und DoD Gate: tsc, lint, build, 22/22 Tests grün. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,57 @@
|
||||
import { z } from "zod";
|
||||
|
||||
/**
|
||||
* Offline sync envelope (ARCHITEKTUR §4.6). The per-op payload schemas live in
|
||||
* src/lib/sync/ops.ts (owned by lane field); this file only fixes the wire format.
|
||||
*/
|
||||
|
||||
export const SYNC_OP_TYPES = [
|
||||
"session.start",
|
||||
"session.pause",
|
||||
"session.resume",
|
||||
"session.end",
|
||||
"work_order.transition",
|
||||
"note.create",
|
||||
"checklist.toggle",
|
||||
"material.upsert",
|
||||
"photo.attach",
|
||||
"voice.attach",
|
||||
"report.save_draft",
|
||||
"report.submit",
|
||||
"signature.capture",
|
||||
"emergency.create",
|
||||
] as const;
|
||||
|
||||
export type SyncOpType = (typeof SYNC_OP_TYPES)[number];
|
||||
|
||||
/** Ops that compare WorkOrder.version and may produce a conflict. All others are additive. */
|
||||
export const CONFLICTING_OPS: readonly SyncOpType[] = ["work_order.transition", "report.submit"];
|
||||
|
||||
export const syncOperationSchema = z.object({
|
||||
clientOpId: z.string().uuid(),
|
||||
opType: z.enum(SYNC_OP_TYPES),
|
||||
entityType: z.string().max(40).optional(),
|
||||
entityId: z.string().max(64).optional(),
|
||||
baseVersion: z.number().int().positive().optional(),
|
||||
payload: z.record(z.string(), z.unknown()),
|
||||
clientCreatedAt: z.string().datetime(),
|
||||
});
|
||||
|
||||
export type SyncOperationInput = z.infer<typeof syncOperationSchema>;
|
||||
|
||||
export const syncRequestSchema = z.object({
|
||||
deviceId: z.string().max(64),
|
||||
operations: z.array(syncOperationSchema).min(1).max(100),
|
||||
});
|
||||
|
||||
export type SyncOpResult = {
|
||||
clientOpId: string;
|
||||
status: "applied" | "duplicate" | "conflict" | "rejected";
|
||||
/** server ids created by the op, keyed by the client id they replace */
|
||||
idMap?: Record<string, string>;
|
||||
entityVersion?: number;
|
||||
errorCode?: "not_found" | "forbidden" | "invalid" | "conflict" | "blocked" | "internal";
|
||||
message?: string;
|
||||
};
|
||||
|
||||
export type SyncResponse = { results: SyncOpResult[]; serverTime: string };
|
||||
Reference in New Issue
Block a user