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..670857dec
--- /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
index a7c035722..851512d15 100644
--- a/js-data-angular/js-data-angular.d.ts
+++ b/js-data-angular/js-data-angular.d.ts
@@ -6,14 +6,25 @@
///
///
-declare module JSData {
+declare module JSData_ {
- class ngDS extends DS {
+ interface DSProvider {
+ defaults:DSConfiguration;
+ }
- // sync methods
- bindAll(scope:ng.IScope, expr:string, resourceName:string, params:DSFilterParams, cb?:(err:DSError, items:Array)=>void):Function;
+ interface DS {
- bindOne(scope:ng.IScope, expr:string, resourceName:string, id:string, cb?:(err:DSError, item:T)=>void):Function;
- bindOne(scope:ng.IScope, expr:string, resourceName:string, id:number, cb?:(err:DSError, item:T)=>void):Function;
+ 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/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..1cb3e8e72
--- /dev/null
+++ b/js-data/js-data-tests.ts
@@ -0,0 +1,493 @@
+///
+
+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
+
+OtherOtherComment.find(5); // GET /comment/5
+
+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
+}
\ No newline at end of file
diff --git a/js-data/js-data.d.ts b/js-data/js-data.d.ts
index ce06edd89..13fc5e691 100644
--- a/js-data/js-data.d.ts
+++ b/js-data/js-data.d.ts
@@ -9,35 +9,29 @@
///
-declare class JSDataPromise extends Promise {
-
- // enhanced with finally
- finally(finallyCb?: () => U): Promise;
-}
-
///////////////////////////////////////////////////////////////////////////////
// js-data module (js-data.js)
///////////////////////////////////////////////////////////////////////////////
-// Support AMD require
-declare module 'js-data' {
- export = JSData;
-}
+// defining what exists in JSData and how it looks
+declare module JSData_ {
-declare module JSData {
+ class JSDataPromise extends Promise {
- class DS {
+ // enhanced with finally
+ finally(finallyCb?:() => U):Promise;
+ }
- constructor(config?:DSConfiguration);
+ //TODO switch to class again when typescript supports open ended class declaration
+ interface DS {
+
+ new(config?:DSConfiguration):DS;
+
+ // rather undocumented
+ errors:DSErrors;
defaults:DSConfiguration;
- //TODO check if still exists
- adapters:any; // Object consists of key-values pairs where the key is the name of the adapter and the value is
- // the adapter itself.
- //TODO check if still exists
- errors:DSErrors;
-
changeHistory(resourceName:string, id?:string):Array