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 🙏

© 2026 – Pkg Stats / Ryan Hefner

@concord-consortium/codap-plugin-api

v0.2.0

Published

An API to ease the development of CODAP plugins

Readme

CODAP Plugin API

Using as a library

This npm library provides two main files that will aid in interfacing with the CODAP Data Interactives API. codapInterface.ts sets up some basic functions for interfacing with CODAP, while codap-helper.ts contains more specialized functions utilizing the CODAP Data Interactive API for a variety of different purposes. Find the full documentation of the CODAP Data Interactive API here.

Installing and usage

In the directory of your plugin project, run npm install @concord-consortium/codap-plugin-api.

In myComponent.js:

import { initializePlugin } from "@concord-consortium/codap-plugin-api";

const myComponent = () => {
  useEffect(() => {
    initializePlugin({
      pluginName: "myPlugin",
      version: "1.0.0",
      dimensions: {
        width: 300,
        height: 400
      }
    }).catch(error => console.warn("could not connect to CODAP", error));
  }, []);
};

Everything the package exports is a named export, including codapInterface for the lower-level request API.

For more examples of how to use the npm package, see the CODAP Plugin Starter Project.

Connecting to CODAP

initializePlugin() performs a handshake with CODAP and resolves with whatever state CODAP had saved for your plugin. If nothing answers that handshake within about two seconds, it rejects and the connection is marked closed, so requests issued afterwards are refused immediately rather than each waiting out the request timeout below.

Two seconds is not proof CODAP is absent. A CODAP that is alive but slow to finish the underlying handshake looks the same from here, so treat a rejection as "not connected yet" rather than "not running inside CODAP":

initializePlugin(options)
  .catch(error => console.warn("could not connect to CODAP", error));

If that CODAP does answer afterwards, the connection reopens by itself and requests resume — but the promise above stays rejected, and the saved state that late answer carried is not delivered. So a plugin that restores saved state should call initializePlugin() again rather than rely on the reopen; otherwise it will answer CODAP's next request for its state with nothing, overwriting what was stored. codapInterface.getConnectionState() reports "preinit", "active" or "closed", and is the only way to observe either the close or the reopen.

codapInterface.destroy() closes the connection deliberately, and that one does not reopen; initializePlugin() can be called again to reconnect.

Request timeouts

A request waits up to 60 seconds for CODAP to respond before it is rejected. Requests over large datasets can legitimately take many seconds — creating several thousand items, say — so the deadline is deliberately generous, and exists only so that a CODAP which never responds at all (page closed, iframe removed) cannot leave a caller waiting forever.

codapInterface.getRequestTimeout();        // 60000
codapInterface.setRequestTimeout(120000);  // allow longer for very large requests

setRequestTimeout applies to requests issued after the call; requests already in flight keep the value they were given. A non-finite or non-positive value falls back to the default.

sendRequest accepts an optional callback in addition to returning a promise. The callback receives CODAP's response, or undefined when the request failed — note that this is distinct from a response of { success: false }, which means CODAP answered and declined.

It is invoked exactly once, synchronously as the promise settles. That is before any .then or await continuation on the same request, since those are microtasks: if you use both, the callback runs first.

In TypeScript the callback's parameter has to admit that absence. The exported RequestCallback type is (response?: IResult, request?: any) => void, so a callback declared to take IResult alone does not compile — it is the one that throws when a request fails:

codapInterface.sendRequest(message, (result?: IResult) => {
  if (!result) { return; }        // the request failed; the promise rejects with the reason
  if (result.success) { /* ... */ }
});

CODAP answers a batched request — an array of requests — with an array of results, so its callback takes that array instead, as BatchRequestCallback. Which shape is required follows from the message: passing an array requires the array form, and passing a single request rejects it. A callback can therefore never be paired with a response it is unable to read.

codapInterface.sendRequest([firstRequest, secondRequest], (results?: IResult[]) => {
  if (!results) { return; }       // the request failed; the promise rejects with the reason
  results.forEach(result => { /* ... */ });
});

sendRequest returns a promise whether or not a callback is passed, and that promise rejects with an Error on the same failures the callback reports as undefined: the request exceeded its deadline, CODAP answered with no value, there was no connection to send it on, or the request could not be sent at all — an uncloneable value in the message, say. A request issued before initializePlugin() is called, or after codapInterface.destroy(), is refused rather than sent, and rejects at once. A rejected promise with nothing attached to it becomes an unhandled rejection, so handle the promise even when the callback is doing the real work:

codapInterface.sendRequest(message, result => { /* ... */ })
              .catch(error => console.warn("request failed", error));

The deadline applies to each request separately, so a helper that chains several can take a multiple of it. createCollectionFromAttribute is the case to know about: when it has to find an unused name for the new collection it searches for one suffix at a time, up to 104 requests in sequence, so its worst case is that multiple of the deadline. That worst case needs a document already holding a hundred similarly-named collections; the ordinary path is four requests.

Exceptions thrown by your callback

If the callback you pass to sendRequest throws, the exception is rethrown as an uncaught error rather than being logged and discarded. By the time the callback runs its request has already settled, so what it throws is a bug in the callback and not a failure of the request — it does not reject the promise, and the request's outcome is unaffected.

It stays loud on purpose. Logging it instead would hide a bug in your plugin from window.onerror and from whatever error reporting you rely on. If you pass callbacks that can throw, handle the error inside the callback.

The classic version of this is reading result.success on the undefined a callback receives when a request fails. In TypeScript RequestCallback makes that a compile error instead, which is the whole reason the parameter is typed; in JavaScript it remains a TypeError at runtime, on whichever failure path reaches it first.

Development

Building

To build a local version, run npm run build. tsc compiles the sources into build/, then rollup bundles them into the package's entry points at the repository root: codap-plugin-api.js and the type declarations in codap-plugin-api.d.ts. The bundle is generated at build time and git-ignored; the declarations are committed, so a change affecting the public API needs a build and the regenerated codap-plugin-api.d.ts committed with it, rather than hand-edited.

Notes

  1. Make sure if you are using Visual Studio Code that you use the workspace version of TypeScript. To ensure that you are open a TypeScript file in VSC and then click on the version number next to TypeScript React in the status bar and select 'Use Workspace Version' in the popup menu.

Releasing

To release a new version:

  1. Make sure you have an npm publishing account under the concordconsortium namespace.

  2. Update the version number in package.json, and add a CHANGELOG.md entry for it.

  3. Run npm install to update the version number in package-lock.json.

  4. Run npm run build, and commit codap-plugin-api.d.ts if the build changed it.

  5. Run npm publish --access public to publish the new version.

Publishing runs prepublishOnly first, which builds, checks that the committed codap-plugin-api.d.ts matches what the build produces, then tests and lints. A failure in any of those aborts the publish, so step 4 exists to get the regenerated declarations committed rather than to make the build happen — publishing would rebuild anyway, and then refuse to continue because the working tree no longer matched the repository.

License

CODAP Plugin API are Copyright 2018 (c) by the Concord Consortium and is distributed under the MIT license.

See license.md for the complete license text.