Full Code of aws/amazon-cognito-js for AI

master 88d50ebdb19a cached
18 files
96.7 KB
20.9k tokens
1 requests
Download .txt
Repository: aws/amazon-cognito-js
Branch: master
Commit: 88d50ebdb19a
Files: 18
Total size: 96.7 KB

Directory structure:
gitextract_ktd83ej4/

├── .gitignore
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── Gruntfile.js
├── LICENSE.txt
├── NOTICE.txt
├── README.md
├── package.json
└── src/
    ├── CognitoSyncConflict.js
    ├── CognitoSyncDataset.js
    ├── CognitoSyncDatasetMetadata.js
    ├── CognitoSyncDatasetUpdates.js
    ├── CognitoSyncLocalStorage.js
    ├── CognitoSyncManager.js
    ├── CognitoSyncRecord.js
    ├── CognitoSyncRemoteStorage.js
    ├── CognitoSyncStoreInMemory.js
    └── CognitoSyncStoreLocalStorage.js

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
node_modules/
.idea/
*-private.html

================================================
FILE: CODE_OF_CONDUCT.md
================================================
## Code of Conduct
This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 
For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 
opensource-codeofconduct@amazon.com with any additional questions or comments.


================================================
FILE: CONTRIBUTING.md
================================================
# Contributing Guidelines

Thank you for your interest in contributing to our project. Whether it's a bug report, new feature, correction, or additional 
documentation, we greatly value feedback and contributions from our community.

Please read through this document before submitting any issues or pull requests to ensure we have all the necessary 
information to effectively respond to your bug report or contribution.


## Reporting Bugs/Feature Requests

We welcome you to use the GitHub issue tracker to report bugs or suggest features.

