npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

mdf-reader

v1.1.7

Published

Reads MDF YAML and returns helpful accessors

Downloads

91

Readme

mdf-reader

MDFReader is a small JavaScript class that will parse a set of model description files (MDF) to provide a set of convenient methods to iterate and navigate the described graph.

It complies with the MDF merge/"overlay" spec, and processes an arbitrary number of input YAML/JSON formatted strings:

let mdf = new MDFReader( model_yaml, model_props_yaml, model_terms_yaml, ... );

To avoid committing to a file access method in the module, you are on your own for acquiring those strings. See Usage below for synchronous and async examples.

MDFReader out of the box attempts to comply with the MDF spec. To add implementation-specific info to the MDF, you're encouraged to use the built-in Tags feature of MDF. See API below for tag accessors and finders.

However, MDF is flexible and you can add custom keys without invalidating it. To parse custom keys in MDFReader, you can add parsing hooks using the static function MDFReader.add_parse_hook(<function>). See API below for details and example.

Installation

$ npm install mdf-reader

Usage

const model = new MDFReader(yaml_string1, yaml_string2, ...);

Synchronous example:

const { MDFReader } = require('mdf-reader');
const fs = require('node:fs');

const yamls = [
  "./icdc-model.yml",
  "./icdc-model-props.yml",
];

const data = yamls.map( (fn) => {
  try {
    let dta = fs.readFileSync(fn);
    return dta.toString();
  } catch (err) {
    throw new Error(err);
  }
});

const model = new MDFReader(...data);
model.terms().forEach( (t) => {
    console.log("Value: %s\n  Definition: %s",
                t.value, t.definition);
});

Async example:

import axios from 'axios';
import { MDFReader } from 'mdf-reader';

var model;
const yaml_urls = [
  "https://github.com/CBIIT/ccdi-model/raw/main/model-desc/ccdi-model.yml",
  "https://github.com/CBIIT/ccdi-model/raw/main/model-desc/ccdi-model-props.yml",
  "https://github.com/CBIIT/ccdi-model/raw/main/model-desc/terms.yaml",
];

let ps = yaml_urls.map( (url) => {
  return axios.get(url)
    .then( (r) => { return r.data; } );
});

let p = Promise.all(ps)
    .then( (results) => {
      let dta = [];
      results.forEach( (result) => {
        dta.push(result);
      });
      return dta;
    })
    .then( (dta) =>
      { return new MDFReader(...dta); }
    )
    .catch( (e) => { throw new Error(e); } );

p.then( (model) => {
    model.nodes().forEach( (n) => {
        console.log("Node name: %s", n.handle);
        });
    });

Objects

Nodes, properties, edges, and terms are represented by plain JS Objects. Each Object has a set of standard keys which correspond to similar keys in MDF for each entity:

{ _kind = 'Node', handle, desc              } = node;
{ _kind = 'Edge', handle, src, dst, type    } = edge;
{ _kind = 'Property', handle, type, desc, 
  is_key, is_nullable, is_deprecated, 
  is_strict, owner                          } = prop;
{ _kind = 'Term', handle, desc,
  origin_name, origin_version, definition   } = term;

Property objects have an attribute owner, which points back to the Node or Edge object to which they belong.

In general, such Objects are returned by the API calls described below.

API

MDFReader instance methods:

  • nodes(), nodes('mynode'), nodes('a_node', ...)
  • edges(), edges('has_a', ...) (see below)
  • props(), props('prop1'), props('a_prop', ...)
  • terms(), terms('myterm'), terms('a_term", ...)

Without arguments, return as an Array the entire set of nodes, props, edges, or terms (in Object form above) for a model.

With one entity handle argument, return the Object or null if no such object exists.

With multiple handles as arguments, return an Array of the Objects corresponding to the handles.

  • edges(<edge type>)

In MDF, edges have a simple string "type" (such as member_of), a source or "from" node, and a destination or "to" node. The edges accessor will accept any number of edge types, and return all edges having those types.

  • outgoing_edges(node_handle)
  • incoming_edges(node_handle)

Return an Array of edges for which the specified node is the source or destination, respectively. For example, to get all outgoing edges for a node "sample" (i.e., all edges where sample is the source nodes), with the edge type "member_of":

const sample_member_of_x = model.outgoing_edges('sample')
                             .filter( (edge) => edge.type === 'member_of' );
  • tagged_items(key, value)

Returns an Array of Objects tagged in the MDF with the key-value pair, or an empty Array if no such tag exists. Use the _kind key to determine the entity type for each Object.

  • tag_kvs(), tag_kvs(key)

Returns an Array of Arrays of the form [key, value]. Without an argument, all [key, value] pairs present in the model are returned. With a key argument, pairs having the given key are returned.

Example: to get all nodes that have a Category tag:

 let cat_nodes = model.tag_kvs('Category')
    .flatMap( (kv) => model.tagged_items(...kv) );

Object own methods

  • node.props(), edge.props()

Return an Array of a node's or edge's properties, if any.

  • node.terms(), edge.terms(), prop.terms()

Return an Array of term Objects annotating these entities in the MDF.

  • prop.valueSet()

Return an Array of acceptable values if a property has an acceptable value list or "Enum" (i.e., if prop.type == 'value_set'). Note these are the actual values to be used in data, not the term handles. So, for this MDF:

    PropDefinition:
      breed:
        Enum:
          - beagle
          - german_shepherd
    Terms:
      beagle:
        Value: Beagle
      german_shepherd:
        Value: German Shepherd

the following would hold:

model.props('breed').valueSet() == ['Beagle', 'German Shepherd']
  • item.tags(), item.tags(<key>)

With no arguments, return an Array of [key, value] pairs tagging the item (node, property, or edge).

If a key is provided as a single argument, the tag value on that item for that key (if any) is returned as a string.

Custom parsing

  • MDFReader.add_parse_hook(function(){...})

Add custom parsing routines to the end of standard parsing. The function should be written considering this as the MDFReader instance under construction. Multiple calls to add_parse_hook add multiple hooks in order to the parse flow.

Example: Suppose your MDF has a non-standard Node key "Export", with an array of property handles as values. Standard parsing will not expose Node[node_handle]['Export'] in MDFReader, but you can add a parsing hook as follows:

MDFReader.add_parse_hook(
  function() {
    this.nodes()
      .forEach( (node) => {
        node.exports = () => {
          return this.mdf.Nodes[node.handle].Export ?
            this.mdf.Nodes[node.handle].Export : [];
        };
      });
    return this;
  });

Now, after parsing, node objects will have an own method exports() returning the custom array.

let mdf = new MDFReader( yaml_mdf_standard, yaml_mdf_custom );
mdf.node('study').exports()
  .forEach( (exported_prop) => { useProp(exported_prop); } );