{"version":3,"sources":["../src/mapper/flat-tree-ws-mapper.ts","../src/search/dfs-tree-search.ts","../src/tree.ts"],"names":["sym","flatTreeWsMapperErrors","FlatTreeWsMapper","data","params","separator","collector","d","key","value","trimmedKey","context","splitted","name","parents","parentId","children","node","e","result","message","issues","errors","issue","treeNodes","queue","count","child","map","parsedErrorMsg","DfsTreeSearch","idOrConditionOrFn","includeChildren","reverse","isIdSearch","isFnSearch","treeNode","stack","_children","rest","Tree"],"mappings":"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","file":"index.cjs","sourcesContent":["import type { TreeNode, TreeNodeValue } from '../tree.types';\nimport type { TreeMapperResult } from './mapper.types';\n\ntype FlatTreeWsParams = {\n  separator: string;\n};\n\n// eslint-disable-next-line sonarjs/redundant-type-aliases\ntype FlatTreeWsUniqueKey = string;\n\nexport type FlatTreeWsMap<\n  TValue extends TreeNodeValue | undefined,\n  TKey extends FlatTreeWsUniqueKey = string,\n> = Map<TKey, TValue>;\n\nexport type FlatTreeWsRecord<\n  TValue extends TreeNodeValue | undefined,\n  TKey extends FlatTreeWsUniqueKey = string,\n> = Record<TKey, TValue>;\n\nexport type FlatTreeWs<\n  TValue extends TreeNodeValue | undefined,\n  TKey extends FlatTreeWsUniqueKey = string,\n> = FlatTreeWsMap<TValue, TKey> | FlatTreeWsRecord<TValue, TKey>;\n\nconst sym = Symbol('@@result');\n/*\ntype CollectorContext<\n  TValue extends TreeNodeValue | undefined = undefined,\n  TKey extends string = string,\n> = Record<'result', TreeNode<TValue, TKey>[]> & Record<string, unknown>;\n*/\n\ninterface CollectorContext<\n  TValue extends TreeNodeValue | undefined = undefined,\n  TKey extends string = string,\n> {\n  [sym]: TreeNode<TValue, TKey>[];\n  [key: string]: unknown;\n}\nexport const flatTreeWsMapperErrors = {\n  toTreeNodes: {\n    parsedErrorMsg: `Can't convert the flat tree to tree nodes`,\n    issues: {\n      ARG_NOT_ARRAY: 'Invalid argument: not an array (FlatTreeWs)',\n      ARG_NOT_OBJECT_ARRAY:\n        'Invalid argument: not an array of objects (FlatTreeWs)',\n      NON_STRING_KEY: 'Non-string key',\n      EMPTY_KEY: 'Empty key given',\n      SPLIT_EMPTY_KEY: 'Split an empty key',\n    },\n  },\n  fromTreeNodes: {\n    parsedErrorMsg: `Can't convert the tree to tree nodes to flat with separator`,\n    issues: {\n      DUPLICATE_KEY: 'Duplicate unique id',\n      INVALID_KEY: 'Invalid key found',\n    },\n  },\n} as const;\n\nexport class FlatTreeWsMapper<\n  TValue extends TreeNodeValue,\n  TKey extends string = string,\n> {\n  toTreeNodes = (\n    data: FlatTreeWs<TValue, TKey>,\n    params: FlatTreeWsParams\n    // eslint-disable-next-line sonarjs/cognitive-complexity\n  ): TreeMapperResult<TValue, TKey> => {\n    const { separator } = params;\n\n    const collector: CollectorContext<TValue, TKey> = { [sym]: [] };\n\n    // eslint-disable-next-line no-restricted-syntax\n    const d = data instanceof Map ? data : new Map(Object.entries(data));\n\n    try {\n      for (const [key, value] of d) {\n        const trimmedKey = key.trim() as TKey;\n        if (trimmedKey.length === 0) {\n          throw new Error(\n            `${flatTreeWsMapperErrors.toTreeNodes.issues.EMPTY_KEY}`\n          );\n        }\n\n        let context: CollectorContext<TValue, TKey> = collector;\n        const splitted = trimmedKey.split(separator);\n        for (const name of splitted) {\n          if (name.trim().length === 0) {\n            throw new Error(\n              `${flatTreeWsMapperErrors.toTreeNodes.issues.SPLIT_EMPTY_KEY}`\n            );\n          }\n          if (!Object.hasOwn(context, name)) {\n            Object.defineProperty(context, name, {\n              value: { [sym]: [] },\n              writable: false,\n            });\n\n            const parents = splitted.slice(0, -1) as TKey[];\n            const parentId = (parents.length > 0\n              ? parents.join(separator)\n              : null) as unknown as TKey | null;\n            const children = (context[name] as CollectorContext<TValue, TKey>)[\n              sym\n            ];\n            const node: TreeNode<TValue, TKey> = {\n              id: trimmedKey,\n              parentId: parentId,\n              children: children,\n            };\n            if (value !== undefined) {\n              node.value = value as TValue;\n            }\n            if (!Array.isArray(context[sym])) {\n              throw new TypeError(JSON.stringify(node));\n            }\n            context[sym].push(node);\n          }\n          context = context[name] as CollectorContext<TValue, TKey>;\n        }\n      }\n    } catch (e) {\n      return {\n        success: false,\n        message: flatTreeWsMapperErrors.toTreeNodes.parsedErrorMsg,\n        issues: [{ message: `${(e as Error).message}` }],\n      };\n    }\n    return {\n      success: true,\n      treeNodes: collector[sym],\n    };\n  };\n\n  /**\n   * @throws Error\n   */\n  toTreeNodesOrThrow = (\n    data: FlatTreeWs<TValue, TKey>,\n    params: FlatTreeWsParams\n  ): TreeNode<TValue, TKey>[] => {\n    const result = this.toTreeNodes(data, params);\n    if (!result.success) {\n      const { message, issues } = result;\n      const errors =\n        issues.length > 0\n          ? issues.map((issue) => issue.message).join(', ')\n          : null;\n      throw new Error(`${message} (${errors})`);\n    }\n    return result.treeNodes;\n  };\n\n  /**\n   * Will convert a tree of nodes to a flat tree.\n   */\n  fromTreeNodesOrThrow = <TId extends string = string>(\n    treeNodes: TreeNode<TValue, TId>[],\n    params: {\n      method: 'breadth-first'; // | 'depth-first';\n    }\n  ): FlatTreeWsMap<TValue, TKey> => {\n    const result: TreeNode<TValue, TId>[] = [];\n    const queue: TreeNode<TValue, TId>[] = [];\n    treeNodes.forEach((node) => queue.push(node));\n    // eslint-disable-next-line no-constant-condition\n    while (true) {\n      let count = queue.length;\n      if (count === 0) break;\n      while (count > 0) {\n        const node = queue.shift();\n        result.push(node!);\n        node!.children?.forEach((child) => queue.push(child));\n        count--;\n      }\n    }\n    const map = new Map() as FlatTreeWsMap<TValue, TKey>;\n    const { parsedErrorMsg, issues } = flatTreeWsMapperErrors.fromTreeNodes;\n    for (const node of result) {\n      const key = node.id as unknown as TKey;\n      if (typeof key !== 'string' && typeof key !== 'number') {\n        throw new TypeError(\n          `${parsedErrorMsg} (${issues.INVALID_KEY}: '${key}')`\n        );\n      }\n      if (map.has(key)) {\n        throw new Error(\n          `${parsedErrorMsg} (${issues.DUPLICATE_KEY}: '${key}' of type ${typeof key}')`\n        );\n      }\n      map.set(key, node.value as TValue);\n    }\n    return map;\n  };\n}\n","import type { TreeNode, TreeNodeValue } from '../tree.types';\n\ntype TreeSearchFindParams = {\n  includeChildren?: boolean;\n  reverse?: boolean;\n};\n\ntype NativeNodeSearchKeys = [\n  key: 'id' | 'parentId',\n  equality: '===',\n  value: string,\n];\n\ntype TreeNodeOptionalChildren<\n  TValue extends TreeNodeValue | undefined,\n  TKey extends string | number = string,\n> = TreeNode<TValue, TKey> & {\n  children?: TreeNode<TValue, TKey>;\n};\n\n/**\n * Depth-First Search (DFS) algorithm for tree structures. It uses a stack rather\n * than recursion in order to support deeply nested trees without call-stack overflows.\n * It is well suited for exploring a branch of a data structure in depth and\n * usually preferred when memory usage is a concern or when the data\n * structure has many nodes with few levels.\n *\n * @see https://hackernoon.com/a-beginners-guide-to-bfs-and-dfs-in-javascript\n */\nexport class DfsTreeSearch<\n  TValue extends TreeNodeValue | undefined,\n  TKey extends string | number = string,\n> {\n  constructor(private readonly treeNodes: TreeNode<TValue, TKey>[]) {}\n\n  /**\n   * Find first matching node in the tree. The `reverse` parameter can be used\n   * to traverse the tree in reverse order.\n   */\n  findOne = (\n    idOrConditionOrFn:\n      | TKey\n      | NativeNodeSearchKeys\n      | ((treeNode: TreeNode<TValue, TKey>) => boolean),\n    params?: TreeSearchFindParams\n    // eslint-disable-next-line sonarjs/cognitive-complexity\n  ): TreeNodeOptionalChildren<TValue, TKey> | undefined => {\n    const { includeChildren = false, reverse = false } = { ...params };\n    if (!Array.isArray(this.treeNodes) || this.treeNodes.length === 0) return;\n    let result;\n\n    const isIdSearch =\n      typeof idOrConditionOrFn === 'string' ||\n      typeof idOrConditionOrFn === 'number';\n\n    const isFnSearch = !Array.isArray(idOrConditionOrFn);\n\n    for (const treeNode of this.treeNodes) {\n      const stack = [treeNode] as TreeNodeOptionalChildren<TValue, TKey>[];\n      while (stack.length > 0) {\n        const node = stack[reverse ? 'pop' : 'shift']()!;\n        const isFound = isIdSearch\n          ? node.id === idOrConditionOrFn\n          : // eslint-disable-next-line sonarjs/no-nested-conditional\n            isFnSearch\n            ? idOrConditionOrFn(node)\n            : idOrConditionOrFn[0] in node &&\n              // eslint-disable-next-line sonarjs/different-types-comparison\n              node[idOrConditionOrFn[0]] === idOrConditionOrFn[2];\n        if (isFound) {\n          result = node;\n          break;\n        }\n        if (node.children) {\n          stack.push(\n            ...(node.children as unknown as TreeNodeOptionalChildren<\n              TValue,\n              TKey\n            >[])\n          );\n        }\n      }\n      if (result) break;\n    }\n    if (includeChildren !== true) {\n      const { children: _children, ...rest } = result ?? {};\n      return {\n        ...rest,\n      } as TreeNodeOptionalChildren<TValue, TKey>;\n    }\n    return result;\n  };\n}\n","import type { TreeNode, TreeNodeValue } from './tree.types';\n\nexport class Tree<\n  TValue extends TreeNodeValue | undefined,\n  TKey extends string = string,\n> {\n  constructor(protected readonly treeNodes: TreeNode<TValue, TKey>[]) {}\n\n  getTreeNodes = (): TreeNode<TValue, TKey>[] => {\n    return this.treeNodes;\n  };\n}\n"]}