parent
6b765db2f5
commit
053d5d94a4
4 changed files with 584 additions and 0 deletions
@ -0,0 +1,21 @@ |
|||||||
|
The MIT License (MIT) |
||||||
|
|
||||||
|
Copyright (c) 2015 Lucas Hosseini |
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy |
||||||
|
of this software and associated documentation files (the "Software"), to deal |
||||||
|
in the Software without restriction, including without limitation the rights |
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||||
|
copies of the Software, and to permit persons to whom the Software is |
||||||
|
furnished to do so, subject to the following conditions: |
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all |
||||||
|
copies or substantial portions of the Software. |
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
||||||
|
SOFTWARE. |
@ -0,0 +1,187 @@ |
|||||||
|
# jsonapi-datastore |
||||||
|
[![Build Status](https://travis-ci.org/beauby/jsonapi-datastore.svg)](https://travis-ci.org/beauby/jsonapi-datastore) |
||||||
|
|
||||||
|
JavaScript client-side [JSON API](http://jsonapi.org) data handling made easy. |
||||||
|
|
||||||
|
Current version is v0.4.0-beta. It is still a work in progress, but should do what it says. |
||||||
|
|
||||||
|
## Description |
||||||
|
|
||||||
|
The [JSONAPI](http://jsonapi.org) standard is great for exchanging data (which is its purpose), but the format is not ideal to work directly with in an application. |
||||||
|
jsonapi-datastore is a JavaScript framework-agnostic library (but an [AngularJS version](#angularjs) is provided for convenience) that takes away the burden of handling [JSONAPI](http://jsonapi.org) data on the client side. |
||||||
|
|
||||||
|
What it does: |
||||||
|
- read JSONAPI payloads, |
||||||
|
- rebuild the underlying data graph, |
||||||
|
- allows you to query models and access their relationships directly, |
||||||
|
- create new models, |
||||||
|
- serialize models for creation/update. |
||||||
|
|
||||||
|
What it does not do: |
||||||
|
- make requests to your API. You design your endpoints URLs, the way you handle authentication, caching, etc. is totally up to you. |
||||||
|
|
||||||
|
## Installing |
||||||
|
|
||||||
|
Install jsonapi-datastore with `bower` by running: |
||||||
|
``` |
||||||
|
$ bower install jsonapi-datastore |
||||||
|
``` |
||||||
|
or with `npm` by running: |
||||||
|
``` |
||||||
|
$ npm install jsonapi-datastore |
||||||
|
``` |
||||||
|
|
||||||
|
## Parsing data |
||||||
|
|
||||||
|
Just call the `.sync()` method of your store. |
||||||
|
```javascript |
||||||
|
var store = new JsonApiDataStore(); |
||||||
|
store.sync(data); |
||||||
|
``` |
||||||
|
This parses the data and incorporates it in the store, taking care of already existing records (by updating them) and relationships. |
||||||
|
|
||||||
|
## Parsing with meta data |
||||||
|
|
||||||
|
If you have meta data in your payload use the `.syncWithMeta` method of your store. |
||||||
|
```javascript |
||||||
|
var store = new JsonApiDataStore(); |
||||||
|
store.syncWithMeta(data); |
||||||
|
``` |
||||||
|
This does everything that `.sync()` does, but returns an object with data and meta split. |
||||||
|
|
||||||
|
## Retrieving models |
||||||
|
|
||||||
|
Just call the `.find(type, id)` method of your store. |
||||||
|
```javascript |
||||||
|
var article = store.find('article', 123); |
||||||
|
``` |
||||||
|
or call the `.findAll(type)` method of your store to get all the models of that type. |
||||||
|
```javascript |
||||||
|
var articles = store.findAll('article'); |
||||||
|
``` |
||||||
|
All the attributes *and* relationships are accessible through the model as object properties. |
||||||
|
```javascript |
||||||
|
console.log(article.author.name); |
||||||
|
``` |
||||||
|
In case a related resource has not been fetched yet (either as a primary resource or as an included resource), the corresponding property on the model will contain only the `type` and `id` (and the `._placeHolder` property will be set to `true`). However, the models are *updated in place*, so you can fetch a related resource later, and your data will remain consistent. |
||||||
|
|
||||||
|
## Serializing data |
||||||
|
|
||||||
|
Just call the `.serialize()` method on the model. |
||||||
|
```javascript |
||||||
|
console.log(article.serialize()); |
||||||
|
``` |
||||||
|
|
||||||
|
## Examples |
||||||
|
|
||||||
|
```javascript |
||||||
|
// Create a store: |
||||||
|
var store = new JsonApiDataStore(); |
||||||
|
|
||||||
|
// Then, given the following payload, containing two `articles`, with a related `user` who is the author of both: |
||||||
|
var payload = { |
||||||
|
data: [{ |
||||||
|
type: 'article', |
||||||
|
id: 1337, |
||||||
|
attributes: { |
||||||
|
title: 'Cool article' |
||||||
|
}, |
||||||
|
relationships: { |
||||||
|
author: { |
||||||
|
data: { |
||||||
|
type: 'user', |
||||||
|
id: 1 |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
}, { |
||||||
|
type: 'article', |
||||||
|
id: 300, |
||||||
|
attributes: { |
||||||
|
title: 'Even cooler article' |
||||||
|
}, |
||||||
|
relationships: { |
||||||
|
author: { |
||||||
|
data: { |
||||||
|
type: 'user', |
||||||
|
id: 1 |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
}] |
||||||
|
}; |
||||||
|
|
||||||
|
// we can sync it: |
||||||
|
var articles = store.sync(payload); |
||||||
|
|
||||||
|
// which will return the list of synced articles. |
||||||
|
|
||||||
|
// Later, we can retrieve one of those: |
||||||
|
var article = store.find('article', 1337); |
||||||
|
|
||||||
|
// If the author resource has not been synced yet, we can only access its id and its type: |
||||||
|
console.log(article.author); |
||||||
|
// { id: 1, _type: 'article' } |
||||||
|
|
||||||
|
// If we do sync the author resource later: |
||||||
|
var authorPayload = { |
||||||
|
data: { |
||||||
|
type: 'user', |
||||||
|
id: 1, |
||||||
|
attributes: { |
||||||
|
name: 'Lucas' |
||||||
|
} |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
store.sync(authorPayload); |
||||||
|
|
||||||
|
// we can then access the author's name through our old `article` reference: |
||||||
|
console.log(article.author.name); |
||||||
|
// 'Lucas' |
||||||
|
|
||||||
|
// We can also serialize any whole model in a JSONAPI-compliant way: |
||||||
|
console.log(article.serialize()); |
||||||
|
// ... |
||||||
|
// or just a subset of its attributes/relationships: |
||||||
|
console.log(article.serialize({ attributes: ['title'], relationships: []})); |
||||||
|
// ... |
||||||
|
``` |
||||||
|
|
||||||
|
## Documentation |
||||||
|
|
||||||
|
See [DOCUMENTATION.md](DOCUMENTATION.md). |
||||||
|
|
||||||
|
## What's missing |
||||||
|
|
||||||
|
Currently, the store does not handle `links` attributes or resource-level or relationship-level meta. |
||||||
|
|
||||||
|
## Notes |
||||||
|
|
||||||
|
### AngularJS |
||||||
|
|
||||||
|
jsonapi-datastore is bundled with an AngularJs wrapper. Just include `ng-jsonapi-datastore.js` in your `index.html` and require the module `beauby.jsonApiDataStore` in your application. |
||||||
|
You can then use the `JsonApiDataStore` factory, which is essentially defined as follows: |
||||||
|
```javascript |
||||||
|
{ |
||||||
|
store: new JsonApiDataStore(), |
||||||
|
Model: JsonApiDataStoreModel |
||||||
|
} |
||||||
|
``` |
||||||
|
so that you can use it as follows: |
||||||
|
|
||||||
|
```javascript |
||||||
|
angular |
||||||
|
.module('myApp') |
||||||
|
.controller('myController', function(JsonApiDataStore) { |
||||||
|
var article = JsonApiDataStore.store.find('article', 1337); |
||||||
|
var newArticle = new JsonApiDataStore.Model('article'); |
||||||
|
newArticle.setAttribute('title', 'My cool article'); |
||||||
|
console.log(newArticle.serialize()); |
||||||
|
}); |
||||||
|
``` |
||||||
|
|
||||||
|
|
||||||
|
## Contributing |
||||||
|
|
||||||
|
All pull-requests welcome! |
@ -0,0 +1,264 @@ |
|||||||
|
/** |
||||||
|
* @class JsonApiDataStoreModel |
||||||
|
*/ |
||||||
|
"use strict"; |
||||||
|
|
||||||
|
var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); |
||||||
|
|
||||||
|
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } |
||||||
|
|
||||||
|
var JsonApiDataStoreModel = (function () { |
||||||
|
/** |
||||||
|
* @method constructor |
||||||
|
* @param {string} type The type of the model. |
||||||
|
* @param {string} id The id of the model. |
||||||
|
*/ |
||||||
|
|
||||||
|
function JsonApiDataStoreModel(type, id) { |
||||||
|
_classCallCheck(this, JsonApiDataStoreModel); |
||||||
|
|
||||||
|
this.id = id; |
||||||
|
this._type = type; |
||||||
|
this._attributes = []; |
||||||
|
this._relationships = []; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @class JsonApiDataStore |
||||||
|
*/ |
||||||
|
|
||||||
|
/** |
||||||
|
* Serialize a model. |
||||||
|
* @method serialize |
||||||
|
* @param {object} opts The options for serialization. Available properties: |
||||||
|
* |
||||||
|
* - `{array=}` `attributes` The list of attributes to be serialized (default: all attributes). |
||||||
|
* - `{array=}` `relationships` The list of relationships to be serialized (default: all relationships). |
||||||
|
* @return {object} JSONAPI-compliant object |
||||||
|
*/ |
||||||
|
|
||||||
|
_createClass(JsonApiDataStoreModel, [{ |
||||||
|
key: "serialize", |
||||||
|
value: function serialize(opts) { |
||||||
|
var self = this, |
||||||
|
res = { data: { type: this._type } }, |
||||||
|
key; |
||||||
|
|
||||||
|
opts = opts || {}; |
||||||
|
opts.attributes = opts.attributes || this._attributes; |
||||||
|
opts.relationships = opts.relationships || this._relationships; |
||||||
|
|
||||||
|
if (this.id !== undefined) res.data.id = this.id; |
||||||
|
if (opts.attributes.length !== 0) res.data.attributes = {}; |
||||||
|
if (opts.relationships.length !== 0) res.data.relationships = {}; |
||||||
|
|
||||||
|
opts.attributes.forEach(function (key) { |
||||||
|
res.data.attributes[key] = self[key]; |
||||||
|
}); |
||||||
|
|
||||||
|
opts.relationships.forEach(function (key) { |
||||||
|
function relationshipIdentifier(model) { |
||||||
|
return { type: model._type, id: model.id }; |
||||||
|
} |
||||||
|
if (!self[key]) { |
||||||
|
res.data.relationships[key] = { data: null }; |
||||||
|
} else if (self[key].constructor === Array) { |
||||||
|
res.data.relationships[key] = { |
||||||
|
data: self[key].map(relationshipIdentifier) |
||||||
|
}; |
||||||
|
} else { |
||||||
|
res.data.relationships[key] = { |
||||||
|
data: relationshipIdentifier(self[key]) |
||||||
|
}; |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
return res; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Set/add an attribute to a model. |
||||||
|
* @method setAttribute |
||||||
|
* @param {string} attrName The name of the attribute. |
||||||
|
* @param {object} value The value of the attribute. |
||||||
|
*/ |
||||||
|
}, { |
||||||
|
key: "setAttribute", |
||||||
|
value: function setAttribute(attrName, value) { |
||||||
|
if (this[attrName] === undefined) this._attributes.push(attrName); |
||||||
|
this[attrName] = value; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Set/add a relationships to a model. |
||||||
|
* @method setRelationship |
||||||
|
* @param {string} relName The name of the relationship. |
||||||
|
* @param {object} models The linked model(s). |
||||||
|
*/ |
||||||
|
}, { |
||||||
|
key: "setRelationship", |
||||||
|
value: function setRelationship(relName, models) { |
||||||
|
if (this[relName] === undefined) this._relationships.push(relName); |
||||||
|
this[relName] = models; |
||||||
|
} |
||||||
|
}]); |
||||||
|
|
||||||
|
return JsonApiDataStoreModel; |
||||||
|
})(); |
||||||
|
|
||||||
|
var JsonApiDataStore = (function () { |
||||||
|
/** |
||||||
|
* @method constructor |
||||||
|
*/ |
||||||
|
|
||||||
|
function JsonApiDataStore() { |
||||||
|
_classCallCheck(this, JsonApiDataStore); |
||||||
|
|
||||||
|
this.graph = {}; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Remove a model from the store. |
||||||
|
* @method destroy |
||||||
|
* @param {object} model The model to destroy. |
||||||
|
*/ |
||||||
|
|
||||||
|
_createClass(JsonApiDataStore, [{ |
||||||
|
key: "destroy", |
||||||
|
value: function destroy(model) { |
||||||
|
delete this.graph[model._type][model.id]; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Retrieve a model by type and id. Constant-time lookup. |
||||||
|
* @method find |
||||||
|
* @param {string} type The type of the model. |
||||||
|
* @param {string} id The id of the model. |
||||||
|
* @return {object} The corresponding model if present, and null otherwise. |
||||||
|
*/ |
||||||
|
}, { |
||||||
|
key: "find", |
||||||
|
value: function find(type, id) { |
||||||
|
if (!this.graph[type] || !this.graph[type][id]) return null; |
||||||
|
return this.graph[type][id]; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Retrieve all models by type. |
||||||
|
* @method findAll |
||||||
|
* @param {string} type The type of the model. |
||||||
|
* @return {object} Array of the corresponding model if present, and empty array otherwise. |
||||||
|
*/ |
||||||
|
}, { |
||||||
|
key: "findAll", |
||||||
|
value: function findAll(type) { |
||||||
|
var self = this; |
||||||
|
|
||||||
|
if (!this.graph[type]) return []; |
||||||
|
return Object.keys(self.graph[type]).map(function (v) { |
||||||
|
return self.graph[type][v]; |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Empty the store. |
||||||
|
* @method reset |
||||||
|
*/ |
||||||
|
}, { |
||||||
|
key: "reset", |
||||||
|
value: function reset() { |
||||||
|
this.graph = {}; |
||||||
|
} |
||||||
|
}, { |
||||||
|
key: "initModel", |
||||||
|
value: function initModel(type, id) { |
||||||
|
this.graph[type] = this.graph[type] || {}; |
||||||
|
this.graph[type][id] = this.graph[type][id] || new JsonApiDataStoreModel(type, id); |
||||||
|
|
||||||
|
return this.graph[type][id]; |
||||||
|
} |
||||||
|
}, { |
||||||
|
key: "syncRecord", |
||||||
|
value: function syncRecord(rec) { |
||||||
|
var self = this, |
||||||
|
model = this.initModel(rec.type, rec.id), |
||||||
|
key; |
||||||
|
|
||||||
|
function findOrInit(resource) { |
||||||
|
if (!self.find(resource.type, resource.id)) { |
||||||
|
var placeHolderModel = self.initModel(resource.type, resource.id); |
||||||
|
placeHolderModel._placeHolder = true; |
||||||
|
} |
||||||
|
return self.graph[resource.type][resource.id]; |
||||||
|
} |
||||||
|
|
||||||
|
delete model._placeHolder; |
||||||
|
|
||||||
|
for (key in rec.attributes) { |
||||||
|
model._attributes.push(key); |
||||||
|
model[key] = rec.attributes[key]; |
||||||
|
} |
||||||
|
|
||||||
|
if (rec.relationships) { |
||||||
|
for (key in rec.relationships) { |
||||||
|
var rel = rec.relationships[key]; |
||||||
|
if (rel.data !== undefined) { |
||||||
|
model._relationships.push(key); |
||||||
|
if (rel.data === null) { |
||||||
|
model[key] = null; |
||||||
|
} else if (rel.data.constructor === Array) { |
||||||
|
model[key] = rel.data.map(findOrInit); |
||||||
|
} else { |
||||||
|
model[key] = findOrInit(rel.data); |
||||||
|
} |
||||||
|
} |
||||||
|
if (rel.links) { |
||||||
|
console.log("Warning: Links not implemented yet."); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return model; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Sync a JSONAPI-compliant payload with the store and return any metadata included in the payload |
||||||
|
* @method syncWithMeta |
||||||
|
* @param {object} data The JSONAPI payload |
||||||
|
* @return {object} The model/array of models corresponding to the payload's primary resource(s) and any metadata. |
||||||
|
*/ |
||||||
|
}, { |
||||||
|
key: "syncWithMeta", |
||||||
|
value: function syncWithMeta(payload) { |
||||||
|
var primary = payload.data, |
||||||
|
syncRecord = this.syncRecord.bind(this); |
||||||
|
if (!primary) return []; |
||||||
|
if (payload.included) payload.included.map(syncRecord); |
||||||
|
return { |
||||||
|
data: primary.constructor === Array ? primary.map(syncRecord) : syncRecord(primary), |
||||||
|
meta: "meta" in payload ? payload.meta : null |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Sync a JSONAPI-compliant payload with the store. |
||||||
|
* @method sync |
||||||
|
* @param {object} data The JSONAPI payload |
||||||
|
* @return {object} The model/array of models corresponding to the payload's primary resource(s). |
||||||
|
*/ |
||||||
|
}, { |
||||||
|
key: "sync", |
||||||
|
value: function sync(payload) { |
||||||
|
return this.syncWithMeta(payload).data; |
||||||
|
} |
||||||
|
}]); |
||||||
|
|
||||||
|
return JsonApiDataStore; |
||||||
|
})(); |
||||||
|
|
||||||
|
if ('undefined' !== typeof module) { |
||||||
|
module.exports = { |
||||||
|
JsonApiDataStore: JsonApiDataStore, |
||||||
|
JsonApiDataStoreModel: JsonApiDataStoreModel |
||||||
|
}; |
||||||
|
} |
@ -0,0 +1,112 @@ |
|||||||
|
{ |
||||||
|
"_args": [ |
||||||
|
[ |
||||||
|
{ |
||||||
|
"raw": "jsonapi-datastore", |
||||||
|
"scope": null, |
||||||
|
"escapedName": "jsonapi-datastore", |
||||||
|
"name": "jsonapi-datastore", |
||||||
|
"rawSpec": "", |
||||||
|
"spec": "latest", |
||||||
|
"type": "tag" |
||||||
|
}, |
||||||
|
"/Users/liuxy/work/wechat_program_front" |
||||||
|
] |
||||||
|
], |
||||||
|
"_from": "jsonapi-datastore@latest", |
||||||
|
"_id": "jsonapi-datastore@0.4.0-beta", |
||||||
|
"_inCache": true, |
||||||
|
"_installable": true, |
||||||
|
"_location": "/jsonapi-datastore", |
||||||
|
"_nodeVersion": "0.12.7", |
||||||
|
"_npmOperationalInternal": { |
||||||
|
"host": "packages-12-west.internal.npmjs.com", |
||||||
|
"tmp": "tmp/jsonapi-datastore-0.4.0-beta.tgz_1458052797419_0.7578040463849902" |
||||||
|
}, |
||||||
|
"_npmUser": { |
||||||
|
"name": "beauby", |
||||||
|
"email": "lucas.hosseini@gmail.com" |
||||||
|
}, |
||||||
|
"_npmVersion": "2.13.5", |
||||||
|
"_phantomChildren": {}, |
||||||
|
"_requested": { |
||||||
|
"raw": "jsonapi-datastore", |
||||||
|
"scope": null, |
||||||
|
"escapedName": "jsonapi-datastore", |
||||||
|
"name": "jsonapi-datastore", |
||||||
|
"rawSpec": "", |
||||||
|
"spec": "latest", |
||||||
|
"type": "tag" |
||||||
|
}, |
||||||
|
"_requiredBy": [ |
||||||
|
"#USER" |
||||||
|
], |
||||||
|
"_resolved": "https://registry.npmjs.org/jsonapi-datastore/-/jsonapi-datastore-0.4.0-beta.tgz", |
||||||
|
"_shasum": "b499fce924d45e2bc3c6178681520063e2361f10", |
||||||
|
"_shrinkwrap": null, |
||||||
|
"_spec": "jsonapi-datastore", |
||||||
|
"_where": "/Users/liuxy/work/wechat_program_front", |
||||||
|
"author": { |
||||||
|
"name": "Lucas Hosseini", |
||||||
|
"email": "lucas.hosseini@gmail.com" |
||||||
|
}, |
||||||
|
"bugs": { |
||||||
|
"url": "https://github.com/beauby/jsonapi-datastore/issues" |
||||||
|
}, |
||||||
|
"dependencies": {}, |
||||||
|
"description": "JavaScript client-side JSON API data handling made easy.", |
||||||
|
"devDependencies": { |
||||||
|
"babel": "^5.8.23", |
||||||
|
"chai": "^3.2.0", |
||||||
|
"gulp": "^3.9.0", |
||||||
|
"gulp-babel": "^5.2.1", |
||||||
|
"gulp-concat": "^2.6.0", |
||||||
|
"gulp-jsbeautify": "^0.1.1", |
||||||
|
"gulp-jscs": "^2.0.0", |
||||||
|
"gulp-jshint": "^1.11.2", |
||||||
|
"gulp-markdox": "^0.1.1", |
||||||
|
"gulp-mocha": "^2.1.3", |
||||||
|
"gulp-rename": "^1.2.2", |
||||||
|
"gulp-uglify": "^1.2.0", |
||||||
|
"gulp-wrap": "^0.11.0", |
||||||
|
"mocha": "^2.2.5" |
||||||
|
}, |
||||||
|
"directories": { |
||||||
|
"test": "test" |
||||||
|
}, |
||||||
|
"dist": { |
||||||
|
"shasum": "b499fce924d45e2bc3c6178681520063e2361f10", |
||||||
|
"tarball": "https://registry.npmjs.org/jsonapi-datastore/-/jsonapi-datastore-0.4.0-beta.tgz" |
||||||
|
}, |
||||||
|
"files": [ |
||||||
|
"dist/" |
||||||
|
], |
||||||
|
"gitHead": "56512d7be9b424cbd0da0a71acbed65eab14e042", |
||||||
|
"homepage": "https://github.com/beauby/jsonapi-datastore#readme", |
||||||
|
"keywords": [ |
||||||
|
"JSON", |
||||||
|
"parsing", |
||||||
|
"serializing", |
||||||
|
"datastore", |
||||||
|
"JSONAPI" |
||||||
|
], |
||||||
|
"license": "MIT", |
||||||
|
"main": "dist/node-jsonapi-datastore.js", |
||||||
|
"maintainers": [ |
||||||
|
{ |
||||||
|
"name": "beauby", |
||||||
|
"email": "lucas.hosseini@gmail.com" |
||||||
|
} |
||||||
|
], |
||||||
|
"name": "jsonapi-datastore", |
||||||
|
"optionalDependencies": {}, |
||||||
|
"readme": "ERROR: No README data found!", |
||||||
|
"repository": { |
||||||
|
"type": "git", |
||||||
|
"url": "git+https://github.com/beauby/jsonapi-datastore.git" |
||||||
|
}, |
||||||
|
"scripts": { |
||||||
|
"test": "gulp test" |
||||||
|
}, |
||||||
|
"version": "0.4.0-beta" |
||||||
|
} |
Loading…
Reference in new issue