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

@getodk/xpath

v0.3.0

Published

XPath implementation for ODK Web Forms

Downloads

74

Readme

@getodk/xpath

An XPath evaluator, with intent to support:

  • Full XPath 1.0 syntax and behavior
  • Context-based extensibility of behavior beyond the XPath 1.0 function library
  • ODK XForms XPath extensions
  • Non-browser environments (provided a subset of standard DOM APIs)
  • Minor accommodations for certain Enketo function aliases:
    • date-time is an alias of date, which is assumed to be additive
    • format-date function is an alias of format-date-time, which differs from the ODK XForms spec

Install

Install this package and its required peer dependencies with npm (or the equivalent command for your preferred package manager):

npm install @getodk/tree-sitter-xpath @getodk/xpath web-tree-sitter

Usage

To use @getodk/xpath at runtime, first create an XFormsXPathEvaluator instance, specifying the XForm rootNode. Usage from that point is API-compatible with the standard DOM evaluate method.

import { XFormsXPathEvaluator } from '@getodk/xpath';

// Given an XForms DOM document...
declare const xform: XMLDocument;

const evaluator = new XFormsXPathEvaluator({ rootNode: xform });

// A namespace resolver is optional, and the context node can be used (which is the default)
const nsResolver: XPathNSResolver = xform;

const result: XPathResult = evaluator.evaluate(
  '/root/...',
  xform,
  nsResolver,
  XPathResult.ORDERED_NODE_ITERATOR_TYPE
);

For typical XForms usage, rootNode will be either an XForm XMLDocument or its primary instance Element.

For XPath 1.0 functionality without XForms extension functions, you may use Evaluator the same way, and rootNode is optional:

import { Evaluator } from '@getodk/xpath';

const evaluator = new Evaluator();

// ...

In either case, the result returned by evaluate is API-compatible with the standard DOM XPathResult.

XForms itext translations

XFormsXPathEvaluator supports the JavaRosa itext function (jr:itext by convention), as specified in ODK XForms, which says:

Obtains an itext value for the provided reference in the active language from the <itext> block in the model.

This active language state is managed at the XFormXPathEvaluator instance level, with the default language (again as specified in ODK XForms) active on construction. You can access a form's available languages, and get or set the active language under the XFormXPathEvaluator.translations object.

Example:

const domParser = new DOMParser();
const xform: XMLDocument = domParser.parseFromString(
  `<h:html>
  <h:head>
    <model>
      <itext>
        <translation lang="English" default="true()">
          <text id="hello">
            <value>hello</value>
          </text>
        </translation>
        <translation lang="Español">
          <text id="hello">
            <value>hola</value>
          </text>
        </translation>
      </itext>
    </model>
  </h:head>
  <!-- ... -->
</h:html>`,
  'text/xml'
);
const evaluator = new XFormsXPathEvaluator({ rootNode: xform });

evaluator.translations.getLanguages(); // ['English', 'Español']
evaluator.translations.getActiveLanguage(); // 'English'
evaluator.evaluate('jr:itext("hello")', xform, null, XPathResult.STRING_TYPE).stringValue; // 'hello'

evaluator.translations.setActiveLanguage('Español'); // 'Español'
evaluator.translations.getActiveLanguage(); // 'Español'
evaluator.evaluate('jr:itext("hello")', xform, null, XPathResult.STRING_TYPE).stringValue; // 'hola'

There are currently a few caveats to jr:itext use:

  • <itext> and its translations are evaluated from the document root of the rootNode specified in XFormsXPathEvaluator options. As such:

    • translations will be resolved if a descendant rootNode (e.g. the XForm's primary <instance> element) is specified

    • translations will not be resolved for an XForm in an unusual DOM structure (e.g. a DocumentFragment, or in an arbitrary subtree of an unrelated document)

  • Translations are treated as static, and cached during construction of XFormsXPathEvaluator. This is based on typical usage, wherein an XForm definition itself is expected to be static, but it will not (yet) support use cases like authoring an XForm definition.

  • <value form="...anything..."> is not yet supported. It's unclear what the interface for this usage might be.

  • The interface for getting and setting language state is currently experimental pending integration experience, and may be changed in the future. The intent of this interface is to be relatively agnostic to outside state management, and to isolate this sort of stateful context from the XForm DOM, but that approach may also change.

Convenience APIs

Both evaluator classes provide the following convenience methods:

  • evaluateBoolean(expression: string, options?: { contextNode?: Node }): boolean
  • evaluateNumber(expression: string, options?: { contextNode?: Node }): number
  • evaluateString(expression: string, options?: { contextNode?: Node }): string
  • evaluateNode(expression: string, options?: { contextNode?: Node }): Node | null
  • evaluateNodes(expression: string, options?: { contextNode?: Node }): Node[]

(Also provided: evaluateElement and evaluateNonNullElement, but these are not type safe so use them at your own risk.)

Supported/tested environments

  • Browsers (latest versions):
    • Chrome/Chromium-based browsers (tested only in Chromium)
    • Firefox
    • Safari/WebKit (tested in WebKit directly)
  • Non-browser runtimes with a DOM compatibility environement:
    • Node (current/LTS; tested with jsdom). Node DOM compatibility does not require any native extensions for. DOM compatibility does not require any underlying XPath evaluation functionality (though it does currently rely on global constants like XPathResult).
    • Other runtimes and DOM compatibility libraries are not currently tested, support is unknown.

Known issues and incompatibilities

XPath 1.0

  • Expressions with variable references are parsed incorrectly. There is also no API to provide variable values, even if variable references were supported.

ODK XForms

We intend to support the full ODK XForms function library, but support is currently incomplete. The following functions are not yet supported (the jr: prefix is used by convention to refer to the JavaRosa namespace):

  • pulldata
  • jr:choice-name

Non-browser environments

The DOM standard is very complex, and compatibility libraries sometimes differ in terms of standards compliance and compatibility with the major browser implementations. It is likely, perhaps inevitable, that there will be edge cases and minor compatiblity issues when using such a library. At time of writing, no such issues are known in our tested environments, but a couple of minor issues have already been found and fixed during the initial test setup.