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

@coedl/nocfl-js

v2.0.0

Published

An opinionated S3 storage library informed by ocfl but without versioning

Downloads

40

Readme

nocfl-js

An opinionated S3 storage library inspired by ocfl but simpler.

Table of Contents

Repository and Documentation

Background

In working with the Oxford Common File Layout - OCFL we came to realise that some quite serious compromises were required. This is not to say that OCFL is not a good specification; just that we needed something different.

The name of this library came from Peter Sefton.

Why not just use OCFL?

Developing this library / tests

This library has extensive tests. To run them: npm run test:watch. You will need docker as this command will start a local S3 service called MinIO. (npm run develop exists as a more semantically meaningful shortcut for test:watch)

The minio credentials are root/rootpass and are defined in docker-compose.yml.

Releasing an update

  • Tag the release with npm version {major|minor|patch} as appropriate
  • Push tags to master with git push origin master --tags which will trigger a github action to build the distributables and update the docs
  • Publish the release to npm with npm publish

About this library

This library is intended to simplify working with data in an S3 bucket. Its primary objective is to ease the creation and management of data objects in the bucket in a well defined way. Accordingly, the API is intentionally simple. You define some properties when creating a hook to the bucket and then get / put data from it.

Research Object Crate Metadata

The library will create a metadata file for you - Research Object Crate - RO-Crate. By default, when you put a file into the object it will be registered in the hasPart property of the root dataset. And when you remove a file, it's content will also be removed from the crate file.

Index files

Object storage has no concept of folders so when looking for objects, you have to walk all of the keys. This can be painful and slow so new items are automatically added to an index file on the storage. See Indexer for more information.

Versioning

This library can version files for you. The versioning is not on by default but it can be turned on per file PUT. When you version a file the following happens - as an example, let's say the file is called something.txt

  • - the existing file (something.txt) will be copied to something.v${Date as ISO String}.txt
  • - the new version will be uploaded to something.txt

Think of the versioned examples as being the content of that file until that point in time. One can retrieve the versions of a given file by calling the listFileVersions method on a given base file name:

listFileVersions({ target: 'something.txt' })

Creating a new item

When creating a new item you need to

  • - pass in a prefix name (a domain name is a good option)
  • - pass in the primary type of the data type (e.g. Collection, Item, Person etc)
  • - pass in the object identifier

Both id and type must start with letter (upper or lowercase) and be followed by any number of letters (upper and lower), numbers and underscore. Any other characters will not be accepted and result in an error. Path creation will use the first letter of the identifier to prefix the item (this is configurable by defining the splay property in the constructor). The domain and class name will be lowercased.

Path creation from the identifier is illustrated following:

Examples:

-   prefix: example.com, type: Item, id: test -> `(bucket)/example.com/item/t/test (splay = default = 1)`
-   prefix: eXamPLe.cOm, type: Item, id: test -> `(bucket)/example.com/item/t/test (splay = default = 1)`

-   prefix: example.com, type: Collection, id: test, splay: 2 -> `(bucket)/example.com/collection/te/test`
-   prefix: example.com, type: Collection, id: test, splay: 4 -> `(bucket)/example.com/collection/test/test`
-   prefix: example.com, type: Collection, id: test, splay: 10 -> `(bucket)/example.com/collection/test/test`

Load the library


# ES modules
import { Store } from "@coedl/nocfl-js";

# CommonJS
const { Store } = require('@coedl/nocfl-js)

Store

The is the workhorse class to interact with the storage. This is how you get / put files to / from the storage and just generally work with them.

Create an item and put a file to it

// get a hook to the storage
const store = new Store({ prefix: "exmaple.com", type: "item", id: "test", credentials });

// create the object
await store.createObject();

// upload a file to it
await store.put({ localPath: path.join(__dirname, file), target: file });

// download a file from the storage
await store.get({ target: file, localPath: path.join("/tmp", file) });

// get a pre signed link to a file
let link = await store.getPresignedUrl({ target: file });

See the tests for more usage examples.

Indexer

This class helps you create and manage file based indices of the content on the storage. In Object storage there is no such thing as a folder. It's key / value pairs where the key (the fully qualified filename you gave it) points to the file data. That means you can't do things like:

ls /data/folder1/today/my/files

even though it looks like you have just such a path. Practically this means that whenever you want to look for something, you have to walk all of the keys. Obviously this becomes more and more painful as the amount of content in the storage grows. So, to shortcut this, you can create index files to the objects on the storage. And you do that via this class.

const indexer = new Indexer({credentials})
await indexer.createIndices({})

This will walk the storage and create an indices folder per prefix which contains a folder for each type it finds (collection, item, etc) and within those folders, an index file for each letter of the alphabet:

- domain1.example.com
    - indices
        - collection
            - a.json
            - b.json
            - ...
        - item
            - a.json
            - b.json
            - ...
- domain2.example.com
        - collection
            - a.json
            - b.json
            - ...
        - item
            - a.json
            - b.json
            - ...

The you can operate on those:

// list all indices in the domain
listIndices({ prefix: 'domain1.example.com' })

// list all indices of type in the domain
listIndices({ prefix: 'domain1.example.com', type: 'collection' })

// get a specific index
getIndex({ prefix: 'domain1.example.com', type: 'collection', file: 'a.json'})

Walker

The class will walk the storage for you and emit an object you can use with the storage class to attach to an object in the storage and operate on it.

const walker = new Walker({ credentials: this.credentials });
walker.on("object", (object) => {
    let { domain, className, id, splay } = object;

    // do something with object
})
await walker.walk({})