/** * Graph-ingest path for ICS calendar archives. * * .ics file ──▶ parseICS ──▶ (dry-run preview │ commit writes) * * Writes one `:Meeting` per VEVENT, anchors it to the importing `:AdminUser` * via `[:IMPORTED]`, resolves attendees to `:Person` by lowercased email, and * MERGEs `[:ATTENDED]` edges with role + partstat. * * Cross-account rule (Task 1304): `Person.email` is unique per account, not * globally. When an attendee email already exists under a different * accountId, this account still gets its own new Person stub and * `:ATTENDED` edge — the two accounts' Person nodes are never linked, * merged, or relabelled. Nothing is skipped; there is no * `skippedCrossAccountPerson` outcome. */ import { Session } from "neo4j-driver"; import { type ParsedAttendee } from "./ics.js"; export interface IcsIngestParams { /** The .ics file content (already read by the tool wrapper). */ fileContent: string; /** Path-cleansed filename used for diagnostics + `:Meeting.sourceFilename`. */ filename: string; mode: "dry-run" | "commit"; accountId: string; /** The importing AdminUser. Required — we anchor every Meeting to them. */ userId: string; sessionId?: string; session: Session; } export interface AttendeePreviewEntry { address: string; displayName?: string; /** * "existing-cross-account" (Task 1304) does NOT mean this attendee will * be skipped — a different account already holding this email/telephone * no longer blocks anything. It means commit will create a brand-new * same-account Person stub for it, same as "will-create". The status is * kept distinct purely so the operator can see the overlap. */ status: "existing-same-account" | "existing-cross-account" | "will-create" | "non-mailto"; /** Set when status is existing-*. */ existingDisplayName?: string; } export interface IcsIngestDryRunResult { mode: "dry-run"; filename: string; importId: string; events: number; validMeetings: number; skippedNoUid: number; skippedNoStartTime: number; /** Aggregate attendee preview. */ preview: { attendees: number; organizers: number; nonMailtoAttendees: number; existingSameAccount: number; existingCrossAccount: number; willCreate: number; }; /** Up to 20 representative attendees, classified — surfaces what a commit would do. */ sampleAttendees: AttendeePreviewEntry[]; parseErrors: string[]; ms: number; } export interface IcsIngestCommitResult { mode: "commit"; filename: string; importId: string; events: number; meetingsCreated: number; meetingsUpdated: number; peopleCreated: number; existingPersonsLinked: number; attendees: number; organizers: number; skippedNoUid: number; skippedNoStartTime: number; parseErrors: string[]; ms: number; } export type IcsIngestResult = IcsIngestDryRunResult | IcsIngestCommitResult; export declare function ingestIcs(params: IcsIngestParams): Promise; export type { ParsedAttendee }; //# sourceMappingURL=ics-graph-ingest.d.ts.map