# ipld-jsonld

> JavaScript implementation of the JSON-LD IPLD format

Latest version **0.1.2** (published 2018-07-11) · MIT license · 0 weekly downloads

## Install

```sh
npm install ipld-jsonld
pnpm add ipld-jsonld
yarn add ipld-jsonld
bun add ipld-jsonld
```

## Health

**Score 15/100 (F)** — status: abandoned.

Positive: no vulnerabilities.

Warnings: low downloads; no types; no esm support; pre 1.0.

Negative: abandoned; low maintenance score.

## Facts

| | |
|---|---|
| Version | 0.1.2 |
| Published | 2018-07-11 |
| First published | 2018-07-01 |
| Weekly downloads | 0 |
| License | MIT |
| TypeScript types | none |
| Module format | CommonJS |
| Dependencies | 5 |
| Unpacked size | 19.8 KB |
| Known vulnerabilities | 0 |
| Install scripts | no |
| Author | Joel Gustafson |
| Maintainers | joelg |
| Keywords | ipfs, ipld, json-ld, jsonld |

## Links

- npm: https://www.npmjs.com/package/ipld-jsonld
- Repository: https://github.com/underlay/js-ipld-jsonld
- Homepage: https://github.com/underlay/js-ipld-jsonld#readme
- Issues: https://github.com/underlay/js-ipld-jsonld/issues
- npm.io page: https://npm.io/package/ipld-jsonld

## Dependencies (5)

