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

njs-api

v0.0.2

Published

Nautral JavaScript Interface

Downloads

2

Readme

NJS API

Native JavaScript API for Bindings.

Introduction

NJS-API is a header-only C++ library that abstracts APIs of JavaScript virtual machines (VMs) into a single API that can be used by embedders to write bindings compatible with multiple JS engines. The motivation to start the project were breaking changes of V8 engine and the way V8 bindings are written (too verbose and too annoying to handle all possible error conditions). In addition, attempt to port node.js to use Chakra javascript engine instead of V8 contributed to the idea of creating a single API that can target multiple JS engines, not just V8.

Features

  • Source-level abstraction layer on top of native VM APIs - JavaScript engine cannot be replaced at runtime, recompilation is needed as NJS wraps VM dependent data structures and classes
  • High-performance interface that maps to native VMs as closely as hand-written bindings - the compiled code should be very close to a code you would have normally written for a particular VM
  • High-level interface to decrease the amount of code you need to write and to decrease the size of the resulting compiled code - no more boilerplate for each function you want to bind
  • Security is part of the contract - JavaScript code is not allowed to corrupt internals that the C++ side expects (function signatures, ...)
  • Comfortable and meaningful error handling - let users of your bindings know where the problem happened. NJS contains many helpers to make error handling less verbose yet still powerful on C++ side (it turns the most annoying type checks to one-liners)
  • Foundation to build extensions that can be used to implement concepts - data serialization and deserialization of custom user types

Disclaimer

NJS is a WORK-IN-PROGRESS that has been published to validate the initial ideas and to design the API with more people. The library started as a separate project when developing blend2d-js to make bindings easier to write, easier to maintain, and also more secure.

Base API

Classes provided by NJS-API:

  • njs::Result - 32-bit unsigned integer representing result-code
  • njs::Utf8Ref - Tagged reference to UTF-8 string
  • njs::Utf16Ref - Tagged reference to UTF-16 string
  • njs::Latin1Ref - Tagged reference to LATIN1 string
  • njs::Range<T> - Minimum and maximum value, that can be used to restrict JS to C++ value conversion
  • njs::Value - Local javascript value (wraps v8::Local<v8::Value>)
  • njs::Persistent - A persistent javascript value (wraps v8::Persistent<v8::Value>)
  • njs::HandleScope - Handle scope, currently maps to v8::HandleScope
  • njs::Runtime - Runtime is mapped to the underlying VM runtime / heap
  • njs::Context - Javascript context
    • njs::ScopedContext - Composition of Context and HandleScope
    • njs::ExecutionContext - Javascript context constructed during execution
    • njs::FunctionCallContext - Function call context
    • njs::ConstructCallContext - Constructor call context
    • njs::GetPropertyContext - Property getter context
    • njs::SetPropertyContext - Property setter context

Namespaces provided by NJS-API:

  • njs::Globals - Provides constants and limits
  • njs::Internal - Private namespace used by the implementation

Minimum Example

// A struct (or class) we want to wrap.
struct Point {
  double x, y;

  bool operator==(const Point& other) const noexcept { return x == other.x && y == other.y; }
  bool operator!=(const Point& other) const noexcept { return x == other.x && y == other.y; }

  Point& translate(double tx, double ty) noexcept {
    x += tx;
    y += ty;
    return *this;
  }

  static Point vectorOf(const Point& a, const Point& b) noexcept {
    return Point { b.x - a.x, b.y - a.y };
  }
};

// Wrapped object.
//
// This can actually be in header if you plan to share the interface
// with other objects or want to provide it for other libraries.
class PointWrap {
public:
  // Adds some data into `PointWrap` that will be used and implemented
  // in the next step. The last argument is signature, which should be
  // unique.
  NJS_BASE_CLASS(PointWrap, "Point", 0x01)

  // Native data.
  Point data;

  // You should always provide constructors that should match JS side.
  inline PointWrap() noexcept
    : data {} {}
  inline PointWrap(double x, double y) noexcept
    : data { x, y} {}
};

// Wrapped object bindings - should be always in `.cpp` file.
NJS_BIND_CLASS(PointWrap) {
  // Every function has an implicit context - use it to access stuff.
  NJS_BIND_CONSTURCTOR() {
    unsigned int argc = ctx.argumentsLength();
    if (argc == 0) {
      return ctx.returnNew<PointWrap>();
    }

    if (argc == 2) {
      // Unpack arguments from the calling context
      double x, y;

      // NJS_CHECK returns error immediately if something fails.
      NJS_CHECK(ctx.unpackArgument(0, x);
      NJS_CHECK(ctx.unpackArgument(1, y);

      return ctx.returnNew<PointWrap>(x, y);
    }

    return ctx.invalidArgumentsLength();
  }

  // Bindings methods, getters, and setters is easy. The bound method has
  // already setup CallContext and can access `self` that is `PointerWrap*`.
  // Unpacking the pointer to `PointWrap*` is done automatically by NJS-API.

  // If you plan to bind both getters and setters of the same property they
  // must be next to each other (a limitation that maybe we can relax in the
  // future).
  NJS_BIND_GET(x) {
    return ctx.returnValue(self->data.x);
  }

  NJS_BIND_SET(x) {
    double x;
    NJS_CHECK(ctx.unpackValue(x));
    self->data.x = x;

    // Setters return no value, you must use njs::ResultCode.
    return njs::Globals::kResultOk;
  }

  NJS_BIND_GET(y) {
    return ctx.returnValue(self->data.y);
  }

  NJS_BIND_SET(y) {
    double y;
    NJS_CHECK(ctx.unpackValue(y));
    self->data.y = y;
    return njs::Globals::kResultOk;
  }

  // You can bind methods too...
  NJS_BIND_METHOD(translate) {
    double tx, ty;

    NJS_CHECK(ctx.verifyArgumentsLength(2));
    NJS_CHECK(ctx.unpackArgument(0, tx));
    NJS_CHECK(ctx.unpackArgument(1, ty));

    // Perform the operation on native data.
    self->data.translate(tx, ty);

    // Return `this`.
    return ctx.returnValue(ctx.This());
  }

  // This shows how to unpack a wrapped object.
  NJS_BIND_METHOD(equals) {
    PointWrap* other;

    NJS_CHECK(ctx.verifyArgumentsLength(1));
    NJS_CHECK(ctx.unwrapArgument<PointWrap>(0, &other));

    return ctx.returnValue(self->data == other->data);
  }

  // Static functions can be bound too.
  NJS_BIND_STATIC(vectorOf) {
    PointWrap* a;
    PointWrap* b;

    NJS_CHECK(ctx.verifyArgumentsLength(2));
    NJS_CHECK(ctx.unwrapArgument<PointWrap>(0, &a));
    NJS_CHECK(ctx.unwrapArgument<PointWrap>(1, &b));

    // There is no reference to the class in the call context, however,
    // NJS-API wrapper always adds the module into the function data field.
    njs::Value PointClass = ctx.propertyOf(ctx.data(), njs::Latin1Ref("Point"));
    NJS_CHECK(PointClass);

    njs::Value instance = ctx.newInstance(PointClass);
    NJS_CHECK(instance);

    PointWrap* instObj = ctx.unwrapUnsafe<PointWrap>(instance);
    instObj->data = Point::vectorOf(a->data, b->data);
    return instance;
  }
};

// This would create a module that is context-aware by default.
NJS_MODULE(mylib) {
  // Each class must be added to `exports`.
  NJS_INIT_CLASS(PointWrap, exports);
}

TODO

Write more documentation and examples.