diff --git a/js-data-angular/js-data-angular-tests.ts b/js-data-angular/js-data-angular-tests.ts new file mode 100644 index 000000000..8e036212f --- /dev/null +++ b/js-data-angular/js-data-angular-tests.ts @@ -0,0 +1,78 @@ +/// + +interface IUser { + +} + +interface CustomScope extends ng.IScope { + + comments: Array; + user: IUser; + users: Array; +} + +angular.module('myApp') + .controller('commentsCtrl', function ($scope:CustomScope, store:JSData.DS, Comment:JSData.DSResourceDefinition, User:JSData.DSResourceDefinition) { + + Comment.findAll().then(function (comments) { + $scope.comments = comments; + }); + + // shortest version + User.bindOne(1, $scope, 'user'); + +// short version + store.bindOne('user', 1, $scope, 'user'); + +// long version + $scope.$watch(function () { + return store.lastModified('user', 1); + }, function () { + $scope.user = store.get('user', 1); + }); + + var params = { + where: { + age: { + '>': 30 + } + } + }; + +// shortest verions + User.bindAll(params, $scope, 'users'); + +// short version + store.bindAll('user', params, $scope, 'users'); + +// long version + $scope.$watch(function () { + return store.lastModified('user'); + }, function () { + $scope.users = store.filter('user', params); + }); + }); + +angular.module('myApp') + .run(function (DS:JSData.DS) { + // We don't register the "User" resource + // as a service, so it can only be used + // via DS.('user', ...) + // The advantage here is that this code + // is guaranteed to be executed, and you + // only ever have to inject "DS" + DS.defineResource('user'); + }) + .factory('Comment', function (DS:JSData.DS) { + // This code won't execute unless you actually + // inject "Comment" somewhere in your code. + // Thanks Angular... + // Some like injecting actual Resource + // definitions, instead of just "DS" + return DS.defineResource('comment'); + }); + +angular.module('myApp') + .config(function (DSProvider:JSData.DSProvider) { + DSProvider.defaults.basePath = '/myApi'; // etc. + }); \ No newline at end of file diff --git a/js-data-angular/js-data-angular.d.ts b/js-data-angular/js-data-angular.d.ts new file mode 100644 index 000000000..11a295562 --- /dev/null +++ b/js-data-angular/js-data-angular.d.ts @@ -0,0 +1,30 @@ +// Type definitions for JSDataAngular v2.1.0 +// Project: https://github.com/js-data/js-data-angular +// Definitions by: Stefan Steinhart +// Definitions: https://github.com/borisyankov/DefinitelyTyped + +/// +/// + +declare module JSData { + + interface DSProvider { + defaults:DSConfiguration; + } + + interface DS { + + bindAll(resourceName:string, params:DSFilterParams, scope:ng.IScope, expr:string, cb?:(err:DSError, items:Array)=>void):Function; + + bindOne(resourceName:string, id:string, scope:ng.IScope, expr:string, cb?:(err:DSError, item:T)=>void):Function; + bindOne(resourceName:string, id:number, scope:ng.IScope, expr:string, cb?:(err:DSError, item:T)=>void):Function; + } + + interface DSResourceDefinition { + + bindAll(params:DSFilterParams, scope:ng.IScope, expr:string, cb?:(err:DSError, items:Array)=>void):Function; + + bindOne(id:string, scope:ng.IScope, expr:string, cb?:(err:DSError, item:T)=>void):Function; + bindOne(id:number, scope:ng.IScope, expr:string, cb?:(err:DSError, item:T)=>void):Function; + } +} \ No newline at end of file diff --git a/js-data-http/js-data-http-tests.ts b/js-data-http/js-data-http-tests.ts new file mode 100644 index 000000000..d7c07d654 --- /dev/null +++ b/js-data-http/js-data-http-tests.ts @@ -0,0 +1,167 @@ +/// + +var adapter = new DSHttpAdapter(); +var store = new JSData.DS(); +store.registerAdapter('http', adapter, { default: true }); + +var ADocument:JSData.DSResourceDefinition = store.defineResource('document'); + +ADocument.inject({ id: 5, author: 'John' }); + +// bypass the data store +adapter.update(ADocument, 5, { author: 'Johnny' }).then(function (document:any) { + document; // { id: 5, author: 'Johnny' } + + // The updated document has NOT been injected into the data store because we bypassed the data store + ADocument.get(document.id); // { id: 5, author: 'John' } +}); + +// Normally you would just go through the data store +ADocument.update(5, { author: 'Johnny' }).then(function (document:any) { + document; // { id: 5, author: 'Johnny' } + + // the updated document has been injected into the data store + ADocument.get(document.id); // { id: 5, author: 'Johnny' } +}); + +ADocument.inject({ id: 5, author: 'John' }); +ADocument.inject({ id: 6, author: 'John' }); + +// bypass the data store +adapter.updateAll(ADocument, { author: 'Johnny' }, { author: 'John' }).then(function (documents) { + documents[0]; // { id: 5, author: 'Johnny' } + + // The updated documents have NOT been injected into the data store because we bypassed the data store + ADocument.filter({ author: 'John' }); // [{...}, {...}] + ADocument.filter({ author: 'Johnny' }); // [] +}); + +// Normally you would just go through the data store +ADocument.updateAll({ author: 'Johnny' }, { author: 'John' }).then(function (documents) { + documents[0]; // { id: 5, author: 'Johnny' } + + // the updated documents have been injected into the data store + ADocument.filter({ author: 'John' }); // [] + ADocument.filter({ author: 'Johnny' }); // [{...}, {...}] +}); + +adapter.PUT('/user/1', { name: 'Johnny' }).then(function (data) { + data.data; // { id: 1, name: 'Johnny', ... } + data.headers; // {...} + data.status; // 200 + data.config; //{...} +}); + +adapter.POST('/user/1', { name: 'John' }).then(function (data) { + data.data; // { id: 1, name: 'John', ... } + data.headers; // {...} + data.status; // 200 + data.config; //{...} +}); + +adapter.HTTP({ url: '/user/1', method: 'put', data: { name: 'Johnny' }}).then(function (data) { + data.data; // { id: 1, name: 'Johnny', ... } + data.headers; // {...} + data.status; // 200 + data.config; //{...} +}); + +adapter.GET('/user/1').then(function (data) { + data.data; // { id: 1, ... } + data.headers; // {...} + data.status; // 200 + data.config; //{...} +}); + +var User:JSData.DSResourceDefinition = store.defineResource('user'); + +var params:any = { + age: { + '>': 30 + } +}; + +// bypass the data store +adapter.findAll(User, params).then(function (users) { + // users[0].age; 55 // etc., etc. + + // the users have NOT been injected into the data store because we bypassed the data store + User.filter(params); // [] +}); + +// normally you would go through the data store +User.findAll(params).then(function (users) { + // users[0].age; 55 // etc., etc. + + // the users have been injected into the data store + User.filter(params); // [{...}, {...}, ...] +}); + +// bypass the data store +adapter.find(ADocument, 5).then(function (document:any) { + document; // { id: 5, author: 'John Anderson' } + + // the document has NOT been injected into the data store because we bypassed the data store + ADocument.get(document.id); // undefined +}); + +// Normally you would just go through the data store +ADocument.find(5).then(function (document:any) { + document; // { id: 5, author: 'John Anderson' } + + // the document has been injected into the data store + ADocument.get(document.id); // { id: 5, author: 'John Anderson' } +}); + +var params:any = { + author: 'John' +}; + +// bypass the data store +adapter.destroyAll(ADocument, params).then(function () { + // the documents have NOT been ejected from the data store because we bypassed the data store + ADocument.filter(params); // [{...}, {...}, ...] +}); + +// normally you would go through the data store +ADocument.destroyAll(params).then(function () { + // the documents have been ejected from the data store + ADocument.filter(params); // [] +}); + +ADocument.inject({ id: 5, author: 'John' }); + +// bypass the data store +adapter.destroy(ADocument, 5).then(function () { + // the document is still in the data store because we bypassed the data store + //ADocument.get(document.id); // { id: 5, author: 'John' } +}); + +// Normally you would just go through the data store +ADocument.destroy(5).then(function () { + // the document has been ejected from the data store + //ADocument.get(document.id); // undefined +}); + +adapter.DEL('/user/1').then(function (data) { + data.data; // 1 + data.headers; // {...} + data.status; // 204 + data.config; //{...} +}); + +// bypass the data store +adapter.create(ADocument, { author: 'John' }).then(function (document:any) { + document; // { id: 5, author: 'John' } + + // The new document has NOT been injected into the data store because we bypassed the data store + ADocument.get(document.id); // undefined +}); + +// Normally you would just go through the data store +ADocument.create({ author: 'John' }).then(function (document:any) { + document; // { id: 5, author: 'John' } + + // the new document has been injected into the data store + ADocument.get(document.id); // { id: 5, author: 'John' } +}); \ No newline at end of file diff --git a/js-data-http/js-data-http.d.ts b/js-data-http/js-data-http.d.ts new file mode 100644 index 000000000..416aa3c8c --- /dev/null +++ b/js-data-http/js-data-http.d.ts @@ -0,0 +1,46 @@ +// Type definitions for JSData Http Adapter v1.2.0 +// Project: https://github.com/js-data/js-data-http +// Definitions by: Stefan Steinhart +// Definitions: https://github.com/borisyankov/DefinitelyTyped + +/// + +declare module JSData { + + interface DSHttpAdapterOptions { + serialize?: (resourceName:string, data:any)=>any; + deserialize?: (resourceName:string, data:any)=>any; + queryTransform?: (resourceName:string, params:DSFilterParams)=>any; + httpConfig?: any; + forceTrailingSlash?: boolean; + log?: any; + // TODO wait for union types to be supported + // log: (message?: any, ...optionalParams: any[])=> void; + // log: boolean; + error?: any; + // TODO wait for union types to be supported + // error: (message?: any, ...optionalParams: any[])=> void; + // error: boolean; + } + + interface DSHttpAdapterPromiseResolveType { + data: any; + headers: any; + status: number; + config: any; + } + + interface DSHttpAdapter extends IDSAdapter { + + new(options?:DSHttpAdapterOptions):DSHttpAdapter; + + // DSHttpAdapter uses axios so options are axios config objects. + HTTP(options?:Object):JSDataPromise; + DEL(url:string, data?:Object, options?:Object):JSDataPromise; + GET(url:string, data?:Object, options?:Object):JSDataPromise; + POST(url:string, data?:Object, options?:Object):JSDataPromise; + PUT(url:string, data?:Object, options?:Object):JSDataPromise; + } +} + +declare var DSHttpAdapter:JSData.DSHttpAdapter; \ No newline at end of file diff --git a/js-data/js-data-node-tests.ts b/js-data/js-data-node-tests.ts new file mode 100644 index 000000000..a0b39a161 --- /dev/null +++ b/js-data/js-data-node-tests.ts @@ -0,0 +1,17 @@ +/// + +import JSData = require('js-data'); +//TODO +//import DSRedisAdapter = require('js-data-redis') +var store = new JSData.DS(); + +// register and use http by default for async operations +//TODO +//store.registerAdapter('redis', new DSRedisAdapter(), {default: true}); + +// simplest model definition +var User = store.defineResource('user'); + +User.find(1).then(function (user: any) { + user; // { id: 1, name: 'John' } +}); \ No newline at end of file diff --git a/js-data/js-data-tests.ts b/js-data/js-data-tests.ts new file mode 100644 index 000000000..c6fe6d13b --- /dev/null +++ b/js-data/js-data-tests.ts @@ -0,0 +1,524 @@ +/// + +interface IUser { + id?: number; + name?: string; + age?: number; + first?: string; + last?: string; + comments?:Array; + profile?:any; +} + +interface IUserWithMethod extends IUser { + fullName?: () => string; +} + +interface IUserWithComputedProperty extends IUser { + fullName?: string; +} + +var store = new JSData.DS(); + +// register and use http by default for async operations +//TODO +//store.registerAdapter('http', new DSHttpAdapter(), {default: true}); + +// simplest model definition +var User = store.defineResource('user'); + +User.find(1).then(function (user:IUser) { + user; // { id: 1, name: 'John' } +}); + +var user:IUser = User.createInstance({name: 'John'}); + +var store = new JSData.DS(); +var User = store.defineResource('user'); +var user:IUser = User.inject({id: 1, name: 'John'}); +var user2:IUser = User.inject({id: 1, age: 30}); + +user; // User { id: 1, name: 'John', age: 30 } +user2; // User { id: 1, name: 'John', age: 30 } +User.get(1); // User { id: 1, name: 'John', age: 30 } +user === user2; // true +user === User.get(1); // true +user2 === User.get(1); // true + +var store = new JSData.DS({ + // set a default lifecycle hook + afterCreate: function () { + } +}); + +var User = store.defineResource({ + name: 'user', + // override the hook for this resource + afterCreate: function () { + } +}); + +User.create({ + name: 'john' +}, { + // override the hook just for this method call + afterCreate: function () { + } +}).then(()=> { + +}); + +var store = new JSData.DS(); + +var UserWithMethod = store.defineResource({ + name: 'user', + methods: { + fullName: function () { + return this.first + ' ' + this.last; + } + } +}); + +var userWithMethod = UserWithMethod.createInstance({first: 'John', last: 'Anderson'}); + +userWithMethod.fullName(); // "John Anderson" + +var store = new JSData.DS(); + +var UserWithComputedProperty = store.defineResource({ + name: 'user', + computed: { + // each function's argument list defines the fields + // that the computed property depends on + fullName: ['first', 'last', function (first:string, last:string) { + return first + ' ' + last; + }], + // shortand, use the array syntax above if you want + // you computed properties to work after you've + // minified your code. Shorthand style won't work when minified + initials: function (first:string, last:string) { + return first.toUpperCase()[0] + '. ' + last.toUpperCase()[0] + '.'; + } + } +}); + +var userWithComputedProperty:IUserWithComputedProperty = UserWithComputedProperty.inject({ + id: 1, + first: 'John', + last: 'Anderson' +}); + +userWithComputedProperty.fullName; // "John Anderson" + +userWithComputedProperty.first = 'Fred'; + +// js-data relies on dirty-checking, so the +// computed property (probably) hasn't been updated yet +userWithComputedProperty.fullName; // "John Anderson" + +// If your browser supports Object.observe this will have no effect +// otherwise it will trigger the dirty-checking +store.digest(); + +userWithComputedProperty.fullName; // "Fred Anderson" + +interface IComment { + comments?: any; + profile?: any; +} + +var aComment:JSData.DSResourceDefinition = store.defineResource('comment'); + +// Get all comments where comment.userId == 5 +aComment.filter({ + where: { + userId: { + '==': 5 + } + } +}); + +// Get all comments where comment.userId == 5 +aComment.filter({ + userId: 5 +}); + +// Get all comments where comment.userId === 5 +aComment.filter({ + where: { + userId: { + '===': 5 + } + } +}); + +// Get all comments where comment.userId != 5 +aComment.filter({ + where: { + userId: { + '!=': 5 + } + } +}); + +// Get all comments where comment.userId !== 5 +aComment.filter({ + where: { + userId: { + '!==': 5 + } + } +}); + +// Get all users where user.age > 30 +User.filter({ + where: { + age: { + '>': 30 + } + } +}); + +// Get all users where user.age >= 30 +User.filter({ + where: { + age: { + '>=': 30 + } + } +}); + +// Get all users where user.age < 30 +User.filter({ + where: { + age: { + '<': 30 + } + } +}); + +// Get all users where user.name is in "John Anderson" +User.filter({ + where: { + name: { + 'in': 'John Anderson' + } + } +}); + +// Get all users where user.role is in ["admin", "owner"] +User.filter({ + where: { + role: { + 'in': ['admin', 'owner'] + } + } +}); + +// Get all users where user.name is NOT in "John Anderson" +User.filter({ + where: { + name: { + 'notIn': 'John Anderson' + } + } +}); + +// Get all users where user.role is NOT in ["admin", "owner"] +User.filter({ + where: { + role: { + 'notIn': ['admin', 'owner'] + } + } +}); + +// Get all users where user.name contains "John" +User.filter({ + where: { + name: { + 'contains': 'John' + } + } +}); + +// Get all users where user.roles contains "admin" +User.filter({ + where: { + roles: { + 'contains': 'admin' + } + } +}); + +// Sorts users by age in ascending order +User.filter({ + orderBy: 'age' +}); + +// Sorts users by age in descending order +User.filter({ + orderBy: ['age', 'DESC'] +}); + +// Sorts users by age in descending order and then sort by name in ascending order to break a tie +User.filter({ + orderBy: [ + ['age', 'DESC'], + ['name', 'ASC'] + ] +}); + +var PAGE_SIZE = 20; +var currentPage = 1; + +interface IPost { + +} + +var Post:JSData.DSResourceDefinition; + +// Grab the first "page" of posts +Post.filter({ + offset: PAGE_SIZE * (currentPage - 1), + limit: PAGE_SIZE +}); + +var User = store.defineResource({ + name: 'user', + relations: { + hasMany: { + comment: { + localField: 'comments', + foreignKey: 'userId' + } + }, + hasOne: { + profile: { + localField: 'profile', + foreignKey: 'userId' + } + }, + belongsTo: { + organization: { + localKey: 'organizationId', + localField: 'organization', + + // if you add this to a belongsTo relation + // then js-data will attempt to use + // a nested url structure, e.g. /organization/15/user/4 + parent: true + } + } + } +}); + +var Organization = store.defineResource({ + name: 'organization', + relations: { + hasMany: { + // this is an example of multiple relations + // of the same type to the same resource + user: [ + { + localField: 'users', + foreignKey: 'organizationId' + }, + { + localField: 'owners', + foreignKey: 'organizationId' + } + ] + } + } +}); + +var Profile = store.defineResource({ + name: 'profile', + relations: { + belongsTo: { + user: { + localField: 'user', + localKey: 'userId' + } + } + } +}); + +var OtherComment = store.defineResource({ + name: 'comment', + relations: { + belongsTo: { + user: { + localField: 'user', + localKey: 'userId' + } + } + } +}); + +User.find(10).then(function (user:IUser) { + // let's assume the server only returned the user + user.comments; // undefined + user.profile; // undefined + + User.loadRelations(user, ['comment', 'profile']).then(function (user:IUser) { + user.comments; // array + user.profile; // object + }); +}); + +var OtherOtherComment = store.defineResource({ + name: 'comment', + relations: { + belongsTo: { + post: { + parent: true, + localKey: 'postId', + localField: 'post' + } + } + } +}); + +// The comment isn't in the data store yet, so js-data wouldn't know +// what the id of the parent "post" would be, so we pass it in manually +OtherOtherComment.find(5, {params: {postId: 4}}); // GET /post/4/comment/5 + +// vs + +var promise = OtherOtherComment.find(5); // GET /comment/5 + +promise.then().catch().finally(); + +OtherOtherComment.inject({id: 1, postId: 2}); + +// We don't have to provide the parentKey here +// because js-data found it in the comment +OtherOtherComment.update(1, {content: 'stuff'}); // PUT /post/2/comment/1 + +// If you don't want the nested for just one of the calls then +// you can do the following: +OtherOtherComment.update(1, {content: 'stuff'}, {params: {postId: false}}); // PUT /comment/1 + +var store = new JSData.DS({ + // set the default + beforeCreate: function (resource, data, cb) { + // do something general + cb(null, data); + } +}); + +var User = store.defineResource({ + name: 'user', + // set just for this resource + beforeCreate: function (resource, data, cb) { + // do something more specific to "users" + cb(null, data); + } +}); + +User.create({name: 'John'}, { + // set just for this method call + beforeCreate: function (resource, data, cb) { + // do something specific for this method call + cb(null, data); + } +}); + +module CustomAdapterTest { + + class MyCustomAdapter implements JSData.IDSAdapter { + + // All of the methods shown here must return a promise + +// "definition" is a resource defintion that would +// be returned by DS#defineResource + +// "options" would be the options argument that +// was passed into the DS method that is calling +// the adapter method + + create(definition:JSData.DSResourceDefinition, attrs:Object, options:JSData.DSConfiguration):JSData.JSDataPromise { + // Must resolve the promise with the created item + + var promise:JSData.JSDataPromise; + return promise; + } + + find(definition:JSData.DSResourceDefinition, id:any, options:JSData.DSConfiguration):JSData.JSDataPromise { + // Must resolve the promise with the found item + + var promise:JSData.JSDataPromise; + return promise; + } + + findAll(definition:JSData.DSResourceDefinition, params:JSData.DSFilterParams, options:JSData.DSConfiguration):JSData.JSDataPromise { + // Must resolve the promise with the found items + + var promise:JSData.JSDataPromise; + return promise; + } + + update(definition:JSData.DSResourceDefinition, id:any, attrs:Object, options:JSData.DSConfiguration):JSData.JSDataPromise { + // Must resolve the promise with the updated items + + var promise:JSData.JSDataPromise; + return promise; + } + + updateAll(definition:JSData.DSResourceDefinition, attrs:Object, params:JSData.DSFilterParams, options:JSData.DSConfiguration):JSData.JSDataPromise { + // Must resolve the promise with the updated items + + var promise:JSData.JSDataPromise; + return promise; + } + + destroy(definition:JSData.DSResourceDefinition, id:any, options:JSData.DSConfiguration):JSData.JSDataPromise { + // Must return a promise + + var promise:JSData.JSDataPromise; + return promise; + } + + destroyAll(definition:JSData.DSResourceDefinition, params:JSData.DSFilterParams, options:JSData.DSConfiguration):JSData.JSDataPromise { + // Must return a promise + + var promise:JSData.JSDataPromise; + return promise; + } + } + + var store = new JSData.DS(); + store.registerAdapter('mca', new MyCustomAdapter(), {default: true}); + // the data store will now use your custom adapter by default +} + +/** + * showing the use of open ended interface to realize typings + * on the Datastore.definitions object where all resource definitions + * are saved. + */ + +interface MyCustomDataStore { + + myResource: JSData.DSResourceDefinition +} + +interface MyResourceDefinition { + +} + +module JSData { + + interface DS { + + definitions: MyCustomDataStore; + } +} + +var store = new JSData.DS(); + +var myResourceDefinition = store.defineResource('myResource'); + +myResourceDefinition = store.definitions.myResource; \ No newline at end of file diff --git a/js-data/js-data.d.ts b/js-data/js-data.d.ts new file mode 100644 index 000000000..7017481ee --- /dev/null +++ b/js-data/js-data.d.ts @@ -0,0 +1,407 @@ +// Type definitions for JSData v1.3.0 +// Project: https://github.com/js-data/js-data +// Definitions by: Stefan Steinhart +// Definitions: https://github.com/borisyankov/DefinitelyTyped + +/////////////////////////////////////////////////////////////////////////////// +// js-data module (js-data.js) +/////////////////////////////////////////////////////////////////////////////// + +// defining what exists in JSData and how it looks +declare module JSData { + + interface JSDataPromise { + + then(onFulfilled?: (value: R) => U | JSDataPromise, onRejected?: (error: any) => U | JSDataPromise): JSDataPromise; + + catch(onRejected?: (error: any) => U | JSDataPromise): JSDataPromise; + + // enhanced with finally + finally(finallyCb?:() => U):JSDataPromise; + } + + //TODO switch to class again when typescript supports open ended class declaration + interface DS { + + new(config?:DSConfiguration):DS; + + // rather undocumented + errors:DSErrors; + + // those are objects containing the defined resources and adapters + definitions:any; + adapters:any; + + defaults:DSConfiguration; + + changeHistory(resourceName:string, id?:string):Array; + changeHistory(resourceName:string, id?:number):Array; + + changes(resourceName:string, id:string):Object; + changes(resourceName:string, id:number):Object; + + compute(resourceName:string, id:number):T; + compute(resourceName:string, id:string):T; + compute(resourceName:string, instance:Object):T; + + create(resourceName:string, attrs:any, options?:DSConfiguration):JSDataPromise; + + createInstance(resourceName:string, attrs?:T, options?:DSAdapterOperationConfiguration):T; + + defineResource(resourceName:string):DSResourceDefinition; + defineResource(definition:DSResourceDefinitionConfiguration):DSResourceDefinition; + + destroy(resourceName:string, id:string, options?:DSAdapterOperationConfiguration):JSDataPromise; + destroy(resourceName:string, id:number, options?:DSAdapterOperationConfiguration):JSDataPromise; + + destroyAll(resourceName:string, params?:DSFilterParams, options?:DSAdapterOperationConfiguration):JSDataPromise; + + digest():void; + + eject(resourceName:string, id:string, options?:DSConfiguration):T; + eject(resourceName:string, id:number, options?:DSConfiguration):T; + + ejectAll(resourceName:string, params:DSFilterParams, options?:DSConfiguration):Array; + + filter(resourceName:string, params:DSFilterParams, options?:DSConfiguration):Array; + + find(resourceName:string, id:string, options?:DSAdapterOperationConfiguration):JSDataPromise; + find(resourceName:string, id:number, options?:DSAdapterOperationConfiguration):JSDataPromise; + + findAll(resourceName:string, params?:DSFilterParams, options?:DSAdapterOperationConfiguration):JSDataPromise>; + + get(resourceName:string, id:string, options?:DSConfiguration):T; + get(resourceName:string, id:number, options?:DSConfiguration):T; + + getAll(resourceName:string, ids?:Array):Array; + getAll(resourceName:string, ids?:Array):Array; + + hasChanges(resourceName:string, id:string):boolean; + hasChanges(resourceName:string, id:number):boolean; + + inject(resourceName:string, attrs:T, options?:DSConfiguration):T; + inject(resourceName:string, items:Array, options?:DSConfiguration):Array; + + is(resourceName:string, object:Object): boolean; + + lastModified(resourceName:string, id?:string):number; // timestamp + lastModified(resourceName:string, id?:number):number; // timestamp + + lastSaved(resourceName:string, id?:string):number; // timestamp + lastSaved(resourceName:string, id?:number):number; // timestamp + + link(resourceName:string, id:string, relations?:Array):T; + link(resourceName:string, id:number, relations?:Array):T; + + linkAll(resourceName:string, params:DSFilterParams, relations?:Array):T; + + linkInverse(resourceName:string, id:string, relations?:Array):T; + linkInverse(resourceName:string, id:number, relations?:Array):T; + + loadRelations(resourceName:string, id:string, relations:string, options?:DSAdapterOperationConfiguration):JSDataPromise; + loadRelations(resourceName:string, id:number, relations:string, options?:DSAdapterOperationConfiguration):JSDataPromise; + loadRelations(resourceName:string, instance:Object, relations:string, options?:DSAdapterOperationConfiguration):JSDataPromise; + loadRelations(resourceName:string, id:string, relations:Array, options?:DSAdapterOperationConfiguration):JSDataPromise; + loadRelations(resourceName:string, id:number, relations:Array, options?:DSAdapterOperationConfiguration):JSDataPromise; + loadRelations(resourceName:string, instance:Object, relations:Array, options?:DSAdapterOperationConfiguration):JSDataPromise; + + previous(resourceName:string, id:string):T; + previous(resourceName:string, id:number):T; + + reap(resourceName:string, options?:DSConfiguration):JSDataPromise; + + refresh(resourceName:string, id:string, options?:DSAdapterOperationConfiguration):JSDataPromise; + refresh(resourceName:string, id:number, options?:DSAdapterOperationConfiguration):JSDataPromise; + + registerAdapter(adapterId:string, adapter:IDSAdapter, options?:{default: boolean}):void; + + save(resourceName:string, id:string, options?:DSSaveConfiguration):JSDataPromise; + save(resourceName:string, id:number, options?:DSSaveConfiguration):JSDataPromise; + + unlinkInverse(resourceName:string, id:string, relations?:Array):T; + unlinkInverse(resourceName:string, id:number, relations?:Array):T; + + update(resourceName:string, id:string, attrs:any, options?:DSSaveConfiguration):JSDataPromise; + update(resourceName:string, id:number, attrs:any, options?:DSSaveConfiguration):JSDataPromise; + + updateAll(resourceName:string, attrs:any, params?:DSFilterParams, options?:DSAdapterOperationConfiguration):JSDataPromise>; + } + + interface DSConfiguration extends IDSResourceLifecycleEventHandlers { + actions?: Object; + allowSimpleWhere?: boolean; + basePath?: string; + bypassCache?: boolean; + cacheResponse?: boolean; + defaultAdapter?: string; + defaultFilter?: (collection:Array, resourceName:string, params:DSFilterParams, options:DSConfiguration)=>Array; + eagerEject?: boolean; + // TODO enable when eagerInject in DS#create is implemented + //eagerInject?: boolean; + endpoint?: string; + error?: (message?:any, ...optionalParams:any[])=> void; + fallbackAdapters?: Array; + findAllFallbackAdapters?: Array; + findAllStrategy?: string; + findBelongsTo?: boolean; + findFallbackAdapters?: Array; + findHasOne?: boolean; + findHasMany?: boolean; + findInverseLinks?: boolean; + findStrategy?: string + idAttribute?: string; + ignoredChanges?: Array; + // TODO ignoreMissing is undocumented + //ignoreMissing: boolean; + keepChangeHistory?: boolean; + loadFromServer?: boolean; + log?: any; + // TODO wait for union types to be supported + // log: (message?: any, ...optionalParams: any[])=> void; + // log: boolean; + maxAge?: number; + notify?: boolean; + reapAction?: string; + reapInterval?: number; + resetHistoryOnInject?: boolean; + strategy?: string; + upsert?: boolean; + useClass?: boolean; + useFilter?: boolean; + } + + interface DSAdapterOperationConfiguration extends DSConfiguration { + adapter?: string + } + + interface DSSaveConfiguration extends DSAdapterOperationConfiguration { + changesOnly?: boolean; + } + + interface DSResourceDefinitionConfiguration extends DSConfiguration { + name: string; + computed?: any; + methods?: any; + relations?: { + hasMany?: Object; + hasOne?: Object; + belongsTo?: Object; + }; + } + + interface DSResourceDefinition extends DSResourceDefinitionConfiguration { + + changeHistory(id?:string):Array; + changeHistory(id?:number):Array; + + changes(id:string):Object; + changes(id:number):Object; + + compute(id:number):T; + compute(id:string):T; + compute(instance:Object):T; + + create(attrs:any, options?:DSConfiguration):JSDataPromise; + + createInstance(attrs?:T, options?:DSAdapterOperationConfiguration):T; + + defineResource(resourceName:string):DSResourceDefinition; + defineResource(definition:DSResourceDefinitionConfiguration):DSResourceDefinition; + + destroy(id:string, options?:DSAdapterOperationConfiguration):JSDataPromise; + destroy(id:number, options?:DSAdapterOperationConfiguration):JSDataPromise; + + destroyAll(params?:DSFilterParams, options?:DSAdapterOperationConfiguration):JSDataPromise; + + digest():void; + + eject(id:string, options?:DSConfiguration):T; + eject(id:number, options?:DSConfiguration):T; + + ejectAll(params:DSFilterParams, options?:DSConfiguration):Array; + + filter(params:DSFilterParams, options?:DSConfiguration):Array; + + find(id:string, options?:DSAdapterOperationConfiguration):JSDataPromise; + find(id:number, options?:DSAdapterOperationConfiguration):JSDataPromise; + + findAll(params?:DSFilterParams, options?:DSAdapterOperationConfiguration):JSDataPromise>; + + get(id:string, options?:DSConfiguration):T; + get(id:number, options?:DSConfiguration):T; + + getAll(ids?:Array):Array; + getAll(ids?:Array):Array; + + hasChanges(id:string):boolean; + hasChanges(id:number):boolean; + + inject(attrs:T, options?:DSConfiguration):T; + inject(items:Array, options?:DSConfiguration):Array; + + is(object:Object): boolean; + + lastModified(id?:string):number; // timestamp + lastModified(id?:number):number; // timestamp + + lastSaved(id?:string):number; // timestamp + lastSaved(id?:number):number; // timestamp + + link(id:string, relations?:Array):T; + link(id:number, relations?:Array):T; + + linkAll(params:DSFilterParams, relations?:Array):T; + + linkInverse(id:string, relations?:Array):T; + linkInverse(id:number, relations?:Array):T; + + loadRelations(id:string, relations:string, options?:DSAdapterOperationConfiguration):JSDataPromise; + loadRelations(id:number, relations:string, options?:DSAdapterOperationConfiguration):JSDataPromise; + loadRelations(instance:T, relations:string, options?:DSAdapterOperationConfiguration):JSDataPromise; + loadRelations(id:string, relations:Array, options?:DSAdapterOperationConfiguration):JSDataPromise; + loadRelations(id:number, relations:Array, options?:DSAdapterOperationConfiguration):JSDataPromise; + loadRelations(instance:T, relations:Array, options?:DSAdapterOperationConfiguration):JSDataPromise; + + previous(id:string):T; + previous(id:number):T; + + reap(options?:DSConfiguration):JSDataPromise; + + refresh(id:string, options?:DSAdapterOperationConfiguration):JSDataPromise; + refresh(id:number, options?:DSAdapterOperationConfiguration):JSDataPromise; + + save(id:string, options?:DSSaveConfiguration):JSDataPromise; + save(id:number, options?:DSSaveConfiguration):JSDataPromise; + + unlinkInverse(id:string, relations?:Array):T; + unlinkInverse(id:number, relations?:Array):T; + + update(id:string, attrs:any, options?:DSSaveConfiguration):JSDataPromise; + update(id:number, attrs:any, options?:DSSaveConfiguration):JSDataPromise; + + updateAll(attrs:any, params?:DSFilterParams, options?:DSAdapterOperationConfiguration):JSDataPromise>; + } + + interface DSFilterParams { + where?: Object; + + limit?: number; + + skip?: number; + offset?: number; + + orderBy?: any; + // TODO wait for union types to be supported + //orderBy?: Array>; + //orderBy?: Array; + //orderBy?: string; + + sort?: any; + // TODO wait for union types to be supported + //sort?: string; + //sort?: Array; + //sort?: Array>; + } + + interface IDSResourceLifecycleValidateEventHandlers { + beforeValidate?: (resourceName:string, data:any, cb:(err:any, data?:any)=>void)=>void; + validate?: (resourceName:string, data:any, cb:(err:any, data?:any)=>void)=>void; + afterValidate?: (resourceName:string, data:any, cb:(err:any, data?:any)=>void)=>void; + } + + interface IDSResourceLifecycleCreateEventHandlers { + beforeCreate?: (resourceName:string, data:any, cb:(err:any, data?:any)=>void)=>void; + afterCreate?: (resourceName:string, data:any, cb:(err:any, data?:any)=>void)=>void; + } + + interface IDSResourceLifecycleCreateInstanceEventHandlers { + beforeCreateInstance?: (resourceName:string, data:any)=>void; + afterCreateInstance?: (resourceName:string, data:any)=>void; + } + + interface IDSResourceLifecycleUpdateEventHandlers { + beforeUpdate?: (resourceName:string, data:any, cb:(err:any, data?:any)=>void)=>void; + afterUpdate?: (resourceName:string, data:any, cb:(err:any, data?:any)=>void)=>void; + } + + interface IDSResourceLifecycleDestroyEventHandlers { + beforeDestroy?: (resourceName:string, data:any, cb:(err:any, data?:any)=>void)=>void; + afterDestroy?: (resourceName:string, data:any, cb:(err:any, data?:any)=>void)=>void; + } + + interface IDSResourceLifecycleInjectEventHandlers { + beforeInject?: (resourceName:string, data:any)=>void; + afterInject?: (resourceName:string, data:any)=>void; + } + + interface IDSResourceLifecycleEjectEventHandlers { + beforeEject?: (resourceName:string, data:any)=>void; + afterEject?: (resourceName:string, data:any)=>void; + } + + interface IDSResourceLifecycleReapEventHandlers { + beforeReap?: (resourceName:string, data:any)=>void; + afterReap?: (resourceName:string, data:any)=>void; + } + + interface IDSResourceLifecycleEventHandlers extends IDSResourceLifecycleCreateEventHandlers, + IDSResourceLifecycleCreateInstanceEventHandlers, + IDSResourceLifecycleValidateEventHandlers, + IDSResourceLifecycleUpdateEventHandlers, + IDSResourceLifecycleDestroyEventHandlers, + IDSResourceLifecycleInjectEventHandlers, + IDSResourceLifecycleEjectEventHandlers, + IDSResourceLifecycleReapEventHandlers { + + } + + // errors + interface DSErrors { + + // types + IllegalArgumentError:DSError; + IA:DSError; + RuntimeError:DSError; + R:DSError; + NonexistentResourceError:DSError; + NER:DSError; + } + + interface DSError extends Error { + new (message?:string):DSError; + message: string; + type: string; + } + + // DSAdapter interface + interface IDSAdapter { + create(config:DSResourceDefinition, attrs:any, options?:DSConfiguration):JSDataPromise; + + destroy(config:DSResourceDefinition, id:string, options?:DSConfiguration):JSDataPromise; + destroy(config:DSResourceDefinition, id:number, options?:DSConfiguration):JSDataPromise; + + destroyAll(config:DSResourceDefinition, params:DSFilterParams, options?:DSConfiguration):JSDataPromise; + + find(config:DSResourceDefinition, id:string, options?:DSConfiguration):JSDataPromise; + find(config:DSResourceDefinition, id:number, options?:DSConfiguration):JSDataPromise; + + findAll(config:DSResourceDefinition, params?:DSFilterParams, options?:DSConfiguration):JSDataPromise; + + update(config:DSResourceDefinition, id:string, attrs:any, options?:DSConfiguration):JSDataPromise; + update(config:DSResourceDefinition, id:number, attrs:any, options?:DSConfiguration):JSDataPromise; + + updateAll(config:DSResourceDefinition, attrs:any, params?:DSFilterParams, options?:DSConfiguration):JSDataPromise; + } +} + +// declaring the existing global js object +declare var JSData:{ + DS: JSData.DS; + DSErrors: JSData.DSErrors; +}; + +//Support node require +declare module 'js-data' { + + export = JSData; +} \ No newline at end of file