- [borc](https://npm.io/package/borc.md) ^2.0.3
- [cids](https://npm.io/package/cids.md) ^0.5.3
- [async](https://npm.io/package/async.md) ^2.6.1
- [jsonld](https://npm.io/package/jsonld.md) ^1.0.2
- [multihashing-async](https://npm.io/package/multihashing-async.md) ^0.5.1

## Alternatives

- [@mapbox/jsonlint-lines-primitives](https://npm.io/package/@mapbox/jsonlint-lines-primitives.md) — 5.3M weekly downloads
- [reftools](https://npm.io/package/reftools.md) — 3.5M weekly downloads
- [@hey-api/openapi-ts](https://npm.io/package/@hey-api/openapi-ts.md) — 3.5M weekly downloads
- [@mapbox/geojson-rewind](https://npm.io/package/@mapbox/geojson-rewind.md) — 2.4M weekly downloads
- [turbo-stream](https://npm.io/package/turbo-stream.md) — 1.7M weekly downloads

## Recent versions

- 0.1.2 (latest) — 2018-07-11
- 0.1.1 — 2018-07-10
- 0.1.0 — 2018-07-01

## README

# js-ipld-jsonld

[![standard-readme compliant](https://img.shields.io/badge/readme%20style-standard-brightgreen.svg?style=flat-square)](https://github.com/RichardLitt/standard-readme)

> JavaScript implementation of a [JSON-LD](https://json-ld.org/spec/latest/json-ld/) [IPLD format](https://github.com/ipld/interface-ipld-format)

Hash-linking all the world's data is great, but sometimes you encounter graphs that really are circular. We can still traverse these by property-path, but only if we address nodes by identifiers other than their hash.

JSON-LD is a (recent!) web standard for encoding Linked Data™, and it's the growing default for serializing graphs on the web. This is an IPLD format for JSON-LD documents that resolves (potentially circular) paths within the graph, and defines a JSON-LD-compliant scheme for linking back to the greater IPLD world.

## Table of Contents

- [Install](#install)
- [Usage](#usage)
  - [No multicodec entry](#no-multicodec-entry)
  - [No slashes in property names](#no-slashes-in-property-names)
  - [Match three cases for root documents](#match-three-cases-for-root-documents)
  - [Index into every array-like structure](#index-into-every-array-like-structure)
  - [Linking back to the outside world](#linking-back-to-the-outside-world)
- [Contribute](#contribute)
- [License](#license)

## Install

```sh
$ npm install ipld-jsonld --save
```

## Usage

Assuming you have a reference `ipld` to an [IPLD Resolver](https://github.com/ipld/js-ipld) (at the moment you can sneak into `node._ipld` from any js-ipfs node), you can add support for the JSON-LD format manually:

```javascript
const {util, resolver} = require("ipld-jsonld")

console.log(resolver.multicodec) // "jsonld"

ipld.support.add(resolver.multicodec, resolver, util)

const your_document = {...some_jsonld}

ipld.put(your_document, {format: "jsonld"}, (err, cid) => {
  console.log(cid.codec) // "jsonld"
  ipld.get(cid, "graph/path/to/traverse", (err, result) => {
    if (err) console.error(err)
    else {
      const {value, remainderPath} = result
      ...
    }
  })
})
```

This package comes with several major caveats:

### No multicodec entry

JSON-LD isn't an official multicodec, so it doesn't have an entry in [the multicodec table](https://github.com/multiformats/multicodec/blob/master/table.csv). This means you can't serialize `cid` to a base-encoded string, because it doesn't know which prefix bytes correspond to the `"jsonld"` codec.

If you want to go rogue and fake an entry, you can install the `multicodec` package and insert them yourself:

```javascript
const codecVarints = require("multicodec/src/varint-table")
const codecNames = require("multicodec/src/name-table")
...
const hex = "77" // or other free byte
const codec = "jsonld"
codecVarints[codec] = Buffer.from(hex, "hex")
codecNames[hex] = codec
...
const b58 = cid.toBaseEncodedString() // zeRd8...
```

### No slashes in property names

JSON-LD requires property names and identifiers to be [IRIs](https://en.wikipedia.org/wiki/Internationalized_Resource_Identifier), which tend to be forward-slash dense. **Slashes in properties are not supported**; paths are sent straight to `parts = path.split("/")`. Fortunately, JSON-LD also promotes the heavy use of the `@context` property with colon-prefixed term expansion. _You should do this; it's good practice anyway._

```javascript
// Bad:
const bad = {
	"http://schema.org/name": "John Doe",
}
// Good:
const good = {
	"@context": { name: "http://schema.org/name" },
	name: "John Doe",
}
// Better:
const better = {
	"@context": { s: "http://schema.org/" },
	"s:name": "John Doe",
	"s:telephone": "(012) 345-6789",
}
// Best:
const best = {
	"@context": { "@vocab": "http://schema.org/" },
	name: "John Doe",
	telephone: "(012) 345-6789",
}
// Worst, for now (fetches from network on every deserialization):
const worst = {
	"@context": "http://example.com/my-context.json",
	name: "John Doe",
}
```

### Match three cases for root documents

A JSON-LD document has three "root" formats:

```javascript
// All three cases: every node has optional @context.

// Case 1: the root document is a node.
// <cid>/name => "Jane Doe"
const case1 = {
  "@context": {...}
  "name": "Jane Doe"
}

// Case 2: the root document is an array of nodes.
// <cid>/0/foo => "bar"
const case2 = [
  {
    "@context": {...},
    "foo": "bar"
  },
  ...
]

// Case 3: the root document is a @context/@graph object,
// where @context is optional and @graph is an array of nodes
// <cid>/w:Q42/s:name => "Douglas Adams"
const case3 = {
  "@context": {
    "w": "http://www.wikidata.org/wiki/",
    "s": "http://schema.org/"
  },
  "@graph": [
    {
      "@id": "w:Q42"
      "s:name": "Douglas Adams"
    },
    ...
  ]
}
```

**The first element of the path is interpreted differently depending on the document's format.**

- Case 1: a property name of the root node
- Case 2: an ingeter index of one of the root nodes
- Case 3: the _@id of a node in the @graph array_

The discrepency between cases 2 and 3 is somewhat arbitrary, but motivated by the suspicion that users are more likely to know their target's `@id` than its index when serialized (for Case 3), but expect a top-level array to be addressed by index (for Case 2).

Note that since JSON-LD requires every `@id` value to be an IRI, successfully indexing a Case 3 format document requires the use of a top-level `@context` to rename `@id` values (this difficulty is further motivation for the Case 2/3 discrepency, since Case 2 has no top-level context at all).

### Index into every array-like structure

JSON-LD uses arrays to indicate multiple values, and also has `@list` and `@set` wrappers to explicitly denote ordered-ness and unordered-ness. Until a better idea comes along, you have to index into every array-like element (including `@set`s) with an integer.

```javascript
const jsonld = {
  "@context": { "@vocab": "http://schema.org/" },
  "name": {
    "@set": ["John Doe", "JDoe", "JD"]
  }
}
const cid = await ipfs.dag.put(jsonld, {format: "jsonld"})
const names = await ipfs.dag.get(cid, "name")
// { value: [ 'John Doe', 'JDoe', 'JD' ], remainderPath: '' } <-- the @set gets unwrapped
const jd = await ipfs.dag.get(cid, "name/2")
// { value: 'JD', remainderPath: '' }
```

### Index maps link back to the outside world

The IPLD spec defines a [link object](https://github.com/ipld/specs/blob/b2108a1396c2dc0b71dc2c1fe4ceae86e4a96834/IPLD.md) to be an object of the form `{"/": <base58-encoded-cid>}`, but foreign, non-IRI keys like `"/"` aren't included the JSON-LD spec (they're not _invalid_, but JSON-LD processors are required to ignore them).

Fortunately, JSON-LD defines a structure called an [index map](https://json-ld.org/spec/latest/json-ld/#index-maps) to solve exactly this issue: signalling to JSON-LD processors that a particular key should not be discarded. This is done with a special `@index` key in one of two ways:

```javascript
// Method 1: including {@index, @value} manually
const method1 = {
	...stuff,
	externalLink: {
		"@index": "/",
		"@value": "<base58-encoded-cid>",
	},
}
// Method 2: using an @container in the context
const method2 = {
	"@context": {
		...context,
		externalLink: { "@container": "@index" },
	},
	...stuff,
	externalLink: { "/": "<base58-encoded-cid>" },
}
```

Note that in method 2, we can use a literal IPLD link as a property value (!!), but we have to declare in the context which properties we expect to be links. Again, this has its tradeoffs, but supports the JSON-LD pattern of relying on a well-articulated context to simplify the graph itself.

So we can run an example like this:

```javascript
const cid = await ipfs.dag.put({foo: 4}, {format: "dag-cbor"})
const b58 = cid.toBaseEncodedString() // zdpuB3WAXUM4VJpSRKFarS2GnwBqbFKLSdt8DB3bgfLdVCvrs
const jsonld = {
  "@context": {
    "@vocab": "http://schema.org/",
    "bar": { "@container": "@index" },
  },
  "@id": "http://people.com/jane_doe",
  "@type": "Person",
  "name": "Jane Doe",
  "spouse": {
    "@id": "http://people.com/john_doe",
    "@type": "Person",
    "name": "John Doe",
    "spouse": { "@id": "http://people.com/jane_doe" },
    "bar": { "/": `${b58}/foo`},
  }
}
const cid2 = await ipfs.dag.put(jsonld, {format: "jsonld"})
const baz = await ipfs.dag.get(cid2, "spouse/spouse/spouse/bar")
// { value: 4, remainderPath: "" }
```

## Contribute

This is an incredibly early and premature project, but if you're interested in contributing or just have any ideas or advice in general, please [open an issue](https://github.com/underlay/js-ipld-jsonld/issues/new)!

## License

[MIT](LICENSE)

---
_Source: https://npm.io/package/ipld-jsonld · Machine-readable twin of the npm.io package page. Health data is recomputed on every publish._
