Linked Data API for JavaScript

Overview

rdflib.js

NPM Version Join the chat at https://gitter.im/linkeddata/rdflib.js

Javascript RDF library for browsers and Node.js.

  • Reads and writes RDF/XML, Turtle and N3; Reads RDFa and JSON-LD
  • Read/Write Linked Data client, using WebDav or SPARQL/Update
  • Real-Time Collaborative editing with web sockets and PATCHes
  • Local API for querying a store
  • Compatible with RDFJS task force spec
  • SPARQL queries (not full SPARQL - just graph match and optional)
  • Smushing of nodes from owl:sameAs, and owl:{f,inverseF}unctionProperty
  • Tracks provenance of triples keeps metadata (in RDF) from HTTP accesses

Documentation

See:

for more information.

Install

Browser (using a bundler like Webpack)

npm install rdflib

Browser (generating a <script> file to include)

git clone [email protected]:linkeddata/rdflib.js.git;
cd rdflib.js;
npm install;

Generate the dist directory

npm run build:browser

Node.js

Make sure you have Node.js and Node Package Manager (npm) installed.

npm install --save rdflib

Contribute

Subdirectories

  • dist: Where the bundled libraries are built. Run npm run build to generate them.
  • test: Tests are here.
  • lib: Transpiled, non-bundled library is built here when the library is published to npm.

Dependencies

- XMLHTTPRequest (Node.js version)

Thanks

Thanks to the many contributors who have been involved along the way. LinkedData team & TimBL

LICENSE

MIT

Comments
  • Build with npm

    Build with npm

    Makefile scripts converted to npm scripts. The scripts from the main and the tests/serialize makefile have been converted and added as npm scripts. Added four small libraries for the required functionalities "grep-cli": "^0.2.1" for grep "node-mkdirp": "0.0.1" for mkdir "rimraf": "^2.6.1" for rm -rf "jsdiff-cli": "^0.1.2" for diff

    This should allow the build and testing process to be platform-independent. All the scripts are working as intended and so are the tests as far as I can tell.

    opened by Neur0mante 41
  • Move solid-auth-client to peerDependencies

    Move solid-auth-client to peerDependencies

    With solid-auth-client as a regular dependency, npm will give rdflib its own instance of it when its version no longer aligns with the one the app itself has installed. This can happen e.g. in the following situation:

    npm install solid-auth-client;
    npm install rdflib;
    # <New version of solid-auth-client is released>
    npm upgrade solid-auth-client;
    

    Now node_modules/rdflib/node_modules/solid-auth-client contains a copy that is still pinned to the older version, whereas node_modules/solid-auth-client was upgraded.

    I'm quite sure that that's what will happen, and that that's not intended to happen, but to be completely sure I'm also pinging @dmitrizagidulin, who I think knows most of solid-auth-client (?), and @RubenVerborgh, who added the warning about multiple instances of solid-auth-client being loaded.

    Edit: For those coming across later and not wanting to read the entire back-and-forth between Ruben and me below, the summary is that Ruben is not against merging this, but thinks that it will not completely solve the problem, and thinks that peerDependencies are not meant for this use case.

    opened by Vinnl 23
  • RDF JS Object Parsing.

    RDF JS Object Parsing.

    Currently RDFlib can facilitate creation of graphs / quads etc when specifically called.

    But there's no simple way to faciliate building an RDF of a vocab (eg: Album) from a JS object representing the same.

    This makes RDFlib developer experience is lacking, IMO. And I'm not alone here

    In an effort to improve this, I've been thining of what I, as a developer would want to make creation of RDF data as easy as possible. This is a suggestion for that, and hopefully it'll trigger some discussion.

    And just to note: I am super down for helping build this out / getting a PR in, but I wanted to present the idea, check it's validity and that it's something that would be wanted for this lib (obviously, I think it probably should be here.)

    Background: This came about as part of my thinking to improve our data APIs on the SAFE Network (where we want RDF data to be standard for simple app interop).

    Automatically parse objects to a given vocab map.

    The idea is to provide a map of keys for a given vocab (eg, Album), which can be used (and could include those that must be used). Ie, what we have available here: https://schema.org/MusicAlbum , just setup for RDFlib to parse easily.

    This would then allow any 'normal' javascript object to be converted to RDF using this map.

    eg:

    
    const ourAlbum = {
        title: 'Muswell Hillbillies'
        uri: 'http://whereever/albums/muswellhilllbillies'
        img: 'http://whereever/some.jpg'
    }
    
    

    If we import a vocab-map (loose term for now... probably something better there), we could apply it to automatically generate an RDF object (using rdflib's current main functionality in the background):

    // here we imagine a set of vocabs defined for this use
    import {Album} from 'schema.org-schema'; 
    // definitely a better name needed...
    rdflib.parseObjectWithSchema(ourAlbum, Album);
    
    

    And the data parsing simply maps the object keys to vocab properties and applies namespacing etc.

    Why is this interesting?

    Current implementation and setup of RDF requires intimate knowledge of vocabs, RDF namespaces and rdflib.js, it is cumbersome (writing full urls for namespaces), and for writing JS objects, largely boilerplate.

    It facilitates creation of common schemas (while not limiting anyone to go further or straight to RDFlib).

    It is simple.

    It's extenable. With a standard/simple library any schema could be exported in this fashion.

    At the end of the day, we have more RDF data (created by our app developer, who could easily get going with RDFlib ), than we otherwise would have.

    Bonus features:

    This set up could even enable type checking vs the defined schema.

    It could also provide a simple man interface for a given schema to see what properties are available therein.

    With a limited set of available maps in this manner (I'd start only with schema.org... YMMV) it could help prevent decision paralysis (which I certainly experience when trying to choose a vocab).

    opened by joshuef 20
  • parsing a html+RDFa resource containing datatype=

    parsing a html+RDFa resource containing datatype="rdf:HTML" return 500

    message: 'Error translating between RDF formats', status: 500

    Sarven Capadisli
    @csarven
    12:46
    @bourgeoa
    
    HTTP/2 500 
    date: Tue, 23 Mar 2021 11:52:46 GMT
    server: Apache/2.4.29 (Ubuntu)
    x-powered-by: solid-server/5.6.6
    vary: Accept,Authorization,Origin
    access-control-allow-credentials: true
    access-control-expose-headers: Authorization, User, Location, Link, Vary, Last-Modified, ETag, Accept-Patch, Accept-Post, Updates-Via, Allow, WAC-Allow, Content-Length, WWW-Authenticate, MS-Author-Via, X-Powered-By
    allow: OPTIONS, HEAD, GET, PATCH, POST, PUT, DELETE
    link: <linked-research-decentralised-web.acl>; rel="acl", <linked-research-decentralised-web.meta>; rel="describedBy", <http://www.w3.org/ns/ldp#Resource>; rel="type"
    wac-allow: user="read",public="read"
    ms-author-via: SPARQL
    updates-via: wss://csarven.ca
    content-type: text/plain; charset=utf-8
    content-length: 38
    etag: W/"26-QBI7EYtDt6zJzGYxyvtGCQrnW5Q"
    
    Error translating between RDF formats
    
      solid:get /linked-research-decentralised-web on csarven.ca +12s
      solid:handlers GET -- Reading /var/www/solid/data/linked-research-decentralised-web$.html +12s
      solid:get error translating: /linked-research-decentralised-web text/html -> text/turtle -- 500 Cannot read property 'htmlOptions' of undefined +426ms
      solid:server Error page because of: [HTTPError: Error translating between RDF formats] {
      name: 'HTTPError',
      message: 'Error translating between RDF formats',
      status: 500
    
    Sarven Capadisli
    @csarven
    14:12
    The request was: curl -iH'Accept: text/turtle' https://csarven.ca/linked-research-decentralised-web
    Alain Bourgeois
    @bourgeoa
    15:48
    
    @csarven
    Thanks for giving the link url.
    I run the html+RDFa tests using a copy of your big file :
    
        test failed
        run test on part of your file : test succeed until it encounters : datatype="rdf:HTML"
        test succeed when replacing every datatype="rdf:HTML" with "rdf:html"
    
    What is the correct parameter ? I suppose the problem lies in rdflib.js or a dependency. Is lowercase rdf:html acceptable ?
    Alain Bourgeois
    @bourgeoa
    15:58
    When I say that the test succeed it is that there is no error translating between RDF formats. I did not check that all expected elements are returned.
    
    opened by bourgeoa 16
  • problem with loading of multiple asynchronous files

    problem with loading of multiple asynchronous files

    I think i found a bug when i'am trying to load 3 foaf profiles with rdflib.js.

    The bug is in the bug/js/loader.js file at line 34

    Only the content from the last URI is loaded in the DOM it seems, even though all 3 uris are fetched as witenessed by the traffic on Wireshark.

    var FOAF = $rdf.Namespace("http://xmlns.com/foaf/0.1/");
    var RDF = $rdf.Namespace("http://www.w3.org/1999/02/22-rdf-syntax-ns#");
    var RDFS = $rdf.Namespace("http://www.w3.org/2000/01/rdf-schema#");
    var OWL = $rdf.Namespace("http://www.w3.org/2002/07/owl#");
    var DC = $rdf.Namespace("http://purl.org/dc/elements/1.1/");
    var RSS = $rdf.Namespace("http://purl.org/rss/1.0/");
    var XSD = $rdf.Namespace("http://www.w3.org/TR/2004/REC-xmlschema-2-20041028/#dt-");
    var CONTACT = $rdf.Namespace("http://www.w3.org/2000/10/swap/pim/contact#");
    
    graphs = {};
    
    var foaf_profiles = ["http://b4mad.net/FOAF/goern.rdf#goern","http://bigasterisk.com/foaf.rdf#drewp","http://crschmidt.net/foaf.rdf#crschmidt"];
    
    function initialize() {
        $rdf.Fetcher.crossSiteProxyTemplate="http://data.fm/proxy?uri={uri}";
        for(var i=0 ; i < foaf_profiles.length ; i++){
            load_foaf_profile(foaf_profiles[i])
        }
    }
    
    /**
     * This function load all foaf profile and put her in the HTML5 DOM
     * @param profile_uri
     */
    function load_foaf_profile(profile_uri){
        var profile = $rdf.sym(profile_uri);
        var docURI = profile_uri.slice(0, profile_uri.indexOf('#'));
        var kb = graphs[docURI];
        if (!kb) {
            kb = graphs[docURI] = new $rdf.IndexedFormula();
        }
        var fetch = $rdf.fetcher(kb, undefined, true);
        fetch.nowOrWhenFetched($rdf.Fetcher.crossSiteProxy(docURI), undefined, function(){ 
             // <---- here is the issue this function is not excuted with the 2 first URI.
    
            var profile_picture = kb.any(profile, FOAF('img'));
            if(profile_picture == undefined){
                profile_picture = kb.any(profile, FOAF('depiction'));
            }
    
            if(profile_picture != undefined){
                // Export data in dom.
                $('#list').append('<p><img src="' + profile_picture.uri + '" alt="img" />' + kb.any(profile, FOAF('name')) +' </p>');
            }
        });
    }
    

    I post the code at : http://grayfoxkiller.free.fr/bug.zip

    opened by rblin 15
  • Problems with types

    Problems with types

    I've done some initial work to try to fix type errors that pop up when using the work on the master branch (e.g. for Solid UI it outputs 209 errors).

    Doing this I came across a couple of issues that can be split into two groups of problems:

    1. Classes that do not extend their parents properly
    2. Difficulties in combining types that relate to RDFJS and RDFLIB.

    Problem 1

    For problem 1 I've had a talk with Tim, and he thinks some reasonable fixes are:

    • To match the signature of Formula#add conform to IndexedFormula#add
    • To rename IndexedFormula#compareTerm to IndexedFormula#compareTerms

    We didn't go into depth of other inconsistencies, but it seems that we will have to break some APIs in any case if we want to fix this properly (currently we've used @ts-ignore to tell TypeScript to ignore them - but this won't fix the problem for projects that depend on this classes, as TypeScript will complain about inconsistent types here as well, which leads to a lot of unneccessary uses of ts-ignore).

    Problem 2

    Problem 2 seems to come from the fact that many outputs from methods now uses the types described in the RDF/JS Data model specification (I'll just refer to them as RDF/JS types later in this text) instead of the corresponding rdflib ones, and this causes errors for projects such as Solid UI which has worked with rdflib types till now. This has been discussed earlier at https://github.com/linkeddata/rdflib.js/issues/374 when @joepio worked on this earlier (and an even older thread at https://github.com/linkeddata/rdflib.js/issues/137).

    The data model types exported from rdflib (e.g. by doing import { NamedNode } from 'rdflib') still refer to rdflib types, which causes type errors for projects such as Solid UI (one fix is to change all type references to the corresponding RDF/JS types, e.g. by doing import { NamedNode } from 'rdflib/src/tf-types', but I think this is a very bad solution.

    One simple fix is to make sure that the types that TypeScript refers to are in fact referring to the RDF/JS types. But this will make it more difficult for people to use the extra functionality that is available in the rdflib types.

    Now, Tim wants to make sure that types returned from rdflib are using the rdflib types (e.g. so that developers have access to some extra methods, such as toNT, doc, and site).

    Since they are a superset, one theory of mine was to allow RDF/JS types as input for all relevant methods, but that they output rdflib types. The idea is that developers who want to work with RDF/JS can still do it (since rdflib types are a superset of RDF/JS types).

    I started doing this work on a branch called fix-types-hybrid for those who want to review what I tried doing (note: it's incomplete code, so it won't run and there are bugs that I would've fixed before a proper PR).

    When working on the branch I realized I was getting into a mess, and decided to raise these issues to get some feedback on how we might want to go about this.

    Another challenge I found wrt types are unions that are different between RDF/JS and rdflib. E.g. for rdflib we have a type that describes which types we allow for many parameters that expect an object (as in subject-predicate-object):

    type ObjectType = RDFlibNamedNode | RDFlibLiteral | Collection | RDFlibBlankNode | RDFlibVariable | Empty
    

    While in RDF/JS it looks like the following:

    type Quad_Object = NamedNode | BlankNode | Literal | Variable | Term
    

    Please don't mind the different names, the important difference here are the types that don't match in the unions; rdflib allows for Collections and Empty, while RDF/JS allows for Term. I think we might still get the concept to work (allow for RDF/JS types, output rdflib types), but I think it needs a bit of work.

    I also tried to work on another branch where the idea was to remove the RDF/JS types altogether, but I realized it was going to require a lot of work and I didn't want to revert the work of @joepio and @fletcher91 before having a discussion on this. For those interested in reviewing that work, it's available on branch fix-types-rdflib.

    Hope to get feedback on this from @timbl, @RubenVerborgh, perhaps @dmitrizagidulin and @elf-pavlik who has been involved in these discussions earlier, as well as @joepio and @fletcher91 in case I forgot something in this issue, and @Vinnl who helped me with my thinking on TypeScript types earlier.

    opened by megoth 14
  • Unable to serialize to JSON-LD

    Unable to serialize to JSON-LD

    Using 1.0.4 (and N3 1.2.1) I'm getting TypeError: n3Writer.addTriple is not a function when trying to serialize to JSON-LD. (The other formats work correctly.)

    Seems that addQuad needs to be used. Can't see any tests about serializing to JSON-LD either.

    opened by thewilkybarkid 14
  • make rdflib use established prefixes (e.g., `xsd:` instead of `XML:`)

    make rdflib use established prefixes (e.g., `xsd:` instead of `XML:`)

    Would be cool if we could make rdflib use established prefixes as well

    Originally posted by @angelo-v in https://github.com/solid/solid-panes/pull/277#issuecomment-768831562

    The pull request linked above changed --

    @prefix    xsd: <http://www.w3.org/2001/XMLSchema#> .
    

    -- in one file, to --

    @prefix    XML: <http://www.w3.org/2001/XMLSchema#> .
    

    -- to match a number of instances of the latter.

    I suggested that the fix should have been in the other direction, but apparently the XML: prefix is used by rdflib, and inherited from there in many places.

    My go-to reference site for prefix common use is prefix.cc, which apparently doesn't support capitalized prefixes at all (which may tell us something). It does have a lowercase xml: prefix, but that doesn't match the above expansion. xs: and xsd: both do match the above; in my experience, the latter is more commonly used.

    It appears there will be multiple pull requests, in part because this issue crosses multiple repos, needed to change everything relevant from XML: to xsd:.

    opened by TallTed 13
  • Skip n3 parser for jsonld

    Skip n3 parser for jsonld

    This removes the n3 library step when parsing json-ld.

    I made two prior commit in order to make it possible for the implementation to use the proper rdfjs task force data factory methods on formula/indexed formula/store.

    opened by rescribet 13
  • Silently ignore statements that cannot be deleted

    Silently ignore statements that cannot be deleted

    Currently, an error is returned when non-existing triples are being removed, however, according to the spec it should just have no effect.

    For DELETE DATA

    Note that the deletion of non-existing triples has no effect, i.e., triples in the QuadData that did not exist in the Graph Store are ignored.

    And for DELETE

    Analogous to DELETE/INSERT, deleting triples that are not present, or from a graph that is not present will have no effect and will result in success.

    From https://www.w3.org/TR/sparql11-update/#deleteData

    This PR thus changes the behavior so deleting non-existing triples does not have any effect.

    bug 
    opened by smessie 12
  • Run all scripts and build dist on Travis

    Run all scripts and build dist on Travis

    This pull request introduces two major changes:

    • npm scripts are reorganized and renamed for consistency
    • the dist folder is removed (and ignored) from master, and instead built automatically by Travis in another branch

    On every push to master, an automated commit to the other branch is performed, labeled Update to {commit_hash}. This avoids the need of the heavy dist folder in the development code, reducing weight and merge conflicts.

    Open question: to what branch should the automatic build be pushed?

    1. to gh_pages: in this case, I propose clearing the existing gh_pages branch. Benefit: the resulting script is accessible from http://linkeddata.github.io/rdflib.js/dist/rdflib.min.js
    2. to another branch: in this case, could you suggest a name?
    enhancement 3 - Done 
    opened by RubenVerborgh 10
  • add @context to jsonld serialization

    add @context to jsonld serialization

    Actually the serialize display of jsonld is not easily readable by humans. The idea is to add a @context that will includes the equivalent to turtle @prefix.

    opened by bourgeoa 2
  • Rdflib accidentally sends out map headers

    Rdflib accidentally sends out map headers

    See: https://github.com/SolidOS/mashlib/issues/133 "When Mashlib makes requests, it accidentally sends out an HTTP header map: [object Object]. I presume a map function is being attached by accident.

    This can result in CORS issues if the server does not allow map header access. " And: https://github.com/SolidOS/solidos/issues/79

    bug 
    opened by timea-solid 0
  • error in some compilation contexts (due to circular dependencies?)

    error in some compilation contexts (due to circular dependencies?)

    I'm experiencing a bug in an edge case of compilation. The issue can be boiled down the to following example:

    git clone https://github.com/eroux/mwe-bug2.git
    cd mwe-bug2
    yarn build
    yarn link
    cd ..
    git clone https://github.com/eroux/mwe-bug3.git
    yarn link mwe-bug2
    yarn start
    

    the compilation works but then the app crashes in the browser with a cryptic error message

    Uncaught TypeError: Super expression must either be null or a function

    This only happens when using yarn link. Note also that changing

    export const defaultGraphNode = new rdf.NamedNode(rdf.Store.defaultGraphURI)

    into

    export const defaultGraphNode = rdf.sym(rdf.Store.defaultGraphURI)

    makes everything work for some reason.

    According to this so answer, this could be due to circular dependencies. I checked rdflib with dpdm and it reports 97 circular dependencies in rdflib.js.

    In my experience, fixing this is not particularly straightforward, but if a PR can be accepted, I might give it a try

    opened by eroux 0
Releases(v2.2.18)
Owner
Read-Write Linked Data
Originally MIT code. Everything must be MIT Licence.
Read-Write Linked Data
Actionhero is a realtime multi-transport nodejs API Server with integrated cluster capabilities and delayed tasks

Actionhero The reusable, scalable, and quick node.js API server for stateless and stateful applications NPM | Web Site | Latest Docs | GitHub | Slack

Actionhero 2.3k Dec 29, 2022
🚀 A RESTful API generator for Node.js

A RESTful API generator rest-hapi is a hapi plugin that generates RESTful API endpoints based on mongoose schemas. It provides a powerful combination

Justin Headley 1.2k Dec 31, 2022
In-memory filesystem with Node's API

In-memory filesystem with Node's API

Vadim Dalecky 1.4k Jan 4, 2023
wolkenkit is an open-source CQRS and event-sourcing framework based on Node.js, and it supports JavaScript and TypeScript.

wolkenkit wolkenkit is a CQRS and event-sourcing framework based on Node.js. It empowers you to build and run scalable distributed web and cloud servi

the native web 1.1k Dec 26, 2022
🔱 Javascript's God Mode. No VM. No Bytecode. No GC. Just native binaries.

Javascript's God Mode: one language to rule them all. Code everything, everywhere, for everything, in JavaScript. No VM. No Bytecode. No packaging. No

null 3.4k Dec 31, 2022
Linked Data API for JavaScript

rdflib.js Javascript RDF library for browsers and Node.js. Reads and writes RDF/XML, Turtle and N3; Reads RDFa and JSON-LD Read/Write Linked Data clie

Read-Write Linked Data 527 Jan 1, 2023
Hemsida för personer i Sverige som kan och vill erbjuda boende till människor på flykt

Getting Started with Create React App This project was bootstrapped with Create React App. Available Scripts In the project directory, you can run: np

null 4 May 3, 2022
Kurs-repo för kursen Webbserver och Databaser

Webbserver och databaser This repository is meant for CME students to access exercises and codealongs that happen throughout the course. I hope you wi

null 14 Jan 3, 2023
Add linked data to the YAML of your Obsidian notes.

Obsidian Linked Data Vocabularies Plugin This plugin adds YAML keys for the selected heading, url (optional), and broader, narrower and related headin

null 53 Oct 13, 2022
LINCD.js - Linked Interoperable Code & Data

LINCD.js Javascript library implementing the LINCD protocol. About LINCD Even today, most projects use their own data structures, thus creating data s

Semantu 9 Dec 28, 2022
Light-weight Linked Open Data native cataloguing and crowdsourcing platform

id name brief-description type release-date release-number work-package keywords licence release link demo running-instance credits clef CLEF, Crowdso

Polifonia H2020 10 Apr 26, 2022
This is email scheduler made using MERN. This repo contains server code, client repo is linked in readme.

Email Scheduler Client This is an email scheduler server (client in different repository). It is made using node.js/express.js. Overview User can sign

Sai Charan 2 Dec 3, 2022
This is an email scheduler made using MERN stack. This repo contains client, server side is linked in readme

Email Scheduler Client This is an email scheduler client (server in different repository). It is made using react. Overview User can sign-up/sign-in,

Sai Charan 3 Dec 3, 2022
This Plugin is For Logseq. If you're using wide monitors, you can place journals, linked references, and journal queries side by side.

Logseq Column-Layout Plugin Journals, linked references, and journal queries can be placed side by side if the minimum screen width is "1850px" or mor

YU 14 Dec 14, 2022
Creates a table of contents in a DOM element optionally linked to with anchors. No jQuery or other dependencies.

HTML-Contents Creates a table of contents in a DOM element optionally linked to with anchors. No dependencies. @psalmody Get It We're on npm: npm i ht

Michael Tallino 3 Oct 25, 2022
A front-end only implementation of linked template cards for Lovelace

Linked Lovelace by @daredoes A Javascript/Websocket way to do templating in the Lovelace UI Support Hey you! Help me out for a couple of ?? or a ☕ ! F

Daniel Evans 13 Dec 12, 2022
This project is built with JavaScript, Webpack, HTML & CSS, Leaderboard api. When user clicks on Refresh button it hits the api and responds with the data, The user can also post data to the api

leaderboad Description the project. this project is about the leaderboad i did during Microverse to build a website for adding Data to the API and fet

Emmanuel Moombe 4 May 30, 2022
Make Your Company Data Driven. Connect to any data source, easily visualize, dashboard and share your data.

Redash is designed to enable anyone, regardless of the level of technical sophistication, to harness the power of data big and small. SQL users levera

Redash 22.4k Dec 30, 2022
JSON Visio is data visualization tool for your json data which seamlessly illustrates your data on graphs without having to restructure anything, paste directly or import file.

JSON Visio is data visualization tool for your json data which seamlessly illustrates your data on graphs without having to restructure anything, paste directly or import file.

Aykut Saraç 20.6k Jan 4, 2023
It shows an effective way to correct bus arrival information using data analytics based on Amazon Serverless such as Kiness Data Stream, Kinesis Data Firehose, S3, and Lambda.

Amazon Serverless를 이용한 실시간 버스 정보 수집 및 저장 본 github repository는 버스 정보를 주기적으로 수집하여 분석할 수 있도록, Amazon Serverless인 Amazon Kinesis Data Stream, Kinesis Data

John Park 4 Nov 13, 2022