When filing an issue, please check [existing open](https://github.com/aws/amazon-cognito-js/issues), or [recently closed](https://github.com/aws/amazon-cognito-js/issues?utf8=%E2%9C%93&q=is%3Aissue%20is%3Aclosed%20), issues to make sure somebody else hasn't already 
reported the issue. Please try to include as much information as you can. Details like these are incredibly useful:

* A reproducible test case or series of steps
* The version of our code being used
* Any modifications you've made relevant to the bug
* Anything unusual about your environment or deployment


## Contributing via Pull Requests
Contributions via pull requests are much appreciated. Before sending us a pull request, please ensure that:

1. You are working against the latest source on the *master* branch.
2. You check existing open, and recently merged, pull requests to make sure someone else hasn't addressed the problem already.
3. You open an issue to discuss any significant work - we would hate for your time to be wasted.

To send us a pull request, please:

1. Fork the repository.
2. Modify the source; please focus on the specific change you are contributing. If you also reformat all the code, it will be hard for us to focus on your change.
3. Ensure local tests pass.
4. Commit to your fork using clear commit messages.
5. Send us a pull request, answering any default questions in the pull request interface.
6. Pay attention to any automated CI failures reported in the pull request, and stay involved in the conversation.

GitHub provides additional document on [forking a repository](https://help.github.com/articles/fork-a-repo/) and 
[creating a pull request](https://help.github.com/articles/creating-a-pull-request/).


## Finding contributions to work on
Looking at the existing issues is a great way to find something to contribute on. As our projects, by default, use the default GitHub issue labels ((enhancement/bug/duplicate/help wanted/invalid/question/wontfix), looking at any ['help wanted'](https://github.com/aws/amazon-cognito-js/labels/help%20wanted) issues is a great place to start. 


## Code of Conduct
This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 
For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 
opensource-codeofconduct@amazon.com with any additional questions or comments.


## Security issue notifications
If you discover a potential security issue in this project we ask that you notify AWS/Amazon Security via our [vulnerability reporting page](http://aws.amazon.com/security/vulnerability-reporting/). Please do **not** create a public github issue.


## Licensing

See the [LICENSE](https://github.com/aws/amazon-cognito-js/blob/master/LICENSE) file for our project's licensing. We will ask you to confirm the licensing of your contribution.

We may ask you to sign a [Contributor License Agreement (CLA)](http://en.wikipedia.org/wiki/Contributor_License_Agreement) for larger changes.


================================================
FILE: Gruntfile.js
================================================
module.exports = function(grunt) {

    grunt.initConfig({

        pkg: grunt.file.readJSON('package.json'),

        banner: '/**\n' +
        ' * Copyright 2014 Amazon.com,\n' +
        ' * Inc. or its affiliates. All Rights Reserved.\n' +
        ' * \n' +
        ' * Licensed under the Amazon Software License (the "License").\n' +
        ' * You may not use this file except in compliance with the\n' +
        ' * License. A copy of the License is located at\n' +
        ' * \n' +
        ' *     http://aws.amazon.com/asl/\n' +
        ' * \n' +
        ' * or in the "license" file accompanying this file. This file is\n' +
        ' * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR\n' +
        ' * CONDITIONS OF ANY KIND, express or implied. See the License\n' +
        ' * for the specific language governing permissions and\n' +
        ' * limitations under the License. \n' +
        ' */\n\n',

        qunit: {
            all: ['tst/**/*.html']
        },

        jshint: {

            options: {
                reporterOutput: '',
                browser: true,
                eqeqeq: true,
                globals: {
                    AWS: true
                },
                undef: true
            },

            src: ['src/*.js']

        },

        jsdoc: {
            dist: {
                src: ['src/*.js'],
                options: {
                    destination: 'docs'
                }
            }
        },

        uglify: {
            options: {
                sourceMap: true,
                drop_console: true,
                banner: '<%= banner %>'
            },
            dist: {
                files: {
                    "dist/amazon-cognito.min.js": [
                        'src/CognitoSyncManager.js',
                        'src/CognitoSyncConflict.js',
                        'src/CognitoSyncDataset.js',
                        'src/CognitoSyncDatasetMetadata.js',
                        'src/CognitoSyncDatasetUpdates.js',
                        'src/CognitoSyncLocalStorage.js',
                        'src/CognitoSyncRecord.js',
                        'src/CognitoSyncRemoteStorage.js',
                        'src/CognitoSyncStoreInMemory.js',
                        'src/CognitoSyncStoreLocalStorage.js'
                    ]
                }
            }
        },

        umd: {
            all: {
                options: {
                    src: 'dist/amazon-cognito.min.js',
                    deps: {
                        default: [{'aws-sdk/global': 'AWS'}],
                        global: [{'AWS': 'AWS'}]
                    }
                }
            }
        },

        watch: {
            scripts: {
                files: ['src/*.js'],
                tasks: ['uglify:dist']
            }
        }

    });

    grunt.loadNpmTasks('grunt-contrib-jshint');
    grunt.loadNpmTasks('grunt-contrib-uglify');
    grunt.loadNpmTasks('grunt-contrib-qunit');
    grunt.loadNpmTasks('grunt-contrib-watch');
    grunt.loadNpmTasks('grunt-jsdoc');
    grunt.loadNpmTasks('grunt-umd');

    grunt.registerTask('default', ['qunit']);

};

================================================
FILE: LICENSE.txt
================================================

                                 Apache License
                           Version 2.0, January 2004
                        http://www.apache.org/licenses/

   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

   1. Definitions.

      "License" shall mean the terms and conditions for use, reproduction,
      and distribution as defined by Sections 1 through 9 of this document.

      "Licensor" shall mean the copyright owner or entity authorized by
      the copyright owner that is granting the License.

      "Legal Entity" shall mean the union of the acting entity and all
      other entities that control, are controlled by, or are under common
      control with that entity. For the purposes of this definition,
      "control" means (i) the power, direct or indirect, to cause the
      direction or management of such entity, whether by contract or
      otherwise, or (ii) ownership of fifty percent (50%) or more of the
      outstanding shares, or (iii) beneficial ownership of such entity.

      "You" (or "Your") shall mean an individual or Legal Entity
      exercising permissions granted by this License.

      "Source" form shall mean the preferred form for making modifications,
      including but not limited to software source code, documentation
      source, and configuration files.

      "Object" form shall mean any form resulting from mechanical
      transformation or translation of a Source form, including but
      not limited to compiled object code, generated documentation,
      and conversions to other media types.

      "Work" shall mean the work of authorship, whether in Source or
      Object form, made available under the License, as indicated by a
      copyright notice that is included in or attached to the work
      (an example is provided in the Appendix below).

      "Derivative Works" shall mean any work, whether in Source or Object
      form, that is based on (or derived from) the Work and for which the
      editorial revisions, annotations, elaborations, or other modifications
      represent, as a whole, an original work of authorship. For the purposes
      of this License, Derivative Works shall not include works that remain
      separable from, or merely link (or bind by name) to the interfaces of,
      the Work and Derivative Works thereof.

      "Contribution" shall mean any work of authorship, including
      the original version of the Work and any modifications or additions
      to that Work or Derivative Works thereof, that is intentionally
      submitted to Licensor for inclusion in the Work by the copyright owner
      or by an individual or Legal Entity authorized to submit on behalf of
      the copyright owner. For the purposes of this definition, "submitted"
      means any form of electronic, verbal, or written communication sent
      to the Licensor or its representatives, including but not limited to
      communication on electronic mailing lists, source code control systems,
      and issue tracking systems that are managed by, or on behalf of, the
      Licensor for the purpose of discussing and improving the Work, but
      excluding communication that is conspicuously marked or otherwise
      designated in writing by the copyright owner as "Not a Contribution."

      "Contributor" shall mean Licensor and any individual or Legal Entity
      on behalf of whom a Contribution has been received by Licensor and
      subsequently incorporated within the Work.

   2. Grant of Copyright License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      copyright license to reproduce, prepare Derivative Works of,
      publicly display, publicly perform, sublicense, and distribute the
      Work and such Derivative Works in Source or Object form.

   3. Grant of Patent License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      (except as stated in this section) patent license to make, have made,
      use, offer to sell, sell, import, and otherwise transfer the Work,
      where such license applies only to those patent claims licensable
      by such Contributor that are necessarily infringed by their
      Contribution(s) alone or by combination of their Contribution(s)
      with the Work to which such Contribution(s) was submitted. If You
      institute patent litigation against any entity (including a
      cross-claim or counterclaim in a lawsuit) alleging that the Work
      or a Contribution incorporated within the Work constitutes direct
      or contributory patent infringement, then any patent licenses
      granted to You under this License for that Work shall terminate
      as of the date such litigation is filed.

   4. Redistribution. You may reproduce and distribute copies of the
      Work or Derivative Works thereof in any medium, with or without
      modifications, and in Source or Object form, provided that You
      meet the following conditions:

      (a) You must give any other recipients of the Work or
          Derivative Works a copy of this License; and

      (b) You must cause any modified files to carry prominent notices
          stating that You changed the files; and

      (c) You must retain, in the Source form of any Derivative Works
          that You distribute, all copyright, patent, trademark, and
          attribution notices from the Source form of the Work,
          excluding those notices that do not pertain to any part of
          the Derivative Works; and

      (d) If the Work includes a "NOTICE" text file as part of its
          distribution, then any Derivative Works that You distribute must
          include a readable copy of the attribution notices contained
          within such NOTICE file, excluding those notices that do not
          pertain to any part of the Derivative Works, in at least one
          of the following places: within a NOTICE text file distributed
          as part of the Derivative Works; within the Source form or
          documentation, if provided along with the Derivative Works; or,
          within a display generated by the Derivative Works, if and
          wherever such third-party notices normally appear. The contents
          of the NOTICE file are for informational purposes only and
          do not modify the License. You may add Your own attribution
          notices within Derivative Works that You distribute, alongside
          or as an addendum to the NOTICE text from the Work, provided
          that such additional attribution notices cannot be construed
          as modifying the License.

      You may add Your own copyright statement to Your modifications and
      may provide additional or different license terms and conditions
      for use, reproduction, or distribution of Your modifications, or
      for any such Derivative Works as a whole, provided Your use,
      reproduction, and distribution of the Work otherwise complies with
      the conditions stated in this License.

   5. Submission of Contributions. Unless You explicitly state otherwise,
      any Contribution intentionally submitted for inclusion in the Work
      by You to the Licensor shall be under the terms and conditions of
      this License, without any additional terms or conditions.
      Notwithstanding the above, nothing herein shall supersede or modify
      the terms of any separate license agreement you may have executed
      with Licensor regarding such Contributions.

   6. Trademarks. This License does not grant permission to use the trade
      names, trademarks, service marks, or product names of the Licensor,
      except as required for reasonable and customary use in describing the
      origin of the Work and reproducing the content of the NOTICE file.

   7. Disclaimer of Warranty. Unless required by applicable law or
      agreed to in writing, Licensor provides the Work (and each
      Contributor provides its Contributions) on an "AS IS" BASIS,
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
      implied, including, without limitation, any warranties or conditions
      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
      PARTICULAR PURPOSE. You are solely responsible for determining the
      appropriateness of using or redistributing the Work and assume any
      risks associated with Your exercise of permissions under this License.

   8. Limitation of Liability. In no event and under no legal theory,
      whether in tort (including negligence), contract, or otherwise,
      unless required by applicable law (such as deliberate and grossly
      negligent acts) or agreed to in writing, shall any Contributor be
      liable to You for damages, including any direct, indirect, special,
      incidental, or consequential damages of any character arising as a
      result of this License or out of the use or inability to use the
      Work (including but not limited to damages for loss of goodwill,
      work stoppage, computer failure or malfunction, or any and all
      other commercial damages or losses), even if such Contributor
      has been advised of the possibility of such damages.

   9. Accepting Warranty or Additional Liability. While redistributing
      the Work or Derivative Works thereof, You may choose to offer,
      and charge a fee for, acceptance of support, warranty, indemnity,
      or other liability obligations and/or rights consistent with this
      License. However, in accepting such obligations, You may act only
      on Your own behalf and on Your sole responsibility, not on behalf
      of any other Contributor, and only if You agree to indemnify,
      defend, and hold each Contributor harmless for any liability
      incurred by, or claims asserted against, such Contributor by reason
      of your accepting any such warranty or additional liability.


================================================
FILE: NOTICE.txt
================================================
Cognito Sync Manager for JavaScript
Copyright 2014 Amazon.com, Inc. or its affiliates. All Rights Reserved.

================================================
FILE: README.md
================================================
# Amazon Cognito Sync Manager for JavaScript

**Developer Preview:** We welcome developer feedback on this project. You can reach us by creating an issue on the GitHub repository or post to the Amazon Cognito forums:

* https://github.com/aws/amazon-cognito-js/issues
* https://forums.aws.amazon.com/forum.jspa?forumID=173

Introduction
============

The Cognito Sync Manager for JavaScript allows your web application to store data in the cloud for your users and
synchronize across other devices. The library uses the browser's local storage API to create a local cache for the
data, similar to our [mobile SDK](http://aws.amazon.com/mobile/sdk/). This allows your web application to access stored data even when there is no
connectivity.

**Note:** This library is designed to run in the browser. It has not been tested for use in other environments.

## Setup

1. Download and include the AWS JavaScript SDK:
  * http://aws.amazon.com/sdk-for-browser/

2. Download and include the Cognito Sync Manager for JavaScript:
  * `<script src="/path/to/amazon-cognito.min.js"></script>`
  * Or... `import 'amazon-cognito-js';`
  * Or... `require('amazon-cognito-js');`

For NPM usage refer to the following issue: [NPM usage](https://github.com/aws/amazon-cognito-js/issues/40).

## Usage

**Step 1.** Log into Amazon Cognito management console and create a new identity pool. Be sure to enable the "unauthenticated
identities" option. On the last step of the wizard, make a note of your Account ID, Identity Pool ID, and
Unauthenticated Role ARN.

* https://console.aws.amazon.com/cognito/home/?region=us-east-1

**Step 2.** Instantiate the AWS JavaScript SDK using the AWS.CognitoIdentityCredentials class, using the information you
gathered from the previous step.

```javascript
AWS.config.region = 'us-east-1';

AWS.config.credentials = new AWS.CognitoIdentityCredentials({
    IdentityPoolId: 'YOUR IDENTITY POOL ID',
});
```

**Step 3.** Make the call to obtain the credentials you configured, and in the callback, instantiate the CognitoSyncManager
class. It will assume the credentials from the AWS SDK.

```javascript
AWS.config.credentials.get(function() {

    client = new AWS.CognitoSyncManager();

    // YOUR CODE HERE

});
```

**Step 4.** Now you need to open or create a new dataset to start saving data to. Call .openOrCreateDataset() and pass in the
desired dataset name.

```javascript
client.openOrCreateDataset('myDatasetName', function(err, dataset) {

   // Do something with the dataset here.

});
```

**Step 5.** Once you have the dataset object, you can write, read, and delete records to that dataset. It is also possible to [get all the records](https://github.com/raptortech-js/amazon-cognito-js/blob/master/src/CognitoSyncDataset.js#L93) from a given dataset, [get the amount of data used](https://github.com/raptortech-js/amazon-cognito-js/blob/master/src/CognitoSyncDataset.js#L102) by a dataset, and [more](https://github.com/raptortech-js/amazon-cognito-js/blob/master/src/CognitoSyncDataset.js).

```javascript
<!-- Read Records -->
dataset.get('myRecord', function(err, value) {
  console.log('myRecord: ' + value);
});

<!-- Write Records -->
dataset.put('newRecord', 'newValue', function(err, record) {
  console.log(record);
});

<!-- Delete Records -->
dataset.remove('oldKey', function(err, record) {
  if (!err) { console.log('success'); }
});
```

**Step 6.** Finally, synchronize the data to Cognito. You pass the synchronize function an object with callbacks to handle the
various outcomes: onSuccess, onFailure, onConflict, onDatasetsMerged, onDatasetDeleted.

```javascript
<!-- Synchronize -->
dataset.synchronize({

  onSuccess: function(dataset, newRecords) {
     //...
  },

  onFailure: function(err) {
     //...
  },

  onConflict: function(dataset, conflicts, callback) {

     var resolved = [];

     for (var i=0; i<conflicts.length; i++) {

        // Take remote version.
        resolved.push(conflicts[i].resolveWithRemoteRecord());

        // Or... take local version.
        // resolved.push(conflicts[i].resolveWithLocalRecord());

        // Or... use custom logic.
        // var newValue = conflicts[i].getRemoteRecord().getValue() + conflicts[i].getLocalRecord().getValue();
        // resolved.push(conflicts[i].resolveWithValue(newValue);

     }

     dataset.resolve(resolved, function() {
        return callback(true);
     });

     // Or... callback false to stop the synchronization process.
     // return callback(false);

  },

  onDatasetDeleted: function(dataset, datasetName, callback) {

     // Return true to delete the local copy of the dataset.
     // Return false to handle deleted datasets outsid ethe synchronization callback.

     return callback(true);

  },

  onDatasetsMerged: function(dataset, datasetNames, callback) {

     // Return true to continue the synchronization process.
     // Return false to handle dataset merges outside the synchroniziation callback.

     return callback(false);

  }

});
```

## Change Log

**v1.0.0:**
* Initial release. Developer preview.


================================================
FILE: package.json
================================================
{
  "name": "amazon-cognito-js",
  "description": "Cognito Sync Manager for AWS JavaScript SDK",
  "main": "./dist/amazon-cognito.min.js",
  "version": "1.1.0",
  "author": {
    "name": "Amazon Web Services",
    "email": "aws@amazon.com",
    "url": "http://aws.amazon.com"
  },
  "homepage": "http://aws.amazon.com/cognito",
  "contributors": [
    "Mike Murry <mmurry@amazon.com>"
  ],
  "repository": {
    "type": "git",
    "url": "https://github.com/aws/amazon-cognito-js.git"
  },
  "license": "Apache-2.0",
  "keywords": ["amazon","aws","cognito","identity","sync"],
  "scripts": {
    "build": "grunt uglify umd"
  },
  "devDependencies": {
    "grunt": "^0.4.5",
    "grunt-contrib-jshint": "^0.10.0",
    "grunt-contrib-qunit": "^0.5.2",
    "grunt-contrib-uglify": "^0.6.0",
    "grunt-contrib-watch": "^0.6.1",
    "grunt-jsdoc": "^0.5.7",
    "grunt-umd": "^2.3.6",
    "qunitjs": "^1.15.0"
  }
}


================================================
FILE: src/CognitoSyncConflict.js
================================================
// Copyright 2014 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

AWS = AWS || {};
AWS.CognitoSyncManager = AWS.CognitoSyncManager || {};

AWS.CognitoSyncManager.Conflict = (function() {

    /**
     * When the local and remote copies of a dataset are in conflict, this object is returned to the synchronize callback.
     * @param remoteRecord the record from remote storage
     * @param localRecord the record from local storage
     * @prop {string} key
     * @prop {CognitoSyncRecord} remoteRecord
     * @prop {CognitoSyncRecord} localRecord
     * @constructor
     */

    var CognitoSyncConflict = function(remoteRecord, localRecord) {

        if (!remoteRecord || !localRecord) { throw new Error('Remote and local records cannot be null.'); }
        if (!remoteRecord.getKey || !localRecord.getKey) { throw new Error('Records are not record objects.'); }
        if (remoteRecord.getKey() !== localRecord.getKey()) { throw new Error('Remote and local keys do not match.'); }

        this.key = remoteRecord.getKey();
        this.remoteRecord = remoteRecord;
        this.localRecord = localRecord;

    };

    /**
     * Get the key of the records in conflict.
     * @returns {string} the record's key
     */

    CognitoSyncConflict.prototype.getKey = function() {
        return this.key;
    };

    /**
     * Get the remote record that is in conflict.
     * @returns {CognitoSyncRecord} the record
     */

    CognitoSyncConflict.prototype.getRemoteRecord = function() {
        return this.remoteRecord;
    };

    /**
     * Get the local record that is in conflict.
     * @returns {CognitoSyncRecord} the record
     */

    CognitoSyncConflict.prototype.getLocalRecord = function() {
        return this.localRecord;
    };

    /**
     * Resolves conflict with remote record.
     * @returns {CognitoSyncRecord} the resulting record
     */

    CognitoSyncConflict.prototype.resolveWithRemoteRecord = function() {
        this.remoteRecord.setModified(false);
        return this.remoteRecord;
    };

    /**
     * Resolves conflict with local record.
     * @returns {CognitoSyncRecord} resolved record
     */

    CognitoSyncConflict.prototype.resolveWithLocalRecord = function() {
        this.localRecord.setSyncCount(this.remoteRecord.getSyncCount());
        this.localRecord.setModified(true);
        return this.localRecord;
    };

    /**
     * Resolves conflict with a new value.
     * @param newValue new value of the record
     * @returns {CognitoSyncRecord} resolved record
     */

    CognitoSyncConflict.prototype.resolveWithValue = function(newValue) {
        return new AWS.CognitoSyncManager.Record({
            Key: this.remoteRecord.getKey(),
            Value: newValue,
            SyncCount: this.remoteRecord.getSyncCount(),
            LastModifiedDate: new Date(),
            LastModifiedBy: this.localRecord.getLastModifiedBy(),
            DeviceLastModifiedDate: new Date(),
            Modified: true
        });
    };

    return CognitoSyncConflict;

})();

================================================
FILE: src/CognitoSyncDataset.js
================================================
// Copyright 2014 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

AWS = AWS || {};
AWS.CognitoSyncManager = AWS.CognitoSyncManager || {};

AWS.CognitoSyncManager.Dataset = (function() {

    /**
     * Constructs a new dataset class.
     * @param {string} datasetName
     * @param provider
     * @param {AWS.CognitoSyncLocalStorage} local
     * @param {AWS.CognitoSyncRemoteStorage} remote
     * @param {function} logger
     * @constructor
     */

    var CognitoSyncDataset = function(datasetName, provider, local, remote, logger) {

        this.MAX_RETRY = 3;

        this.datasetName = datasetName;
        this.provider = provider;
        this.local = local;
        this.remote = remote;
        this.logger = logger || function(){};

    };

    /**
     * Validates the record key to ensure it is not empty and shorter than 128 characters.
     * @param {String} key The key to validate.
     * @returns {boolean}
     */

    CognitoSyncDataset.prototype.validateKey = function(key) {
        var namePattern = new RegExp('^[a-zA-Z0-9_.:-]{1,128}$');
        return namePattern.test(key);
    };

    /**
     * Writes a record to local storage.
     * @param {string} key
     * @param {string} value
     * @param {function} callback Callback(Error, Record)
     */

    CognitoSyncDataset.prototype.put = function(key, value, callback) {
        var valueType = typeof value;
        if (!this.validateKey(key)) { return callback(new Error('Invalid key.')); }
        if (valueType !== 'string') {
            return callback(new Error('The value type must be a string but was ' + valueType + '.'));
        }
        this.local.putValue(this.getIdentityId(), this.datasetName, key, value, callback);
    };

    /**
     * Writes a record as null, to be cleaned up on the next synchronization.
     * @param {string} key The key to remove.
     * @param {function} callback Callback(Error, Record)
     */

    CognitoSyncDataset.prototype.remove = function(key, callback) {
        if (!this.validateKey(key)) { return callback(new Error('Invalid key.')); }
        this.local.putValue(this.getIdentityId(), this.datasetName, key, null, callback);
    };

    /**
     * Gets a record's value from local storage.
     * @param {string} key
     * @param {function} callback Callback(Error, Value)
     */

    CognitoSyncDataset.prototype.get = function(key, callback) {
        if (!this.validateKey(key)) { return callback(new Error('Invalid key.')); }
        this.local.getValue(this.getIdentityId(), this.datasetName, key, callback);
    };

    /**
     * Gets all records in a dataset from local storage.
     * @param {function} callback Callback(Error, Records)
     */

    CognitoSyncDataset.prototype.getAllRecords = function(callback) {
        this.local.getRecords(this.getIdentityId(), this.datasetName, callback);
    };

    /**
     * Returns the amount of data stored on the server.
     * @param callback
     */

    CognitoSyncDataset.prototype.getDataStorage = function(callback) {

        this.getDatasetMetadata(function(err, meta) {
            if (err) { return callback(err); }
            if (!meta) { return callback(null, 0); }
            return callback(null, meta.getDataStorage());
        });

    };

    /**
     * Returns if a specific record has changed.
     * @param key
     * @param callback
     */

    CognitoSyncDataset.prototype.isChanged = function(key, callback) {
        if (!this.validateKey(key)) { return callback(new Error('Invalid key.')); }
        this.local.getRecord(this.getIdentityId(), this.datasetName, key, function(err, record) {
            callback(null, (record && record.isModified()));
        });
    };

    /**
     * Returns the dataset metadata.
     * @param callback
     */

    CognitoSyncDataset.prototype.getDatasetMetadata = function(callback) {
        this.local.getDatasetMetadata(this.getIdentityId(), this.datasetName, callback);
    };

    /**
     * Resolves conflicts using the records provided.
     * @param resolvedRecords
     * @param callback
     */

    CognitoSyncDataset.prototype.resolve = function(resolvedRecords, callback) {
        this.local.putRecords(this.getIdentityId(), this.datasetName, resolvedRecords, callback);
    };

    /**
     * Puts all values into the dataset.
     * @param values
     * @param callback
     * @returns {*}
     */

    CognitoSyncDataset.prototype.putAll = function(values, callback) {

        var isValid = true;

        for (var key in values) {
            if (values.hasOwnProperty(key)) {
                if (!this.validateKey(key)) { isValid = false; }
            }
        }

        if (!isValid) { return callback(new Error('Object contains invalid keys.')); }

        this.local.putAllValues(this.getIdentityId(), this.datasetName, values, callback);

    };

    /**
     * Returns all records from a dataset.
     * @param callback
     */

    CognitoSyncDataset.prototype.getAll = function(callback) {

        var map = {};
        var record;

        this.local.getRecords(this.getIdentityId(), this.datasetName, function(err, records) {

            if (err) { return callback(err); }

            for (var r in records) {
                if (records.hasOwnProperty(r)) {
                    record = records[r];
                    if (!record.isDeleted()) { map[record.getKey()] = record.getValue(); }
                }
            }

            callback(null, map);

        });

    };

    /**
     * Returns the current user's identity id.
     * @returns {string};
     */

    CognitoSyncDataset.prototype.getIdentityId = function() {
        return this.provider.identityId;
    };

    /**
     * Returns the records that have been modified.
     * @param callback
     */

    CognitoSyncDataset.prototype.getModifiedRecords = function(callback) {
        this.local.getModifiedRecords(this.getIdentityId(), this.datasetName, callback);
    };

    /**
     * Returns a list of datasets that have been merged.
     * @param callback
     */

    CognitoSyncDataset.prototype.getLocalMergedDatasets = function(callback) {

        var mergedDatasets = [];
        var prefix = this.datasetName + '.';
        var dataset;

        this.local.getDatasets(this.getIdentityId(), function(err, datasets) {

            for (var d in datasets) {
                if (datasets.hasOwnProperty(d)) {

                    dataset = datasets[d];

                    if (dataset.getDatasetName().indexOf(prefix) === 0) {
                        mergedDatasets.push(dataset.getDatasetName());
                    }

                }
            }

            callback(null, mergedDatasets);

        });

    };

    /**
     * Starts the synchronization process.
     * @param callback
     * @param retry
     */

    CognitoSyncDataset.prototype.synchronize = function(callback, retry) {

        var root = this;

        // Validate callback object.
        callback = callback || {};
        callback.onSuccess = callback.onSuccess || function(dataset, updates) {};
        callback.onFailure = callback.onFailure || function(err) {};
        callback.onConflict = callback.onConflict || function(dataset, conflicts, callback) { return callback(false); };
        callback.onDatasetDeleted = callback.onDatasetDeleted || function(dataset, deletedDataset, callback) { return callback(false); };
        callback.onDatasetsMerged = callback.onDatasetsMerged || function(dataset, merges, callback) { return callback(false); };

        // Validate/initialize retry count.
        if (retry === undefined) { retry = this.MAX_RETRY; }

        root.logger('Starting synchronization... (retries: ' + retry + ')');

        if (retry < 0) {
            return callback.onFailure(new Error('Synchronize failed: exceeded maximum retry count.'));
        }

        // First check if any datasets have been merged locally.

        this.getLocalMergedDatasets(function(err, mergedDatasets) {

            if (err) { callback.onFailure(err); }

            root.logger('Checking for locally merged datasets... found ' + mergedDatasets.length + '.');

            // Detect if merged datasets.
            if (mergedDatasets.length > 0) {

                root.logger('Deferring to .onDatasetsMerged.');

                return callback.onDatasetsMerged(root, mergedDatasets, function(isContinue) {

                    if (!isContinue) {

                        // Merges were not handled by callback. Cancel sync.
                        return callback.onFailure(new Error('Synchronization cancelled by onDatasetsMerged() callback returning false.'));

                    } else {

                        // Merges are handled within callback. Restart sync.
                        return root.synchronize(callback, --retry);

                    }

                });

            } else {

                // Get the last sync count so we can tell the server what to diff.

                root.local.getLastSyncCount(root.getIdentityId(), root.datasetName, function(err, syncCount) {

                    if (err) { return callback.onFailure(err); }

                    root.logger('Detecting last sync count... ' + syncCount);

                    if (parseInt(syncCount) === -1) {

                        // Dataset has been deleted locally
                        root.remote.deleteDataset(root.datasetName, function(err, data) {
                            if (err) { return callback.onFailure(err); }
                            root.local.purgeDataset(root.getIdentityId(), root.datasetName, function(err) {
                               if (err) { return callback.onFailure(err); }
                               return callback.onSuccess(root);
                            });
                        });

                    } else {

                        // Get all the remote records that have changed since the latest sync count.

                        root.remote.listUpdates(root.datasetName, syncCount, function(err, remoteRecords) {

                            if (err) { return callback.onFailure(err); }

                            root.logger('Fetching remote updates... found ' + remoteRecords.records.length + '.');

                            var mergedNameList = remoteRecords.getMergedDatasetNameList();

                            root.logger('Checking for remote merged datasets... found ' + mergedNameList.length + '.');

                            if (mergedNameList.length > 0) {

                                root.logger('Deferring to .onDatasetsMerged.');

                                // Merged datasets exist. Use callback to determine action.
                                return callback.onDatasetsMerged(root, mergedNameList, function(doContinue) {
                                    if (!doContinue) { callback.onFailure(new Error('Cancelled due to .onDatasetsMerged result.')); }
                                    else { root._synchronizeInternal(callback, --retry); }
                                });
                            }

                            // Check if dataset doesn't exist or is deleted.

                            if (syncCount !== 0 && !remoteRecords || remoteRecords.isDeleted()) {

                                return callback.onDatasetDeleted(root, remoteRecords.getDatasetName(), function(doContinue) {

                                    root.logger('Dataset should be deleted. Deferring to .onDatasetDeleted.');

                                    if (doContinue) {
                                        root.logger('.onDatasetDeleted returned true, purging dataset locally.');
                                        return root.local.purgeDataset(root.getIdentityId(), root.datasetName, function(err) {
                                            if (err) { return callback.onFailure(err); }
                                            return root._synchronizeInternal(callback, --retry);
                                        });
                                    } else {
                                        root.logger('.onDatasetDeleted returned false, cancelling sync.');
                                        return callback.onFailure(new Error('Cancelled due to .onDatasetDeleted result.'));
                                    }

                                });

                            }

                            var updatedRemoteRecords = remoteRecords.getRecords();
                            var lastSyncCount = remoteRecords.getSyncCount();
                            var sessionToken = remoteRecords.getSyncSessionToken();

                            // Check if there have been any updates since the last sync count.

                            root.logger('Checking for remote updates since last sync count... found ' + updatedRemoteRecords.length + '.');

                            if (updatedRemoteRecords.length > 0) {

                                root._synchronizeResolveLocal(updatedRemoteRecords, function(err, conflicts) {

                                    if (err) { return callback.onFailure(err); }

                                    root.logger('Checking for conflicts... found ' + conflicts.length + '.');

                                    if (conflicts.length > 0) {

                                        root.logger('Conflicts detected. Deferring to .onConflict.');

                                        callback.onConflict(root, conflicts, function(isContinue) {

                                            if (!isContinue) {

                                                root.logger('.onConflict returned false. Cancelling sync.');
                                                return callback.onFailure(new Error('Sync cancelled. Conflict callback returned false.'));

                                            } else {

                                                // Update remote records or we will just hit another sync conflict next go around.

                                                root._synchronizePushRemote(sessionToken, syncCount, function(){
                                                    return root.synchronize(callback, --retry);
                                                });

                                            }

                                        });

                                    } else {

                                        // No conflicts, update local records.
                                        root.logger('No conflicts. Updating local records.');

                                        root.local.putRecords(root.getIdentityId(), root.datasetName, updatedRemoteRecords, function(err) {

                                            if (err) { return callback.onFailure(err); }

                                            // Update the local sync count to match.

                                            root.local.updateLastSyncCount(root.getIdentityId(), root.datasetName, lastSyncCount, function(err) {

                                                if (err) { return callback.onFailure(err); }

                                                root.logger('Finished resolving records. Restarting sync.');

                                                // Callback returned true, starting sync.
                                                return root.synchronize(callback, --retry);

                                            });
                                        });

                                    }

                                });


                            } else {

                                // Nothing updated remotely. Push local changes to remote.
                                root.logger('Nothing updated remotely. Pushing local changes to remote.');

                                root._synchronizePushRemote(sessionToken, lastSyncCount, function(err) {

                                    if (err) {
                                        root.logger('Remote push failed. Likely concurrent sync conflict. Retrying...');
                                        return root.synchronize(callback, --retry);
                                    }

                                    root.logger('Sync successful.');
                                    return callback.onSuccess(root, updatedRemoteRecords);

                                });

                            }
                        });

                    }

                });

            }

        });
    };

    /**
     * An internal function for helping the synchronization call to resolve local conflicts.
     * @param remoteRecords
     * @param callback
     * @private
     */

    CognitoSyncDataset.prototype._synchronizeResolveLocal = function(remoteRecords, callback) {

        // Step two of the synchronization flow.
        // The dataset exists remotely so we need to determine if there are any deletions or conflicts.
        // Once everything is resolved, we update the local records.

        var root = this;
        var conflicts = [];

        // Make sure there are remote records that need resolving.

        if (remoteRecords && remoteRecords.length > 0) {

            // Get the local records so we can compare them to the remote records.

            root.local.getRecords(root.getIdentityId(), root.datasetName, function(err, localRecords) {

                var localMap = {};
                var i, key, local;

                // Build a map of the local records array for easier key lookup.

                for (i=0; i<localRecords.length; i++) {
                    localMap[localRecords[i].getKey()] = localRecords[i];
                }

                // Compare local and remote records.

                for (i=0; i<remoteRecords.length; i++) {

                    key = remoteRecords[i].getKey();
                    local = localMap[key];

                    if (local && local.isModified() && local.getValue() !== remoteRecords[i].getValue()) {
                        conflicts.push(new AWS.CognitoSyncManager.Conflict(remoteRecords[i], local));
                    }

                }

                return callback(null, conflicts);

            });

        } else {

            // There are no remote records. Nothing to resolve.
            return callback(null, conflicts);

        }

    };

    /**
     * An internal function for helping the synchronization call to push changes to the server.
     * @param sessionToken
     * @param syncCount
     * @param callback
     * @private
     */

    CognitoSyncDataset.prototype._synchronizePushRemote = function(sessionToken, syncCount, callback) {

        // Step three of the synchronization flow.
        // The local dataset has modifications so we need to push the local changes to remote.
        // Then we need to update the local metadata and update/verify the sync count.

        var root = this;

        // Push changes to remote.

        this.getModifiedRecords(function(err, localChanges) {

            if (localChanges.length > 0) {

                root.remote.putRecords(root.datasetName, localChanges, sessionToken, function(err, records) {

                    if (err) { callback(err); }

                    // Update local metadata.
                    root.local.putRecords(root.getIdentityId(), root.datasetName, records, function(err) {

                        if (err) { return callback(err); }

                        var newSyncCount = 0;

                        // Calculate new sync count.

                        for (var r in records) {
                            if (records.hasOwnProperty(r)) {
                                newSyncCount = newSyncCount < records[r].getSyncCount() ? records[r].getSyncCount() : newSyncCount;
                            }
                        }

                        root.local.updateLastSyncCount(root.getIdentityId(), root.datasetName, newSyncCount, function(err) {
                            if (err) { return callback(err); }
                            return callback(null, true);
                        });

                    });

                });

            } else {

                // Nothing to change.
                return callback(null, true);

            }

        });

    };

    return CognitoSyncDataset;

})();


================================================
FILE: src/CognitoSyncDatasetMetadata.js
================================================
// Copyright 2014 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

/**
 * Constructs a new CognitoSyncDatasetMetadata object.
 * @param metadata the serialized metadata
 * @constructor
 */

AWS = AWS || {};
AWS.CognitoSyncManager = AWS.CognitoSyncManager || {};

AWS.CognitoSyncManager.DatasetMetadata = (function(){

    var CognitoSyncDatasetMetadata = function(metadata) {

        metadata = metadata || {};

        // Assign object.
        this.datasetName = metadata.DatasetName || '';
        this.creationDate = new Date(metadata.CreationDate) || new Date();
        this.lastModifiedDate = new Date(metadata.LastModifiedDate) || new Date();
        this.lastModifiedBy = metadata.LastModifiedBy || '';
        this.dataStorage = metadata.DataStorage || 0;
        this.recordCount = metadata.NumRecords || 0;

        // Meta metadata.
        this.lastSyncCount = metadata.LastSyncCount || 0;
        this.lastSyncDate = metadata.LastSyncDate ? new Date(metadata.LastSyncDate) : new Date();

        // Validate object.
        if (this.dataStorage < 0) { throw new RangeError('Storage size cannot be negative.'); }
        if (this.recordCount < 0) { throw new RangeError('Record count cannot be negative.'); }

    };

    /**
     * Get the dataset name.
     * @returns {string} the dataset's name.
     */

    CognitoSyncDatasetMetadata.prototype.getDatasetName = function() {
        return this.datasetName;
    };

    /**
     * Sets the dataset name.
     * @param {string} datasetName the name of the dataset
     * @returns {CognitoSyncDatasetMetadata} the dataset object
     */

    CognitoSyncDatasetMetadata.prototype.setDatasetName = function(datasetName) {
        this.datasetName = datasetName;
        return this;
    };

    /**
     * Get the dataset's creation date.
     * @returns {Date} the creation date
     */

    CognitoSyncDatasetMetadata.prototype.getCreationDate = function() {
        return this.creationDate;
    };

    /**
     * Sets the dataset creation date.
     * @param {Date} creationDate
     * @returns {CognitoSyncDatasetMetadata}
     */

    CognitoSyncDatasetMetadata.prototype.setCreationDate = function(creationDate) {
        this.creationDate = new Date(creationDate);
        return this;
    };

    /**
     * Gets the dataset last modified date.
     * @returns {Date}
     */

    CognitoSyncDatasetMetadata.prototype.getLastModifiedDate = function() {
        return this.lastModifiedDate;
    };

    /**
     * Sets the dataset last modified date.
     * @param modifiedDate
     * @returns {CognitoSyncDatasetMetadata}
     */

    CognitoSyncDatasetMetadata.prototype.setLastModifiedDate = function(modifiedDate) {
        this.lastModifiedDate = new Date(modifiedDate);
        return this;
    };

    /**
     * Returns the user/device who last modified the dataset.
     * @returns {String}
     */

    CognitoSyncDatasetMetadata.prototype.getLastModifiedBy = function() {
        return this.lastModifiedBy;
    };

    /**
     * Sets the user/device who last modified the dataset.
     * @param {String} modifiedBy
     * @returns {CognitoSyncDatasetMetadata}
     */

    CognitoSyncDatasetMetadata.prototype.setLastModifiedBy = function(modifiedBy) {
        this.lastModifiedBy = modifiedBy;
        return this;
    };

    /**
     * Gets the data storage size.
     * @returns {number}
     */

    CognitoSyncDatasetMetadata.prototype.getDataStorage = function() {
        return this.dataStorage;
    };

    /**
     * Sets the data storage size.
     * @param {Number} storageSize
     * @returns {CognitoSyncDatasetMetadata}
     */

    CognitoSyncDatasetMetadata.prototype.setDataStorage = function(storageSize) {
        this.dataStorage = storageSize;
        return this;
    };

    /**
     * Gets the record count.
     * @returns {number}
     */

    CognitoSyncDatasetMetadata.prototype.getRecordCount = function() {
        return this.recordCount;
    };

    /**
     * Sets the record count.
     * @param {Number} recordCount
     * @returns {CognitoSyncDatasetMetadata}
     */

    CognitoSyncDatasetMetadata.prototype.setRecordCount = function(recordCount) {
        this.recordCount = recordCount;
        return this;
    };

    /**
     * Gets the last sync count.
     * @returns {number}
     */

    CognitoSyncDatasetMetadata.prototype.getLastSyncCount = function() {
        return this.lastSyncCount;
    };

    /**
     * Sets the last sync count.
     * @param {Number} syncCount
     * @returns {CognitoSyncDatasetMetadata}
     */

    CognitoSyncDatasetMetadata.prototype.setLastSyncCount = function(syncCount) {
        this.lastSyncCount = syncCount;
        return this;
    };

    /**
     * Gets the last sync date.
     * @returns {Date}
     */

    CognitoSyncDatasetMetadata.prototype.getLastSyncDate = function() {
        return this.lastSyncDate;
    };

    /**
     * Sets the last sync date.
     * @param {Date} syncDate
     * @returns {CognitoSyncDatasetMetadata}
     */

    CognitoSyncDatasetMetadata.prototype.setLastSyncDate = function(syncDate) {
        this.lastSyncDate = syncDate;
        return this;
    };

    /**
     * Returns a JSON string of the metadata object.
     * @returns {String}
     */

    CognitoSyncDatasetMetadata.prototype.toString = function() {
        return JSON.stringify(this.toJSON());
    };

    /**
     * Returns a flat object representing the metadata.
     * @returns {Object}
     */

    CognitoSyncDatasetMetadata.prototype.toJSON = function() {
        return {
            DatasetName: this.datasetName,
            CreationDate: this.creationDate,
            LastModifiedDate: this.lastModifiedDate,
            LastModifiedBy: this.lastModifiedBy,
            DataStorage: this.dataStorage,
            NumRecords: this.recordCount,
            LastSyncCount: this.lastSyncCount,
            LastSyncDate: this.lastSyncDate
        };
    };

    return CognitoSyncDatasetMetadata;

})();


================================================
FILE: src/CognitoSyncDatasetUpdates.js
================================================
// Copyright 2014 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

AWS = AWS || {};
AWS.CognitoSyncManager = AWS.CognitoSyncManager || {};

AWS.CognitoSyncManager.DatasetUpdates = (function() {

    /**
     * Constructs a new dataset update class.
     * @param datasetName
     * @constructor
     */

    var CognitoSyncDatasetUpdates = function (datasetName) {

        this.datasetName = datasetName;
        this.records = [];
        this.syncCount = 0;
        this.syncSessionToken = '';
        this.exists = true;
        this.deleted = false;
        this.mergedDatasetNameList = [];

    };

    CognitoSyncDatasetUpdates.prototype.getDatasetName = function () {
        return this.datasetName;
    };

    CognitoSyncDatasetUpdates.prototype.setDatasetName = function (datasetName) {
        this.datasetName = datasetName;
        return this;
    };

    CognitoSyncDatasetUpdates.prototype.getRecords = function () {
        return this.records;
    };

    CognitoSyncDatasetUpdates.prototype.addRecord = function (record) {
        this.records.push(record);
        return this;
    };

    CognitoSyncDatasetUpdates.prototype.getSyncCount = function () {
        return this.syncCount;
    };

    CognitoSyncDatasetUpdates.prototype.setSyncCount = function (syncCount) {
        this.syncCount = syncCount;
        return this;
    };

    CognitoSyncDatasetUpdates.prototype.getSyncSessionToken = function () {
        return this.syncSessionToken;
    };

    CognitoSyncDatasetUpdates.prototype.setSyncSessionToken = function (syncToken) {
        this.syncSessionToken = syncToken;
        return this;
    };

    CognitoSyncDatasetUpdates.prototype.isExists = function () {
        return this.exists;
    };

    CognitoSyncDatasetUpdates.prototype.setExists = function (exists) {
        this.exists = exists;
        return this;
    };

    CognitoSyncDatasetUpdates.prototype.isDeleted = function () {
        return this.deleted;
    };

    CognitoSyncDatasetUpdates.prototype.setDeleted = function (deleted) {
        this.deleted = deleted;
        return this;
    };

    CognitoSyncDatasetUpdates.prototype.getMergedDatasetNameList = function () {
        return this.mergedDatasetNameList;
    };

    CognitoSyncDatasetUpdates.prototype.setMergedDatasetNameList = function (mergedList) {
        this.mergedDatasetNameList = mergedList;
        return this;
    };

    return CognitoSyncDatasetUpdates;

})();


================================================
FILE: src/CognitoSyncLocalStorage.js
================================================
// Copyright 2014 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

AWS = AWS || {};
AWS.CognitoSyncManager = AWS.CognitoSyncManager || {};

AWS.CognitoSyncManager.LocalStorage = (function() {

    /**
     * Constructs a new local storage class.
     * @param options
     * @constructor
     */

    var CognitoSyncLocalStorage = function (options) {

        options = options || {};

        this.store = null;
        this.meta = null;

        // Choose a data store
        if (options.DataStore) { this.store = new options.DataStore(); }
        else { this.store = new AWS.CognitoSyncManager.StoreInMemory(); }

    };

    /**
     * Returns the string used to store dataset metadata.
     * @param identityId
     * @param datasetName
     * @returns {string}
     */

    CognitoSyncLocalStorage.prototype.getMetadataKey = function (identityId, datasetName) {
        return identityId + '.' + datasetName;
    };

    /**
     * Load the metadata cache from the local store.
     * @param identityId
     * @param callback
     */

    CognitoSyncLocalStorage.prototype.loadMetadataCache = function (identityId, callback) {

        var root = this;

        this.store.get('_internal', '_metadata', identityId, function (err, data) {

            if (err) {
                return callback(err, null);
            }
            if (!data) {
                data = {};
            }

            root.meta = data;
            callback(null, data);

        });

    };

    /**
     * Save the metadata cache to the local store.
     * @param identityId
     * @param metadata
     * @param callback
     */

    CognitoSyncLocalStorage.prototype.saveMetadataCache = function (identityId, metadata, callback) {
        this.store.set('_internal', '_metadata', identityId, metadata, function (err) {
            if (err) {
                return callback(err);
            }
            return callback(null, metadata);
        });
    };

    /**
     * Creates a new dataset.
     * @param identityId
     * @param datasetName
     * @param callback
     * @returns {CognitoSyncLocalStorage}
     */

    CognitoSyncLocalStorage.prototype.createDataset = function (identityId, datasetName, callback) {

        var root = this;

        this.getDatasetMetadata(identityId, datasetName, function (err, metadata) {

            var stamp = new Date().getTime();

            if (!metadata) {

                metadata = new AWS.CognitoSyncManager.DatasetMetadata({
                    DatasetName: datasetName,
                    CreationDate: stamp,
                    LastModifiedDate: stamp
                });

                root.setDatasetMetadata(identityId, datasetName, metadata, function (err, data) {
                    // No-op. Silent update.
                });

                callback(null, datasetName);

            } else {
                callback(null, datasetName);
            }

        });

        return this;

    };

    /**
     * Returns the dataset metadata.
     * @param identityId
     * @param datasetName
     * @param callback
     * @returns {CognitoSyncLocalStorage}
     */

    CognitoSyncLocalStorage.prototype.getDatasetMetadata = function (identityId, datasetName, callback) {

        var key = this.getMetadataKey(identityId, datasetName);

        if (this.meta !== null) {

            // Meta data is already loaded. Look it up and return.
            if (this.meta[key]) {
                callback(null, new AWS.CognitoSyncManager.DatasetMetadata(this.meta[key]));
            }
            else {
                callback(null, undefined);
            }

        } else {

            // Load metadata from cache.
            this.loadMetadataCache(identityId, function (err, cache) {
                if (cache[key]) {
                    callback(null, new AWS.CognitoSyncManager.DatasetMetadata(cache[key]));
                }
                else {
                    callback(null, undefined);
                }
            });

        }

        return this;

    };

    /**
     * Sets a dataset's metadata.
     * @param identityId
     * @param datasetName
     * @param metadata
     * @param callback
     * @returns {CognitoSyncLocalStorage}
     */

    CognitoSyncLocalStorage.prototype.setDatasetMetadata = function (identityId, datasetName, metadata, callback) {

        // Write metadata.
        this.meta[this.getMetadataKey(identityId, datasetName)] = metadata.toJSON();

        // Save metadata.
        this.saveMetadataCache(identityId, this.meta, callback);

        return this;

    };


    /**
     * Returns a record's value from the local store.
     * @param identityId
     * @param datasetName
     * @param key
     * @param callback
     */

    CognitoSyncLocalStorage.prototype.getValue = function (identityId, datasetName, key, callback) {

        this.getRecord(identityId, datasetName, key, function (err, record) {

            if (!record) {
                return callback(null, undefined);
            }

            return callback(null, record.getValue());

        });

    };

    /**
     * Sets a record's value from the local store.
     * @param identityId
     * @param datasetName
     * @param key
     * @param value
     * @param callback
     */

    CognitoSyncLocalStorage.prototype.putValue = function (identityId, datasetName, key, value, callback) {

        var root = this;

        this.getRecord(identityId, datasetName, key, function (err, record) {


            if (record && record.getValue() === value) {
                // Record hasn't changed. All done.
                return callback(null, record);
            }

            // If record doesn't exist, create a new instance.
            if (!record) {
                record = new AWS.CognitoSyncManager.Record();
            }

            // Update the record with the new properties.
            record.setKey(key)
                .setValue(value)
                .setModified(true)
                .setSyncCount(record ? record.getSyncCount() : 0)
                .setDeviceLastModifiedDate(new Date());

            root.store.set(identityId, datasetName, key, record.toJSON(), function (err) {

                if (err) {
                    return callback(err);
                }

                root.updateLastModifiedTimestamp(identityId, datasetName, function (err) {
                    return callback(err, record);
                });

            });

        });

    };

    /**
     * Returns a map of values for a dataset from the local store.
     * @param identityId
     * @param datasetName
     * @param callback
     */

    CognitoSyncLocalStorage.prototype.getValueMap = function (identityId, datasetName, callback) {

        var values = {};
        var record;

        this.getRecords(identityId, datasetName, function (err, records) {

            for (var r in records) {
                if (records.hasOwnProperty(r)) {
                    record = records[r];
                    if (!record.isDeleted()) {
                        values[record.getKey()] = record.getValue();
                    }
                }
            }

            callback(null, values);

        });

    };

    /**
     * Sets multiple records in a dataset in the local store.
     * @param identityId
     * @param datasetName
     * @param values
     * @param callback
     */

    CognitoSyncLocalStorage.prototype.putAllValues = function (identityId, datasetName, values, callback) {

        var root = this;

        var remain = [];

        // Build a list of each value to put.

        for (var v in values) {
            if (values.hasOwnProperty(v)) {
                remain.push(v);
            }
        }

        var request = function (err) {

            var item;

            if (err) {
                return callback(err);
            }

            if (remain.length > 0) {

                // Put each item in the request.
                item = remain.shift();
                root.putValue(identityId, datasetName, item, values[item], request);

            } else {

                // Nothing else to update. Break the loop.
                callback(null, true);

            }

        };

        request(null, null);

    };

    /**
     * Returns a list of datasets in the local store.
     * @param identityId
     * @param callback
     */

    CognitoSyncLocalStorage.prototype.getDatasets = function (identityId, callback) {

        var datasets = [];

        if (this.meta !== null) {

            for (var m in this.meta) {
                if (this.meta.hasOwnProperty(m)) {
                    datasets.push(new AWS.CognitoSyncManager.DatasetMetadata(this.meta[m]));
                }
            }

            return callback(null, datasets);

        } else {

            // Meta data is not loaded. Load it.
            this.loadMetadataCache(identityId, function (err, metadata) {

                for (var m in metadata) {
                    if (metadata.hasOwnProperty(m)) {
                        datasets.push(new AWS.CognitoSyncManager.DatasetMetadata(metadata[m]));
                    }
                }

                return callback(null, datasets);

            });

        }

    };

    /**
     * Updates dataset metadata and saves to the cache.
     * @param identityId
     * @param metadata
     * @param callback
     */

    CognitoSyncLocalStorage.prototype.updateDatasetMetadata = function (identityId, metadata, callback) {

        var root = this;

        this.getDatasetMetadata(identityId, metadata.getDatasetName(), function (err, local) {

            if (err) { callback(err); }

            if (!local) { local = new AWS.CognitoSyncManager.DatasetMetadata(); }

            local.setDatasetName(metadata.getDatasetName())
                .setCreationDate(metadata.getCreationDate())
                .setLastModifiedDate(metadata.getLastModifiedDate())
                .setLastModifiedBy(metadata.getLastModifiedBy())
                .setLastSyncCount(metadata.getLastSyncCount())
                .setRecordCount(metadata.getRecordCount())
                .setDataStorage(metadata.getDataStorage());

            // Save the updated metadata to the in-memory store.

            root.meta[root.getMetadataKey(identityId, metadata.getDatasetName())] = local.toJSON();

            // Save the updated metadata to the on-disk store.

            root.saveMetadataCache(identityId, root.meta, function (err) {
                if (err) { return callback(err); }
                return callback(null, local);
            });

        });

    };

    /**
     * Returns a record from the local store.
     * @param identityId
     * @param datasetName
     * @param key
     * @param callback
     */

    CognitoSyncLocalStorage.prototype.getRecord = function (identityId, datasetName, key, callback) {
        this.store.get(identityId, datasetName, key, function (err, record) {
            if (record) { return callback(null, new AWS.CognitoSyncManager.Record(record)); }
            return callback(new Error('Key doesn\'t exist.'), null);
        });
    };

    /**
     * Returns all records from a dataset in the local store.
     * @param identityId
     * @param datasetName
     * @param callback
     */

    CognitoSyncLocalStorage.prototype.getRecords = function (identityId, datasetName, callback) {

        var records = [];

        this.store.getAll(identityId, datasetName, function (err, local) {

            for (var l in local) {
                if (local.hasOwnProperty(l)) {
                    records.push(new AWS.CognitoSyncManager.Record(local[l]));
                }
            }

            callback(null, records);

        });

    };

    /**
     * Puts multiple records into a dataset in the local store.
     * @param identityId
     * @param datasetName
     * @param records
     * @param callback
     * @returns {CognitoSyncLocalStorage}
     */

    CognitoSyncLocalStorage.prototype.putRecords = function (identityId, datasetName, records, callback) {

        var root = this;
        records = records || [];
        records = records.slice();

        var request = function () {

            if (records.length > 0) {

                root.updateAndClearRecord(identityId, datasetName, records.shift(), function (err) {

                    if (err) { return callback(err); }
                    if (records.length === 0) { return callback(null, true); }

                    request();

                });

            }

        };

        request();

    };

    /**
     * Deletes a dataset from the local store.
     * @param identityId
     * @param datasetName
     * @param callback
     */

    CognitoSyncLocalStorage.prototype.deleteDataset = function (identityId, datasetName, callback) {

        var root = this;

        // Delete the records.

        this.store.removeAll(identityId, datasetName, function (err) {

            if (err) { return callback(err); }

            // Update dataset metadata.

            root.getDatasetMetadata(identityId, datasetName, function (err, metadata) {

                if (err) { return callback(err); }

                metadata.setLastModifiedDate(new Date());
                metadata.setLastSyncCount(-1);

                root.updateDatasetMetadata(identityId, metadata, function (err) {
                    if (err) { return callback(err); }
                    return callback(null, true);
                });

            });

        });

    };

    /**
     * Removes dataset from local storage. Does not remove dataset from remote storage.
     * @param identityId
     * @param datasetName
     * @param callback
     */

    CognitoSyncLocalStorage.prototype.purgeDataset = function (identityId, datasetName, callback) {

        var root = this;

        // Delete records.
        this.deleteDataset(identityId, datasetName, function (err) {

            if (err) { callback(err); }

            // Delete metadata.
            delete(root.meta[root.getMetadataKey(identityId, datasetName)]);

            // Save metadata.
            root.saveMetadataCache(identityId, root.meta, callback);

        });

    };

    /**
     * Returns the last sync count for a dataset in the local store.
     * @param identityId
     * @param datasetName
     * @param callback
     */

    CognitoSyncLocalStorage.prototype.getLastSyncCount = function (identityId, datasetName, callback) {

        this.getDatasetMetadata(identityId, datasetName, function (err, metadata) {

            if (metadata) { return callback(null, metadata.getLastSyncCount()); }

            callback(new Error('Dataset doesn\'t exist.'), null);

        });

    };

    /**
     * Returns the modified records in a dataset from the local store.
     * @param identityId
     * @param datasetName
     * @param callback
     */

    CognitoSyncLocalStorage.prototype.getModifiedRecords = function (identityId, datasetName, callback) {

        var modified = [];

        this.getRecords(identityId, datasetName, function (err, records) {

            for (var i = 0; i < records.length; i++) {

                if (records[i].isModified()) {
                    modified.push(records[i]);
                }

            }

            callback(null, modified);

        });

    };

    /**
     * Updates the last sync count for a dataset in the local store.
     * @param identityId
     * @param datasetName
     * @param lastSyncCount
     * @param callback
     */

    CognitoSyncLocalStorage.prototype.updateLastSyncCount = function (identityId, datasetName, lastSyncCount, callback) {

        var root = this;

        this.getDatasetMetadata(identityId, datasetName, function (err, meta) {

            if (err) {
                callback(err);
            }

            meta.setLastSyncCount(lastSyncCount).setLastSyncDate(new Date());

            root.updateDatasetMetadata(identityId, meta, function (err) {
                if (err) {
                    callback(err);
                }
                callback(null, true);
            });

        });

    };

    /**
     * Removes all data from the local store.
     * @param callback
     */

    CognitoSyncLocalStorage.prototype.wipeData = function (callback) {
        this.store.wipe(callback);
    };

    /**
     * Modifies the date a dataset was last modified in the local store.
     * @param identityId
     * @param datasetName
     * @param callback
     */

    CognitoSyncLocalStorage.prototype.updateLastModifiedTimestamp = function (identityId, datasetName, callback) {

        var root = this;

        this.getDatasetMetadata(identityId, datasetName, function (err, meta) {

            if (err) {
                return callback(err);
            }

            meta.setLastModifiedDate(new Date());

            root.updateDatasetMetadata(identityId, meta, function (err) {
                if (err) {
                    return callback(err);
                }
                return callback(null, true);
            });

        });

    };

    /**
     * Removes a record from the local store.
     * @param identityId
     * @param datasetName
     * @param record
     * @param callback
     */

    CognitoSyncLocalStorage.prototype.removeRecord = function (identityId, datasetName, record, callback) {
        this.store.remove(identityId, datasetName, record, function (err) {
            if (err) { return callback(err); }
            return callback(null, true);
        });
    };

    /**
     * Saves a record to the local store.
     * @param identityId
     * @param datasetName
     * @param record
     * @param callback
     */

    CognitoSyncLocalStorage.prototype.updateAndClearRecord = function (identityId, datasetName, record, callback) {
        this.store.set(identityId, datasetName, record.getKey(), record.toJSON(), function (err) {
            if (err) { return callback(err); }
            return callback(null, true);
        });
    };

    return CognitoSyncLocalStorage;

})();


================================================
FILE: src/CognitoSyncManager.js
================================================
// Copyright 2014 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

if (AWS === undefined) {
    throw new Error("AWS SDK must be loaded before loading the Sync Manager.");
} else {

    /**
     * Constructs a new Cognito Sync Manager class.
     * @constructor
     */


    AWS.CognitoSyncManager = function (options) {

        options = options || {};

        var USER_AGENT = 'CognitoJavaScriptSDK/1';

        this.provider = AWS.config.credentials;
        this.identityPoolId = this.provider.params.IdentityPoolId;
        this.region = AWS.config.region;

        // Setup logger.
        this.logger = options.log;
        if (typeof this.logger !== 'function') {
            this.logger = function () {
            };
        }

        // Initialize local store.
        this.local = new AWS.CognitoSyncManager.LocalStorage({DataStore: options.DataStore ? options.DataStore : AWS.CognitoSyncManager.StoreLocalStorage});

        // Initialize remote store.
        this.remote = new AWS.CognitoSyncManager.RemoteStorage(this.identityPoolId, this.provider);
        this.remote.setUserAgent(USER_AGENT);

    };

    /**
     * Returns a dataset object, creating it if it doesn't already exist.
     * @param {string} datasetName
     * @param {function} callback
     */

    AWS.CognitoSyncManager.prototype.openOrCreateDataset = function (datasetName, callback) {

        var root = this;
        var namePattern = new RegExp('^[a-zA-Z0-9_.:-]{1,128}$');

        // Validate the proposed dataset name.

        if (namePattern.test(datasetName)) {

            this.local.createDataset(this.getIdentityId(), datasetName, function (err, data) {
                if (err) {
                    return callback(err, null);
                }
                callback(null, new AWS.CognitoSyncManager.Dataset(data, root.provider, root.local, root.remote, root.logger));
            });

        } else {

            callback(new Error('Dataset name must match the pattern ' + namePattern.toString()));

        }

    };

    /**
     * Returns a list of datasets.
     * @param {function} callback
     */

    AWS.CognitoSyncManager.prototype.listDatasets = function (callback) {
        this.local.getDatasets(this.getIdentityId(), callback);
    };

    /**
     * Replaces the local dataset metadata with the latest remote metadata.
     * @param callback
     */

    AWS.CognitoSyncManager.prototype.refreshDatasetMetadata = function (callback) {

        var root = this;

        this.remote.getDatasets(function (err, datasets) {

            var metadata = [];

            var request = function (ds) {
                root.local.updateDatasetMetadata(root.getIdentityId(), ds, response);
            };

            var response = function (err, md) {
                metadata.push(md);
                if (datasets.length > 0) {
                    request(datasets.shift());
                }
                else {
                    callback(null, metadata);
                }
            };

            if (datasets.length > 0) {
                request(datasets.shift(), callback);
            } else {
                callback(null, []);
            }

        });
    };

    /**
     * Removes the local storage and invalidates the cached identity id.
     */

    AWS.CognitoSyncManager.prototype.wipeData = function () {
        this.provider.clearCachedId();
        this.local.wipeData();
    };

    /**
     * Returns the cached identity id.
     * @returns {string}
     */

    AWS.CognitoSyncManager.prototype.getIdentityId = function () {
        return this.provider.identityId;
    };

}


================================================
FILE: src/CognitoSyncRecord.js
================================================
// Copyright 2014 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

AWS = AWS || {};
AWS.CognitoSyncManager = AWS.CognitoSyncManager || {};

AWS.CognitoSyncManager.Record = (function() {

    /**
     * Constructs a new remote storage class.
     * @param {Object} data
     * @param {string} data.Key - The record's key
     * @param {string} data.Value - The record's key
     * @param {number} data.SyncCount
     * @param {Date} data.LastModifiedDate
     * @param {string} data.LastModifiedBy
     * @param {Date} data.DeviceLastModifiedDate
     * @param {boolean} data.Modified
     * @constructor
     */

    var CognitoSyncRecord = function (data) {

        data = data || {};

        // Assign object
        this.key = data.Key || '';
        this.value = data.Value || '';
        this.syncCount = data.SyncCount || 0;
        this.lastModifiedDate = data.LastModifiedDate ? new Date(data.LastModifiedDate) : new Date();
        this.lastModifiedBy = data.LastModifiedBy || '';
        this.deviceLastModifiedDate = data.DeviceLastModifiedDate ? new Date(data.DeviceLastModifiedDate) : new Date();
        this.modified = data.Modified || false;

    };

    /**
     * Returns the record's key.
     * @returns {string}
     */

    CognitoSyncRecord.prototype.getKey = function () {
        return this.key;
    };

    /**
     * Sets the record's key.
     * @param key
     * @returns {CognitoSyncRecord}
     */

    CognitoSyncRecord.prototype.setKey = function (key) {
        this.key = key;
        return this;
    };

    /**
     * Returns the record's value.
     * @returns {string}
     */

    CognitoSyncRecord.prototype.getValue = function () {
        return this.value;
    };

    /**
     * Sets the record's value.
     * @param value
     * @returns {CognitoSyncRecord}
     */

    CognitoSyncRecord.prototype.setValue = function (value) {
        this.value = value;
        return this;
    };

    /**
     * Returns the current sync count.
     * @returns {number}
     */

    CognitoSyncRecord.prototype.getSyncCount = function () {
        return this.syncCount;
    };

    /**
     * Sets the current sync count.
     * @param syncCount
     * @returns {CognitoSyncRecord}
     */

    CognitoSyncRecord.prototype.setSyncCount = function (syncCount) {
        this.syncCount = syncCount;
        return this;
    };

    /**
     * Returns the date the record was last modified.
     * @returns {Date}
     */

    CognitoSyncRecord.prototype.getLastModifiedDate = function () {
        return new Date(this.lastModifiedDate);
    };

    /**
     * Sets the date the record was last modified.
     * @param modifiedDate
     * @returns {CognitoSyncRecord}
     */

    CognitoSyncRecord.prototype.setLastModifiedDate = function (modifiedDate) {
        this.lastModifiedDate = new Date(modifiedDate);
        return this;
    };

    /**
     * Returns the user/device who last modified the record.
     * @returns {string}
     */

    CognitoSyncRecord.prototype.getLastModifiedBy = function () {
        return this.lastModifiedBy;
    };

    /**
     * Sets the user/device who last modified the record.
     * @param modifiedBy
     * @returns {CognitoSyncRecord}
     */

    CognitoSyncRecord.prototype.setLastModifiedBy = function (modifiedBy) {
        this.lastModifiedBy = modifiedBy;
        return this;
    };

    /**
     * Returns the date when the record was last modified on the local device.
     * @returns {Date}
     */

    CognitoSyncRecord.prototype.getDeviceLastModifiedDate = function () {
        return new Date(this.deviceLastModifiedDate);
    };

    /**
     * Sets the date when the record was last modified on the local device.
     * @param modifiedDate
     * @returns {CognitoSyncRecord}
     */

    CognitoSyncRecord.prototype.setDeviceLastModifiedDate = function (modifiedDate) {
        this.deviceLastModifiedDate = new Date(modifiedDate);
        return this;
    };

    /**
     * Returns if the record has been modified.
     * @returns {boolean}
     */

    CognitoSyncRecord.prototype.isModified = function () {
        return this.modified;
    };

    /**
     * Sets if the record has been modified.
     * @param modified
     * @returns {CognitoSyncRecord}
     */

    CognitoSyncRecord.prototype.setModified = function (modified) {
        this.modified = modified;
        return this;
    };

    /**
     * Returns if the record has been deleted locally.
     * @returns {boolean}
     */

    CognitoSyncRecord.prototype.isDeleted = function () {
        return this.value === null;
    };

    /**
     * Returns a string representation of the record.
     * @returns {string}
     */

    CognitoSyncRecord.prototype.toString = function () {
        return JSON.stringify(this);
    };

    /**
     * Returns a flat object representing the record.
     * @returns {object}
     */

    CognitoSyncRecord.prototype.toJSON = function () {
        return {
            Key: this.key,
            Value: this.value,
            SyncCount: this.syncCount,
            LastModifiedDate: this.lastModifiedDate,
            LastModifiedBy: this.lastModifiedBy,
            DeviceLastModifiedDate: this.deviceLastModifiedDate,
            Modified: this.modified
        };
    };

    return CognitoSyncRecord;

})();


================================================
FILE: src/CognitoSyncRemoteStorage.js
================================================
// Copyright 2014 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

AWS = AWS || {};
AWS.CognitoSyncManager = AWS.CognitoSyncManager || {};

AWS.CognitoSyncManager.RemoteStorage = (function() {

    /**
     * Constructs a new remote storage class.
     * @param {string} identityPoolId
     * @param provider
     * @constructor
     */

    var CognitoSyncRemoteStorage = function (identityPoolId, provider) {

        this.identityPoolId = identityPoolId;
        this.provider = provider;
        this.client = new AWS.CognitoSync();

    };

    CognitoSyncRemoteStorage.prototype.userAgent = '';

    /**
     * Gets the current identity ID from the AWS credentials provider.
     * @returns {string}
     */

    CognitoSyncRemoteStorage.prototype.getIdentityId = function () {
        return this.provider.identityId;
    };

    /**
     * Returns a list of datasets.
     * @param {function} callback Callback(Error, Datasets)
     */

    CognitoSyncRemoteStorage.prototype.getDatasets = function (callback) {

        var root = this;
        var datasets = [];
        var nextToken = null;

        // Define the request function. Will be called once per page of results.

        var fetch = function (token, cb) {
            root.client.listDatasets({
                IdentityId: root.getIdentityId(),
                IdentityPoolId: root.identityPoolId,
                MaxResults: 64,
                NextToken: token
            }, cb);
        };

        // Define the response function. Will be called after each request returns.

        var process = function (err, data) {

            var results = data.Datasets || [];

            // Add the new page of results to the list of datasets.

            for (var i = 0; i < results.length; i++) {
                datasets.push(new AWS.CognitoSyncManager.DatasetMetadata(results[i]));
            }

            // Get the NextToken. If it exists, fetch the next page of results, otherwise callback.

            nextToken = data.NextToken;

            if (nextToken) {
                fetch(nextToken, process);
            }
            else {
                callback(null, datasets);
            }

        };

        // Start the first fetch.

        fetch(nextToken, process);

    };

    /**
     * Lists all updates on the remote store since the given sync count.
     * @param {string} datasetName
     * @param {number} lastSyncCount
     * @param {function} callback Callback(Error, Updates)
     */

    CognitoSyncRemoteStorage.prototype.listUpdates = function (datasetName, lastSyncCount, callback) {

        var root = this;
        var nextToken = null;
        var updatedRecords = new AWS.CognitoSyncManager.DatasetUpdates(datasetName);

        var request = function (token, cb) {
            root.client.listRecords({
                DatasetName: datasetName,
                IdentityId: root.getIdentityId(),
                IdentityPoolId: root.identityPoolId,
                LastSyncCount: lastSyncCount,
                MaxResults: 1024,
                NextToken: token
            }, cb);
        };

        var response = function (err, data) {

            if (err) { return callback(err); }

            data = data || {};

            var results = data.Records || [], r;

            for (var i = 0; i < results.length; i++) {
                r = new AWS.CognitoSyncManager.Record(results[i]);
                r.setModified(false);
                updatedRecords.addRecord(r);
            }

            updatedRecords.setSyncSessionToken(data.SyncSessionToken)
                .setSyncCount(data.DatasetSyncCount)
                .setExists(data.DatasetExists)
                .setDeleted(data.DatasetDeletedAfterRequestedSyncCount);

            if (data.MergedDatasetNames) {
                updatedRecords.setMergedDatasetNameList(data.MergedDatasetNames);
            }

            nextToken = data.NextToken;

            if (nextToken) {
                request(nextToken, response);
            } else {
                callback(null, updatedRecords);
            }

        };


        request(null, response);

    };

    /**
     * Write records to the remote data store.
     * @param {string} datasetName
     * @param {array} records
     * @param {string} syncSessionToken
     * @param {function} callback
     */

    CognitoSyncRemoteStorage.prototype.putRecords = function (datasetName, records, syncSessionToken, callback) {

        var root = this;

        var patches = [];
        var record;

        for (var r in records) {
            if (records.hasOwnProperty(r)) {

                record = records[r];

                patches.push({
                    Key: record.getKey(),
                    Op: record.getValue() ? 'replace' : 'remove',
                    SyncCount: record.getSyncCount(),
                    DeviceLastModifiedDate: record.getDeviceLastModifiedDate(),
                    Value: record.getValue()
                });

            }
        }

        this.client.updateRecords({
            DatasetName: datasetName,
            IdentityId: root.getIdentityId(),
            IdentityPoolId: root.identityPoolId,
            SyncSessionToken: syncSessionToken,
            RecordPatches: patches
        }, function (err, data) {

            var dsName = typeof datasetName === 'string' ? datasetName : '(invalid dataset name)';

            if (err) {
                return callback(new Error('Failed to update records in dataset: ' + dsName + ' (' + err.message + ')'), null);
            }

            var records = [], r;

            for (var i = 0; i < data.Records.length; i++) {
                r = new AWS.CognitoSyncManager.Record(data.Records[i]);
                r.setModified(false);
                records.push(r);
            }

            return callback(null, records);

        });

    };

    /**
     * Delete the dataset from the remote data store.
     * @param {string} datasetName
     * @param {function} callback Callback(Error, IsSuccessful)
     */

    CognitoSyncRemoteStorage.prototype.deleteDataset = function (datasetName, callback) {

        this.client.deleteDataset({
            DatasetName: datasetName,
            IdentityId: this.getIdentityId(),
            IdentityPoolId: this.identityPoolId
        }, function (err, data) {

            if (err) {
                return callback(new Error('Failed to delete dataset.'), null);
            }

            return callback(null, data);

        });

    };

    /**
     * Gets the dataset metdata from the remote data store.
     * @param {string} datasetName
     * @param {function} callback Callback(Error, Metadata)
     */

    CognitoSyncRemoteStorage.prototype.getDatasetMetadata = function (datasetName, callback) {

        this.client.describeDataset({
            DatasetName: datasetName,
            IdentityId: this.getIdentityId(),
            IdentityPoolId: this.identityPoolId
        }, function (err, data) {

            if (err) {
                return callback(new Error('Failed to get dataset metadata.'), null);
            }

            return callback(null, new AWS.CognitoSyncManager.DatasetMetadata(data.Dataset));

        });

    };

    CognitoSyncRemoteStorage.prototype.setUserAgent = function (userAgent) {
        this.userAgent = userAgent;
    };

    return CognitoSyncRemoteStorage;

})();

================================================
FILE: src/CognitoSyncStoreInMemory.js
================================================
// Copyright 2014 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

AWS = AWS || {};
AWS.CognitoSyncManager = AWS.CognitoSyncManager || {};

AWS.CognitoSyncManager.StoreInMemory = (function() {

    /**
     * Storage adapter for using the browser's memory as the Cognito Sync data cache.
     * @prop {object} store A reference to an object in memory.
     * @constructor
     */

    var CognitoSyncStoreInMemory = function () {
        this.store = {};
    };

    /**
     * Constructs the key by combining the identity ID and the dataset name.
     * @param identityId
     * @param datasetName
     * @returns {string}
     */

    CognitoSyncStoreInMemory.prototype.makeKey = function (identityId, datasetName) {
        return identityId + '.' + datasetName;
    };

    /**
     * Gets an item from the local store.
     * @param identityId
     * @param datasetName
     * @param key
     * @param callback
     */

    CognitoSyncStoreInMemory.prototype.get = function (identityId, datasetName, key, callback) {

        var k = this.makeKey(identityId, datasetName);

        if (!identityId || !datasetName) {
            return callback(new Error('You must provide an identity id and dataset name.'), null);
        }

        if (this.store[k] && this.store[k][key]) {
            return callback(null, this.store[k][key]);
        }

        return callback(null, undefined);

    };

    /**
     * Gets a dataset from the local store.
     * @param identityId
     * @param datasetName
     * @param callback
     */

    CognitoSyncStoreInMemory.prototype.getAll = function (identityId, datasetName, callback) {

        var k = this.makeKey(identityId, datasetName);

        if (!identityId || !datasetName) {
            return callback(new Error('You must provide an identity id and dataset name.'), null);
        }

        return callback(null, this.store[k]);

    };

    /**
     * Sets a record in the local store.
     * @param identityId
     * @param datasetName
     * @param key
     * @param value
     * @param callback
     */

    CognitoSyncStoreInMemory.prototype.set = function (identityId, datasetName, key, value, callback) {

        var k = this.makeKey(identityId, datasetName);

        var entry = this.store[k] || {};
        entry[key] = value;

        this.store[k] = entry;

        return callback(null, entry);

    };

    /**
     * Sets an entire dataset in the local store.
     * @param identityId
     * @param datasetName
     * @param obj
     * @param callback
     */

    CognitoSyncStoreInMemory.prototype.setAll = function (identityId, datasetName, obj, callback) {

        var k = this.makeKey(identityId, datasetName);
        this.store[k] = obj;

        return callback(null, obj);

    };

    /**
     * Removes a record from the local store.
     * @param identityId
     * @param datasetName
     * @param key
     * @param callback
     */

    CognitoSyncStoreInMemory.prototype.remove = function (identityId, datasetName, key, callback) {

        var k = this.makeKey(identityId, datasetName);

        var records = JSON.parse(this.store[k]);
        if (!records) {
            records = {};
        }

        delete(records[key]);

        this.store[k] = JSON.stringify(records);

        return callback(null, true);

    };

    /**
     * Removes dataset from local store.
     * @param identityId
     * @param datasetName
     * @param callback
     */

    CognitoSyncStoreInMemory.prototype.removeAll = function (identityId, datasetName, callback) {

        var k = this.makeKey(identityId, datasetName);
        delete(this.store[k]);

        return callback(null, true);

    };

    /**
     * Clears the local store, including cached values.
     * @param callback
     */

    CognitoSyncStoreInMemory.prototype.wipe = function (callback) {
        this.store = {};
        return callback(null, true);
    };

    return CognitoSyncStoreInMemory;

})();

================================================
FILE: src/CognitoSyncStoreLocalStorage.js
================================================
// Copyright 2014 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

AWS = AWS || {};
AWS.CognitoSyncManager = AWS.CognitoSyncManager || {};

AWS.CognitoSyncManager.StoreLocalStorage = (function() {

    /**
     * Storage adapter for using the browser's local storage as the Cognito Sync data cache.
     * @prop {window.localStorage} store A reference to the browser's local storage API.
     * @constructor
     */

    var CognitoSyncStoreLocalStorage = function () {
        this.store = window.localStorage;
    };

    /**
     * Constructs the key by combining the identity ID and the dataset name.
     * @param {string} identityId
     * @param {string} datasetName
     * @returns {string}
     */

    CognitoSyncStoreLocalStorage.prototype.makeKey = function (identityId, datasetName) {
        return identityId + '.' + datasetName;
    };

    /**
     * Returns a value from local storage.
     * @param {string} identityId The identity that owns the dataset.
     * @param {string} datasetName The name of the dataset.
     * @param {string} key The key of the record to return.
     * @param {function} callback
     */

    CognitoSyncStoreLocalStorage.prototype.get = function (identityId, datasetName, key, callback) {

        var k = this.makeKey(identityId, datasetName);

        if (!identityId || !datasetName) {
            return callback(new Error('You must provide an identity id and dataset name.'), null);
        }

        var records = JSON.parse(this.store.getItem(k));

        if (records && records[key]) {
            return callback(null, records[key]);
        }

        return callback(null, undefined);

    };

    /**
     * Gets all records from local storage.
     * @param identityId
     * @param datasetName
     * @param callback Callback(Error, Items)
     */

    CognitoSyncStoreLocalStorage.prototype.getAll = function (identityId, datasetName, callback) {

        var k = this.makeKey(identityId, datasetName);

        if (!identityId || !datasetName) {
            return callback(new Error('You must provide an identity id and dataset name.'), null);
        }

        return callback(null, JSON.parse(this.store.getItem(k)));

    };

    /**
     * Sets a value in local storage.
     * @param identityId
     * @param datasetName
     * @param key
     * @param value
     * @param callback Callback(Error, Records)
     */

    CognitoSyncStoreLocalStorage.prototype.set = function (identityId, datasetName, key, value, callback) {

        var k = this.makeKey(identityId, datasetName);

        var records = JSON.parse(this.store.getItem(k));
        if (!records) {
            records = {};
        }

        records[key] = value;

        this.store.setItem(k, JSON.stringify(records));

        callback(null, records);

        return this;

    };

    /**
     * Sets all values of a dataset.
     * @param identityId
     * @param datasetName
     * @param obj
     * @param callback Callback(Error, Object)
     */

    CognitoSyncStoreLocalStorage.prototype.setAll = function (identityId, datasetName, obj, callback) {

        var k = this.makeKey(identityId, datasetName);

        this.store.setItem(k, JSON.stringify(obj));

        return callback(null, obj);

    };

    /**
     * Removes an item from local storage.
     * @param identityId
     * @param datasetName
     * @param key
     * @param callback
     */

    CognitoSyncStoreLocalStorage.prototype.remove = function (identityId, datasetName, key, callback) {

        var k = this.makeKey(identityId, datasetName);

        var records = JSON.parse(this.store.getItem(k));
        if (!records) {
            records = {};
        }

        delete(records[key]);

        this.store.setItem(k, JSON.stringify(records));

        return callback(null, true);

    };

    /**
     * Removes dataset from local storage.
     * @param identityId
     * @param datasetName
     * @param callback
     */

    CognitoSyncStoreLocalStorage.prototype.removeAll = function (identityId, datasetName, callback) {

        var k = this.makeKey(identityId, datasetName);
        this.store.removeItem(k);

        return callback(null, true);

    };

    /**
     * Clears local storage, including cached values.
     * @param callback
     */

    CognitoSyncStoreLocalStorage.prototype.wipe = function (callback) {

        // We don't want to remove the cached identity id. Remove all other keys.

        for (var prop in this.store) {
            if (this.store.hasOwnProperty(prop)) {
                if (prop.indexOf('aws.cognito.identity') === -1) {
                    this.store.removeItem(prop);
                }
            }
        }

        if (callback) {
            return callback(null, true);
        }
        return this;

    };

    return CognitoSyncStoreLocalStorage;

})();
Download .txt
gitextract_ktd83ej4/

├── .gitignore
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── Gruntfile.js
├── LICENSE.txt
├── NOTICE.txt
├── README.md
├── package.json
└── src/
    ├── CognitoSyncConflict.js
    ├── CognitoSyncDataset.js
    ├── CognitoSyncDatasetMetadata.js
    ├── CognitoSyncDatasetUpdates.js
    ├── CognitoSyncLocalStorage.js
    ├── CognitoSyncManager.js
    ├── CognitoSyncRecord.js
    ├── CognitoSyncRemoteStorage.js
    ├── CognitoSyncStoreInMemory.js
    └── CognitoSyncStoreLocalStorage.js
Condensed preview — 18 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (103K chars).
[
  {
    "path": ".gitignore",
    "chars": 35,
    "preview": "node_modules/\n.idea/\n*-private.html"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "chars": 311,
    "preview": "## Code of Conduct\nThis project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-condu"
  },
  {
    "path": "CONTRIBUTING.md",
    "chars": 3579,
    "preview": "# Contributing Guidelines\n\nThank you for your interest in contributing to our project. Whether it's a bug report, new fe"
  },
  {
    "path": "Gruntfile.js",
    "chars": 3158,
    "preview": "module.exports = function(grunt) {\n\n    grunt.initConfig({\n\n        pkg: grunt.file.readJSON('package.json'),\n\n        b"
  },
  {
    "path": "LICENSE.txt",
    "chars": 10143,
    "preview": "\n                                 Apache License\n                           Version 2.0, January 2004\n                 "
  },
  {
    "path": "NOTICE.txt",
    "chars": 107,
    "preview": "Cognito Sync Manager for JavaScript\nCopyright 2014 Amazon.com, Inc. or its affiliates. All Rights Reserved."
  },
  {
    "path": "README.md",
    "chars": 5081,
    "preview": "# Amazon Cognito Sync Manager for JavaScript\n\n**Developer Preview:** We welcome developer feedback on this project. You"
  },
  {
    "path": "package.json",
    "chars": 913,
    "preview": "{\n  \"name\": \"amazon-cognito-js\",\n  \"description\": \"Cognito Sync Manager for AWS JavaScript SDK\",\n  \"main\": \"./dist/amazo"
  },
  {
    "path": "src/CognitoSyncConflict.js",
    "chars": 3085,
    "preview": "// Copyright 2014 Amazon.com, Inc. or its affiliates. All Rights Reserved.\n// SPDX-License-Identifier: Apache-2.0\n\nAWS ="
  },
  {
    "path": "src/CognitoSyncDataset.js",
    "chars": 20487,
    "preview": "// Copyright 2014 Amazon.com, Inc. or its affiliates. All Rights Reserved.\n// SPDX-License-Identifier: Apache-2.0\n\nAWS ="
  },
  {
    "path": "src/CognitoSyncDatasetMetadata.js",
    "chars": 6045,
    "preview": "// Copyright 2014 Amazon.com, Inc. or its affiliates. All Rights Reserved.\n// SPDX-License-Identifier: Apache-2.0\n\n/**\n "
  },
  {
    "path": "src/CognitoSyncDatasetUpdates.js",
    "chars": 2502,
    "preview": "// Copyright 2014 Amazon.com, Inc. or its affiliates. All Rights Reserved.\n// SPDX-License-Identifier: Apache-2.0\n\nAWS ="
  },
  {
    "path": "src/CognitoSyncLocalStorage.js",
    "chars": 18150,
    "preview": "// Copyright 2014 Amazon.com, Inc. or its affiliates. All Rights Reserved.\n// SPDX-License-Identifier: Apache-2.0\n\nAWS ="
  },
  {
    "path": "src/CognitoSyncManager.js",
    "chars": 3684,
    "preview": "// Copyright 2014 Amazon.com, Inc. or its affiliates. All Rights Reserved.\n// SPDX-License-Identifier: Apache-2.0\n\nif (A"
  },
  {
    "path": "src/CognitoSyncRecord.js",
    "chars": 5374,
    "preview": "// Copyright 2014 Amazon.com, Inc. or its affiliates. All Rights Reserved.\n// SPDX-License-Identifier: Apache-2.0\n\nAWS ="
  },
  {
    "path": "src/CognitoSyncRemoteStorage.js",
    "chars": 7452,
    "preview": "// Copyright 2014 Amazon.com, Inc. or its affiliates. All Rights Reserved.\n// SPDX-License-Identifier: Apache-2.0\n\nAWS ="
  },
  {
    "path": "src/CognitoSyncStoreInMemory.js",
    "chars": 4003,
    "preview": "// Copyright 2014 Amazon.com, Inc. or its affiliates. All Rights Reserved.\n// SPDX-License-Identifier: Apache-2.0\n\nAWS ="
  },
  {
    "path": "src/CognitoSyncStoreLocalStorage.js",
    "chars": 4891,
    "preview": "// Copyright 2014 Amazon.com, Inc. or its affiliates. All Rights Reserved.\n// SPDX-License-Identifier: Apache-2.0\n\nAWS ="
  }
]

About this extraction

This page contains the full source code of the aws/amazon-cognito-js GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 18 files (96.7 KB), approximately 20.9k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!