{"id":19179500,"url":"https://github.com/hypermedia-app/clownface-shacl-path","last_synced_at":"2025-10-29T03:35:11.083Z","repository":{"id":40676550,"uuid":"382580665","full_name":"hypermedia-app/clownface-shacl-path","owner":"hypermedia-app","description":"Find nodes in graph by following SHACL Paths","archived":false,"fork":false,"pushed_at":"2025-02-18T14:04:55.000Z","size":670,"stargazers_count":5,"open_issues_count":2,"forks_count":0,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-09-11T13:05:20.730Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"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/hypermedia-app.png","metadata":{"files":{"readme":"readme.md","changelog":"CHANGELOG.md","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,"zenodo":null}},"created_at":"2021-07-03T09:53:22.000Z","updated_at":"2024-11-27T03:24:12.000Z","dependencies_parsed_at":"2024-02-29T13:05:28.151Z","dependency_job_id":"aa27f4e4-733b-4985-9950-b3c309dc3a9e","html_url":"https://github.com/hypermedia-app/clownface-shacl-path","commit_stats":{"total_commits":111,"total_committers":3,"mean_commits":37.0,"dds":"0.18018018018018023","last_synced_commit":"069d9b5ea79f45dd353e006332dbe31ac439330b"},"previous_names":[],"tags_count":17,"template":false,"template_full_name":null,"purl":"pkg:github/hypermedia-app/clownface-shacl-path","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hypermedia-app%2Fclownface-shacl-path","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hypermedia-app%2Fclownface-shacl-path/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hypermedia-app%2Fclownface-shacl-path/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hypermedia-app%2Fclownface-shacl-path/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/hypermedia-app","download_url":"https://codeload.github.com/hypermedia-app/clownface-shacl-path/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hypermedia-app%2Fclownface-shacl-path/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279014804,"owners_count":26085594,"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-13T02:00:06.723Z","response_time":61,"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-09T10:43:22.129Z","updated_at":"2025-10-13T11:43:02.414Z","avatar_url":"https://github.com/hypermedia-app.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# clownface-shacl-path\n\nProvides functions to work with [SHACL Property Paths](https://www.w3.org/TR/shacl/#property-paths)\n\n## Install\n\n```bash\nyarn add clownface-shacl-path\n```\n\n## Usage\n\n### `findNodes`\n\nFind nodes in RDF/JS graphs by following [SHACL Property Paths](https://www.w3.org/TR/shacl/#property-paths) using [clownface](https://npm.im/clownface) graph traversal library.\n\nThe exported function takes two parameters:\n\n1. starting graph pointer node\n2. graph pointer to a SHACL Property Path\n\n```js\nimport { findNodes } from 'clownface-shacl-path'\nimport fetch from '@rdfjs/fetch'\nimport $rdf from 'rdf-ext'\nimport clownface from 'clownface'\nimport { sh } from '@tpluscode/rdf-ns-builders'\n\n// prepare a clownface Graph Pointer\nconst response = await fetch('http://zazuko.github.io/tbbt-ld/dist/tbbt.nt', { factory: $rdf })\nconst amy = clownface({ dataset: await response.dataset() })\n    .namedNode('http://localhost:8080/data/person/amy-farrah-fowler')\n\n// prepare a SHACL Property Path structure as clownface\nconst path = clownface({ dataset: $rdf.dataset() }).blankNode()\n\n/*\n  sh:path [\n    sh:alternativePath ( # find both\n      [ sh:inversePath schema:spouse ] # Sheldon, who is Amy's spouse\n      [ sh:inversePath schema:knows ] # Leonard, who knows Amy\n    )\n  ]\n*/\npath.addList(sh.alternativePath, [\n  path.blankNode().addOut(sh.inversePath, schema.spouse),\n  path.blankNode().addOut(sh.inversePath, schema.knows)\n])\n\n// find nodes connected by the path\nfindNodes(amy, path)\n```\n\n### `toSparql`\n\nConverts a [SHACL Property Path](https://www.w3.org/TR/shacl/#property-paths) to SPARQL Property Path string template object. Use the property path with [@tpluscode/sparql-builder](https://npm.im/@tpluscode/sparql-builder)\n\n```typescript\nimport type {GraphPointer} from 'clownface'\nimport { toSparql } from 'clownface-shacl-path'\nimport { SELECT } from '@tpluscode/sparql-builder'\n\n/*\n [ sh:path \n   [\n     sh:alternativePath (\n       ( schema:knows schema:name )\n       ( foaf:knows foaf:name )\n     )\n   ]\n ]\n */\nlet path: GraphPointer\n\n/*\n  SELECT ?friendName\n  WHERE {\n    ?person a \u003chttp://schema.org/Person\u003e .\n    ?person (schema:knows|schema:name)|(foaf:knows|foaf:name) ?friendName\n  }\n */\nSELECT`?friendName`\n  .WHERE`\n    ?person a \u003chttp://schema.org/Person\u003e .\n    ?person ${toSparql(path)} ?friendName .\n  `.build()\n```\n\n### `toSparql.sequence`\n\nIn cases when the intermediate nodes of a Sequence Path are important, that path can be split, so that authors can\ncreate and capture variables for all the nodes.\n\nFor that purpose, call `toSparql.sequence()`\n\n```typescript\nimport type {GraphPointer} from 'clownface'\nimport { toSparql } from 'clownface-shacl-path'\nimport { SELECT } from '@tpluscode/sparql-builder'\nimport $rdf from 'rdf-ext'\n\n/*\n [ sh:path ( schema:employee schema:spouse schema:name ) ]\n */\nlet path: GraphPointer\n\nconst sequence = toSparql.sequence(path)\n\n/*\n  SELECT *\n  WHERE {\n    ?path0 schema:employee ?path1 .\n    ?path1 schema:spouse ?path2 .\n    ?path2 schema:name ?path3 .\n  }\n */\nconst query = sequence.reduce((query, segment, index) =\u003e {\n  const subject = $rdf.variable(`path${index}`)\n  const object = $rdf.variable(`path${index + 1}`)\n    \n  return query.WHERE`${subject} ${segment} ${object}`\n}, SELECT.ALL)\n```\n\n## Advanced options\n\n### Allow Named Node Sequence Paths\n\nThe SHACL specification requires that lists in Sequence Paths are blank nodes. However, some implementations\nmay use Named Nodes instead. To allow that, you can manually create the SHACL Property Path object from a graph pointer\nand pass it to `findNodes` or `toSparql`:\n\n```typescript\nimport type { GraphPointer } from 'clownface'\nimport { findNodes, fromNode } from 'clownface-shacl-path'\n\nlet pathNode: GraphPointer\nlet startNode: GraphPointer\n\nconst path = fromNode(pathNode, { allowNamedNodeSequencePaths: true })\nconst nodes = findNodes(startNode, path)\n```\n\n## Advanced Property Path handling\n\nIf it is necessary to implement a custom logic for processing of Property Paths, create a class extending from\n[`PathVisitor`](src/lib/path.ts). \n\n```ts\nimport * as Path from 'clownface-shacl-path'\nimport type { GraphPointer } from 'clownface'\n\nclass MyVisitor extends Path.PathVisitor\u003cTOut, TArg\u003e {\n  visitAlternativePath(path: Path.AlternativePath, arg?: TArg): TOut {\n  }\n\n  visitInversePath(path: Path.InversePath, arg?: TArg): TOut {\n  }\n\n  visitOneOrMorePath(path: Path.OneOrMorePath, arg?: TArg): TOut {\n  }\n\n  visitPredicatePath(path: Path.PredicatePath, arg?: TArg): TOut {\n  }\n\n  visitSequencePath(path: Path.SequencePath, arg?: TArg): TOut {\n  }\n\n  visitZeroOrMorePath(path: Path.ZeroOrMorePath, arg?: TArg): TOut {\n  }\n\n  visitZeroOrOnePath(path: Path.ZeroOrOnePath, arg?: TArg): TOut {\n  }\n}\n```\n\nThe type arguments are optional. `TOut` defaults to `void` and `TArg` defaults to `unknown`.\n\nSee the classes [`ToSparqlPropertyPath`](src/lib/toSparql.ts) and [`FindNodesVisitor`](src/lib/findNodes.ts)\nfor inspiration\n\nTo start visiting path nodes:\n\n```ts\nlet pathNode: GraphPointer\nconst visitor = new MyVisitor()\n  .visit(Path.fromPointer(pathNode)/*, optional initial arg */)\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhypermedia-app%2Fclownface-shacl-path","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhypermedia-app%2Fclownface-shacl-path","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhypermedia-app%2Fclownface-shacl-path/lists"}