( existing: Record<string, unknown> | null, pluginHooks: Record<string, HookEntry[]>, pluginName: string, )
| 694 | * injecting bookkeeping fields into hook entry objects. |
| 695 | */ |
| 696 | export function mergeCodexHooks( |
| 697 | existing: Record<string, unknown> | null, |
| 698 | pluginHooks: Record<string, HookEntry[]>, |
| 699 | pluginName: string, |
| 700 | ): Record<string, unknown> { |
| 701 | const result: Record<string, unknown[]> = {} |
| 702 | const managed: ManagedIndex = (existing?._managed as ManagedIndex) ?? {} |
| 703 | |
| 704 | // Collect indices of entries managed by this plugin (to be removed) |
| 705 | const ownedIndices: Record<string, Set<number>> = {} |
| 706 | if (managed[pluginName]) { |
| 707 | for (const [event, indices] of Object.entries(managed[pluginName])) { |
| 708 | ownedIndices[event] = new Set(indices) |
| 709 | } |
| 710 | } |
| 711 | |
| 712 | // Preserve existing hooks, filtering out this plugin's managed entries |
| 713 | const existingHooks = (existing?.hooks ?? {}) as Record<string, unknown[]> |
| 714 | for (const [event, matchers] of Object.entries(existingHooks)) { |
| 715 | if (!Array.isArray(matchers)) continue |
| 716 | const owned = ownedIndices[event] |
| 717 | result[event] = owned |
| 718 | ? matchers.filter((_, idx) => !owned.has(idx)) |
| 719 | : [...matchers] |
| 720 | } |
| 721 | |
| 722 | // Also filter out entries with legacy `_source` tag from this plugin |
| 723 | // (migration path from the previous `_source`-in-entry format) |
| 724 | for (const [event, matchers] of Object.entries(result)) { |
| 725 | result[event] = (matchers as Array<Record<string, unknown>>).filter((m) => { |
| 726 | if (typeof m === "object" && m !== null && "_source" in m) { |
| 727 | return m._source !== pluginName |
| 728 | } |
| 729 | return true |
| 730 | }) |
| 731 | } |
| 732 | |
| 733 | // Build new managed index for this plugin |
| 734 | const newManagedForPlugin: Record<string, number[]> = {} |
| 735 | |
| 736 | // Add this plugin's hooks (clean entries, no _source field) |
| 737 | for (const [event, matchers] of Object.entries(pluginHooks)) { |
| 738 | if (!result[event]) result[event] = [] |
| 739 | const indices: number[] = [] |
| 740 | for (const matcher of matchers) { |
| 741 | indices.push(result[event].length) |
| 742 | result[event].push({ ...matcher }) |
| 743 | } |
| 744 | if (indices.length > 0) { |
| 745 | newManagedForPlugin[event] = indices |
| 746 | } |
| 747 | } |
| 748 | |
| 749 | // Remove empty event arrays |
| 750 | for (const event of Object.keys(result)) { |
| 751 | if (result[event].length === 0) delete result[event] |
| 752 | } |
| 753 |
no outgoing calls
no test coverage detected