{"id":21496491,"url":"https://github.com/mlibrary/lumberyard","last_synced_at":"2025-10-24T08:47:21.344Z","repository":{"id":57290851,"uuid":"92987703","full_name":"mlibrary/lumberyard","owner":"mlibrary","description":":clipboard: Sanely log process trees as they appear","archived":false,"fork":false,"pushed_at":"2018-02-27T17:42:30.000Z","size":416,"stargazers_count":1,"open_issues_count":0,"forks_count":1,"subscribers_count":19,"default_branch":"master","last_synced_at":"2025-10-03T04:47:13.542Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mlibrary.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-05-31T20:51:36.000Z","updated_at":"2018-02-27T20:19:07.000Z","dependencies_parsed_at":"2022-09-01T15:14:16.329Z","dependency_job_id":null,"html_url":"https://github.com/mlibrary/lumberyard","commit_stats":null,"previous_names":[],"tags_count":10,"template":false,"template_full_name":null,"purl":"pkg:github/mlibrary/lumberyard","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mlibrary%2Flumberyard","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mlibrary%2Flumberyard/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mlibrary%2Flumberyard/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mlibrary%2Flumberyard/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mlibrary","download_url":"https://codeload.github.com/mlibrary/lumberyard/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mlibrary%2Flumberyard/sbom","scorecard":{"id":653351,"data":{"date":"2025-08-11","repo":{"name":"github.com/mlibrary/lumberyard","commit":"505cb32ab095ed2854bea8e5b3711bcdb28e6979"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":1.9,"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":"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":"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":"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":"Code-Review","score":0,"reason":"Found 0/8 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":"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":"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":"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":9,"reason":"license file detected","details":["Info: project has a license file: LICENSE.txt:0","Warn: project license file does not contain an FSF or OSI license."],"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":-1,"reason":"internal error: error during branchesHandler.setup: internal error: githubv4.Query: Resource not accessible by integration","details":null,"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":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 25 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Vulnerabilities","score":0,"reason":"44 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: GHSA-v88g-cgmw-v5xw","Warn: Project is vulnerable to: GHSA-93q8-gq69-wqmw","Warn: Project is vulnerable to: GHSA-v6h2-p8h4-qcjw","Warn: Project is vulnerable to: GHSA-3xgq-45jj-v275","Warn: Project is vulnerable to: GHSA-rq8g-5pc5-wrhr","Warn: Project is vulnerable to: GHSA-h6ch-v84p-w6p9","Warn: Project is vulnerable to: GHSA-qrmc-fj45-qfc2","Warn: Project is vulnerable to: GHSA-fjxv-7rqg-78g4","Warn: Project is vulnerable to: GHSA-4q6p-r6v2-jvc5","Warn: Project is vulnerable to: GHSA-q42p-pg8m-cqh6","Warn: Project is vulnerable to: GHSA-w457-6q6x-cgp9","Warn: Project is vulnerable to: GHSA-62gr-4qp9-h98f","Warn: Project is vulnerable to: GHSA-f52g-6jhx-586p","Warn: Project is vulnerable to: GHSA-2cf5-4w76-r9qv","Warn: Project is vulnerable to: GHSA-3cqr-58rm-57f8","Warn: Project is vulnerable to: GHSA-g9r4-xpmj-mj65","Warn: Project is vulnerable to: GHSA-q2c6-c6pm-g3gh","Warn: Project is vulnerable to: GHSA-765h-qjxv-5f44","Warn: Project is vulnerable to: GHSA-f2jv-r9rf-7988","Warn: Project is vulnerable to: GHSA-44pw-h2cw-w3vq","Warn: Project is vulnerable to: GHSA-jp4x-w63m-7wgm","Warn: Project is vulnerable to: GHSA-c429-5p7v-vgjp","Warn: Project is vulnerable to: GHSA-43f8-2h32-f4cj","Warn: Project is vulnerable to: GHSA-2pr6-76vf-7546","Warn: Project is vulnerable to: GHSA-8j8c-7jfh-h6hx","Warn: Project is vulnerable to: GHSA-896r-f27r-55mw","Warn: Project is vulnerable to: GHSA-4xc9-xhrj-v574","Warn: Project is vulnerable to: GHSA-x5rq-j2xg-h7qm","Warn: Project is vulnerable to: GHSA-jf85-cpcp-j695","Warn: Project is vulnerable to: GHSA-p6mc-m468-83gw","Warn: Project is vulnerable to: GHSA-29mw-wpgm-hmr9","Warn: Project is vulnerable to: GHSA-35jh-r3h4-6jhm","Warn: Project is vulnerable to: GHSA-f8q6-p94x-37v3","Warn: Project is vulnerable to: GHSA-vh95-rmgr-6w4m","Warn: Project is vulnerable to: GHSA-xvch-5gv4-984h","Warn: Project is vulnerable to: GHSA-hj48-42vr-x3v9","Warn: Project is vulnerable to: GHSA-g6ww-v8xp-vmwg","Warn: Project is vulnerable to: GHSA-hrpp-h998-j3pp","Warn: Project is vulnerable to: GHSA-p8p7-x288-28g6","Warn: Project is vulnerable to: GHSA-c2qf-rxjj-qqgw","Warn: Project is vulnerable to: GHSA-2m39-62fm-q8r3","Warn: Project is vulnerable to: GHSA-mf6x-7mm4-x2g7","Warn: Project is vulnerable to: GHSA-52f5-9888-hmc6","Warn: Project is vulnerable to: GHSA-72xf-g2v4-qvf3"],"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-21T14:05:12.662Z","repository_id":57290851,"created_at":"2025-08-21T14:05:12.663Z","updated_at":"2025-08-21T14:05:12.663Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":280768831,"owners_count":26387531,"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-24T02:00:06.418Z","response_time":73,"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":[],"created_at":"2024-11-23T16:16:40.171Z","updated_at":"2025-10-24T08:47:21.313Z","avatar_url":"https://github.com/mlibrary.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"Lumberyard\n==========\n\n[![npm install lumberyard][npm-image]][npm]\n\n[![Build Status][status-image]][travis]\n[![Test Coverage][cover-image]][coverage]\n\n```javascript\nconst Lumberyard = require(\"lumberyard\");\n```\n\nIt's got a few things you can use.\n\n```javascript\nLumberyard.Scheduler\nLumberyard.ProcessTree\nLumberyard.LogTree\nLumberyard.tempName\n```\n\nScheduler\n---------\n\nFirst, you need to define a task.\n\n```javascript\nlet tasks = {};\ntasks.example = {};\n\n// One function should look for files/directories to process and resolve\n// with a list of them. If no files are present at the moment, it should\n// resolve with an empty list.\ntasks.example.find = () =\u003e new Promise((resolve, reject) =\u003e {\n  // ...\n  resolve([...]);\n});\n\n// This will be run with the output from the previous promise once we're\n// sure all appropriate files have finished copying.\ntasks.example.move = filesToMove =\u003e new Promise((resolve, reject) =\u003e {\n  // make a new working directory\n  // move all files in the list into there\n  resolve(newWorkingDir);\n});\n\ntasks.example.run = workingDir =\u003e Promise((resolve, reject) =\u003e {\n  // whatever you wanted to do goes here\n});\n```\n\nYou can define additional tasks by adding them as keys on the initial\n`tasks` object you define. Once done, run `Scheduler()` with them.\n\n```javascript\nLumberyard.Scheduler(tasks); // returns a new promise\n```\n\nThe scheduler will run the `find()` methods on each task you define. If\nit finds no files, it'll exit.\n\nIf it finds files related to any task, it'll start keeping an eye on\nthem until it's certain they're finished copying. Then it'll run your\n`move()` and `run()` methods.\n\nWhile any processes are running, we'll continue running `find()` on\nother tasks until all processes are done.\n\nProcessTree and LogTree\n-----------------------\n\nProcess trees are designed for cases where you want to validate and\nprocess trees of things. For example, you might have shipment folders\nfull of volume folders full of images which need to be converted in some\nway. You also might want to validate the input to some degree before\ndoing any actual processing.\n\nHere's a file tree example:\n\n```\n./\n|-- Shipment_1234567/\n|   |-- Volume_001/\n|   |   |-- 00000001.tif\n|   |   |-- 00000002.tif\n|   |   |-- 00000003.tif\n|   |   \\-- 00000004.tif\n|   |-- Volume_002/\n|   |   |-- 00000001.tif\n|   |   |-- 00000002.tif\n|   |   |-- 00000003.tif\n|   |   \\-- 00000004.tif\n|   \\-- Volume_003/\n|       |-- 00000001.tif\n|       |-- 00000002.tif\n|       |-- 00000003.tif\n|       \\-- 00000004.tif\n\\-- Shipment_1234568/\n    |-- Volume_004/\n    |   |-- 00000001.tif\n    |   |-- 00000002.tif\n    |   |-- 00000003.tif\n    |   \\-- 00000004.tif\n    |-- Volume_005/\n    |   |-- 00000001.tif\n    |   |-- 00000002.tif\n    |   |-- 00000003.tif\n    |   \\-- 00000004.tif\n    \\-- Volume_006/\n        |-- 00000001.tif\n        |-- 00000002.tif\n        |-- 00000003.tif\n        \\-- 00000004.tif\n```\n\nTo deal with this, you'd use ProcessTree like this:\n\n```javascript\n// getShipmentDirs, getVolumeDirs, and getPageImages are examples of\n// helper methods you might define.\n\nLumberyard.ProcessTree(root =\u003e {\n  for (let shipmentDir of getShipmentDirs(\".\")) {\n    root.add(shipment =\u003e {\n      shipment.description = shipmentDir; // useful for logging\n\n      // validate the shipment structure; possibly throw errors\n\n      for (let volumeDir of getVolumeDirs(shipmentDir)) {\n        shipment.add(volume =\u003e {\n          volume.description = volumeDir;\n\n          // validate etc.\n\n          for (let pageImage of getPageImages(volumeDir)) {\n            volume.add(page =\u003e {\n              page.description = pageImage;\n\n              // validate etc.\n\n              page.run = () =\u003e {\n                // Put everything you want to do for each page in here.\n                // It will only run if everything passes the validation\n                // steps.\n              };\n            });\n          }\n        });\n      }\n    });\n  }\n});\n```\n\nYou could (and proabably should) flatten something like this. If you\nwant volumes to execute their own code as well, it could look like this:\n\n```javascript\nvolume.run = () =\u003e { ... };\n```\n\nMore formally, `Lumberyard.ProcessTree()` takes two arguments:\n\n1.  (optional) filename to write logging output to\n2.  (required) the root validation function\n\nIf called with only one argument, it'll write its logging output to\nstdout instead of to a file.\n\nValidation functions that you write will be called with one argument,\nwhich you can treat as a payload.\n\n```javascript\nLumberyard.ProcessTree(payload =\u003e {\n  payload.description = \"this text will show up in logs\"\n\n  payload.add(child =\u003e {\n    // This is how you add child nodes in the process tree. The single\n    // argument in this method has all the same properties as payload,\n    // but child will bind to this subprocess.\n  });\n\n  // These three run() methods are all optional:\n\n  payload.run = () =\u003e {\n    // Put action code for the node here. This method can also be\n    // asynchronous if that's what you want.\n  };\n\n  payload.runBefore = () =\u003e {\n    // If this node has children, this method will run before any\n    // methods are run on any children.\n  };\n\n  payload.runAfter = () =\u003e {\n    // If this node has children, this method will run after any and all\n    // childrens' methods have completed successfully.\n  };\n\n  // These will all go to the log. They can also be run from inside any\n  // of the run() methods.\n  payload.log(\"info\", \"Some informational message\");\n  payload.log(\"warn\", \"Uh oh a warning; bad but not too bad\");\n  payload.log(\"error\", \"Error messages are as serious as it gets\");\n\n  // Feel free to set other keys in the payload to whatever you want.\n  // Only description, add, run, runBefore, runAfter, and log have\n  // special meaning.\n  payload.flipflop = \"blipblop\";\n});\n```\n\ntempName\n--------\n\nThis is just a function that acts a lot like `mktemp`. You pass it a\ntemplate string, and it will convert any serieses of four or more `X`s\ninto random base-58 characters. Or, if you want to use a timestamp, you\ncan instead hand it `YYYYmmddHHMMSS`.\n\n\n```javascript\nLumberyard.tempName();                      // \"nSzZEw\"\nLumberyard.tempName();                      // \"HRepr6\"\nLumberyard.tempName();                      // \"f88ye8\"\nLumberyard.tempName(\"XXXXXXXX\");            // \"3GLvJSy2\"\nLumberyard.tempName(\"XXXX\");                // \"pNBS\"\nLumberyard.tempName(\"XXX\");                 // \"XXX\"\nLumberyard.tempName(\"helloXXXXbye\");        // \"hello4YV3bye\"\nLumberyard.tempName(\"aXXXXbXXXXc\");         // \"adSPFbx5X7c\"\n\nLumberyard.tempName(\"YYYY-mm-ddTHH:MM:SS\")  // \"2017-11-03T16:15:40\"\nLumberyard.tempName(\"YYYYmmddHHMMSS\")       // \"20171103161540\"\nLumberyard.tempName(\"hey-YYYYmmddHHMMSS\")   // \"hey-20171103161540\"\nLumberyard.tempName(\"hey-YYYYmmdd\")         // \"hey-20171103\"\n\nLumberyard.tempName(\"hey-YYYYmmdd-XXXX\")    // \"hey-YYYYmmdd-pcX8\"\n```\n\n[travis]:       https://travis-ci.org/mlibrary/lumberyard\n[status-image]: https://travis-ci.org/mlibrary/lumberyard.svg?branch=master\n[npm]:          https://www.npmjs.com/package/lumberyard\n[npm-image]:    https://nodei.co/npm/lumberyard.png\n[coverage]:     https://coveralls.io/github/mlibrary/lumberyard\n[cover-image]:  https://coveralls.io/repos/github/mlibrary/lumberyard/badge.svg?branch=master\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmlibrary%2Flumberyard","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmlibrary%2Flumberyard","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmlibrary%2Flumberyard/lists"}