{"id":40702455,"url":"https://github.com/sourcevault/valleydate","last_synced_at":"2026-01-21T12:06:27.944Z","repository":{"id":57390469,"uuid":"278012805","full_name":"sourcevault/valleydate","owner":"sourcevault","description":"schema / object validator ","archived":false,"fork":false,"pushed_at":"2021-03-11T13:12:51.000Z","size":261,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"dev","last_synced_at":"2026-01-18T14:19:21.437Z","etag":null,"topics":["functional","json","json-schema","monad","schema","types","validator"],"latest_commit_sha":null,"homepage":"","language":"LiveScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sourcevault.png","metadata":{"files":{"readme":"readme.md","changelog":null,"contributing":null,"funding":null,"license":"COPYING.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-07-08T06:51:27.000Z","updated_at":"2021-03-11T13:12:54.000Z","dependencies_parsed_at":"2022-09-26T16:51:18.959Z","dependency_job_id":null,"html_url":"https://github.com/sourcevault/valleydate","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/sourcevault/valleydate","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sourcevault%2Fvalleydate","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sourcevault%2Fvalleydate/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sourcevault%2Fvalleydate/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sourcevault%2Fvalleydate/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sourcevault","download_url":"https://codeload.github.com/sourcevault/valleydate/tar.gz/refs/heads/dev","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sourcevault%2Fvalleydate/sbom","scorecard":{"id":839481,"data":{"date":"2025-08-11","repo":{"name":"github.com/sourcevault/valleydate","commit":"48ea3bf8d1fa67ed7c0f05c9c02bc894fd9beaef"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3,"checks":[{"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":"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":"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":"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":"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":"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":"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":"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":"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: LICENCE:0","Info: FSF or OSI recognized license: BSD 3-Clause \"New\" or \"Revised\" License: LICENCE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"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 'dev'"],"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"}}]},"last_synced_at":"2025-08-23T20:06:04.774Z","repository_id":57390469,"created_at":"2025-08-23T20:06:04.774Z","updated_at":"2025-08-23T20:06:04.774Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28632781,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-21T04:47:28.174Z","status":"ssl_error","status_checked_at":"2026-01-21T04:47:22.943Z","response_time":86,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"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":["functional","json","json-schema","monad","schema","types","validator"],"created_at":"2026-01-21T12:06:27.431Z","updated_at":"2026-01-21T12:06:27.933Z","avatar_url":"https://github.com/sourcevault.png","language":"LiveScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"#### MODULE HAS BEEN DEPRECATED AND MOVED TO [hoplon](https://github.com/sourcevault/hoplon)\n\n- you can find all the functions under `hoplon.types` in the new module.\n\n- `valleydate` will still be available, but will not receive any updates.\n\n- in case newer features are needed in your project, please refactor any code to use `hoplon.types` instead of `valleydate`.\n\n------------------\n\n![](https://raw.githubusercontent.com/sourcevault/valleydate/dev/logo.jpg)\n\n**Install**\n\n```js\nnpm install valleydate\n////| github.com | much install\nnpm install sourcevault/valleydate#dist\n```\n\n[![Build Status](https://travis-ci.org/sourcevault/valleydate.svg?branch=dev)](https://travis-ci.org/sourcevault/valleydate)\n\nvalleydate is a functional approach to schema validation that puts composability and extensibility as it's core feature.\n1. [Introduction](#introduction)\n1. [Initializing Validator](#initializing-validator)\n1. [Chainable Functions](#chainable-functions)\n      - [and](#--and)\n      - [or](#--or)\n      - [alt](#--alt)\n      - [map](#--map)\n      - [on](#--on)\n      - [edit / cont](#--cont)\n      - [fix](#--fix)\n      - [err](#--err)\n      - [jam](#--jam)\n1. [Creating Custom Basetypes](#creating-custom-basetypes)\n1. [Context Variable](#context-variable)\n1. [Helper Validators](#helper-validators)\n      - [required](#helper-validators)\n      - [integer](#helper-validators)\n      - [maybe\\*](#maybe)\n1. [.flatato](#flatato)\n1. [common pitfall](#common-pitfall)\n\n.. **quick examples** ..\n\n🟡 Object with required properties `foo` and `bar`.\n\n```js\nvar IS = require(\"valleydate\")\n\nvar V = IS.required(\"foo\",\"bar\")\n\nconsole.log(V.auth({}))\n\n/*\n{\n  continue: false,\n  error: true,\n  value: {},\n  message: [ 'foo', 'bar' ],\n  path: [ 'foo' ]\n}\n*/\n```\n\n🟡 Object with required properties `name` `age` and `address`, with `address` having required fields of `city` and `country.`\n\n\n```js\n\nvar IS = require(\"valleydate\")\n\nvar address = IS.required(\"city\",\"country\")\n.on(\"city\",IS.str)\n.on(\"country\",IS.str)\n\nvar V = IS.required(\"address\",\"name\",\"age\")\n.on(\"address\",address)\n.on(\"name\",IS.str)\n.on(\"age\",IS.num)\n\n\nvar sample =\n  {\n    name:\"Fred\",\n    age:30,\n    address:\n      {\n        city:\"foocity\"\n      }\n  }\n\nconsole.log(V.auth(sample))\n\n/*{\n  continue: false,\n  error: true,\n  value: { name: 'Fred', age: 30, address: { city: 'foocity' } },\n  message: [ 'city', 'country' ],\n  path: [ 'address', 'country' ]\n}*/\n\n```\n\n🟢 Table 1 - method names and their mapping to which underlying type check.\n\n\n```\nSHORTHANDS     ..FOR\n-------------------------------\nobj            Object\narr            Array\nundef          Undefined\nbool           Boolean\nnull           Null\nnum            Number\nstr            String\nfun            Function\narg            Argument\n-------------------------------\ncont           continue\nerr            error\nalt            alternative\n```\n\n#### Introduction\n***.. why another schema validator ?***\n\n- Monadic chainable functions.\n\n- custom validators that are easy to build and extend.\n\n`valleydate` exposes few key operators for creating data validators, for handling arbitrary complex data types.\n\nWe start by defining our basetypes:\n\n- `num`,`arr`,`str`,`null`,`bool`,`undef`,`arg`,`obj` and `fun`.\n\n.. then chainable units :\n\n- `and`,`or`,`alt`,`map`,`on`.\n\n.. and finally consumption units :\n\n- `cont/edit`,`jam`, `err` and `fix`.\n\n\n#### Initializing Validator\n\nEach validator chain starts with a *basetype*.\n\n```js\nvar V = IS.num\nV(1) // {continue: true, error: false, value:1}\n```\n\n```js\nvar V = IS.obj\nV({}) // {continue: true, error: false, value:{}}\n```\n\n```js\nvar V = IS.arr\nV([]) // {continue: true, error: false, value:[]}\n```\n\n```js\nvar V = IS.obj\nV([]) // {continue: false, error: true, message:\"not an array\",path:[]}\n```\n\nThe return object will always return `.continue`, `.error` and `.value`. First two are boolean, and will always be opposite in value. The final output is kept in the `.value` attribute.\n\n⚠️ `.value` may be **modified** if consumption units are used in the chain , so be careful. ⚠️\n\nIf `{cotinue:false,error:true,...}` the return object would also have attributes `.message` and `.path`, both are `Array` , with message values :\n\n- `message`- that passes along error messages from the validator.\n- `path` - in case the input is of type array or object, the path within the object where the validator function failed.\n\n\n#### Chainable Functions\n\nAfter initilizating a validator with its basetype, you are returned a unit object that can be chained ( infinitely ) using a few operators.\n\nThese operators all accept custom validators but also other `valleydate` objects.\n\n### - `and`\n\n- when validators need to be combined, and data has to satisfy conditions set by **both** validator.\n\n- a common situation is validating string enums.\n\n```js\n\nvar G7 = new Set([\n  \"USA\",\"EU\",\"UK\",\"Japan\",\"Italy\",\"Germany\",\"France\"\n]);\n\nvar valG7 = function(s){\n  if (G7.has(s)){\n   return true\n  }\n  else {\n   return [false,\"not in G7\"]\n  }\n}\nvar isG7 = IS.str.and(valG7)\n\nisG7.auth(\"UK\")\n\n//{ continue: true, error: false, value: 'UK' }\n\nisG7.auth(\"Spain\")\n\n/*{ continue: false,\n  error: true,\n  message: [ 'not in G7' ],\n  value: 'Spain'\n  }\n*/\n```\n\n⛔️ `valG7` is a **custom validator** in the above example, they can be any function that returns `boolean` or `[boolean,string]`.\n\n### - `or`\n\n- when validators need to be combined, here data can satisfy **either** validator.\n\n- a useful example would be accepting a single string or multiple strings in an array to define ipaddress to use in an application.\n\n```js\nvar canbeIP = IS.str.or(IS.arr.map(IS.str))\n```\n\n### - `alt`\n\n- functionally similar to `or` using **either** condition **but** the result ( or error ) is merged with upstream validator chain.\n\n```js\nvar canbeIP = IS.str.or(IS.arr.map(IS.str))\n```\n\n### - `map`\n\n###### `⛔️ .map only works for basetype Array, Object and Argument. ⛔️`\n\n- map allows to run validators on each value in an array or object.\n\n- an example of this would be an object of names with age.\n\n```js\nvar example = {\n  \"adam\":22,\n  \"charles\":35,\n  \"henry\":30,\n  \"joe\":24\n}\n```\n\nA validator for it would look something like this :\n\n```js\nvar ratifydata = IS.obj.map(IS.num);\n```\n\n### - `on`\n\n###### `⛔️ .on only works for basetype Array, Object and Argument. ⛔️`\n\n- apply validator to specific value in an object or array.\n\n- if there are multiple `on`, instead of chaining them, you could just pass an object with the validator for each key.\n\n```js\n\n\nvar V = IS.obj\n.on(\"foo\",IS.num)\n.on(\"bar\",IS.num)\n\nV.auth((foo:1,bar:2))\n\n// Also ...\n\nvar V1 = IS.obj.on({foo:IS.num,bar:IS.num})\n\nV1.auth((foo:1,bar:2))\n\n// Also ...\n\nvar V2 = IS.obj.on([\"foo\",\"bar\"],IS.num)\n\nV2.auth((foo:1,bar:2))\n\n```\n\n### - `cont`\n\nAlias: **edit**\n\n- accepts functions that run based on output of validation.\n\n- After validating some data, it needs to be consumed ( if valid ) or throw an error.\n\n- `.cont/edit`,`jam`,`fix` and `err` are consumption unit function that can be used to do just that.\n\n- return value of consumption units are important, they replace some parts of return object.\n\nusing the IP example from above :\n\n```js\nvar sendData = function(data){...}\n\nvar data = [\"209.85.231.104\",\"207.46.170.123\"]\n\nvar V = canbeIP\n.cont(sendDate) // \u003c-- only this is called as data is valid\n.err(console.log)\n\n```\n\n🟡 `.cont` can be used to making values **consistent**, using the IP address validator from above :\n\n\n```js\nIS = require(\"valleydate\")\n\nvar canbeIP = IS.arr.map(IS.str)\n.or(IS.str.cont (x) =\u003e [x]) // \u003c-- we want string to go inside an array\n// so we do not have to do extra prcessing downstream.\n\nvar ret = canbeIP.auth(\"209.85.231.104\")\n\nconsole.log(ret)\n//{error: false, continue: true, value: ['209.85.231.104']}\n//                                           ↑  ↑  ↑\n//                                       value is an array\n```\n\n### - `fix`\n\n- When errors can be dealt with locally without being passed upstream.\n\n- Used commonly in creating default, using the IP address from above :\n\n```js\nIS = require(\"valleydate\")\n\nvar canbeIP = IS.arr.map(IS.str)\n.or(IS.string.cont((x) =\u003e [x]))\n.fix([\"127.0.0.1\"])\n\nvar ret = canbeIP.auth(null)\n\nconsole.log(ret) // [\"127.0.0.1\"]\n```\n\n### - `err`\n\n- When validation fails, callback provided to `.err` is invoked.\n\n- The return value of `.err` replaces the `.error` message to be sent upstream.\n\n### - `jam`\n\n- `jam` allows to \"jam\" (raise an error) within a validation chain.\n\n- The return value of `.jam` replaces the `.error` message to be sent upstream.\n\n\n#### Creating Custom Basetypes\n\nIn case defaults are not sufficient, clean validators can be easily created.\n\n1. create a validator function with return types :\n  - `boolean`\n  - `[boolean,any]`\n\n2. provide it as first argument into `valleydate` as shown below :\n\n```js\nvar IS = require(\"valleydate\")\n\nvar simpleEmail = function(value){\n\n  var isemail = value.match (/[\\w-]+@([\\w-]+\\.)+[\\w-]+/)\n\n  if (isemail) {return true}\n  else {return [false,\"not a valid email address\"] }\n\n}\n\nvar isEmail = IS(simpleEmail)\n\n// isEmail is now a valleydate validator which means it gets\n\n// .and, .or, .cont, .err , .jam and .fix methods.\n\nisEmail.and\nisEmail.or\nisEmail.cont\n```\n\n#### Context Variable\n\n- `.auth` actually accepts **any number** of arguments.\n\n- but expects the first argument to be what needs to be validated.\n\n🟡 *so, what does `valleydate` do with the extra arguments ?*\n\n- It simply passes it downstream ( as subsequent ) arguments in case they need them.\n\n- We refer to these extra arguments as ***context variables***.\n\n- In cases where `.map` of `.on` are used, the context variables are appended with the key value.\n\n🟡 These context variables are useful in two important ways :\n\n- data needs to be provided to `.err` to create better error message, it could be things like filename.\n\n- `.map`, `on` modification is index / key dependant.\n\n#### Helper Validators\n\nSome validators are common enough to be added in core.\n\n- `required` - accepts a list of strings and checks *if they are not undefined*  in an object.\n\n- `restricted` - checks if object has properties that are restricted to provided keys. examples\n\n- `int` - checks if input is a integer\n\n🟡 using `int` :\n\n```js\nvar IS = require(\"valleydate\")\n\nIS.int(2)\n//{continue:true,error:false,value:1}\n\nIS.int(-1.1) //{continue:false,error:true,message:['not an integer']}\n\nIS.int(2.1)\n//{continue:false,error:true,message:['not an integer']}\n```\n\n#### `maybe.*`\n\n- maybe namespace can be used to validate optional value that conform to a type.\n\n- The function exposed through `maybe.*` using `IS.int` :\n\n```js\nvar IS = require(\"valleydate\")\n\nvar V = IS.maybe.int\n\nV.auth(undefined) // { continue: true, error: false, value: undefined }\n\nV.auth(2) // { continue: true, error: false, value: 2}\n\nV.auth(\"foo bar\")\n\n/*{\n  continue: false,\n  error: true,\n  message: [ 'not an integer ( or number )', 'not undefined' ],\n  value: 'foo bar'\n}*/\n\n```\n\n🟢 All possible primitive and helper function provided in core.\n\n```js\n// how to see both helper and primitive validators\n\u003e console.log((require(\"valleydate\")))\n{.*}\nint.neg              int.pos\nlist.ofint           list.ofnum\nlist.ofstr           maybe.arr\nmaybe.bool           maybe.boolnum\nmaybe.fun            maybe.int.neg\nmaybe.int.pos        maybe.list.ofint\nmaybe.list.ofnum     maybe.list.ofstr\nmaybe.null           maybe.num\nmaybe.obj            maybe.str\nmaybe.undef          not.arr\nnot.bool             not.fun\nnot.null             not.num\nnot.obj              not.str\nnot.undef            arg\narr                  bool\nboolnum              fun\nflatato              null\nnum                  obj\nreqres               required\nrestricted           str\nundef                undefnull\n```\n\n####  `.flatato`\n\n`.err` function by default gives the raw chain of errors.\n\nflatting it gets quite messy 🤷🏼‍♂️.\n\n`valleydate` provides a helper function `.flatato` to smoothly flatten raw error values.\n\nbut it requires your messages to follow a specific message passing protocol :\n\n- error value should always be an array.\n\n- first value of said array should always be a string that starts with a colon \":\".\n\n- to help with sorting, a number can be provided after a second colon to tell flatato the hierarchy of your messages.\n\n```js\n// Examples of message that flatato matches against\n[\n  ':not_tuple',\n  [' value is not tuple type.']\n]\n\n[\n  ':not_tuple:1',\n  ['length',' value is not tuple type.']\n]\n\n[\n  ':not_tuple:2',\n  ['innertype',' value is not tuple type.']\n]\n```\n\n#### .. common pitfall ..\n\n1. **why does mutating variable in function does not change it downstream ?**\n\neach value is rewritten *at every return*, so for example using context variable to try and change a value will lead to confusing output.\n\n```ls\n# .. in livescript instead of javascript ..\n\nV = be.obj.on \\foo,\n  (foo,__,data) -\u003e\n    data.foo = \"i got changed !\"\n    true\n\ndata = {foo:void}\n\ntorn = (V data,data).value\n\nconsole.log torn #{foo:undefined} 🡐 ( wont change, can't change )\n```\nIt's one of the trade off of having hidden **mutability**, it's easy to avoid such \"bugs\" by restricting the use of the chainable functions for their stated purpose ( e.g don't use `.and` to edit variables, use `.edit` instead ).\n\n## LICENCE\n\n- Code released under BSD-3-Clause Licence.\n- Documentation and Images released under CC BY-NC-ND 4.0.\n- details can be found [here](https://github.com/sourcevault/valleydate/blob/dev/COPYING.txt).","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsourcevault%2Fvalleydate","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsourcevault%2Fvalleydate","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsourcevault%2Fvalleydate/lists"}