{"id":21615830,"url":"https://github.com/valango/duke","last_synced_at":"2025-10-12T04:32:34.432Z","repository":{"id":42236101,"uuid":"233010329","full_name":"valango/duke","owner":"valango","description":"Asynchronous rule-based file system walker","archived":false,"fork":false,"pushed_at":"2023-01-06T02:26:32.000Z","size":2486,"stargazers_count":6,"open_issues_count":2,"forks_count":0,"subscribers_count":1,"default_branch":"development","last_synced_at":"2025-10-12T04:32:14.457Z","etag":null,"topics":["asynchronous","directory-walker","parallel","rule-based","walker"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"isc","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/valango.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-01-10T09:16:27.000Z","updated_at":"2021-10-08T09:27:17.000Z","dependencies_parsed_at":"2023-02-05T03:02:00.999Z","dependency_job_id":null,"html_url":"https://github.com/valango/duke","commit_stats":null,"previous_names":[],"tags_count":36,"template":false,"template_full_name":null,"purl":"pkg:github/valango/duke","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/valango%2Fduke","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/valango%2Fduke/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/valango%2Fduke/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/valango%2Fduke/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/valango","download_url":"https://codeload.github.com/valango/duke/tar.gz/refs/heads/development","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/valango%2Fduke/sbom","scorecard":{"id":914878,"data":{"date":"2025-08-11","repo":{"name":"github.com/valango/duke","commit":"90c59d29e3a74d4828dc57660fef7109990c0fce"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":1.7,"checks":[{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Code-Review","score":0,"reason":"Found 0/30 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"SAST","score":0,"reason":"no SAST tool detected","details":["Warn: no pull requests merged into dev branch"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: ISC License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'development'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"Vulnerabilities","score":0,"reason":"12 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: GHSA-968p-4wvh-cqc8","Warn: Project is vulnerable to: GHSA-67hx-6x53-jw92","Warn: Project is vulnerable to: GHSA-v6h2-p8h4-qcjw","Warn: Project is vulnerable to: GHSA-grv7-fg5c-xmjg","Warn: Project is vulnerable to: GHSA-3xgq-45jj-v275","Warn: Project is vulnerable to: GHSA-4q6p-r6v2-jvc5","Warn: Project is vulnerable to: GHSA-9c47-m6qq-7p4h","Warn: Project is vulnerable to: GHSA-f8q6-p94x-37v3","Warn: Project is vulnerable to: GHSA-mwcw-c2x4-8c55","Warn: Project is vulnerable to: GHSA-c2qf-rxjj-qqgw","Warn: Project is vulnerable to: GHSA-76p7-773f-r4q5","Warn: Project is vulnerable to: GHSA-j8xg-fqg3-53r7"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-24T20:36:20.140Z","repository_id":42236101,"created_at":"2025-08-24T20:36:20.141Z","updated_at":"2025-08-24T20:36:20.141Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279010259,"owners_count":26084719,"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","status":"online","status_checked_at":"2025-10-12T02:00:06.719Z","response_time":53,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":["asynchronous","directory-walker","parallel","rule-based","walker"],"created_at":"2024-11-24T22:12:57.170Z","updated_at":"2025-10-12T04:32:34.399Z","avatar_url":"https://github.com/valango.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# dwalker [![Build Status](https://travis-ci.org/valango/duke.svg?branch=master)](https://travis-ci.org/valango/duke) [![Code coverage](https://codecov.io/gh/valango/duke/branch/master/graph/badge.svg)](https://codecov.io/gh/valango/duke)\n\n![](https://github.com/valango/duke/blob/master/assets/quote.png)\n\nAsynchronous rule-based file system walker. It:\n   * does things most regexp-based walkers hardly can;\n   * uses super simple rule definitions;\n   * handles most file system errors by default;\n   * provides powerful extendable API;\n   * runs real fast.\n\nThis is what a simple [demo app](doc/examples.md)\ndoes on my old 2,7 GHz MacBook Pro:\n![](https://github.com/valango/duke/blob/master/assets/counts.png)\n\nThe version 6 is hugely different from its [ancestors](#version-history).\n\nThe further text describes the [usage](#usage), [API](#api) and [version history](#version-history).\n\n## Usage\n**NB:** This package needs Node.js v12.12 or higher.\n\n**Install** with _yarn_ or _npm_\n```\nyarn add dwalker   ## npm i -S dwalker\n```\nThe \u003ca name=\"simple\"\u003efollowing code\u003c/a\u003e walks all given directory trees in parallel, gathering basic statistics:\n```javascript\nconst walker = new (require('dwalker')).Walker()\nconst dirs = '/dev ..'.split(' ')\n\nPromise.all(dirs.map(dir =\u003e walker.walk(dir))).then(res =\u003e {\n  console.log('Done(%d):', res.length)\n}).catch(error =\u003e {\n  console.log('EXCEPTION!', error)\n}).finally(() =\u003e {\n  console.log(walker.stats)\n})\n// -\u003e Done(1): { dirs: 8462, entries: 65444, errors: 2472, retries: 0, revoked: 0 }\n// -\u003e Elapsed: 1012 ms\n```\n\n### What it does\nThe _`Walker#walk()`_ method recursively walks the directory tree _width-first_.\nIt scans all directory entries, invoking the _handler functions_ as it goes,\nkeeping track of its internal rules tree.\nFor speed, all this is done asynchronously.\n\nPlease have a glance at its [_**core concepts**_](doc/walker-concepts.md),\nif you haven't done so already.\n\n## API\nContents: [package exports](#package-exports), [Walker](#walker-class), \n[common helpers](#common-helpers), [special helpers](#special-helpers), \n[rule system](#rule-system)\n### Package exports\n   * [_**`Walker`** class_](#walker-class)\n   * [_**`Ruler`** class_](doc/ruler.md)\n   * [_constants_](src/constants.js)\n   * [_common helpers_](#common-helpers)\n   \nTypes referred to below are declared in\n[src/typedefs.js](src/typedefs.js).\n\n### _`Walker`_ class\nThe most of the magic happens here. For details, see: [methods](#walker-instance-methods),\n[properties](#walker-instance-properties), [class/static API](#walker-class-methods-and-properties),\n[protected API](doc/walker-protected.md), and [exceptions handling](#exceptions-handling).\n\n**`constructor`**`(options : {TWalkerOptions})`\u003cbr /\u003e\n   * `avoid : string | strig[]` - the `avoid()` instance method will be called.\n   * `interval : number=` - instance property setting.\n   * `rules : *` - [rule definitions](#rules), or a _`Ruler`_ instance to be cloned.\n   * `symlinks : boolean=` - enable symbolic links checking by _`onEntry()`_ handler.\n\n_Walker_ instance stores given (even unrecognized) options in private _`_options`_ property.\n\n#### Walker instance methods\n\nSee the [separate description](doc/walker-concepts.md#handlers)\nof _`onDir()`_, _`onEntry()`_ and _`onFinal()`_ handler methods.\n\n**`avoid`**`(...path) : Walker` - method\u003cbr /\u003e\nInjects the _paths_ into _`visited`_ collection thus preventing them from being visited.\nThe arguments must be strings or arrays of strings - absolute or relative paths.\n\n**`getDataFor`**`(dirPath) : * ` - method\u003cbr/ \u003e\nFor accessing the data in the internal dictionary. Empty entries are created there before calling\nthe _`onDir()`_ handler. The _`Walker`_ itself does not use those values.\n\n**`getOverride`**`(error) : number` - \u003ca name=\"get-override\"\u003emethod\u003c/a\u003e\u003cbr /\u003e\nReturns an overriding action code (if any) for the current exception and its context.\nThe _`Walker`_ calls this method internally and assigns its numeric return value\nto `error.context.override` before calling its `onError()` method. A non-numeric return value\nhas no effect. Instead of overriding this method, you can directly modify the\n[overrides export](#walker-class-methods-and-properties) of the package.\n\n**`onError`**`(error: Error, context: TDirContext) : *` - method\u003cbr /\u003e\nCalled with trapped error after _`error.context`_ has been set up.\nDefault just returns _`error.context.override`_.\nReturned action code will be checked for special values; a non-numeric return means this\nwas an unexpected error rejecting the _walk_ promise.\n\nThe _`Walker`_ may provide the following _`context.locus`_ values:\n`'onDir', 'openDir', 'iterateDir', 'onEntry', 'closeDir', 'onFinal'`.\nOverriding handlers may define their own locus names.\n\n**`reset`**`([hard : boolean]) : Walker` - method\u003cbr /\u003e\nResets a possible _STC_. In a _hard_ case, it resets all internal state properties,\nincluding those available via _`stats`_.\nCalling this method during walk throws an unrecoverable error.\n\n**`tick`**`(count : number)` - method\u003cbr /\u003e\nCalled during walk automatically. Default does nothing. \nOverride this for progress monitoring etc.\n\n**`trace`**`(handlerName, result, context, args)` - method\u003cbr /\u003e\nCalled right after every handler call. _Use this for **debugging only**!_\nDefault is an empty function.\n\n**`walk`**`(startPath : string, [options : TWalkOptions]) : Promise` - method\u003cbr /\u003e\nWalks the walk. The _`startPath`_ may be any valid pathname defaulting to _`process.cwd()`_.\nVia _`options`_ you can override  _`trace()`_ method, any _handler methods_, as well as \n_`data`_ and _`ruler`_ instance properties. \nThe promise resolves to _`data`_, to non-numeric return value from a handler or\nrejects to unexpected error instance.\n\n#### Walker instance properties\n**`duration`**` : number` - microseconds elapsed from start of the current _walk batch_\nor duration of the most recent batch.\n\n**`failures`**` : Error[]` - any \u003ca name=\"failures\"\u003eexceptions overridden\u003c/a\u003e during a walk.\nThe _`Error`_ instances in there will have a `context : TDirContext` property set.\n\n**`ruler`**` : Ruler` - initial ruler instance for a new walk.\n\n**`stats`**` : Object r/o` - general statistics as object with numeric properties:\n   * `dirs` - number of visited directories;\n   * `entries` - number of checked directory entries;\n   * `errors` - number of exceptions encountered;\n   * `retries` - number of operation retries (e.g. in case of out of file handles);\n   * `revoked` - number of directories recognized as already visited (may happen with **`symlinks`** option set);\n   * `walks` - number of _currently active_ walks.\n\n**`walks`**` : number r/o` - number of currently active walks.\n\n#### Walker class methods and properties\nAll those are directly available via the package exports.\n\n**`newRuler`**`(...args) : Ruler` - factory method.\n\n**`overrides`**` : Object` - error override rules as a tree:\n( locus -\u003e _`error.code`_ -\u003e actionCode ).\n\n**`shadow`**` : atring[]` - mask for omitting certain parts of context parameter,\nbefore injecting it to Error instance for logging.\n\n#### Walker protected API\nIs described in a [separate document](doc/walker-protected.md). \n\n#### Exceptions handling\n**The good news** is: whatever will happen during a walk, the _`Walker`_ instance won't throw\nan exception!\n\nIf an exception occurs and there is an [override defined](#get-override) for it, a new entry\nwill be added to the [failures instance property](#failures), and the walk will continue.\n\nWithout an override defined, however, we'll have _an unexpected exception_.\nIn this case, the walk will terminate with an augmented _`Error`_ instance via rejection,\nand the [example program above](#simple) would output something like this:\n```\nEXCEPTION! TypeError: Cannot read property 'filter' of undefined\n    at ProjectWalker.onDir (/Users/me/dev-npm/nsweep/lib/ProjectWalker.js:111:38)\n    at async doDir (/Users/me/dev-npm/nsweep/node_modules/dwalker/src/Walker.js:491:15)\n  context: {\n    depth: 0,\n    dirPath: '/Users/me/dev-npm/nsweep',\n    done: undefined,\n    locus: 'onDir',\n    rootPath: '/Users/me/dev-npm/nsweep',\n    override: undefined\n  }\n} \n```\nAn error stack combined with a walk context snapshot should be enough to spot the bug.\n\n### Common helpers\nThose helpers are available via package exports and may be useful on writing handlers.\n\n**`checkDirEntryType`**`(type : TEntryType) : TEntryType` - function\u003cbr /\u003e\nreturns the argument if it is a valid type code; throws an assertion error otherwise. \n\n**`dirEntryTypeToLabel`**`(type : TEntryType, [inPlural : boolean]) : string` - function\u003cbr /\u003e\nreturns human readable type name for valid type; throws an assertion error otherwise. \n\n**`makeDirEntry`**`(name : string , type : TEntryType, [action : number]) : TDirEntry` - function\u003cbr /\u003e\nconstructs and returns a ned directory entry with _`action`_ defaulting to `DO_NOTHING`.\n\n**`makeDirEntry`**`(nativeEntry : fs.Dirent) : TDirEntry` - function\u003cbr /\u003e\nreturns a new directory entry based on \n[Node.js native one](https://nodejs.org/dist/latest-v14.x/docs/api/fs.html#fs_class_fs_dirent).\n\n### Special helpers\nTo use those helpers, load them first, like:\n```javascript\nconst symlinksFinal = require('dwalker/symlinksFinal')\n```\n**`pathTranslate`**`(path, [absolute]) : string` function.\u003cbr /\u003e\nTranslate the `path` from POSIX to native format, resolves the\nleading '~' to user home directory. If `absolute` is on, then\nmakes the path absolute, always ending with path separator.\n\n**`relativize`**`(path, [rootPath, [prefix]]) : string` function.\u003cbr /\u003e\nStrips the _`rootPath`_ (defaulting to _`homeDir`_)part from given `path`, if it is there.\nOptional _`prefix`_ string will be applied to resulting relative path.\nMay help to make some reports easier to read.\n\n**`relativize.homeDir`**` : string` - initialized to _current user's home directory_.\n\n**`symlinksFinal`**`(entries, context) : *` async handler.\u003cbr /\u003e\nUse it inside _`onFinal`_ handler for following the symbolic links.\nExample:\n```javascript\nconst onFinal = function (entries, context) {\n  return this._useSymLinks\n    ? symlinksFinal.call(this, entries, context) : Promise.resolve(0)\n}\n```\n\n### Rule system\nThe main goal here was to keep rules simple (atomic), even when describing \ncontext-sensitive rules and special exclusions.\n\nRule definitions are tuples `(action-code, {pattern})`,\nquite similar to _bash_ glob patterns or _.gitignore_ rules. Example:\n```javascript\nruler.add(\n  DO_SKIP, '.*', '!/.git/', 'node_modules/', 'test/**/*',\n  11, 'package.json', '/.git/', '/LICENSE;f', '*;l')\n```\nHere the first rule tells to ignore the dreaded `node_modules` directory and\nany entries starting with '.', except the top-level `.git` directory. Also, nothing\nunder the `test` directory, where ever found, will count. The trailing `'/'`\nindicates the directory.\n\nThe second rule asks for some sort of special care to be taken for all `package.json`\nentries with no regard to their type, for top-level `.git` directory, for top-level\n`LICENSE` file and for all symbolic links. And, yes, the `.weirdos/package.json`\nwill be ignored.\n\nWithout _explicit type_, all rules created are typeless or `T_DIR` ('d').\nExplicit type must match one in [`S_TYPES` constant](src/constants.js).\n\nBehind the scenes, a _`Ruler`_ instance creates and interprets a _**rule tree**_\nformed as an array on records \u003cbr/\u003e\n_`(type, expression, ancestorIndex, actionCode)`_.\nFor the above example, the _`Ruler` dump_ would be like:\n```\n       node typ regex            parent  action\n      -----+---+-----------------------+-------------\n         0: 'd' null,               -1,  DO_NOTHING,\n         1: ' ' /^\\./,               0,  DO_SKIP,\n         2: 'd' /^\\.git$/,          -1, -DO_SKIP,\n         3: 'd' /^node_modules$/,    0,  DO_SKIP,\n         4: 'd' /^test$/,           -1,  DO_NOTHING,\n         5: 'd' null,                4,  DO_NOTHING,\n         6: ' ' /./,                 5,  DO_SKIP,\n         7: ' ' /^package\\.json$/,   0,  11,\n         8: 'd' /^\\.git$/,          -1,  11,\n         9: 'f' /^LICENSE$/,        -1,  11,\n        10: 'l' /./,                 0,  11,\n_ancestors: [ [ 0, -1 ] ]\n\n```\nThe internal _`ancestors`_ array contains tuples _`(actionCode, ruleIndex)`_.\n\nThe _`Ruler#check()`_ method typically called from _`Walker#onEntry()`_ finds\nall rules matching the given entry _`(name, type)`_ and fills in the\nlastMatch array, analogous to ancestors array. Then it returns the most\nprominent (the highest) action code value. The `DO_SKIP` and other system action codes\nprevail the user-defined codes simply because they have higher values.\n\nA negative value screens the actual one. _**Do not**_ use negative values in rule definitions -\nthe ruler will do this for you, when it encounters a pattern starting with '!'.\n\nThe sub-directories opened later will inherit new _`Ruler`_ instances with _`ancestors`_\nset to _`lastMatch`_ contents from the upper level.\nSo, the actual rule matching is trivial, and the rules can be switched dynamically.\n\nFor further details, check the\n[_`Ruler`_ reference](doc/ruler.md) and\nthe special [demo app](doc/examples.md#parsejs). \n\n## Version history\n* v6.0.0 @20201225\n   - cleaned code and API (breaking changes) after using _`dwalker`_ in some actual projects,\n   so the basic use cases are clear now. As the general concepts persist,\n   migration sould not be a major headache and reading the updated\n   [core concepts](doc/walker-concepts.md) should help.\n* v5.2.0 @20201202\n   - added: Walker#getOverride instance method.\n* v5.1.0 @20201121\n   - removed: hadAction(), hasAction() Ruler instance methods.\n* v5.0.0 @20201120\n   - Walker totally re-designed (_a **breaking** change_);\n   - Ruler#check() refactored (_a non-breaking change_);\n   - documentation and examples re-designed.\n* v4.0.0 @20200218\n   - several important fixes;\n   - Walker throws error if on illegal action code returned by handler;\n   - added: Walker#expectedErrors, removed: Walker#getMaster;\n   - added: check(), hadAction(), hasAction() to Ruler, removed: match();\n   - an up-to-date documentation;\n* v3.1.0 @20200217\n* v3.0.0 @20200211\n* v2.0.0 @20200126\n* v1.0.0 @20200124\n* v0.8.3 @20200123: first (remotely) airworthy version.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvalango%2Fduke","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fvalango%2Fduke","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvalango%2Fduke/lists"}