// **The first registrations switch the merge on** (novox/hq design 47 §95, §353): until the catalog half has said its first // `registration-changed`, the seat git's `merge-pull` refuses every merge naming the missing registrations, because a merge // made with no Approve could reach the old assigner, which writes wants from the trunk with no pipeline. The module learns it // two ways, both through the seat `mesh-catalog`: its verb `registrations` asked at the start (a restart after the first // registrations were written), and its event `registration-changed` (the first being written, or heard again from the // seat's kept stream). Neither is the holder's state or a module named. import { seat as CATALOG, verbs as CATALOG_VERBS, events as CATALOG_EVENTS, type RegistrationChangedEvent, type RegistrationsAnswer, type RegistrationsInput, } from "@novox/mesh-delivery-contracts-mesh-catalog"; /** The seat verb asked, as the manifest's `invokes` names it. */ export const REGISTRATIONS_VERB = `seat:${CATALOG}.${CATALOG_VERBS.registrations}`; /** The seat event heard, as the manifest's `consumes` names it. */ export const REGISTRATION_CHANGED_EVENT = `${CATALOG}.${CATALOG_EVENTS.registrationChanged}`; export class Registrations { private first = false; private byRepository = new Map>(); constructor(private readonly ask: (verb: string, input: RegistrationsInput) => Promise, private readonly log: (line: string) => void = () => {}) {} /** Whether the first registrations are written: the one fact the merge switches on. */ get written(): boolean { return this.first; } /** The watched branches the registrations name, by their origin repository (`/`) as of the last read. */ get watched(): ReadonlyMap> { return this.byRepository; } /** One `registration-changed`: the first registration written, however many were before this module started. */ hear(ev: RegistrationChangedEvent | undefined): void { if (!ev || typeof ev.module !== "string" || ev.module === "") { throw new Error(`not a ${REGISTRATION_CHANGED_EVENT} event: ${JSON.stringify(ev ?? null).slice(0, 200)}`); } if (!this.first) this.log(`the first registration is written (${ev.module}); merge-pull merges from here`); this.first = true; } /** The start's first view, asked again after each `registration-changed`: any registration held is the first written, and its * watched branches are read. A failed ask is said and left to the event. */ async start(): Promise { try { let a: unknown = await this.ask(REGISTRATIONS_VERB, {}); if (typeof a === "string") a = JSON.parse(a); const r = (a as RegistrationsAnswer | undefined)?.registrations; if (!Array.isArray(r)) throw new Error(`${REGISTRATIONS_VERB} answered no registrations: ${JSON.stringify(a ?? null).slice(0, 200)}`); const by = new Map>(); for (const x of r) for (const b of x.branches ?? []) (by.get(x.origin) ?? by.set(x.origin, new Set()).get(x.origin)!).add(b); this.byRepository = by; if (r.length > 0) { this.first = true; this.log(`${r.length} registrations are held; merge-pull merges`); } } catch (err) { this.log(`the registrations could not be read: ${err instanceof Error ? err.message : String(err)}; merge-pull waits for registration-changed`); } } }