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