Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

mobx-rest

Package Overview
Dependencies
Maintainers
2
Versions
71
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

mobx-rest - npm Package Compare versions

Comparing version 2.2.5 to 3.0.0

lib/apiClient.d.ts

26

CHANGELOG.md
# Changelog
## `3.0.0`
New features 🎩:
- Reimplemented `mustFind` and `mustGet` methods in Collection.
- Brought back the `request` attribute in Model. The attribute tracks the last issued request.
- Brought back `onProgress` hook in `save` method.
Solved bugs 🐛:
- Fix https://github.com/masylum/mobx-rest/issues/47
- Fix https://github.com/masylum/mobx-rest/issues/41
What's changed 💅:
- Migrated to Typescript
- Made RPC label optional with a default `fetching` value
Breaking changes ☢️:
- Migrated to Mobx 5+
- Rollback default `keepChanges` flag value to `false`.
- Compatible adapters need to implement `data` as their second argument [as such](https://github.com/masylum/mobx-rest-jquery-adapter/commit/1e55c15dc37d372db1ae4345dedef855b8fb7611#diff-1fdf421c05c1140f6d71444ea2b27638R135).
## `3.0.0.alpha`
:sparkles:
Full description here: https://github.com/masylum/mobx-rest/pull/39
Kudos to @rdiazv
## `2.2.5`

@@ -4,0 +30,0 @@

821

lib/index.js
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.Request = exports.apiClient = exports.Model = exports.Collection = undefined;
Object.defineProperty(exports, '__esModule', { value: true });
var _Collection = require('./Collection');
function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
var _Collection2 = _interopRequireDefault(_Collection);
var mobx = require('mobx');
var lodash = require('lodash');
var deepmerge = _interopDefault(require('deepmerge'));
var _Model = require('./Model');
/*! *****************************************************************************
Copyright (c) Microsoft Corporation. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
this file except in compliance with the License. You may obtain a copy of the
License at http://www.apache.org/licenses/LICENSE-2.0
var _Model2 = _interopRequireDefault(_Model);
THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
MERCHANTABLITY OR NON-INFRINGEMENT.
var _Request = require('./Request');
See the Apache Version 2.0 License for specific language governing permissions
and limitations under the License.
***************************************************************************** */
/* global Reflect, Promise */
var _Request2 = _interopRequireDefault(_Request);
var extendStatics = function(d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
return extendStatics(d, b);
};
var _apiClient = require('./apiClient');
function __extends(d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
}
var _apiClient2 = _interopRequireDefault(_apiClient);
var __assign = function() {
__assign = Object.assign || function __assign(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function __rest(s, e) {
var t = {};
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
t[p] = s[p];
if (s != null && typeof Object.getOwnPropertySymbols === "function")
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) if (e.indexOf(p[i]) < 0)
t[p[i]] = s[p[i]];
return t;
}
exports.Collection = _Collection2.default;
exports.Model = _Model2.default;
exports.apiClient = _apiClient2.default;
exports.Request = _Request2.default;
function __decorate(decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
}
var ErrorObject = /** @class */ (function () {
function ErrorObject(error) {
this.error = null;
this.payload = {};
this.requestResponse = null;
if (error instanceof Error) {
console.error(error);
this.requestResponse = null;
this.error = error;
}
else if (typeof error === 'string') {
this.requestResponse = null;
this.error = error;
}
else if (error.requestResponse || error.error) {
this.requestResponse = error.requestResponse;
this.error = error.error;
}
else {
this.payload = error;
}
}
return ErrorObject;
}());
var Request = /** @class */ (function () {
function Request(promise, _a) {
var _this = this;
var _b = _a === void 0 ? {} : _a, labels = _b.labels, abort = _b.abort, _c = _b.progress, progress = _c === void 0 ? 0 : _c;
this.state = 'pending';
this.labels = labels;
this.abort = abort;
this.progress = progress = 0;
this.promise = promise;
promise
.then(function () { _this.state = 'fulfilled'; })
.catch(function () { _this.state = 'rejected'; });
}
// This allows to use async/await on the request object
Request.prototype.then = function (onFulfilled, onRejected) {
return this.promise.then(function (data) { return onFulfilled(data || {}); }, onRejected);
};
__decorate([
mobx.observable
], Request.prototype, "progress", void 0);
__decorate([
mobx.observable
], Request.prototype, "state", void 0);
return Request;
}());
var currentAdapter;
/**
* Sets or gets the api client instance
*/
function apiClient(adapter, options) {
if (options === void 0) { options = {}; }
if (adapter) {
currentAdapter = Object.assign({}, adapter, options);
}
if (!currentAdapter) {
throw new Error('You must set an adapter first!');
}
return currentAdapter;
}
var Base = /** @class */ (function () {
function Base() {
this.requests = mobx.observable.array([]);
}
/**
* Returns the resource's url.
*
* @abstract
*/
Base.prototype.url = function () {
throw new Error('You must implement this method');
};
Base.prototype.withRequest = function (labels, promise, abort) {
var _this = this;
if (typeof labels === 'string') {
labels = [labels];
}
var handledPromise = promise
.then(function (response) {
_this.requests.remove(request);
return response;
})
.catch(function (error) {
_this.requests.remove(request);
throw new ErrorObject(error);
});
var request = new Request(handledPromise, {
labels: labels,
abort: abort
});
this.requests.push(request);
return request;
};
Base.prototype.getRequest = function (label) {
return this.requests.find(function (request) { return lodash.includes(request.labels, label); });
};
Base.prototype.getAllRequests = function (label) {
return this.requests.filter(function (request) { return lodash.includes(request.labels, label); });
};
/**
* Questions whether the request exists
* and matches a certain label
*/
Base.prototype.isRequest = function (label) {
return !!this.getRequest(label);
};
/**
* Call an RPC action for all those
* non-REST endpoints that you may have in
* your API.
*/
// TODO: Type endpoint with string | { rootUrl: string }
Base.prototype.rpc = function (endpoint, options, label) {
if (label === void 0) { label = 'fetching'; }
var url = lodash.isObject(endpoint) ? endpoint.rootUrl : this.url() + "/" + endpoint;
var _a = apiClient().post(url, options), promise = _a.promise, abort = _a.abort;
return this.withRequest(label, promise, abort);
};
__decorate([
mobx.observable
], Base.prototype, "request", void 0);
__decorate([
mobx.observable.shallow
], Base.prototype, "requests", void 0);
__decorate([
mobx.action
], Base.prototype, "rpc", null);
return Base;
}());
var dontMergeArrays = function (_oldArray, newArray) { return newArray; };
var Model = /** @class */ (function (_super) {
__extends(Model, _super);
function Model(attributes, defaultAttributes) {
if (attributes === void 0) { attributes = {}; }
if (defaultAttributes === void 0) { defaultAttributes = {}; }
var _this = _super.call(this) || this;
_this.defaultAttributes = {};
_this.attributes = mobx.observable.map();
_this.committedAttributes = mobx.observable.map();
_this.optimisticId = lodash.uniqueId('i_');
_this.collection = null;
_this.defaultAttributes = defaultAttributes;
var mergedAttributes = __assign({}, _this.defaultAttributes, attributes);
_this.attributes.replace(mergedAttributes);
_this.commitChanges();
return _this;
}
/**
* Returns a JSON representation
* of the model
*/
Model.prototype.toJS = function () {
return mobx.toJS(this.attributes, { exportMapsAsObjects: true });
};
Object.defineProperty(Model.prototype, "primaryKey", {
/**
* Determine what attribute do you use
* as a primary id
*
* @abstract
*/
get: function () {
return 'id';
},
enumerable: true,
configurable: true
});
/**
* Return the base url used in
* the `url` method
*
* @abstract
*/
Model.prototype.urlRoot = function () {
return null;
};
/**
* Return the url for this given REST resource
*/
Model.prototype.url = function () {
var urlRoot = this.urlRoot();
if (!urlRoot && this.collection) {
urlRoot = this.collection.url();
}
if (!urlRoot) {
throw new Error('implement `urlRoot` method or `url` on the collection');
}
if (this.isNew) {
return urlRoot;
}
else {
return urlRoot + "/" + this.get(this.primaryKey);
}
};
Object.defineProperty(Model.prototype, "isNew", {
/**
* Wether the resource is new or not
*
* We determine this asking if it contains
* the `primaryKey` attribute (set by the server).
*/
get: function () {
return !this.has(this.primaryKey) || !this.get(this.primaryKey);
},
enumerable: true,
configurable: true
});
/**
* Get the attribute from the model.
*
* Since we want to be sure changes on
* the schema don't fail silently we
* throw an error if the field does not
* exist.
*
* If you want to deal with flexible schemas
* use `has` to check wether the field
* exists.
*/
Model.prototype.get = function (attribute) {
if (this.has(attribute)) {
return this.attributes.get(attribute);
}
throw new Error("Attribute \"" + attribute + "\" not found");
};
/**
* Returns whether the given field exists
* for the model.
*/
Model.prototype.has = function (attribute) {
return this.attributes.has(attribute);
};
Object.defineProperty(Model.prototype, "id", {
/**
* Get an id from the model. It will use either
* the backend assigned one or the client.
*/
get: function () {
return this.has(this.primaryKey)
? this.get(this.primaryKey)
: this.optimisticId;
},
enumerable: true,
configurable: true
});
Object.defineProperty(Model.prototype, "changedAttributes", {
/**
* Get an array with the attributes names that have changed.
*/
get: function () {
return getChangedAttributesBetween(mobx.toJS(this.committedAttributes), mobx.toJS(this.attributes));
},
enumerable: true,
configurable: true
});
Object.defineProperty(Model.prototype, "changes", {
/**
* Gets the current changes.
*/
get: function () {
return getChangesBetween(mobx.toJS(this.committedAttributes), mobx.toJS(this.attributes));
},
enumerable: true,
configurable: true
});
/**
* If an attribute is specified, returns true if it has changes.
* If no attribute is specified, returns true if any attribute has changes.
*/
Model.prototype.hasChanges = function (attribute) {
if (attribute) {
return lodash.includes(this.changedAttributes, attribute);
}
return this.changedAttributes.length > 0;
};
Model.prototype.commitChanges = function () {
this.committedAttributes.replace(mobx.toJS(this.attributes));
};
Model.prototype.discardChanges = function () {
this.attributes.replace(mobx.toJS(this.committedAttributes));
};
/**
* Replace all attributes with new data
*/
Model.prototype.reset = function (data) {
this.attributes.replace(data
? __assign({}, this.defaultAttributes, data) : this.defaultAttributes);
};
/**
* Merge the given attributes with
* the current ones
*/
Model.prototype.set = function (data) {
this.attributes.merge(data);
};
/**
* Fetches the model from the backend.
*/
Model.prototype.fetch = function (_a) {
var _this = this;
if (_a === void 0) { _a = {}; }
var data = _a.data, otherOptions = __rest(_a, ["data"]);
var _b = apiClient().get(this.url(), data, otherOptions), abort = _b.abort, promise = _b.promise;
promise
.then(function (data) {
_this.set(data);
_this.commitChanges();
});
return this.withRequest('fetching', promise, abort);
};
/**
* Merges old attributes with new ones.
* By default it doesn't merge arrays.
*/
Model.prototype.applyPatchChanges = function (oldAttributes, changes) {
return deepmerge(oldAttributes, changes, {
arrayMerge: dontMergeArrays
});
};
/**
* Saves the resource on the backend.
*
* If the item has a `primaryKey` it updates it,
* otherwise it creates the new resource.
*
* It supports optimistic and patch updates.
*/
Model.prototype.save = function (attributes, _a) {
var _this = this;
if (_a === void 0) { _a = {}; }
var _b = _a.optimistic, optimistic = _b === void 0 ? true : _b, _c = _a.patch, patch = _c === void 0 ? false : _c, _d = _a.keepChanges, keepChanges = _d === void 0 ? false : _d, otherOptions = __rest(_a, ["optimistic", "patch", "keepChanges"]);
var currentAttributes = this.toJS();
var label = this.isNew ? 'creating' : 'updating';
var data;
if (patch && attributes && !this.isNew) {
data = attributes;
}
else if (patch && !this.isNew) {
data = this.changes;
}
else {
data = __assign({}, currentAttributes, attributes);
}
var method;
if (this.isNew) {
method = 'post';
}
else if (patch) {
method = 'patch';
}
else {
method = 'put';
}
if (optimistic && attributes) {
this.set(patch
? this.applyPatchChanges(currentAttributes, attributes)
: attributes);
}
var onProgress = lodash.debounce(function (progress) {
if (optimistic && _this.request)
_this.request.progress = progress;
});
var _e = apiClient()[method](this.url(), data, __assign({ onProgress: onProgress }, otherOptions)), promise = _e.promise, abort = _e.abort;
promise
.then(function (data) {
var changes = getChangesBetween(currentAttributes, mobx.toJS(_this.attributes));
mobx.runInAction('save success', function () {
_this.set(data);
_this.commitChanges();
if (keepChanges) {
_this.set(_this.applyPatchChanges(data, changes));
}
});
})
.catch(function (error) {
_this.set(currentAttributes);
throw error;
});
this.request = this.withRequest(['saving', label], promise, abort);
return this.request;
};
/**
* Destroys the resurce on the client and
* requests the backend to delete it there
* too
*/
Model.prototype.destroy = function (_a) {
var _this = this;
if (_a === void 0) { _a = {}; }
var data = _a.data, _b = _a.optimistic, optimistic = _b === void 0 ? true : _b, otherOptions = __rest(_a, ["data", "optimistic"]);
var collection = this.collection;
if (this.isNew && collection) {
collection.remove(this);
return new Request(Promise.resolve());
}
if (this.isNew) {
return new Request(Promise.resolve());
}
var _c = apiClient().del(this.url(), data, otherOptions), promise = _c.promise, abort = _c.abort;
if (optimistic && collection) {
collection.remove(this);
}
promise
.then(function (data) {
if (!optimistic && collection)
collection.remove(_this);
})
.catch(function (error) {
if (optimistic && collection)
collection.add(_this);
throw error;
});
return this.withRequest('destroying', promise, abort);
};
__decorate([
mobx.computed
], Model.prototype, "isNew", null);
__decorate([
mobx.computed
], Model.prototype, "changedAttributes", null);
__decorate([
mobx.computed
], Model.prototype, "changes", null);
__decorate([
mobx.action
], Model.prototype, "commitChanges", null);
__decorate([
mobx.action
], Model.prototype, "discardChanges", null);
__decorate([
mobx.action
], Model.prototype, "reset", null);
__decorate([
mobx.action
], Model.prototype, "set", null);
__decorate([
mobx.action
], Model.prototype, "fetch", null);
__decorate([
mobx.action
], Model.prototype, "save", null);
__decorate([
mobx.action
], Model.prototype, "destroy", null);
return Model;
}(Base));
var getChangedAttributesBetween = function (source, target) {
var keys = lodash.union(Object.keys(source), Object.keys(target));
return keys.filter(function (key) { return !lodash.isEqual(source[key], target[key]); });
};
var getChangesBetween = function (source, target) {
var changes = {};
getChangedAttributesBetween(source, target).forEach(function (key) {
changes[key] = lodash.isPlainObject(source[key]) && lodash.isPlainObject(target[key])
? getChangesBetween(source[key], target[key])
: target[key];
});
return changes;
};
var Collection = /** @class */ (function (_super) {
__extends(Collection, _super);
function Collection(data) {
if (data === void 0) { data = []; }
var _this = _super.call(this) || this;
_this.models = mobx.observable.array([]);
_this.set(data);
return _this;
}
Object.defineProperty(Collection.prototype, "length", {
/**
* Alias for models.length
*/
get: function () {
return this.models.length;
},
enumerable: true,
configurable: true
});
/**
* Alias for models.map
*/
Collection.prototype.map = function (callback) {
return this.models.map(callback);
};
/**
* Alias for models.forEach
*/
Collection.prototype.forEach = function (callback) {
return this.models.forEach(callback);
};
/**
* Returns the URL where the model's resource would be located on the server.
*
* @abstract
*/
Collection.prototype.url = function () {
throw new Error('You must implement this method');
};
/**
* Returns a JSON representation
* of the collection
*/
Collection.prototype.toJS = function () {
return this.models.map(function (model) { return model.toJS(); });
};
/**
* Alias of slice
*/
Collection.prototype.toArray = function () {
return this.slice();
};
/**
* Returns a defensive shallow array representation
* of the collection
*/
Collection.prototype.slice = function () {
return this.models.slice();
};
Object.defineProperty(Collection.prototype, "isEmpty", {
/**
* Wether the collection is empty
*/
get: function () {
return this.length === 0;
},
enumerable: true,
configurable: true
});
/**
* Gets the ids of all the items in the collection
*/
Collection.prototype._ids = function () {
return this.models.map(function (item) { return item.id; }).filter(Boolean);
};
/**
* Get a resource at a given position
*/
Collection.prototype.at = function (index) {
return this.models[index];
};
/**
* Get a resource with the given id or uuid
*/
Collection.prototype.get = function (id, _a) {
var _b = (_a === void 0 ? {} : _a).required, required = _b === void 0 ? false : _b;
var model = this.models.find(function (item) { return item.id === id; });
if (!model && required) {
throw Error("Invariant: Model must be found with id: " + id);
}
return model;
};
/**
* Get a resource with the given id or uuid or fail loudly.
*/
Collection.prototype.mustGet = function (id) {
return this.get(id, { required: true });
};
/**
* Get resources matching criteria
*/
Collection.prototype.filter = function (query) {
return this.models.filter(function (model) {
return typeof query === 'function'
? query(model)
: lodash.isMatch(model.toJS(), query);
});
};
/**
* Finds an element with the given matcher
*/
Collection.prototype.find = function (query, _a) {
var _b = (_a === void 0 ? {} : _a).required, required = _b === void 0 ? false : _b;
var model = this.models.find(function (model) {
return typeof query === 'function'
? query(model)
: lodash.isMatch(model.toJS(), query);
});
if (!model && required) {
throw Error("Invariant: Model must be found");
}
return model;
};
/**
* Get a resource with the given id or uuid or fails loudly.
*/
Collection.prototype.mustFind = function (query) {
return this.find(query, { required: true });
};
/**
* Adds a model or collection of models.
*/
Collection.prototype.add = function (data) {
var _this = this;
var _a;
if (!Array.isArray(data))
data = [data];
var models = data.map(function (m) { return _this.build(m); });
(_a = this.models).push.apply(_a, models);
return models;
};
/**
* Resets the collection of models.
*/
Collection.prototype.reset = function (data) {
var _this = this;
this.models.replace(data.map(function (m) { return _this.build(m); }));
};
/**
* Removes the model with the given ids or uuids
*/
Collection.prototype.remove = function (ids) {
var _this = this;
if (!Array.isArray(ids)) {
ids = [ids];
}
ids.forEach(function (id) {
var model;
if (id instanceof Model && id.collection === _this) {
model = id;
}
else if (typeof id === 'number') {
model = _this.get(id);
}
if (!model)
return;
_this.models.splice(_this.models.indexOf(model), 1);
model.collection = undefined;
});
};
/**
* Sets the resources into the collection.
*
* You can disable adding, changing or removing.
*/
Collection.prototype.set = function (resources, _a) {
var _this = this;
var _b = _a === void 0 ? {} : _a, _c = _b.add, add = _c === void 0 ? true : _c, _d = _b.change, change = _d === void 0 ? true : _d, _e = _b.remove, remove = _e === void 0 ? true : _e;
if (remove) {
var ids = resources.map(function (r) { return r.id; });
var toRemove = lodash.difference(this._ids(), ids);
if (toRemove.length)
this.remove(toRemove);
}
resources.forEach(function (resource) {
var model = _this.get(resource.id);
if (model && change)
model.set(resource);
if (!model && add)
_this.add([resource]);
});
};
/**
* Creates a new model instance with the given attributes
*/
Collection.prototype.build = function (attributes) {
if (attributes === void 0) { attributes = {}; }
if (attributes instanceof Model) {
attributes.collection = this;
return attributes;
}
var ModelClass = this.model(attributes);
var model = new ModelClass(attributes);
model.collection = this;
return model;
};
/**
* Creates the model and saves it on the backend
*
* The default behaviour is optimistic but this
* can be tuned.
*/
Collection.prototype.create = function (attributesOrModel, _a) {
var _this = this;
var _b = (_a === void 0 ? {} : _a).optimistic, optimistic = _b === void 0 ? true : _b;
var model = this.build(attributesOrModel);
var _c = model.save(), abort = _c.abort, promise = _c.promise;
if (optimistic) {
this.add(model);
}
promise
.then(function (response) {
if (!optimistic)
_this.add(model);
})
.catch(function (error) {
if (optimistic)
_this.remove(model);
throw error;
});
return this.withRequest('creating', promise, abort);
};
/**
* Fetches the models from the backend.
*
* It uses `set` internally so you can
* use the options to disable adding, changing
* or removing.
*/
Collection.prototype.fetch = function (_a) {
var _this = this;
if (_a === void 0) { _a = {}; }
var data = _a.data, otherOptions = __rest(_a, ["data"]);
var _b = apiClient().get(this.url(), data, otherOptions), abort = _b.abort, promise = _b.promise;
promise.then(function (data) { return _this.set(data, otherOptions); });
return this.withRequest('fetching', promise, abort);
};
__decorate([
mobx.observable
], Collection.prototype, "models", void 0);
__decorate([
mobx.computed
], Collection.prototype, "length", null);
__decorate([
mobx.computed
], Collection.prototype, "isEmpty", null);
__decorate([
mobx.action
], Collection.prototype, "add", null);
__decorate([
mobx.action
], Collection.prototype, "reset", null);
__decorate([
mobx.action
], Collection.prototype, "remove", null);
__decorate([
mobx.action
], Collection.prototype, "set", null);
__decorate([
mobx.action
], Collection.prototype, "create", null);
__decorate([
mobx.action
], Collection.prototype, "fetch", null);
return Collection;
}(Base));
exports.Collection = Collection;
exports.ErrorObject = ErrorObject;
exports.Model = Model;
exports.Request = Request;
exports.apiClient = apiClient;

102

package.json
{
"name": "mobx-rest",
"version": "2.2.5",
"version": "3.0.0",
"description": "REST conventions for mobx.",
"jest": {
"roots": [
"."
],
"transform": {
".+\\.tsx?$": "ts-jest"
},
"testRegex": "/__tests__/.*\\.spec\\.tsx?$"
},
"repository": {

@@ -10,73 +19,37 @@ "type": "git",

"license": "MIT",
"jest": {
"collectCoverage": true,
"testRegex": "/__tests__/.*\\.spec\\.js$",
"collectCoverageFrom": [
"src/**/*.js"
]
},
"standard": {
"parser": "babel-eslint",
"globals": [
"it",
"describe",
"beforeEach",
"expect",
"Class",
"jest"
]
},
"dependencies": {
"lodash": "^4.17.4"
},
"peerDependencies": {
"mobx": "^3.2.0"
"deepmerge": "3.2.0",
"lodash": "4.17.11",
"mobx": "5.9.4"
},
"devDependencies": {
"babel-cli": "^6.24.1",
"babel-core": "^6.25.0",
"babel-eslint": "^7.2.3",
"babel-jest": "^20.0.3",
"babel-plugin-lodash": "^3.3.2",
"babel-plugin-transform-async-to-generator": "^6.24.1",
"babel-plugin-transform-decorators-legacy": "^1.3.4",
"babel-plugin-transform-flow-strip-types": "^6.22.0",
"babel-plugin-transform-runtime": "^6.23.0",
"babel-polyfill": "^6.23.0",
"babel-preset-es2015": "^6.24.1",
"babel-preset-stage-1": "^6.24.1",
"babel-register": "^6.24.1",
"eslint": "^3.19.0",
"eslint-config-standard": "^10.2.1",
"eslint-plugin-flowtype": "2.34.0",
"eslint-plugin-import": "^2.3.0",
"eslint-plugin-node": "^5.0.0",
"eslint-plugin-promise": "^3.5.0",
"eslint-plugin-standard": "^3.0.1",
"flow-bin": "^0.47.0",
"flow-copy-source": "^1.1.0",
"husky": "^0.13.4",
"jest": "^20.0.4",
"lint-staged": "^3.6.0",
"mobx": "^3.2.0",
"prettier-standard": "^5.0.0",
"rimraf": "^2.6.1"
"@types/jest": "24.0.13",
"@typescript-eslint/eslint-plugin": "1.9.0",
"@typescript-eslint/parser": "1.9.0",
"eslint": "5.16.0",
"husky": "0.13.4",
"jest": "24.8.0",
"lint-staged": "3.6.0",
"rimraf": "2.6.1",
"rollup": "1.12.1",
"rollup-plugin-node-resolve": "5.0.0",
"rollup-plugin-typescript2": "^0.21.1",
"ts-jest": "24.0.2",
"tslib": "1.9.3",
"typescript": "3.4.5"
},
"main": "lib",
"scripts": {
"build": "yarn build:clean && rollup --config",
"build:clean": "rimraf lib",
"build:lib": "babel -d lib src --ignore '**/__tests__/**'",
"build:flow": "flow-copy-source -v -i '**/__tests__/**' src lib",
"build": "npm run build:clean && npm run build:lib && npm run build:flow",
"prepublish": "npm run build",
"jest": "BABEL_ENV=test NODE_PATH=src jest --no-cache",
"lint": "eslint --ext .jsx,.js --cache src/ __tests__/",
"flow": "flow",
"test": "npm run flow && npm run lint && npm run jest",
"format": "prettier-standard --print-width 60 \"{src,__tests__}/**/*.js\"",
"prepush": "npm test",
"build:lib": "yarn build:clean && rollup --config",
"jest": "NODE_PATH=src jest --no-cache",
"lint": "eslint --ext .ts --cache src/ __tests__/",
"prepublish": "yarn build",
"prepush": "yarn test",
"test": "yarn lint && yarn jest",
"watch": "rollup --config -w",
"lint-staged": {
"linters": {
"{src|__tests__}/**/*.js": [
"prettier-standard",
"git add"

@@ -86,3 +59,8 @@ ]

}
},
"dependencies": {
"deepmerge": "3.2.0",
"lodash": "4.17.11",
"mobx": "5.9.4"
}
}

@@ -35,3 +35,3 @@ # mobx-rest

- **Component state**: Each state can have their own state, like a button
- **Component state**: Each component can have their own state, like a button
being pressed, a text input value, etc.

@@ -88,2 +88,6 @@ - **Application state**: Sometimes we need components to share state between them and

#### `defaultAttributes: Object`
An object literal that holds the default attributes of the model. {} by default.
#### `attributes: ObservableMap`

@@ -139,2 +143,67 @@

#### changedAttributes: Array<string>
Get an array with the attributes names that have changed.
Example:
```js
model.set({ name: 'Pau'})
model.changedAttributes // => ['name']
```
#### changes: { [string]: any }
Gets the current changes.
Example:
```js
model.set({ name: 'Pau'})
model.changes // => { name: 'Pau' }
```
#### hasChanges(attribute?: string): boolean
If an attribute is specified, returns true if it has changes.
If no attribute is specified, returns true if any attribute has changes.
Example:
```js
model.set({ name: 'Pau'})
// with attribute
model.hasChanges('name') // => true
// without attribute
model.hasChanges() // => true
```
#### commitChanges(): void
Commit attributes to model.
Example:
```js
model.set({ name: 'Pau' })
model.hasChanges // => true
model.commitChanges()
model.hasChanges // => false
```
#### discardChanges(): void
This will reset the model attributes to the last committed ones.
Example:
```js
const model = new Model({ name: 'Foo' })
model.set({ name: 'Pau' })
model.get('name') // => Pau
model.discardChanges()
model.get('name') // => 'Foo'
```
#### `isNew: boolean`

@@ -240,3 +309,3 @@

#### `rpc(method: 'string', body: {}): Promise`
#### `rpc(method: 'string', body?: {}, label?: 'fetching'): Promise`

@@ -308,3 +377,3 @@ When dealing with REST there are always cases when we have some actions beyond

#### `isEmpty(): boolean`
#### `isEmpty: boolean`

@@ -328,8 +397,12 @@ Helper method that asks the collection whether there is any

#### `get(id: number): ?Model`
#### `get(id: number, { required?: boolean = false }): ?Model`
Find a model (or not) with the given id.
Find a model (or not) with the given id. If `required` it will raise an error if not found.
#### `filter(query: Object): Array<Model>`
#### `mustGet(id: number): Model`
Find a model with the given id or raise an Error.
#### `filter(query: Object | Function): Array<Model>`
Helper method that filters the collection by the given conditions represented

@@ -345,6 +418,6 @@ as a key value.

#### `find(query: Object): ?Model`
#### `find(query: Object, { required?: boolean = false }): ?Model`
Same as `filter` but it will halt and return when the first model matches
the conditions.
the conditions. If `required` it will raise an error if not found.

@@ -356,6 +429,19 @@ Example:

pau.get('name') // => 'pau'
usersCollection.find({ name: 'foo'}) // => Error(`Invariant: Model must be found`)
```
#### `add(data: Array<Object>): Array<Model>`
#### `mustFind(query: Object): Model`
Same as `find` but it will raise an Error if the model is not found.
Example:
```js
const pau = usersCollection.mustFind({ name: 'pau' })
pau.get('name') // => 'pau'
```
#### `add(data: Array<Object|T>|T|Object): Array<Model>`
Adds models with the given array of attributes.

@@ -367,3 +453,3 @@

#### `reset(data: Array<Object>): Array<Model>`
#### `reset(data: Array<Object|T>): Array<Model>`

@@ -376,3 +462,3 @@ Resets the collection with the given models.

#### `remove(ids: Array<number>): void`
#### `remove(ids: Array<number|T>|number|T): void`

@@ -412,3 +498,3 @@ Remove any model with the given ids.

#### `build(attributes: Object): Model`
#### `build(attributes: Object|T): Model`

@@ -458,9 +544,30 @@ Instantiates and links a model to the current collection.

### forEach (callback: (model: T) => void): void
Alias for models.forEach
Example: `collection.forEach(model => console.log(model.get('id')))`
### map<P> (callback: (model: T) => P): Array<P>
Alias for models.map
Example: `collection.map(model => model.get('id')) // => [1,2,3...]`
### peek (): Array<T: Model>
Returns a shallow array representation of the collection
### slice (): Array<T: Model>
Returns a defensive shallow array representation of the collection
### `apiClient`
This is the object that is going to make the `xhr` requests to interact with your API.
There are currently two implementations:
There are currently three implementations:
- One using `jQuery` in the [mobx-rest-jquery-adapter](https://github.com/masylum/mobx-rest-jquery-adapter) package.
- One using `fetch` in the [mobx-rest-fetch-adapter](https://github.com/masylum/mobx-rest-fetch-adapter) package.
- One Using `axios` in the [mobx-rest-axios-adapter](https://github.com/IranTIP/mobx-rest-axios-adapter) package.

@@ -540,3 +647,3 @@ Initially you need to configure `apiClient()` with an adapter and the `apiPath`. You can also set additional options, like headers to send with all requests.

class Tasks extends React.Component {
componentWillMount () {
componentDidMount () {
// This will call `/api/tasks?all=true`

@@ -543,0 +650,0 @@ tasksCollection.fetch({ data: { all: true } })

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc