import type { DocumentSavedEvent } from '@kubun/graphql'; import type { MergeOutcome } from '@kubun/mutation'; import type { DocumentMutation, DocumentNode } from '@kubun/protocol'; import type { DocumentScope } from '@kubun/store-graph'; import type { AccessRule } from './access-control.js'; /** * Merge diagnostics carried on a mutation-applied event for a document whose * model declares a provider merge policy. All four are ABSENT for an ordinary * (non-merge-policy) model. `winnerChanged` is the event-local truth of whether * this application moved the stored winning state digest, so a connector * listener can re-arm a repair watch only when the frontier actually moved. */ type MutationMergeOutcomeFields = { outcome?: MergeOutcome; candidateDigest?: string; winnerDigest?: string; winnerChanged?: boolean; }; /** * Event types emitted by the engine during document operations. * * These events are emitted after successful mutations and can be * observed by plugins or application code via the EngineEventBus. */ export type EngineEvents = { 'engine:document:scope-changed': { documentID: string; scope: DocumentScope; }; 'document:saved': DocumentSavedEvent; 'engine:document:created': { documentID: string; modelID: string; viewerDID: string; }; 'engine:document:updated': { documentID: string; modelID: string; viewerDID: string; }; 'engine:document:removed': { documentID: string; viewerDID: string; }; /** * Emitted when a mutation signed locally (authored on this peer) is applied. * Fires exactly once per local-origin mutation, regardless of classification. */ 'engine:mutation:authored': { previous: DocumentNode | null; document: DocumentNode; mutation: DocumentMutation; previousReadRule: AccessRule | null; type: 'create' | 'update' | 'remove'; documentID: string; viewerDID: string; /** Signed mutation JWT — consumed by broadcast senders to route to peers. */ mutationJWT: string; /** HLC string identifying this mutation's version. */ version: string; /** Document model ID — stable across a batch of mutations on the same doc. */ modelID: string; } & MutationMergeOutcomeFields; /** * Emitted when a mutation received from a peer (authored elsewhere) is applied. * Fires exactly once per peer-origin mutation, regardless of classification. */ 'engine:mutation:received': { type: 'create' | 'update' | 'remove'; documentID: string; viewerDID: string; /** Signed mutation JWT — same shape as `engine:mutation:authored`. */ mutationJWT: string; /** HLC string identifying this mutation's version. */ version: string; /** Document model ID. */ modelID: string; } & MutationMergeOutcomeFields; /** * Emitted when this peer sets its own model access-default for a * (modelID, permissionType). Carries the full rule so a subscriber can * replicate the owner's sharing policy to co-members without re-reading it. */ 'engine:access-default:set': { ownerDID: string; modelID: string; permissionType: 'read' | 'write'; accessLevel: string; allowedDIDs: Array | null; allowedCircles: Array | null; allowedGroups: Array | null; /** LWW anchor stamped by this peer. */ hlc: string; }; /** Emitted when this peer removes its own model access-default(s). */ 'engine:access-default:removed': { ownerDID: string; modelID: string; permissionTypes: Array<'read' | 'write'>; /** LWW anchor stamped by this peer. */ hlc: string; }; /** Emitted once this device's own custody assignment, acceptance or eligibility write has committed. */ 'engine:custody:written': { custodyOwner: string; type: 'catalog.custody' | 'catalog.custody-acceptance' | 'catalog.custody-eligibility'; }; /** * Emitted when a controller DID's log is seen to fork: a peer served a * branch that neither supersedes nor is superseded by the one already * stored. The stored authoritative branch is kept; the verification that * hit the fork fails closed. A signal for the app to surface a possible * key-compromise/duplicity, not a recoverable error. * * Unlike the document/mutation events, this one fires from inside the verify * path — while a peer-ingest apply transaction may still be open (and about to * roll back). A listener MUST NOT synchronously read the engine DB in response: * on single-connection SQLite that can deadlock against the open write * transaction. Do notification-only work here (surface a warning, enqueue an * out-of-band task); defer any DB access to a later tick. */ 'engine:controller:forked': { did: string; }; 'engine:controller:head-changed': { did: string; }; }; export {};