diff --git a/docs/long-term-plans/sync-core-extraction-plan.md b/docs/long-term-plans/sync-core-extraction-plan.md index 1cb1d2bcf3..ac698fa923 100644 --- a/docs/long-term-plans/sync-core-extraction-plan.md +++ b/docs/long-term-plans/sync-core-extraction-plan.md @@ -466,6 +466,9 @@ Initial candidate-file audit: applied fixes now route through `SyncLogger` with path/type/count metadata only; raw invalid values, defaults, and full Typia error objects stay out of exportable logs. +- `op-log/validation/repair-menu-tree.ts`: menu-tree repair logs now use + `SyncLogger` metadata for removed references/invalid nodes; raw node objects + and folder names stay out of exportable logs. - `op-log/util/sync-file-prefix.ts`: now delegates to the package helper with app-supplied prefix and error construction. The app-facing shim should remain until consumers are deliberately switched to injected/configured helpers. diff --git a/src/app/op-log/validation/repair-menu-tree.spec.ts b/src/app/op-log/validation/repair-menu-tree.spec.ts index 00ce7271fa..a9f95e3cfe 100644 --- a/src/app/op-log/validation/repair-menu-tree.spec.ts +++ b/src/app/op-log/validation/repair-menu-tree.spec.ts @@ -3,8 +3,17 @@ import { MenuTreeKind, MenuTreeState, } from '../../features/menu-tree/store/menu-tree.model'; +import { OP_LOG_SYNC_LOGGER } from '../core/sync-logger.adapter'; describe('repairMenuTree', () => { + let logSpy: jasmine.Spy; + let warnSpy: jasmine.Spy; + + beforeEach(() => { + logSpy = spyOn(OP_LOG_SYNC_LOGGER, 'log').and.stub(); + warnSpy = spyOn(OP_LOG_SYNC_LOGGER, 'warn').and.stub(); + }); + it('should remove orphaned project references from projectTree', () => { const validProjectIds = new Set(['project1', 'project2']); const validTagIds = new Set(); @@ -188,4 +197,40 @@ describe('repairMenuTree', () => { expect(result.projectTree).toEqual([{ k: MenuTreeKind.PROJECT, id: 'project1' }]); expect(result.tagTree).toEqual([{ k: MenuTreeKind.TAG, id: 'tag1' }]); }); + + it('should log invalid nodes without raw node values', () => { + const privateNodeName = 'Private Folder Name'; + const validProjectIds = new Set(); + const validTagIds = new Set(); + + const menuTree: MenuTreeState = { + projectTree: [ + { + k: 'unexpected', + id: 'invalid-node-id', + name: privateNodeName, + } as any, + ], + tagTree: [], + }; + + const result = repairMenuTree(menuTree, validProjectIds, validTagIds); + + expect(result.projectTree).toEqual([]); + expect(warnSpy).toHaveBeenCalledWith( + '[repair-menu-tree] Removed invalid node', + jasmine.objectContaining({ + treeType: 'projectTree', + nodeKind: 'unknown', + hasNodeId: true, + }), + ); + + const serializedLogArgs = JSON.stringify([ + ...logSpy.calls.allArgs(), + ...warnSpy.calls.allArgs(), + ]); + expect(serializedLogArgs).not.toContain(privateNodeName); + expect(serializedLogArgs).not.toContain('invalid-node-id'); + }); }); diff --git a/src/app/op-log/validation/repair-menu-tree.ts b/src/app/op-log/validation/repair-menu-tree.ts index e055fefa8c..dcb32c94e7 100644 --- a/src/app/op-log/validation/repair-menu-tree.ts +++ b/src/app/op-log/validation/repair-menu-tree.ts @@ -3,7 +3,39 @@ import { MenuTreeState, MenuTreeTreeNode, } from '../../features/menu-tree/store/menu-tree.model'; -import { OpLog } from '../../core/log'; +import { OP_LOG_SYNC_LOGGER } from '../core/sync-logger.adapter'; + +const KNOWN_MENU_TREE_KINDS = new Set(Object.values(MenuTreeKind)); + +const getNodeKindForLog = (kind: unknown): string => + typeof kind === 'string' && KNOWN_MENU_TREE_KINDS.has(kind) ? kind : 'unknown'; + +const logOrphanedReferenceRemoval = ( + treeType: 'projectTree' | 'tagTree', + referenceType: 'project' | 'tag', + referenceId: string, +): void => { + OP_LOG_SYNC_LOGGER.log('[repair-menu-tree] Removed orphaned reference', { + treeType, + referenceType, + referenceId, + }); +}; + +const logInvalidNodeRemoval = ( + treeType: 'projectTree' | 'tagTree', + node: MenuTreeTreeNode, +): void => { + OP_LOG_SYNC_LOGGER.warn('[repair-menu-tree] Removed invalid node', { + treeType, + nodeKind: getNodeKindForLog(node.k), + hasNodeId: typeof node.id === 'string', + childCount: + node.k === MenuTreeKind.FOLDER && Array.isArray(node.children) + ? node.children.length + : undefined, + }); +}; /** * Repairs menuTree by removing orphaned project/tag references @@ -17,7 +49,7 @@ export const repairMenuTree = ( validProjectIds: Set, validTagIds: Set, ): MenuTreeState => { - OpLog.log('Repairing menuTree - removing orphaned references'); + OP_LOG_SYNC_LOGGER.log('[repair-menu-tree] Repairing orphaned references'); /** * Recursively filters tree nodes, removing orphaned project/tag references @@ -41,18 +73,18 @@ export const repairMenuTree = ( if (validProjectIds.has(node.id)) { filtered.push(node); } else { - OpLog.log(`Removing orphaned project reference ${node.id} from ${treeType}`); + logOrphanedReferenceRemoval(treeType, 'project', node.id); } } else if (treeType === 'tagTree' && node.k === MenuTreeKind.TAG) { // Keep tag only if it exists if (validTagIds.has(node.id)) { filtered.push(node); } else { - OpLog.log(`Removing orphaned tag reference ${node.id} from ${treeType}`); + logOrphanedReferenceRemoval(treeType, 'tag', node.id); } } else { // kind mismatch or unknown - OpLog.warn(`Removing invalid node from ${treeType}:`, node); + logInvalidNodeRemoval(treeType, node); } }