{"id":20949629,"url":"https://github.com/tckerr/walk","last_synced_at":"2025-04-11T00:51:50.200Z","repository":{"id":3683533,"uuid":"50271366","full_name":"tckerr/walk","owner":"tckerr","description":"walk.js is a 0 dependency Javascript library for traversing object trees","archived":false,"fork":false,"pushed_at":"2024-07-02T12:23:53.000Z","size":584,"stargazers_count":55,"open_issues_count":1,"forks_count":4,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-04-11T00:51:42.650Z","etag":null,"topics":["javascript","tree","typescript"],"latest_commit_sha":null,"homepage":"https://www.npmjs.com/package/walkjs","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/tckerr.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2016-01-24T04:01:32.000Z","updated_at":"2025-02-21T23:14:24.000Z","dependencies_parsed_at":"2023-07-05T20:02:02.629Z","dependency_job_id":"18663ebb-7e2a-4b73-a280-21bee27d14af","html_url":"https://github.com/tckerr/walk","commit_stats":{"total_commits":104,"total_committers":4,"mean_commits":26.0,"dds":0.09615384615384615,"last_synced_commit":"119004f1d55cba06a454c6c75fce6ea856e237fb"},"previous_names":[],"tags_count":14,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tckerr%2Fwalk","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tckerr%2Fwalk/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tckerr%2Fwalk/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tckerr%2Fwalk/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tckerr","download_url":"https://codeload.github.com/tckerr/walk/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248322609,"owners_count":21084336,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["javascript","tree","typescript"],"created_at":"2024-11-19T00:41:13.613Z","updated_at":"2025-04-11T00:51:50.182Z","avatar_url":"https://github.com/tckerr.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Contents\n\n1. [Description](#description)\n2. [Installation](#installation)\n3. [Quickstart](#quickstart)\n    - [Async](#async)\n4. [Reference](#reference)\n    - [Halting the walk](#halting-the-walk)\n    - [Configuration](#configuration)\n        - [Defaults](#config-defaults)\n        - [Builder](#using-the-builder)\n    - [Callbacks](#callbacks)\n    - [Nodes](#nodes)\n5. [Extra utilities](#extra-functions)\n    - [Apply](#apply)\n    - [Deep copy](#deep-copy)\n    - [Compare](#compare)\n    - [Reduce](#reduce)\n6. [Running walk as a generator](#running-walk-as-a-generator)\n\n# Description\n\nWalk is a 0 dependency Javascript/Typescript library for traversing object trees. The library includes:\n\n- Functions for recursive processing of nested object trees and directed graphs\n- User defined callback hooks that get executed during the traversal\n- Incremental graph traversal through generators\n- A variety of convenience functions, which double as implementation examples for the library\n\n# Installation\n\n`npm install walkjs`\n\n# Quickstart\n\nBelow is a simple example of usage, in which we execute a single callback for each node in the object graph. The  callback simply prints metadata about the node. We also add a global filter to exclude any nodes whose value is equal to `1`.\n\n```typescript\nimport {walk} from 'walkjs';\n\nconst obj = {\n    'a': 1,\n    'b': [2, 3],\n    'c': {'d': 4}\n}\n\nwalk(obj, {\n    onVisit: {\n        callback: node =\u003e console.log(\"obj\" + node.getPath(), \"=\", node.val),\n        filters: node =\u003e node.val !== 1,\n    }\n})\n```\n\noutputs:\n```text\nobj = { a: 1, b: [ 2, 3 ], c: { d: 4 } }\nobj[\"b\"] = [ 2, 3 ]\nobj[\"b\"][0] = 2\nobj[\"b\"][1] = 3\nobj[\"c\"] = { d: 4 }\nobj[\"c\"][\"d\"] = 4\n```\n\n## Async\n\nAsync walks work almost exactly the same as the sync ones, but have an async signature. All callbacks will be awaited, and therefore still run in sequence. For the async versions below, callback functions may either return `Promise\u003cvoid\u003e` or `void`;\n\n```typescript\nimport {walkAsync} from 'walkjs';\n\nconst obj = {\n    //...\n}\n\nawait walkAsync(exampleObject, {\n    onVisit: {\n        callback: async function callApi(node: WalkNode): Promise\u003cvoid\u003e {\n            // do some async work here\n        }\n    }\n})\n```\n\nSee the reference for more details!\n\n# Reference\n\n```typescript\n// The primary method for traversing an object and injecting callbacks into the traversal. \nfunction walk(target: any, config: Config\u003cCallback\u003e): void {/*...*/}\n\n// Async version of `walk` which returns a promise.\nfunction walkAsync(target: any, config: Config\u003cAsyncCallback\u003e): Promise\u003cvoid\u003e  {/*...*/}\n```\n\n## Configuration:\n\n```typescript\ntype Config\u003cT\u003e = {\n    // (Only applies to async variations). If set to true, the walk will ignore `Callback.executionOrder` and instead \n    // run all async callbacks in parallel. However callbacks will still be grouped by `preVisit` or `postVisit`.\n    parallelizeAsyncCallbacks: boolean;\n\n    // One or many callback objects. See the Callbacks section for more information.\n    onVisit: OneOrMany\u003cCallback\u003cT\u003e\u003e;\n\n    // the mode for traversing the tree. Options are `depth` for *depth-first* processing and `breadth` for \n    // *breadth-first* processing.\n    traversalMode: 'depth' | 'breadth';\n\n    // if the object that gets passed in doesn't comply with this configuration setting, an error will occur. \n    // Finite trees will error if an object/array reference is encountered more than once, determined by set membership \n    // of the WalkNode's `val`. Graphs will only process object/array references one time. Infinite trees will always \n    // process nodes -- use `throw new Break()` to end the processing manually. *Warning: infinite trees will never \n    // complete processing if a callback doesn't `throw new Break()`.*\n    graphMode: 'finiteTree' | 'graph' | 'infinite';\n\n    // As mentioned in the `graphMode` config option, walk uses a set membership on the node's `val` to determine \n    // whether a node has been visited (for arrays and objects only). This setting can be overridden to change that \n    // behavior.\n    visitationRegister: NodeVisitationRegister;\n\n    // set to `false` to prevent tracking which callbacks have been invoked on a node. `WalkNode.executedCallbacks` \n    // will always be empty if this is set to `false`. This may help with memory management for larger objects.\n    trackExecutedCallbacks: boolean;\n}\n```\n\n\n### Config Defaults\n\n```typescript\nconst defaultConfig: Config\u003cT\u003e = {\n   traversalMode: 'depth',\n   graphMode: 'finiteTree',\n   parallelizeAsyncCallbacks: false,\n   onVisit: [],\n   trackExecutedCallbacks: true,\n   visitationRegister: SetVisitationRegister\n}\n```\n\n### Using the builder\n\nAn alternative way to configure a walk is to use either the `WalkBuilder` or `AsyncWalkBuilder`.\n\nCall `WalkBuilder.walk(target: any)` to execute the walk with the builder's configuration.\n\nExample:\n```typescript\nimport {WalkBuilder} from 'walkjs';\n\nconst logCallback = (node: WalkNode) =\u003e console.log(node);\nconst myObject = {}\n\nconst result = new WalkBuilder()\n        // runs for every node\n        .withSimpleCallback(logCallback)\n        // configured callback\n        .withCallback({\n           timing: 'postVisit',\n           executionOrder: 0,\n           callback: logCallback\n        })\n        // alternative way to configure callbacks\n        .withConfiguredCallback(logCallback)\n           .withTiming('postVisit')\n           .withFilter(node =\u003e !!node.parent)\n           .withExecutionOrder(1)\n           .done()\n        .withGraphMode('graph')\n        .withTraversalMode('breadth')\n        // execute the walk\n        .walk(myObject)\n```\n\n## Callbacks\n\nCallbacks are a way to execute custom functionality on certain nodes within our object tree.\n\n```typescript\ntype Callback\u003cT extends (node: WalkNode) =\u003e void\u003e = {\n    // The function to run when the node is visited. The callback function will be passed a single argument: a \n    // `WalkNode` object (see the Nodes section for more detail). For async functions, `callback` may alternatively \n    // return a `Promise\u003cvoid\u003e`, in which case it will be awaited.\n    callback: T;\n\n    // When the callback will execute. Options are `'preVisit'` (before any list/object is traversed), and `'postVisit'`\n    // (after any list/object is traversed). You may also supply `'both'`. When the walk is run in `'breadth'` mode, the \n    // only difference here is whether the callback is invoked prior to yielding the node. However when running in \n    // `'depth'` mode, `'postVisit'` callbacks for a node will run *after all the callbacks of its children*. \n    //\n    // For example, if our object is `{ a: b: { c: 1, d: 2 } }`, we would expect `'postVisit'` callbacks to run in the \n    // following order: `c`, `d`, `b`, `a`.\n    timing?: CallbackTiming;\n\n    // an integer value for controlling order of callback operations. Lower values run earlier. Callback stacks are \n    // grouped by timing and property, so the sort will only apply to callbacks in the same grouping.\n    executionOrder?: number;\n\n    // A function or list of functions which will exclude nodes when the result of the function for that \n    // node is `false`.\n    filters?: ((node: WalkNode) =\u003e boolean) | ((node: WalkNode) =\u003e boolean)[];\n}\n\n```\n\n### Callback Defaults\n\n```typescript\nconst defaultCallback: Callback = {\n    timing: 'preVisit',\n    executionOrder: 0,\n    filters: []\n}\n```\n\n## Nodes\n\n`WalkNode` objects represent a single node in the tree, providing metadata about the value, its parents, siblings, and children. Nodes have the following properties:\n\n```typescript\ntype WalkNode = {\n   // The key of this property as defined on its parent. For example, if this callback is running on the `'weight'` \n   // property of a `person`, the `key` would be `'weight'`. This will be the numerical index for members in arrays.\n   key: string | number;\n\n   // The value of the property. To use the above example, the value would be something like `183`.\n   val: any;\n\n   //Possible `NodeType` are `'array' | 'object' | 'value'`. Objects and arrays will be traversed, while values are \n   // leaf nodes.\n   nodeType: NodeType;\n\n   // Will be set to `true` if the property is a root object, otherwise `false`.\n   isRoot: boolean;\n\n   // An array of all callback functions that have already run on this property. The current function will *not* be \n   // in the list. Tracking this can be disabled in the config.\n   executedCallbacks: Callback[];\n\n   // The fully qualified path to the value, formatted with the optional formatter passed in. For example, if the \n   // variable being walked is named `myObject`, the path will look something like \n   // `[\"friends\"][10][\"friends\"][2][\"name\"]`, such that calling `myObject[\"friends\"][10][\"friends\"][2][\"name\"]` \n   // will return the `val`. The `pathFormat` parameter should take a node and return the path segment for only that \n   // node since `getPath` will automatically prepend the path of the node's parent as well.\n   getPath(pathFormat?: (node: WalkNode) =\u003e string);\n\n   // The node under which the property exists. `node.parent` is another instance of node, and will have all the same \n   // properties.\n   parent: WalkNode;\n\n   // A list of all child nodes.\n   children: WalkNode[];\n\n   // A list of all sibling nodes (nodes which share a parent).\n   siblings: WalkNode[];\n\n   // A list of all descendant nodes (recursively traversing children).\n   descendants: WalkNode[];\n\n   // A list of nodes formed by recursively traversing parents back to the root.\n   ancestors: WalkNode[];\n}\n\n```\n\n## Halting the walk\n\nThrowing an instance of \"Break\" within a callback will halt processing completely. This allows for early exit, usually in cases such as processing circular graphs or when you simply no longer need to continue.\n\nExample:\n\n```typescript\nimport {apply, Break} from \"walkjs\";\n\napply([1, 2, 3], ({val}) =\u003e {\n    if(val === 2)\n       throw new Break()\n})\n```\n\n# Extra utilities\n\nWalk has some extra utility functions built-in that you may find useful.\n\n## Apply\n\nA shorthand version of `walk()` that runs the supplied callbacks for all nodes.\n\n```typescript\nfunction apply(\n    target: any, \n    ...onVisit: ((node: NodeType) =\u003e void)[]\n): void {/*...*/}\n    \nfunction applyAsync(\n    target: any, \n    ...onVisit: (((node: NodeType) =\u003e void) | ((node: NodeType) =\u003e Promise\u003cvoid\u003e))[]\n): Promise\u003cvoid\u003e {/*...*/}\n```\n\n\n## Deep copy\n\nReturns a deep copy of an object, with all array and object references replaced with new objects/arrays.\n\n```typescript\nfunction deepCopy(target: object) : object { /*...*/ }\n```\n\n## Compare\n\nComputes a deep comparison between objects `a` and `b` based on the keys of each node:\n\n```typescript\ntype NodeComparison = {\n   path: string,\n   a?: any\n   b?: any\n   hasDifference: boolean,\n   difference?: 'added' | 'removed' | {before: any, after: any}\n}\n\nfunction compare(\n    a: any, \n    b: any, \n    leavesOnly=false, \n    formatter: NodePathSegmentFormatter=defaultFormatter,\n    nodeComparison: NodeComparisonFn = (a, b) =\u003e Object.is(a.val, b.val)\n): NodeComparison  {/*...*/}\n```\n\n## Reduce\n\nAccumulates a value of type T, starting with `initialValue`, by invoking `fn` on each node in `source` and adding the result to the accumulated value T.\n\n```typescript\nfunction reduce(\n    source: object, \n    initialValue: T, \n    fn: (accumulator: T, node: WalkNode) =\u003e T\n): T  {/*...*/}\n```\n\n# Running walk as a generator\n\nBehind the scenes, `walk` and `walkAsync` run as generators (`Generator\u003cWalkNode\u003e` and `AsyncGenerator\u003cWalkNode\u003e`, respectively). As they step through the object graph, nodes are yielded. \n\nThe default `walk`/`walkAsync` functions coerce the generator to a list before returning. However, you can access the generator directly; simply use the following imports instead:\n\n```typescript\nimport {walkStep, walkAsyncStep} from \"walkjs\";\n\n// sync\nfor (const node of walkStep(obj, config))\n    console.log(node);\n\n// async\nfor await (const node of walkAsyncStep(obj, config))\n    console.log(node)\n\n```\n\nNote: `preVisit` callbacks are invoked prior to yielding a node, and `postVisit` callbacks after.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftckerr%2Fwalk","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftckerr%2Fwalk","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftckerr%2Fwalk/lists"}