diff --git a/node_modules/jsonapi-datastore/LICENSE.md b/node_modules/jsonapi-datastore/LICENSE.md new file mode 100644 index 0000000..6bb7ffd --- /dev/null +++ b/node_modules/jsonapi-datastore/LICENSE.md @@ -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. diff --git a/node_modules/jsonapi-datastore/README.md b/node_modules/jsonapi-datastore/README.md new file mode 100644 index 0000000..a3c7650 --- /dev/null +++ b/node_modules/jsonapi-datastore/README.md @@ -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! diff --git a/node_modules/jsonapi-datastore/dist/jsonapi-datastore.js b/node_modules/jsonapi-datastore/dist/jsonapi-datastore.js new file mode 100644 index 0000000..9ef1874 --- /dev/null +++ b/node_modules/jsonapi-datastore/dist/jsonapi-datastore.js @@ -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 + }; +} \ No newline at end of file diff --git a/node_modules/jsonapi-datastore/package.json b/node_modules/jsonapi-datastore/package.json new file mode 100644 index 0000000..9c870c5 --- /dev/null +++ b/node_modules/jsonapi-datastore/package.json @@ -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" +}