├── .circleci └── config.yml ├── .editorconfig ├── .eslintrc.json ├── .gitignore ├── .npmignore ├── CONTRIBUTING.md ├── ISSUE_TEMPLATE.md ├── LICENSE ├── README.md ├── bower.json ├── build ├── args.js ├── babel-options.js ├── paths.js ├── tasks │ ├── build.js │ ├── clean.js │ ├── dev.js │ ├── doc.js │ ├── lint.js │ ├── prepare-release.js │ └── test.js └── typescript-options.js ├── config.js ├── dist ├── amd │ ├── aurelia-http-client.js │ └── index.js ├── aurelia-http-client.d.ts ├── aurelia-http-client.js ├── commonjs │ ├── aurelia-http-client.js │ └── index.js ├── es2015 │ ├── aurelia-http-client.js │ └── index.js ├── index.d.ts ├── native-modules │ ├── aurelia-http-client.js │ └── index.js └── system │ ├── aurelia-http-client.js │ └── index.js ├── doc ├── CHANGELOG.md └── api.json ├── gulpfile.js ├── karma.conf.js ├── package-lock.json ├── package.json ├── src ├── error-http-response-message.js ├── headers.js ├── http-client.js ├── http-request-message.js ├── http-response-message.js ├── jsonp-request-message.js ├── request-builder.js ├── request-message-processor.js ├── request-message.js └── xhr-transformers.js ├── test ├── headers.spec.js ├── http-client.spec.js ├── http-request-message.spec.js ├── http-response-message.spec.js ├── jsonp-request-message.spec.js ├── request-builder.spec.js ├── request-message-processor.spec.js ├── request-message.spec.js ├── setup.js └── xhr-transformers.spec.js ├── tsconfig.json └── typings.json /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2.1 2 | 3 | map-1: &filter_only_develop 4 | filters: 5 | branches: 6 | only: develop 7 | 8 | map-2: &filter_only_tag 9 | filters: 10 | branches: 11 | ignore: /.*/ 12 | tags: 13 | only: /^v?[0-9]+(\.[0-9]+)*$/ 14 | 15 | orbs: 16 | v1: aurelia/v1@volatile 17 | 18 | workflows: 19 | main: 20 | jobs: 21 | - v1/build_test 22 | - v1/build_merge: 23 | <<: *filter_only_develop 24 | requires: 25 | - v1/build_test 26 | - v1/npm_publish: 27 | <<: *filter_only_tag 28 | name: npm_publish_dry 29 | args: "--dry-run" 30 | - request_publish_latest: 31 | <<: *filter_only_tag 32 | type: approval 33 | requires: 34 | - npm_publish_dry 35 | - v1/npm_publish: 36 | <<: *filter_only_tag 37 | name: npm_publish 38 | context: Aurelia 39 | requires: 40 | - request_publish_latest 41 | - v1/merge_back: 42 | <<: *filter_only_tag 43 | requires: 44 | - npm_publish 45 | 46 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: http://EditorConfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | # Unix-style newlines with a newline ending every file 7 | [*] 8 | end_of_line = lf 9 | insert_final_newline = true 10 | 11 | # 2 space indentation 12 | [**.*] 13 | indent_style = space 14 | indent_size = 2 -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./node_modules/aurelia-tools/.eslintrc.json" 3 | } 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | jspm_packages 3 | bower_components 4 | .idea 5 | .DS_STORE 6 | build/reports 7 | dist 8 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | jspm_packages 2 | bower_components 3 | .idea 4 | build/reports 5 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | We'd love for you to contribute and to make this project even better than it is today! If this interests you, please begin by reading [our contributing guidelines](https://github.com/DurandalProject/about/blob/master/CONTRIBUTING.md). The contributing document will provide you with all the information you need to get started. Once you have read that, you will need to also [sign our CLA](http://goo.gl/forms/dI8QDDSyKR) before we can accept a Pull Request from you. More information on the process is included in the [contributor's guide](https://github.com/DurandalProject/about/blob/master/CONTRIBUTING.md). 4 | -------------------------------------------------------------------------------- /ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 19 | **I'm submitting a bug report** 20 | **I'm submitting a feature request** 21 | 22 | * **Library Version:** 23 | major.minor.patch-pre 24 | 25 | 26 | **Please tell us about your environment:** 27 | * **Operating System:** 28 | OSX 10.x|Linux (distro)|Windows [7|8|8.1|10] 29 | 30 | * **Node Version:** 31 | 6.2.0 32 | 36 | 37 | * **NPM Version:** 38 | 3.8.9 39 | 43 | 44 | * **JSPM OR Webpack AND Version** 45 | JSPM 0.16.32 | webpack 2.1.0-beta.17 46 | 52 | 53 | * **Browser:** 54 | all | Chrome XX | Firefox XX | Edge XX | IE XX | Safari XX | Mobile Chrome XX | Android X.X Web Browser | iOS XX Safari | iOS XX UIWebView | iOS XX WKWebView 55 | 56 | * **Language:** 57 | all | TypeScript X.X | ESNext 58 | 59 | 60 | **Current behavior:** 61 | 62 | 63 | **Expected/desired behavior:** 64 | 71 | 72 | 73 | * **What is the expected behavior?** 74 | 75 | 76 | * **What is the motivation / use case for changing the behavior?** 77 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2010 - 2018 Blue Spire Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # aurelia-http-client 2 | 3 | [![npm Version](https://img.shields.io/npm/v/aurelia-http-client.svg)](https://www.npmjs.com/package/aurelia-http-client) 4 | [![ZenHub](https://raw.githubusercontent.com/ZenHubIO/support/master/zenhub-badge.png)](https://zenhub.io) 5 | [![Join the chat at https://gitter.im/aurelia/discuss](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/aurelia/discuss?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 6 | [![CircleCI](https://circleci.com/gh/aurelia/http-client.svg?style=shield)](https://circleci.com/gh/aurelia/http-client) 7 | 8 | This library is part of the [Aurelia](http://www.aurelia.io/) platform and contains a simple, restful, message-based wrapper around XMLHttpRequest. 9 | 10 | To keep up to date on [Aurelia](http://www.aurelia.io/), please visit and subscribe to [the official blog](http://blog.aurelia.io/) and [our email list](http://eepurl.com/ces50j). We also invite you to [follow us on twitter](https://twitter.com/aureliaeffect). If you have questions look around our [Discourse forums](https://discourse.aurelia.io/), chat in our [community on Gitter](https://gitter.im/aurelia/discuss) or use [stack overflow](http://stackoverflow.com/search?q=aurelia). Documentation can be found [in our developer hub](http://aurelia.io/docs). If you would like to have deeper insight into our development process, please install the [ZenHub](https://zenhub.io) Chrome or Firefox Extension and visit any of our repository's boards. 11 | 12 | ## Documentation 13 | 14 | If possible, prefer to use the `aurelia-fetch-client` instead of this library. You can read documentation on the fetch client [here](http://aurelia.io/docs/plugins/http-services#options) along with documentation on this library. If you would like to help improve this documentation, the source for these docs can be found in the `aurelia-fetch-client` repo's doc folder. 15 | 16 | ## Platform Support 17 | 18 | This library can be used in the **browser** only. 19 | 20 | ## Building The Code 21 | 22 | To build the code, follow these steps. 23 | 24 | 1. Ensure that [NodeJS](http://nodejs.org/) is installed. This provides the platform on which the build tooling runs. 25 | 2. From the project folder, execute the following command: 26 | 27 | ```shell 28 | npm install 29 | ``` 30 | 3. Ensure that [Gulp](http://gulpjs.com/) is installed. If you need to install it, use the following command: 31 | 32 | ```shell 33 | npm install -g gulp 34 | ``` 35 | 4. To build the code, you can now run: 36 | 37 | ```shell 38 | gulp build 39 | ``` 40 | 5. You will find the compiled code in the `dist` folder, available in three module formats: AMD, CommonJS and ES6. 41 | 42 | 6. See `gulpfile.js` for other tasks related to generating the docs and linting. 43 | 44 | ## Running The Tests 45 | 46 | To run the unit tests, first ensure that you have followed the steps above in order to install all dependencies and successfully build the library. Once you have done that, proceed with these additional steps: 47 | 48 | 1. Ensure that the [Karma](http://karma-runner.github.io/) CLI is installed. If you need to install it, use the following command: 49 | 50 | ```shell 51 | npm install -g karma-cli 52 | ``` 53 | 2. Ensure that [jspm](http://jspm.io/) is installed. If you need to install it, use the following commnand: 54 | 55 | ```shell 56 | npm install -g jspm 57 | ``` 58 | 3. Install the client-side dependencies with jspm: 59 | 60 | ```shell 61 | jspm install 62 | ``` 63 | 64 | 4. You can now run the tests with this command: 65 | 66 | ```shell 67 | karma start 68 | ``` 69 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "aurelia-http-client", 3 | "version": "1.3.2", 4 | "description": "A simple, restful, message-based wrapper around XMLHttpRequest.", 5 | "keywords": [ 6 | "aurelia", 7 | "http", 8 | "ajax" 9 | ], 10 | "homepage": "http://aurelia.io", 11 | "main": "dist/commonjs/aurelia-http-client.js", 12 | "moduleType": "node", 13 | "license": "MIT", 14 | "authors": [ 15 | "Rob Eisenberg (http://robeisenberg.com/)" 16 | ], 17 | "repository": { 18 | "type": "git", 19 | "url": "http://github.com/aurelia/http-client" 20 | }, 21 | "dependencies": { 22 | "aurelia-pal": "^1.0.0", 23 | "aurelia-path": "^1.1.1" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /build/args.js: -------------------------------------------------------------------------------- 1 | var yargs = require('yargs'); 2 | 3 | var argv = yargs.argv, 4 | validBumpTypes = "major|minor|patch|prerelease".split("|"), 5 | bump = (argv.bump || 'patch').toLowerCase(); 6 | 7 | if(validBumpTypes.indexOf(bump) === -1) { 8 | throw new Error('Unrecognized bump "' + bump + '".'); 9 | } 10 | 11 | module.exports = { 12 | bump: bump 13 | }; 14 | -------------------------------------------------------------------------------- /build/babel-options.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | var paths = require('./paths'); 3 | 4 | exports.base = function() { 5 | var config = { 6 | filename: '', 7 | filenameRelative: '', 8 | sourceMap: true, 9 | sourceRoot: '', 10 | moduleRoot: path.resolve('src').replace(/\\/g, '/'), 11 | moduleIds: false, 12 | comments: false, 13 | compact: false, 14 | code: true, 15 | presets: [ 'es2015-loose', 'stage-1' ], 16 | plugins: [ 17 | 'syntax-flow', 18 | 'transform-decorators-legacy', 19 | ] 20 | }; 21 | if (!paths.useTypeScriptForDTS) { 22 | config.plugins.push( 23 | ['babel-dts-generator', { 24 | packageName: paths.packageName, 25 | typings: '', 26 | suppressModulePath: true, 27 | suppressComments: false, 28 | memberOutputFilter: /^_.*/, 29 | suppressAmbientDeclaration: true 30 | }] 31 | ); 32 | }; 33 | config.plugins.push('transform-flow-strip-types'); 34 | return config; 35 | } 36 | 37 | exports.commonjs = function() { 38 | var options = exports.base(); 39 | options.plugins.push('transform-es2015-modules-commonjs'); 40 | return options; 41 | }; 42 | 43 | exports.amd = function() { 44 | var options = exports.base(); 45 | options.plugins.push('transform-es2015-modules-amd'); 46 | return options; 47 | }; 48 | 49 | exports.system = function() { 50 | var options = exports.base(); 51 | options.plugins.push('transform-es2015-modules-systemjs'); 52 | return options; 53 | }; 54 | 55 | exports.es2015 = function() { 56 | var options = exports.base(); 57 | options.presets = ['stage-1'] 58 | return options; 59 | }; 60 | 61 | exports['native-modules'] = function() { 62 | var options = exports.base(); 63 | options.presets[0] = 'es2015-loose-native-modules'; 64 | return options; 65 | } 66 | -------------------------------------------------------------------------------- /build/paths.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | var fs = require('fs'); 3 | 4 | // hide warning // 5 | var emitter = require('events'); 6 | emitter.defaultMaxListeners = 20; 7 | 8 | var appRoot = 'src/'; 9 | var pkg = JSON.parse(fs.readFileSync('./package.json', 'utf-8')); 10 | 11 | var paths = { 12 | root: appRoot, 13 | source: appRoot + '**/*.js', 14 | html: appRoot + '**/*.html', 15 | style: 'styles/**/*.css', 16 | output: 'dist/', 17 | doc:'./doc', 18 | e2eSpecsSrc: 'test/e2e/src/*.js', 19 | e2eSpecsDist: 'test/e2e/dist/', 20 | packageName: pkg.name, 21 | ignore: [], 22 | useTypeScriptForDTS: false, 23 | importsToAdd: [], 24 | sort: false 25 | }; 26 | 27 | paths.files = [ 28 | 'headers.js', 29 | 'request-message.js', 30 | 'http-response-message.js', 31 | 'request-message-processor.js', 32 | 'xhr-transformers.js', 33 | 'jsonp-request-message.js', 34 | 'http-request-message.js', 35 | 'error-http-response-message.js', 36 | 'request-builder.js', 37 | 'http-client.js' 38 | ].map(function(file){ 39 | return paths.root + file; 40 | }); 41 | 42 | module.exports = paths; 43 | -------------------------------------------------------------------------------- /build/tasks/build.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | var runSequence = require('run-sequence'); 3 | var to5 = require('gulp-babel'); 4 | var paths = require('../paths'); 5 | var compilerOptions = require('../babel-options'); 6 | var compilerTsOptions = require('../typescript-options'); 7 | var assign = Object.assign || require('object.assign'); 8 | var through2 = require('through2'); 9 | var concat = require('gulp-concat'); 10 | var insert = require('gulp-insert'); 11 | var rename = require('gulp-rename'); 12 | var tools = require('aurelia-tools'); 13 | var ts = require('gulp-typescript'); 14 | var gutil = require('gulp-util'); 15 | var gulpIgnore = require('gulp-ignore'); 16 | var merge = require('merge2'); 17 | var jsName = paths.packageName + '.js'; 18 | var compileToModules = ['es2015', 'commonjs', 'amd', 'system', 'native-modules']; 19 | 20 | function cleanGeneratedCode() { 21 | return through2.obj(function(file, enc, callback) { 22 | file.contents = new Buffer(tools.cleanGeneratedCode(file.contents.toString('utf8'))); 23 | this.push(file); 24 | return callback(); 25 | }); 26 | } 27 | 28 | gulp.task('build-index', function() { 29 | var importsToAdd = paths.importsToAdd.slice(); 30 | 31 | var src = gulp.src(paths.files); 32 | 33 | if (paths.sort) { 34 | src = src.pipe(tools.sortFiles()); 35 | } 36 | 37 | if (paths.ignore) { 38 | paths.ignore.forEach(function(filename){ 39 | src = src.pipe(gulpIgnore.exclude(filename)); 40 | }); 41 | } 42 | 43 | return src.pipe(through2.obj(function(file, enc, callback) { 44 | file.contents = new Buffer(tools.extractImports(file.contents.toString('utf8'), importsToAdd)); 45 | this.push(file); 46 | return callback(); 47 | })) 48 | .pipe(concat(jsName)) 49 | .pipe(insert.transform(function(contents) { 50 | return tools.createImportBlock(importsToAdd) + contents; 51 | })) 52 | .pipe(gulp.dest(paths.output)); 53 | }); 54 | 55 | function gulpFileFromString(filename, string) { 56 | var src = require('stream').Readable({ objectMode: true }); 57 | src._read = function() { 58 | this.push(new gutil.File({ cwd: paths.appRoot, base: paths.output, path: filename, contents: new Buffer(string) })) 59 | this.push(null) 60 | } 61 | return src; 62 | } 63 | 64 | function srcForBabel() { 65 | return merge( 66 | gulp.src(paths.output + jsName), 67 | gulpFileFromString(paths.output + 'index.js', "export * from './" + paths.packageName + "';") 68 | ); 69 | } 70 | 71 | function srcForTypeScript() { 72 | return gulp 73 | .src(paths.output + paths.packageName + '.js') 74 | .pipe(rename(function (path) { 75 | if (path.extname == '.js') { 76 | path.extname = '.ts'; 77 | } 78 | })); 79 | } 80 | 81 | compileToModules.forEach(function(moduleType){ 82 | gulp.task('build-babel-' + moduleType, function () { 83 | return srcForBabel() 84 | .pipe(to5(assign({}, compilerOptions[moduleType]()))) 85 | .pipe(cleanGeneratedCode()) 86 | .pipe(gulp.dest(paths.output + moduleType)); 87 | }); 88 | 89 | if (moduleType === 'native-modules') return; // typescript doesn't support the combination of: es5 + native modules 90 | 91 | gulp.task('build-ts-' + moduleType, function () { 92 | var tsProject = ts.createProject( 93 | compilerTsOptions({ module: moduleType, target: moduleType == 'es2015' ? 'es2015' : 'es5' }), ts.reporter.defaultReporter()); 94 | var tsResult = srcForTypeScript().pipe(ts(tsProject)); 95 | return tsResult.js 96 | .pipe(gulp.dest(paths.output + moduleType)); 97 | }); 98 | }); 99 | 100 | gulp.task('build-dts', function() { 101 | var tsProject = ts.createProject( 102 | compilerTsOptions({ removeComments: false, target: "es2015", module: "es2015" }), ts.reporter.defaultReporter()); 103 | var tsResult = srcForTypeScript().pipe(ts(tsProject)); 104 | return tsResult.dts 105 | .pipe(gulp.dest(paths.output)); 106 | }); 107 | 108 | gulp.task('build', function(callback) { 109 | return runSequence( 110 | 'clean', 111 | 'build-index', 112 | compileToModules 113 | .map(function(moduleType) { return 'build-babel-' + moduleType }) 114 | .concat(paths.useTypeScriptForDTS ? ['build-dts'] : []), 115 | callback 116 | ); 117 | }); 118 | 119 | gulp.task('build-ts', function(callback) { 120 | return runSequence( 121 | 'clean', 122 | 'build-index', 123 | 'build-babel-native-modules', 124 | compileToModules 125 | .filter(function(moduleType) { return moduleType !== 'native-modules' }) 126 | .map(function(moduleType) { return 'build-ts-' + moduleType }) 127 | .concat(paths.useTypeScriptForDTS ? ['build-dts'] : []), 128 | callback 129 | ); 130 | }); 131 | -------------------------------------------------------------------------------- /build/tasks/clean.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | var paths = require('../paths'); 3 | var del = require('del'); 4 | var vinylPaths = require('vinyl-paths'); 5 | 6 | gulp.task('clean', function() { 7 | return gulp.src([paths.output]) 8 | .pipe(vinylPaths(del)); 9 | }); 10 | -------------------------------------------------------------------------------- /build/tasks/dev.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | var tools = require('aurelia-tools'); 3 | 4 | gulp.task('update-own-deps', function(){ 5 | tools.updateOwnDependenciesFromLocalRepositories(); 6 | }); 7 | 8 | gulp.task('build-dev-env', function () { 9 | tools.buildDevEnv(); 10 | }); 11 | -------------------------------------------------------------------------------- /build/tasks/doc.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | var paths = require('../paths'); 3 | var typedoc = require('gulp-typedoc'); 4 | var runSequence = require('run-sequence'); 5 | var through2 = require('through2'); 6 | 7 | gulp.task('doc-generate', function(){ 8 | return gulp.src([paths.output + paths.packageName + '.d.ts']) 9 | .pipe(typedoc({ 10 | target: 'es6', 11 | includeDeclarations: true, 12 | moduleResolution: 'node', 13 | json: paths.doc + '/api.json', 14 | name: paths.packageName + '-docs',  15 | mode: 'modules', 16 | excludeExternals: true, 17 | ignoreCompilerErrors: false, 18 | version: true 19 | })); 20 | }); 21 | 22 | gulp.task('doc-shape', function(){ 23 | return gulp.src([paths.doc + '/api.json']) 24 | .pipe(through2.obj(function(file, enc, callback) { 25 | var json = JSON.parse(file.contents.toString('utf8')).children[0]; 26 | 27 | json = { 28 | name: paths.packageName, 29 | children: json.children, 30 | groups: json.groups 31 | }; 32 | 33 | file.contents = new Buffer(JSON.stringify(json)); 34 | this.push(file); 35 | return callback(); 36 | })) 37 | .pipe(gulp.dest(paths.doc)); 38 | }); 39 | 40 | gulp.task('doc', function(callback){ 41 | return runSequence( 42 | 'doc-generate', 43 | 'doc-shape', 44 | callback 45 | ); 46 | }); 47 | -------------------------------------------------------------------------------- /build/tasks/lint.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | var paths = require('../paths'); 3 | var eslint = require('gulp-eslint'); 4 | 5 | gulp.task('lint', function() { 6 | return gulp.src(paths.source) 7 | .pipe(eslint()) 8 | .pipe(eslint.format()) 9 | .pipe(eslint.failOnError()); 10 | }); 11 | -------------------------------------------------------------------------------- /build/tasks/prepare-release.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | var runSequence = require('run-sequence'); 3 | var paths = require('../paths'); 4 | var fs = require('fs'); 5 | var bump = require('gulp-bump'); 6 | var args = require('../args'); 7 | var conventionalChangelog = require('gulp-conventional-changelog'); 8 | 9 | gulp.task('changelog', function () { 10 | return gulp.src(paths.doc + '/CHANGELOG.md', { 11 | buffer: false 12 | }).pipe(conventionalChangelog({ 13 | preset: 'angular' 14 | })) 15 | .pipe(gulp.dest(paths.doc)); 16 | }); 17 | 18 | gulp.task('bump-version', function(){ 19 | return gulp.src(['./package.json', './bower.json']) 20 | .pipe(bump({type:args.bump })) //major|minor|patch|prerelease 21 | .pipe(gulp.dest('./')); 22 | }); 23 | 24 | gulp.task('prepare-release', function(callback){ 25 | return runSequence( 26 | 'build', 27 | 'lint', 28 | 'bump-version', 29 | 'doc', 30 | 'changelog', 31 | callback 32 | ); 33 | }); 34 | -------------------------------------------------------------------------------- /build/tasks/test.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | var karma = require('karma').server; 3 | 4 | /** 5 | * Run test once and exit 6 | */ 7 | gulp.task('test', function (done) { 8 | karma.start({ 9 | configFile: __dirname + '/../../karma.conf.js', 10 | singleRun: true 11 | }, function(e) { 12 | done(); 13 | }); 14 | }); 15 | 16 | /** 17 | * Watch for file changes and re-run tests on each change 18 | */ 19 | gulp.task('tdd', function (done) { 20 | karma.start({ 21 | configFile: __dirname + '/../../karma.conf.js' 22 | }, function(e) { 23 | done(); 24 | }); 25 | }); 26 | 27 | /** 28 | * Run test once with code coverage and exit 29 | */ 30 | gulp.task('cover', function (done) { 31 | karma.start({ 32 | configFile: __dirname + '/../../karma.conf.js', 33 | singleRun: true, 34 | reporters: ['coverage'], 35 | preprocessors: { 36 | 'test/**/*.js': ['babel'], 37 | 'src/**/*.js': ['babel', 'coverage'] 38 | }, 39 | coverageReporter: { 40 | type: 'html', 41 | dir: 'build/reports/coverage' 42 | } 43 | }, function (e) { 44 | done(); 45 | }); 46 | }); 47 | -------------------------------------------------------------------------------- /build/typescript-options.js: -------------------------------------------------------------------------------- 1 | var tsconfig = require('../tsconfig.json'); 2 | var assign = Object.assign || require('object.assign'); 3 | 4 | module.exports = function(override) { 5 | return assign(tsconfig.compilerOptions, { 6 | "target": override && override.target || "es5", 7 | "typescript": require('typescript') 8 | }, override || {}); 9 | } 10 | -------------------------------------------------------------------------------- /config.js: -------------------------------------------------------------------------------- 1 | System.config({ 2 | defaultJSExtensions: true, 3 | transpiler: "babel", 4 | babelOptions: { 5 | "optional": [ 6 | "runtime", 7 | "optimisation.modules.system" 8 | ] 9 | }, 10 | paths: { 11 | "github:*": "jspm_packages/github/*", 12 | "aurelia-http-client/*": "dist/system/*", 13 | "npm:*": "jspm_packages/npm/*" 14 | }, 15 | 16 | map: { 17 | "aurelia-pal": "npm:aurelia-pal@1.0.0", 18 | "aurelia-pal-browser": "npm:aurelia-pal-browser@1.0.0", 19 | "aurelia-path": "npm:aurelia-path@1.1.1", 20 | "babel": "npm:babel-core@5.8.38", 21 | "babel-runtime": "npm:babel-runtime@5.8.38", 22 | "core-js": "npm:core-js@2.4.1", 23 | "github:jspm/nodelibs-assert@0.1.0": { 24 | "assert": "npm:assert@1.4.1" 25 | }, 26 | "github:jspm/nodelibs-buffer@0.1.0": { 27 | "buffer": "npm:buffer@3.6.0" 28 | }, 29 | "github:jspm/nodelibs-path@0.1.0": { 30 | "path-browserify": "npm:path-browserify@0.0.0" 31 | }, 32 | "github:jspm/nodelibs-process@0.1.2": { 33 | "process": "npm:process@0.11.6" 34 | }, 35 | "github:jspm/nodelibs-util@0.1.0": { 36 | "util": "npm:util@0.10.3" 37 | }, 38 | "github:jspm/nodelibs-vm@0.1.0": { 39 | "vm-browserify": "npm:vm-browserify@0.0.4" 40 | }, 41 | "npm:assert@1.4.1": { 42 | "assert": "github:jspm/nodelibs-assert@0.1.0", 43 | "buffer": "github:jspm/nodelibs-buffer@0.1.0", 44 | "process": "github:jspm/nodelibs-process@0.1.2", 45 | "util": "npm:util@0.10.3" 46 | }, 47 | "npm:aurelia-pal-browser@1.0.0": { 48 | "aurelia-pal": "npm:aurelia-pal@1.0.0" 49 | }, 50 | "npm:babel-runtime@5.8.38": { 51 | "process": "github:jspm/nodelibs-process@0.1.2" 52 | }, 53 | "npm:buffer@3.6.0": { 54 | "base64-js": "npm:base64-js@0.0.8", 55 | "child_process": "github:jspm/nodelibs-child_process@0.1.0", 56 | "fs": "github:jspm/nodelibs-fs@0.1.2", 57 | "ieee754": "npm:ieee754@1.1.6", 58 | "isarray": "npm:isarray@1.0.0", 59 | "process": "github:jspm/nodelibs-process@0.1.2" 60 | }, 61 | "npm:core-js@2.4.1": { 62 | "fs": "github:jspm/nodelibs-fs@0.1.2", 63 | "path": "github:jspm/nodelibs-path@0.1.0", 64 | "process": "github:jspm/nodelibs-process@0.1.2", 65 | "systemjs-json": "github:systemjs/plugin-json@0.1.2" 66 | }, 67 | "npm:inherits@2.0.1": { 68 | "util": "github:jspm/nodelibs-util@0.1.0" 69 | }, 70 | "npm:path-browserify@0.0.0": { 71 | "process": "github:jspm/nodelibs-process@0.1.2" 72 | }, 73 | "npm:process@0.11.6": { 74 | "assert": "github:jspm/nodelibs-assert@0.1.0", 75 | "fs": "github:jspm/nodelibs-fs@0.1.2", 76 | "vm": "github:jspm/nodelibs-vm@0.1.0" 77 | }, 78 | "npm:util@0.10.3": { 79 | "inherits": "npm:inherits@2.0.1", 80 | "process": "github:jspm/nodelibs-process@0.1.2" 81 | }, 82 | "npm:vm-browserify@0.0.4": { 83 | "indexof": "npm:indexof@0.0.1" 84 | } 85 | } 86 | }); 87 | -------------------------------------------------------------------------------- /dist/amd/index.js: -------------------------------------------------------------------------------- 1 | define(['exports', './aurelia-http-client'], function (exports, _aureliaHttpClient) { 2 | 'use strict'; 3 | 4 | Object.defineProperty(exports, "__esModule", { 5 | value: true 6 | }); 7 | Object.keys(_aureliaHttpClient).forEach(function (key) { 8 | if (key === "default" || key === "__esModule") return; 9 | Object.defineProperty(exports, key, { 10 | enumerable: true, 11 | get: function () { 12 | return _aureliaHttpClient[key]; 13 | } 14 | }); 15 | }); 16 | }); -------------------------------------------------------------------------------- /dist/aurelia-http-client.d.ts: -------------------------------------------------------------------------------- 1 | import { 2 | join, 3 | buildQueryString 4 | } from 'aurelia-path'; 5 | import { 6 | PLATFORM, 7 | DOM 8 | } from 'aurelia-pal'; 9 | 10 | /** 11 | * Creates an XHR implementation. 12 | */ 13 | export declare interface XHRConstructor { 14 | 15 | } 16 | 17 | /** 18 | * Represents an XHR. 19 | */ 20 | export declare interface XHR { 21 | 22 | /** 23 | * The status code of the response. 24 | */ 25 | status: number; 26 | 27 | /** 28 | * The status text. 29 | */ 30 | statusText: string; 31 | 32 | /** 33 | * The raw response. 34 | */ 35 | response: any; 36 | 37 | /** 38 | * The raw response text. 39 | */ 40 | responseText: string; 41 | 42 | /** 43 | * The load callback. 44 | */ 45 | onload: Function; 46 | 47 | /** 48 | * The timeout callback. 49 | */ 50 | ontimeout: Function; 51 | 52 | /** 53 | * The error callback. 54 | */ 55 | onerror: Function; 56 | 57 | /** 58 | * The abort callback. 59 | */ 60 | onabort: Function; 61 | 62 | /** 63 | * Aborts the request. 64 | */ 65 | abort(): void; 66 | 67 | /** 68 | * Opens the XHR channel. 69 | */ 70 | open(method: string, url: string, isAsync: boolean, user?: string, password?: string): void; 71 | 72 | /** 73 | * Sends the request. 74 | */ 75 | send(content?: any): void; 76 | } 77 | 78 | /** 79 | * Represents an XHR transformer. 80 | */ 81 | /** 82 | * Represents an XHR transformer. 83 | */ 84 | export declare interface XHRTransformer { 85 | (client: HttpClient, processor: RequestMessageProcessor, message: RequestMessage, xhr: XHR): void; 86 | } 87 | 88 | /** 89 | * Intercepts requests, responses and errors. 90 | */ 91 | export declare interface Interceptor { 92 | 93 | /** 94 | * Intercepts the response. 95 | */ 96 | response?: (message: HttpResponseMessage) => HttpResponseMessage | Promise; 97 | 98 | /** 99 | * Intercepts a response error. 100 | */ 101 | responseError?: (error: HttpResponseMessage) => HttpResponseMessage | Promise; 102 | 103 | /** 104 | * Intercepts the request. 105 | */ 106 | request?: (message: RequestMessage) => RequestMessage | Promise; 107 | 108 | /** 109 | * Intercepts a request error. 110 | */ 111 | requestError?: (error: Error) => RequestMessage | Promise; 112 | } 113 | 114 | /** 115 | * Transforms a request. 116 | */ 117 | /** 118 | * Transforms a request. 119 | */ 120 | export declare interface RequestTransformer { 121 | (client: HttpClient, processor: RequestMessageProcessor, message: RequestMessage): void; 122 | } 123 | 124 | /** 125 | * Represents http request/response headers. 126 | */ 127 | export declare class Headers { 128 | 129 | /** 130 | * Creates an instance of the headers class. 131 | * @param headers A set of key/values to initialize the headers with. 132 | */ 133 | constructor(headers?: Object); 134 | 135 | /** 136 | * Adds a header. 137 | * @param key The header key. 138 | * @param value The header value. 139 | */ 140 | add(key: string, value: string): void; 141 | 142 | /** 143 | * Gets a header value. 144 | * @param key The header key. 145 | * @return The header value. 146 | */ 147 | get(key: string): string; 148 | 149 | /** 150 | * Clears the headers. 151 | */ 152 | clear(): void; 153 | 154 | /** 155 | * Determines whether or not the indicated header exists in the collection. 156 | * @param header The header key to check. 157 | * @return True if it exists, false otherwise. 158 | */ 159 | has(header: string): boolean; 160 | 161 | /** 162 | * Configures an XMR object with the headers. 163 | * @param xhr The XHRT instance to configure. 164 | */ 165 | configureXHR(xhr: XHR): void; 166 | 167 | /** 168 | * XmlHttpRequest's getAllResponseHeaders() method returns a string of response 169 | * headers according to the format described here: 170 | * http://www.w3.org/TR/XMLHttpRequest/#the-getallresponseheaders-method 171 | * This method parses that string into a user-friendly key/value pair object. 172 | * @param headerStr The string from the XHR. 173 | * @return A Headers instance containing the parsed headers. 174 | */ 175 | static parse(headerStr: string): Headers; 176 | } 177 | 178 | /** 179 | * Represents a request message. 180 | */ 181 | export declare class RequestMessage { 182 | 183 | /** 184 | * The HTTP method. 185 | */ 186 | method: string; 187 | 188 | /** 189 | * The url to submit the request to. 190 | */ 191 | url: string; 192 | 193 | /** 194 | * The content of the request. 195 | */ 196 | content: any; 197 | 198 | /** 199 | * The headers to send along with the request. 200 | */ 201 | headers: Headers; 202 | 203 | /** 204 | * Use tradional style for param serialization. 205 | */ 206 | traditional: boolean; 207 | 208 | /** 209 | * The base url that the request url is joined with. 210 | */ 211 | baseUrl: string; 212 | 213 | /** 214 | * Creates an instance of RequestMessage. 215 | * @param method The HTTP method. 216 | * @param url The url to submit the request to. 217 | * @param content The content of the request. 218 | * @param headers The headers to send along with the request. 219 | */ 220 | constructor(method: string, url: string, content: any, headers?: Headers); 221 | 222 | /** 223 | * Builds the url to make the request from. 224 | * @return The constructed url. 225 | */ 226 | buildFullUrl(): string; 227 | } 228 | 229 | /** 230 | * Represents a response message from an HTTP or JSONP request. 231 | */ 232 | export declare class HttpResponseMessage { 233 | 234 | /** 235 | * The request message that resulted in this response. 236 | */ 237 | requestMessage: RequestMessage; 238 | 239 | /** 240 | * The status code of the response. 241 | */ 242 | statusCode: number; 243 | 244 | /** 245 | * The raw response. 246 | */ 247 | response: any; 248 | 249 | /** 250 | * The type of the response. 251 | */ 252 | responseType: string; 253 | 254 | /** 255 | * The success status of the request based on status code. 256 | */ 257 | isSuccess: boolean; 258 | 259 | /** 260 | * The status text. 261 | */ 262 | statusText: string; 263 | 264 | /** 265 | * A reviver function to use in transforming the content. 266 | */ 267 | reviver: ((key: string, value: any) => any); 268 | 269 | /** 270 | * The mime type of the response. 271 | */ 272 | mimeType: string; 273 | 274 | /** 275 | * The headers received with the response. 276 | */ 277 | headers: Headers; 278 | 279 | /** 280 | * Creates an instance of HttpResponseMessage. 281 | * @param requestMessage The request message that resulted in this response. 282 | * @param xhr The XHR instance that made the request. 283 | * @param responseType The type of the response. 284 | * @param reviver? A reviver function to use in transforming the content. 285 | */ 286 | constructor(requestMessage: RequestMessage, xhr: XHR, responseType: string, reviver?: ((key: string, value: any) => any)); 287 | 288 | /** 289 | * Gets the content of the response. 290 | * @return the response content. 291 | */ 292 | content: any; 293 | } 294 | 295 | /** 296 | * MimeTypes mapped to responseTypes 297 | * 298 | * @type {Object} 299 | */ 300 | export declare let mimeTypes: any; 301 | 302 | /** 303 | * Processes request messages. 304 | */ 305 | /** 306 | * Processes request messages. 307 | */ 308 | export declare class RequestMessageProcessor { 309 | 310 | /** 311 | * Creates an instance of RequestMessageProcessor. 312 | */ 313 | constructor(xhrType: XHRConstructor, xhrTransformers: XHRTransformer[]); 314 | 315 | /** 316 | * Aborts the request. 317 | */ 318 | abort(): void; 319 | 320 | /** 321 | * Processes the request. 322 | * @param client The HttpClient making the request. 323 | * @param requestMessage The message to process. 324 | * @return A promise for an HttpResponseMessage. 325 | */ 326 | process(client: HttpClient, requestMessage: RequestMessage): Promise; 327 | } 328 | 329 | /** 330 | * Adds a timeout to the request. 331 | * @param client The http client. 332 | * @param processor The request message processor. 333 | * @param message The request message. 334 | * @param xhr The xhr instance. 335 | */ 336 | export declare function timeoutTransformer(client: HttpClient, processor: RequestMessageProcessor, message: RequestMessage, xhr: XHR): any; 337 | 338 | /** 339 | * Adds a callback parameter name to the request. 340 | * @param client The http client. 341 | * @param processor The request message processor. 342 | * @param message The request message. 343 | * @param xhr The xhr instance. 344 | */ 345 | export declare function callbackParameterNameTransformer(client: HttpClient, processor: RequestMessageProcessor, message: RequestMessage, xhr: XHR): any; 346 | 347 | /** 348 | * Sets withCredentials on the request. 349 | * @param client The http client. 350 | * @param processor The request message processor. 351 | * @param message The request message. 352 | * @param xhr The xhr instance. 353 | */ 354 | export declare function credentialsTransformer(client: HttpClient, processor: RequestMessageProcessor, message: RequestMessage, xhr: XHR): any; 355 | 356 | /** 357 | * Adds an upload.onprogress callback to the request. 358 | * @param client The http client. 359 | * @param processor The request message processor. 360 | * @param message The request message. 361 | * @param xhr The xhr instance. 362 | */ 363 | export declare function progressTransformer(client: HttpClient, processor: RequestMessageProcessor, message: RequestMessage, xhr: XHR): any; 364 | 365 | /** 366 | * Adds an onprogress callback to the request. 367 | * @param client The http client. 368 | * @param processor The request message processor. 369 | * @param message The request message. 370 | * @param xhr The xhr instance. 371 | */ 372 | export declare function downloadProgressTransformer(client: HttpClient, processor: RequestMessageProcessor, message: RequestMessage, xhr: XHR): any; 373 | 374 | /** 375 | * Adds a response type transformer to the request. 376 | * @param client The http client. 377 | * @param processor The request message processor. 378 | * @param message The request message. 379 | * @param xhr The xhr instance. 380 | */ 381 | export declare function responseTypeTransformer(client: HttpClient, processor: RequestMessageProcessor, message: RequestMessage, xhr: XHR): any; 382 | 383 | /** 384 | * Adds headers to the request. 385 | * @param client The http client. 386 | * @param processor The request message processor. 387 | * @param message The request message. 388 | * @param xhr The xhr instance. 389 | */ 390 | export declare function headerTransformer(client: HttpClient, processor: RequestMessageProcessor, message: RequestMessage, xhr: XHR): any; 391 | 392 | /** 393 | * Transforms the content of the request. 394 | * @param client The http client. 395 | * @param processor The request message processor. 396 | * @param message The request message. 397 | * @param xhr The xhr instance. 398 | */ 399 | export declare function contentTransformer(client: HttpClient, processor: RequestMessageProcessor, message: RequestMessage, xhr: XHR): any; 400 | 401 | /** 402 | * Represents an JSONP request message. 403 | */ 404 | export declare class JSONPRequestMessage extends RequestMessage { 405 | 406 | /** 407 | * Creates an instance of JSONPRequestMessage. 408 | * @param url The url to submit the request to. 409 | * @param callbackParameterName The name of the callback parameter that the api expects. 410 | */ 411 | constructor(url: string, callbackParameterName: string); 412 | } 413 | 414 | /** 415 | * Creates a RequestMessageProcessor for handling JSONP request messages. 416 | * @return A processor instance for JSONP request messages. 417 | */ 418 | export declare function createJSONPRequestMessageProcessor(): RequestMessageProcessor; 419 | 420 | /** 421 | * Represents an HTTP request message. 422 | */ 423 | export declare class HttpRequestMessage extends RequestMessage { 424 | 425 | /** 426 | * A replacer function to use in transforming the content. 427 | */ 428 | replacer: ((key: string, value: any) => any); 429 | 430 | /** 431 | * Creates an instance of HttpRequestMessage. 432 | * @param method The http method. 433 | * @param url The url to submit the request to. 434 | * @param content The content of the request. 435 | * @param headers The headers to send along with the request. 436 | */ 437 | constructor(method: string, url: string, content: any, headers?: Headers); 438 | } 439 | 440 | /** 441 | * Creates a RequestMessageProcessor for handling HTTP request messages. 442 | * @return A processor instance for HTTP request messages. 443 | */ 444 | export declare function createHttpRequestMessageProcessor(): RequestMessageProcessor; 445 | 446 | /** 447 | * Represents an error like object response message from an HTTP or JSONP request. 448 | */ 449 | export declare class ErrorHttpResponseMessage extends HttpResponseMessage { 450 | 451 | /** 452 | * Error like name 453 | */ 454 | name: string; 455 | 456 | /** 457 | * Error like message 458 | */ 459 | message: string; 460 | 461 | /** 462 | * Instanciate a new error response message 463 | * ErrorHttpResponseMessage instanceof Error is false but with two members 'name' and 'message' we have an error like object 464 | * @param responseMessage response message 465 | */ 466 | constructor(responseMessage: HttpResponseMessage); 467 | } 468 | 469 | /** 470 | * A builder class allowing fluent composition of HTTP requests. 471 | */ 472 | /** 473 | * A builder class allowing fluent composition of HTTP requests. 474 | */ 475 | export declare class RequestBuilder { 476 | 477 | /** 478 | * The HttpClient instance. 479 | */ 480 | client: HttpClient; 481 | 482 | /** 483 | * Creates an instance of RequestBuilder 484 | * @param client An instance of HttpClient 485 | */ 486 | constructor(client: HttpClient); 487 | 488 | /** 489 | * Makes the request a DELETE request. 490 | * @return The chainable RequestBuilder to use in further configuration of the request. 491 | */ 492 | asDelete(): RequestBuilder; 493 | 494 | /** 495 | * Makes the request a GET request. 496 | * @return The chainable RequestBuilder to use in further configuration of the request. 497 | */ 498 | asGet(): RequestBuilder; 499 | 500 | /** 501 | * Makes the request a HEAD request. 502 | * @return The chainable RequestBuilder to use in further configuration of the request. 503 | */ 504 | asHead(): RequestBuilder; 505 | 506 | /** 507 | * Makes the request a OPTIONS request. 508 | * @return The chainable RequestBuilder to use in further configuration of the request. 509 | */ 510 | asOptions(): RequestBuilder; 511 | 512 | /** 513 | * Makes the request a PATCH request. 514 | * @return The chainable RequestBuilder to use in further configuration of the request. 515 | */ 516 | asPatch(): RequestBuilder; 517 | 518 | /** 519 | * Makes the request a POST request. 520 | * @return The chainable RequestBuilder to use in further configuration of the request. 521 | */ 522 | asPost(): RequestBuilder; 523 | 524 | /** 525 | * Makes the request a PUT request. 526 | * @return The chainable RequestBuilder to use in further configuration of the request. 527 | */ 528 | asPut(): RequestBuilder; 529 | 530 | /** 531 | * Makes the request a JSONP request. 532 | * @param callbackParameterName The name of the callback to use. 533 | * @return The chainable RequestBuilder to use in further configuration of the request. 534 | */ 535 | asJsonp(callbackParameterName: string): RequestBuilder; 536 | 537 | /** 538 | * Sets the request url. 539 | * @param url The url to use. 540 | * @return The chainable RequestBuilder to use in further configuration of the request. 541 | */ 542 | withUrl(url: string): RequestBuilder; 543 | 544 | /** 545 | * Sets the request content. 546 | * @param The content to send. 547 | * @return The chainable RequestBuilder to use in further configuration of the request. 548 | */ 549 | withContent(content: any): RequestBuilder; 550 | 551 | /** 552 | * Sets the base url that will be prepended to the url. 553 | * @param baseUrl The base url to use. 554 | * @return The chainable RequestBuilder to use in further configuration of the request. 555 | */ 556 | withBaseUrl(baseUrl: string): RequestBuilder; 557 | 558 | /** 559 | * Sets params that will be added to the request url as a query string. 560 | * @param params The key/value pairs to use to build the query string. 561 | * @return The chainable RequestBuilder to use in further configuration of the request. 562 | */ 563 | withParams(params: Object, traditional?: boolean): RequestBuilder; 564 | 565 | /** 566 | * Sets the response type. 567 | * @param responseType The response type to expect. 568 | * @return The chainable RequestBuilder to use in further configuration of the request. 569 | */ 570 | withResponseType(responseType: string): RequestBuilder; 571 | 572 | /** 573 | * Sets a timeout for the request. 574 | * @param timeout The timeout for the request. 575 | * @return The chainable RequestBuilder to use in further configuration of the request. 576 | */ 577 | withTimeout(timeout: number): RequestBuilder; 578 | 579 | /** 580 | * Sets a header on the request. 581 | * @param key The header key to add. 582 | * @param value The header value to add. 583 | * @return The chainable RequestBuilder to use in further configuration of the request. 584 | */ 585 | withHeader(key: string, value: string): RequestBuilder; 586 | 587 | /** 588 | * Sets the withCredentials flag on the request. 589 | * @param value The value of the withCredentials flag to set. 590 | * @return The chainable RequestBuilder to use in further configuration of the request. 591 | */ 592 | withCredentials(value: boolean): RequestBuilder; 593 | 594 | /** 595 | * Sets the user and password to use in opening the request. 596 | * @param user The username to send. 597 | * @param password The password to send. 598 | * @return The chainable RequestBuilder to use in further configuration of the request. 599 | */ 600 | withLogin(user: string, password: string): RequestBuilder; 601 | 602 | /** 603 | * Sets a reviver to transform the response content. 604 | * @param reviver The reviver to use in processing the response. 605 | * @return The chainable RequestBuilder to use in further configuration of the request. 606 | */ 607 | withReviver(reviver: ((key: string, value: any) => any)): RequestBuilder; 608 | 609 | /** 610 | * Sets a replacer to transform the request content. 611 | * @param replacer The replacer to use in preparing the request. 612 | * @return The chainable RequestBuilder to use in further configuration of the request. 613 | */ 614 | withReplacer(replacer: ((key: string, value: any) => any)): RequestBuilder; 615 | 616 | /** 617 | * Sets an upload progress callback. 618 | * @param progressCallback The progress callback function. 619 | * @return The chainable RequestBuilder to use in further configuration of the request. 620 | */ 621 | withProgressCallback(progressCallback: Function): RequestBuilder; 622 | 623 | /** 624 | * Sets an download progress callback. 625 | * @param progressCallback The progress callback function. 626 | * @return The chainable RequestBuilder to use in further configuration of the request. 627 | */ 628 | withDownloadProgressCallback(downloadProgressCallback: Function): RequestBuilder; 629 | 630 | /** 631 | * Sets a callback parameter name for JSONP. 632 | * @param callbackParameterName The name of the callback parameter that the JSONP request requires. 633 | * @return The chainable RequestBuilder to use in further configuration of the request. 634 | */ 635 | withCallbackParameterName(callbackParameterName: string): RequestBuilder; 636 | 637 | /** 638 | * Adds an interceptor to the request. 639 | * @param interceptor The interceptor to add. 640 | * @return The chainable RequestBuilder to use in further configuration of the request. 641 | */ 642 | withInterceptor(interceptor: Interceptor): RequestBuilder; 643 | 644 | /** 645 | * Skips the request content processing transform. 646 | * @return The chainable RequestBuilder to use in further configuration of the request. 647 | */ 648 | skipContentProcessing(): RequestBuilder; 649 | 650 | /** 651 | * Adds a user-defined request transformer to the RequestBuilder. 652 | * @param name The name of the helper to add. 653 | * @param fn The helper function. 654 | */ 655 | static addHelper(name: string, fn: (() => RequestTransformer)): void; 656 | 657 | /** 658 | * Sends the request. 659 | * @return {Promise} A cancellable promise object. 660 | */ 661 | send(): Promise; 662 | } 663 | 664 | /** 665 | * The main HTTP client object. 666 | */ 667 | export declare class HttpClient { 668 | 669 | /** 670 | * Return true if promises are rejected with an error like object. Default false 671 | */ 672 | rejectPromiseWithErrorObject: boolean; 673 | 674 | /** 675 | * Indicates whether or not the client is in the process of requesting resources. 676 | */ 677 | isRequesting: boolean; 678 | 679 | /** 680 | * Creates an instance of HttpClient. 681 | */ 682 | constructor(); 683 | 684 | /** 685 | * Configure this HttpClient with default settings to be used by all requests. 686 | * @param fn A function that takes a RequestBuilder as an argument. 687 | */ 688 | configure(fn: ((builder: RequestBuilder) => void)): HttpClient; 689 | 690 | /** 691 | * Returns a new RequestBuilder for this HttpClient instance that can be used to build and send HTTP requests. 692 | * @param url The target URL. 693 | */ 694 | createRequest(url: string): RequestBuilder; 695 | 696 | /** 697 | * Sends a message using the underlying networking stack. 698 | * @param message A configured HttpRequestMessage or JSONPRequestMessage. 699 | * @param transformers A collection of transformers to apply to the HTTP request. 700 | * @return A cancellable promise object. 701 | */ 702 | send(requestMessage: RequestMessage, transformers: Array): Promise; 703 | 704 | /** 705 | * Sends an HTTP DELETE request. 706 | * @param url The target URL. 707 | * @return A cancellable promise object. 708 | */ 709 | delete(url: string): Promise; 710 | 711 | /** 712 | * Sends an HTTP GET request. 713 | * @param url The target URL. 714 | * @return {Promise} A cancellable promise object. 715 | */ 716 | get(url: string, params?: Object, traditional?: boolean): Promise; 717 | 718 | /** 719 | * Sends an HTTP HEAD request. 720 | * @param url The target URL. 721 | * @return A cancellable promise object. 722 | */ 723 | head(url: string): Promise; 724 | 725 | /** 726 | * Sends a JSONP request. 727 | * @param url The target URL. 728 | * @return A cancellable promise object. 729 | */ 730 | jsonp(url: string, callbackParameterName?: string): Promise; 731 | 732 | /** 733 | * Sends an HTTP OPTIONS request. 734 | * @param url The target URL. 735 | * @return A cancellable promise object. 736 | */ 737 | options(url: string): Promise; 738 | 739 | /** 740 | * Sends an HTTP PUT request. 741 | * @param url The target URL. 742 | * @param content The request payload. 743 | * @return A cancellable promise object. 744 | */ 745 | put(url: string, content: any): Promise; 746 | 747 | /** 748 | * Sends an HTTP PATCH request. 749 | * @param url The target URL. 750 | * @param content The request payload. 751 | * @return A cancellable promise object. 752 | */ 753 | patch(url: string, content: any): Promise; 754 | 755 | /** 756 | * Sends an HTTP POST request. 757 | * @param url The target URL. 758 | * @param content The request payload. 759 | * @return A cancellable promise object. 760 | */ 761 | post(url: string, content: any): Promise; 762 | } -------------------------------------------------------------------------------- /dist/commonjs/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | 7 | var _aureliaHttpClient = require('./aurelia-http-client'); 8 | 9 | Object.keys(_aureliaHttpClient).forEach(function (key) { 10 | if (key === "default" || key === "__esModule") return; 11 | Object.defineProperty(exports, key, { 12 | enumerable: true, 13 | get: function get() { 14 | return _aureliaHttpClient[key]; 15 | } 16 | }); 17 | }); -------------------------------------------------------------------------------- /dist/es2015/aurelia-http-client.js: -------------------------------------------------------------------------------- 1 | import { join, buildQueryString } from 'aurelia-path'; 2 | import { PLATFORM, DOM } from 'aurelia-pal'; 3 | 4 | export let Headers = class Headers { 5 | constructor(headers = {}) { 6 | this.headers = {}; 7 | 8 | for (let key in headers) { 9 | this.headers[key.toLowerCase()] = { key, value: headers[key] }; 10 | } 11 | } 12 | 13 | add(key, value) { 14 | this.headers[key.toLowerCase()] = { key, value }; 15 | } 16 | 17 | get(key) { 18 | let header = this.headers[key.toLowerCase()]; 19 | return header ? header.value : undefined; 20 | } 21 | 22 | clear() { 23 | this.headers = {}; 24 | } 25 | 26 | has(header) { 27 | return this.headers.hasOwnProperty(header.toLowerCase()); 28 | } 29 | 30 | configureXHR(xhr) { 31 | for (let name in this.headers) { 32 | if (this.headers.hasOwnProperty(name)) { 33 | xhr.setRequestHeader(this.headers[name].key, this.headers[name].value); 34 | } 35 | } 36 | } 37 | 38 | static parse(headerStr) { 39 | let headers = new Headers(); 40 | if (!headerStr) { 41 | return headers; 42 | } 43 | 44 | let headerPairs = headerStr.split('\u000d\u000a'); 45 | for (let i = 0; i < headerPairs.length; i++) { 46 | let headerPair = headerPairs[i]; 47 | 48 | let index = headerPair.indexOf('\u003a\u0020'); 49 | if (index > 0) { 50 | let key = headerPair.substring(0, index); 51 | let val = headerPair.substring(index + 2); 52 | headers.add(key, val); 53 | } 54 | } 55 | 56 | return headers; 57 | } 58 | }; 59 | 60 | export let RequestMessage = class RequestMessage { 61 | constructor(method, url, content, headers) { 62 | this.method = method; 63 | this.url = url; 64 | this.content = content; 65 | this.headers = headers || new Headers(); 66 | this.baseUrl = ''; 67 | } 68 | 69 | buildFullUrl() { 70 | let absoluteUrl = /^([a-z][a-z0-9+\-.]*:)?\/\//i; 71 | let url = absoluteUrl.test(this.url) ? this.url : join(this.baseUrl, this.url); 72 | 73 | if (this.params) { 74 | let qs = buildQueryString(this.params, this.traditional); 75 | url = qs ? url + (this.url.indexOf('?') < 0 ? '?' : '&') + qs : url; 76 | } 77 | 78 | return url; 79 | } 80 | }; 81 | 82 | export let HttpResponseMessage = class HttpResponseMessage { 83 | constructor(requestMessage, xhr, responseType, reviver) { 84 | this.requestMessage = requestMessage; 85 | this.statusCode = xhr.status; 86 | this.response = xhr.response || xhr.responseText; 87 | this.isSuccess = xhr.status >= 200 && xhr.status < 400; 88 | this.statusText = xhr.statusText; 89 | this.reviver = reviver; 90 | this.mimeType = null; 91 | 92 | if (xhr.getAllResponseHeaders) { 93 | this.headers = Headers.parse(xhr.getAllResponseHeaders()); 94 | } else { 95 | this.headers = new Headers(); 96 | } 97 | 98 | let contentType; 99 | 100 | if (this.headers && this.headers.headers) { 101 | contentType = this.headers.get('Content-Type'); 102 | } 103 | 104 | if (contentType) { 105 | this.mimeType = responseType = contentType.split(';')[0].trim(); 106 | if (mimeTypes.hasOwnProperty(this.mimeType)) responseType = mimeTypes[this.mimeType]; 107 | } 108 | 109 | this.responseType = responseType; 110 | } 111 | 112 | get content() { 113 | try { 114 | if (this._content !== undefined) { 115 | return this._content; 116 | } 117 | 118 | if (this.response === undefined || this.response === null || this.response === '') { 119 | this._content = this.response; 120 | return this._content; 121 | } 122 | 123 | if (this.responseType === 'json') { 124 | this._content = JSON.parse(this.response, this.reviver); 125 | return this._content; 126 | } 127 | 128 | if (this.reviver) { 129 | this._content = this.reviver(this.response); 130 | return this._content; 131 | } 132 | 133 | this._content = this.response; 134 | return this._content; 135 | } catch (e) { 136 | if (this.isSuccess) { 137 | throw e; 138 | } 139 | 140 | this._content = null; 141 | return this._content; 142 | } 143 | } 144 | }; 145 | 146 | export let mimeTypes = { 147 | 'text/html': 'html', 148 | 'text/javascript': 'js', 149 | 'application/javascript': 'js', 150 | 'text/json': 'json', 151 | 'application/json': 'json', 152 | 'application/rss+xml': 'rss', 153 | 'application/atom+xml': 'atom', 154 | 'application/xhtml+xml': 'xhtml', 155 | 'text/markdown': 'md', 156 | 'text/xml': 'xml', 157 | 'text/mathml': 'mml', 158 | 'application/xml': 'xml', 159 | 'text/yml': 'yml', 160 | 'text/csv': 'csv', 161 | 'text/css': 'css', 162 | 'text/less': 'less', 163 | 'text/stylus': 'styl', 164 | 'text/scss': 'scss', 165 | 'text/sass': 'sass', 166 | 'text/plain': 'txt' 167 | }; 168 | 169 | function applyXhrTransformers(xhrTransformers, client, processor, message, xhr) { 170 | let i; 171 | let ii; 172 | 173 | for (i = 0, ii = xhrTransformers.length; i < ii; ++i) { 174 | xhrTransformers[i](client, processor, message, xhr); 175 | } 176 | } 177 | 178 | export let RequestMessageProcessor = class RequestMessageProcessor { 179 | constructor(xhrType, xhrTransformers) { 180 | this.XHRType = xhrType; 181 | this.xhrTransformers = xhrTransformers; 182 | this.isAborted = false; 183 | } 184 | 185 | abort() { 186 | if (this.xhr && this.xhr.readyState !== PLATFORM.XMLHttpRequest.UNSENT) { 187 | this.xhr.abort(); 188 | } 189 | 190 | this.isAborted = true; 191 | } 192 | 193 | process(client, requestMessage) { 194 | let promise = new Promise((resolve, reject) => { 195 | let rejectResponse; 196 | if (client.rejectPromiseWithErrorObject) { 197 | rejectResponse = resp => { 198 | const errorResp = new ErrorHttpResponseMessage(resp); 199 | reject(errorResp); 200 | }; 201 | } else { 202 | rejectResponse = resp => { 203 | reject(resp); 204 | }; 205 | } 206 | 207 | let xhr = this.xhr = new this.XHRType(); 208 | xhr.onload = e => { 209 | let response = new HttpResponseMessage(requestMessage, xhr, requestMessage.responseType, requestMessage.reviver); 210 | if (response.isSuccess) { 211 | resolve(response); 212 | } else { 213 | rejectResponse(response); 214 | } 215 | }; 216 | 217 | xhr.ontimeout = e => { 218 | rejectResponse(new HttpResponseMessage(requestMessage, { 219 | response: e, 220 | status: xhr.status, 221 | statusText: xhr.statusText 222 | }, 'timeout')); 223 | }; 224 | 225 | xhr.onerror = e => { 226 | rejectResponse(new HttpResponseMessage(requestMessage, { 227 | response: e, 228 | status: xhr.status, 229 | statusText: xhr.statusText 230 | }, 'error')); 231 | }; 232 | 233 | xhr.onabort = e => { 234 | rejectResponse(new HttpResponseMessage(requestMessage, { 235 | response: e, 236 | status: xhr.status, 237 | statusText: xhr.statusText 238 | }, 'abort')); 239 | }; 240 | }); 241 | 242 | return Promise.resolve(requestMessage).then(message => { 243 | let processRequest = () => { 244 | if (this.isAborted) { 245 | this.xhr.abort(); 246 | } else { 247 | this.xhr.open(message.method, message.buildFullUrl(), true, message.user, message.password); 248 | applyXhrTransformers(this.xhrTransformers, client, this, message, this.xhr); 249 | if (typeof message.content === 'undefined') { 250 | this.xhr.send(); 251 | } else { 252 | this.xhr.send(message.content); 253 | } 254 | } 255 | 256 | return promise; 257 | }; 258 | 259 | let chain = [[processRequest, undefined]]; 260 | 261 | let interceptors = message.interceptors || []; 262 | interceptors.forEach(function (interceptor) { 263 | if (interceptor.request || interceptor.requestError) { 264 | chain.unshift([interceptor.request ? interceptor.request.bind(interceptor) : undefined, interceptor.requestError ? interceptor.requestError.bind(interceptor) : undefined]); 265 | } 266 | 267 | if (interceptor.response || interceptor.responseError) { 268 | chain.push([interceptor.response ? interceptor.response.bind(interceptor) : undefined, interceptor.responseError ? interceptor.responseError.bind(interceptor) : undefined]); 269 | } 270 | }); 271 | 272 | let interceptorsPromise = Promise.resolve(message); 273 | 274 | while (chain.length) { 275 | interceptorsPromise = interceptorsPromise.then(...chain.shift()); 276 | } 277 | 278 | return interceptorsPromise; 279 | }); 280 | } 281 | }; 282 | 283 | export function timeoutTransformer(client, processor, message, xhr) { 284 | if (message.timeout !== undefined) { 285 | xhr.timeout = message.timeout; 286 | } 287 | } 288 | 289 | export function callbackParameterNameTransformer(client, processor, message, xhr) { 290 | if (message.callbackParameterName !== undefined) { 291 | xhr.callbackParameterName = message.callbackParameterName; 292 | } 293 | } 294 | 295 | export function credentialsTransformer(client, processor, message, xhr) { 296 | if (message.withCredentials !== undefined) { 297 | xhr.withCredentials = message.withCredentials; 298 | } 299 | } 300 | 301 | export function progressTransformer(client, processor, message, xhr) { 302 | if (message.progressCallback) { 303 | xhr.upload.onprogress = message.progressCallback; 304 | } 305 | } 306 | 307 | export function downloadProgressTransformer(client, processor, message, xhr) { 308 | if (message.downloadProgressCallback) { 309 | xhr.onprogress = message.downloadProgressCallback; 310 | } 311 | } 312 | 313 | export function responseTypeTransformer(client, processor, message, xhr) { 314 | let responseType = message.responseType; 315 | 316 | if (responseType === 'json') { 317 | responseType = 'text'; 318 | } 319 | 320 | xhr.responseType = responseType; 321 | } 322 | 323 | export function headerTransformer(client, processor, message, xhr) { 324 | message.headers.configureXHR(xhr); 325 | } 326 | 327 | export function contentTransformer(client, processor, message, xhr) { 328 | if (message.skipContentProcessing) { 329 | return; 330 | } 331 | 332 | if (PLATFORM.global.FormData && message.content instanceof FormData) { 333 | return; 334 | } 335 | 336 | if (PLATFORM.global.Blob && message.content instanceof Blob) { 337 | return; 338 | } 339 | 340 | if (PLATFORM.global.ArrayBuffer && message.content instanceof ArrayBuffer) { 341 | return; 342 | } 343 | 344 | if (message.content instanceof Document) { 345 | return; 346 | } 347 | 348 | if (typeof message.content === 'string') { 349 | return; 350 | } 351 | 352 | if (message.content === null || message.content === undefined) { 353 | return; 354 | } 355 | 356 | message.content = JSON.stringify(message.content, message.replacer); 357 | 358 | if (!message.headers.has('Content-Type')) { 359 | message.headers.add('Content-Type', 'application/json'); 360 | } 361 | } 362 | 363 | export let JSONPRequestMessage = class JSONPRequestMessage extends RequestMessage { 364 | constructor(url, callbackParameterName) { 365 | super('JSONP', url); 366 | this.responseType = 'jsonp'; 367 | this.callbackParameterName = callbackParameterName; 368 | } 369 | }; 370 | 371 | let JSONPXHR = class JSONPXHR { 372 | open(method, url) { 373 | this.method = method; 374 | this.url = url; 375 | this.callbackName = 'jsonp_callback_' + Math.round(100000 * Math.random()); 376 | } 377 | 378 | send() { 379 | let url = this.url + (this.url.indexOf('?') >= 0 ? '&' : '?') + encodeURIComponent(this.callbackParameterName) + '=' + this.callbackName; 380 | let script = DOM.createElement('script'); 381 | 382 | script.src = url; 383 | script.onerror = e => { 384 | cleanUp(); 385 | 386 | this.status = 0; 387 | this.onerror(new Error('error')); 388 | }; 389 | 390 | let cleanUp = () => { 391 | delete PLATFORM.global[this.callbackName]; 392 | DOM.removeNode(script); 393 | }; 394 | 395 | PLATFORM.global[this.callbackName] = data => { 396 | cleanUp(); 397 | 398 | if (this.status === undefined) { 399 | this.status = 200; 400 | this.statusText = 'OK'; 401 | this.response = data; 402 | this.onload(this); 403 | } 404 | }; 405 | 406 | DOM.appendNode(script); 407 | 408 | if (this.timeout !== undefined) { 409 | setTimeout(() => { 410 | if (this.status === undefined) { 411 | this.status = 0; 412 | this.ontimeout(new Error('timeout')); 413 | } 414 | }, this.timeout); 415 | } 416 | } 417 | 418 | abort() { 419 | if (this.status === undefined) { 420 | this.status = 0; 421 | this.onabort(new Error('abort')); 422 | } 423 | } 424 | 425 | setRequestHeader() {} 426 | }; 427 | 428 | export function createJSONPRequestMessageProcessor() { 429 | return new RequestMessageProcessor(JSONPXHR, [timeoutTransformer, callbackParameterNameTransformer]); 430 | } 431 | 432 | export let HttpRequestMessage = class HttpRequestMessage extends RequestMessage { 433 | constructor(method, url, content, headers) { 434 | super(method, url, content, headers); 435 | this.responseType = 'json'; 436 | } 437 | }; 438 | 439 | export function createHttpRequestMessageProcessor() { 440 | return new RequestMessageProcessor(PLATFORM.XMLHttpRequest, [timeoutTransformer, credentialsTransformer, progressTransformer, downloadProgressTransformer, responseTypeTransformer, contentTransformer, headerTransformer]); 441 | } 442 | 443 | export let ErrorHttpResponseMessage = class ErrorHttpResponseMessage extends HttpResponseMessage { 444 | constructor(responseMessage) { 445 | super(responseMessage.requestMessage, { 446 | response: responseMessage.response, 447 | status: responseMessage.statusCode, 448 | statusText: responseMessage.statusText 449 | }, responseMessage.responseType); 450 | 451 | this.name = responseMessage.responseType; 452 | this.message = `Error: ${responseMessage.statusCode} Status: ${responseMessage.statusText}`; 453 | } 454 | 455 | }; 456 | 457 | export let RequestBuilder = class RequestBuilder { 458 | constructor(client) { 459 | this.client = client; 460 | this.transformers = client.requestTransformers.slice(0); 461 | this.useJsonp = false; 462 | } 463 | 464 | asDelete() { 465 | return this._addTransformer(function (client, processor, message) { 466 | message.method = 'DELETE'; 467 | }); 468 | } 469 | 470 | asGet() { 471 | return this._addTransformer(function (client, processor, message) { 472 | message.method = 'GET'; 473 | }); 474 | } 475 | 476 | asHead() { 477 | return this._addTransformer(function (client, processor, message) { 478 | message.method = 'HEAD'; 479 | }); 480 | } 481 | 482 | asOptions() { 483 | return this._addTransformer(function (client, processor, message) { 484 | message.method = 'OPTIONS'; 485 | }); 486 | } 487 | 488 | asPatch() { 489 | return this._addTransformer(function (client, processor, message) { 490 | message.method = 'PATCH'; 491 | }); 492 | } 493 | 494 | asPost() { 495 | return this._addTransformer(function (client, processor, message) { 496 | message.method = 'POST'; 497 | }); 498 | } 499 | 500 | asPut() { 501 | return this._addTransformer(function (client, processor, message) { 502 | message.method = 'PUT'; 503 | }); 504 | } 505 | 506 | asJsonp(callbackParameterName) { 507 | this.useJsonp = true; 508 | return this._addTransformer(function (client, processor, message) { 509 | message.callbackParameterName = callbackParameterName; 510 | }); 511 | } 512 | 513 | withUrl(url) { 514 | return this._addTransformer(function (client, processor, message) { 515 | message.url = url; 516 | }); 517 | } 518 | 519 | withContent(content) { 520 | return this._addTransformer(function (client, processor, message) { 521 | message.content = content; 522 | }); 523 | } 524 | 525 | withBaseUrl(baseUrl) { 526 | return this._addTransformer(function (client, processor, message) { 527 | message.baseUrl = baseUrl; 528 | }); 529 | } 530 | 531 | withParams(params, traditional) { 532 | return this._addTransformer(function (client, processor, message) { 533 | message.traditional = traditional; 534 | message.params = params; 535 | }); 536 | } 537 | 538 | withResponseType(responseType) { 539 | return this._addTransformer(function (client, processor, message) { 540 | message.responseType = responseType; 541 | }); 542 | } 543 | 544 | withTimeout(timeout) { 545 | return this._addTransformer(function (client, processor, message) { 546 | message.timeout = timeout; 547 | }); 548 | } 549 | 550 | withHeader(key, value) { 551 | return this._addTransformer(function (client, processor, message) { 552 | message.headers.add(key, value); 553 | }); 554 | } 555 | 556 | withCredentials(value) { 557 | return this._addTransformer(function (client, processor, message) { 558 | message.withCredentials = value; 559 | }); 560 | } 561 | 562 | withLogin(user, password) { 563 | return this._addTransformer(function (client, processor, message) { 564 | message.user = user;message.password = password; 565 | }); 566 | } 567 | 568 | withReviver(reviver) { 569 | return this._addTransformer(function (client, processor, message) { 570 | message.reviver = reviver; 571 | }); 572 | } 573 | 574 | withReplacer(replacer) { 575 | return this._addTransformer(function (client, processor, message) { 576 | message.replacer = replacer; 577 | }); 578 | } 579 | 580 | withProgressCallback(progressCallback) { 581 | return this._addTransformer(function (client, processor, message) { 582 | message.progressCallback = progressCallback; 583 | }); 584 | } 585 | 586 | withDownloadProgressCallback(downloadProgressCallback) { 587 | return this._addTransformer(function (client, processor, message) { 588 | message.downloadProgressCallback = downloadProgressCallback; 589 | }); 590 | } 591 | 592 | withCallbackParameterName(callbackParameterName) { 593 | return this._addTransformer(function (client, processor, message) { 594 | message.callbackParameterName = callbackParameterName; 595 | }); 596 | } 597 | 598 | withInterceptor(interceptor) { 599 | return this._addTransformer(function (client, processor, message) { 600 | message.interceptors = message.interceptors || []; 601 | message.interceptors.unshift(interceptor); 602 | }); 603 | } 604 | 605 | skipContentProcessing() { 606 | return this._addTransformer(function (client, processor, message) { 607 | message.skipContentProcessing = true; 608 | }); 609 | } 610 | 611 | _addTransformer(fn) { 612 | this.transformers.push(fn); 613 | return this; 614 | } 615 | 616 | static addHelper(name, fn) { 617 | RequestBuilder.prototype[name] = function () { 618 | return this._addTransformer(fn.apply(this, arguments)); 619 | }; 620 | } 621 | 622 | send() { 623 | let message = this.useJsonp ? new JSONPRequestMessage() : new HttpRequestMessage(); 624 | return this.client.send(message, this.transformers); 625 | } 626 | }; 627 | 628 | function trackRequestStart(client, processor) { 629 | client.pendingRequests.push(processor); 630 | client.isRequesting = true; 631 | } 632 | 633 | function trackRequestEnd(client, processor) { 634 | let index = client.pendingRequests.indexOf(processor); 635 | 636 | client.pendingRequests.splice(index, 1); 637 | client.isRequesting = client.pendingRequests.length > 0; 638 | 639 | if (!client.isRequesting) { 640 | let evt = DOM.createCustomEvent('aurelia-http-client-requests-drained', { bubbles: true, cancelable: true }); 641 | setTimeout(() => DOM.dispatchEvent(evt), 1); 642 | } 643 | } 644 | 645 | export let HttpClient = class HttpClient { 646 | constructor() { 647 | this.isRequesting = false; 648 | 649 | this.rejectPromiseWithErrorObject = false; 650 | this.requestTransformers = []; 651 | this.requestProcessorFactories = new Map(); 652 | this.requestProcessorFactories.set(HttpRequestMessage, createHttpRequestMessageProcessor); 653 | this.requestProcessorFactories.set(JSONPRequestMessage, createJSONPRequestMessageProcessor); 654 | this.pendingRequests = []; 655 | } 656 | 657 | configure(fn) { 658 | let builder = new RequestBuilder(this); 659 | fn(builder); 660 | this.requestTransformers = builder.transformers; 661 | return this; 662 | } 663 | 664 | createRequest(url) { 665 | let builder = new RequestBuilder(this); 666 | 667 | if (url) { 668 | builder.withUrl(url); 669 | } 670 | 671 | return builder; 672 | } 673 | 674 | send(requestMessage, transformers) { 675 | let createProcessor = this.requestProcessorFactories.get(requestMessage.constructor); 676 | let processor; 677 | let promise; 678 | let i; 679 | let ii; 680 | 681 | if (!createProcessor) { 682 | throw new Error(`No request message processor factory for ${requestMessage.constructor}.`); 683 | } 684 | 685 | processor = createProcessor(); 686 | trackRequestStart(this, processor); 687 | 688 | transformers = transformers || this.requestTransformers; 689 | 690 | promise = Promise.resolve(requestMessage).then(message => { 691 | for (i = 0, ii = transformers.length; i < ii; ++i) { 692 | transformers[i](this, processor, message); 693 | } 694 | 695 | return processor.process(this, message).then(response => { 696 | trackRequestEnd(this, processor); 697 | return response; 698 | }).catch(response => { 699 | trackRequestEnd(this, processor); 700 | throw response; 701 | }); 702 | }); 703 | 704 | promise.abort = promise.cancel = function () { 705 | processor.abort(); 706 | }; 707 | 708 | return promise; 709 | } 710 | 711 | delete(url) { 712 | return this.createRequest(url).asDelete().send(); 713 | } 714 | 715 | get(url, params, traditional) { 716 | let req = this.createRequest(url).asGet(); 717 | 718 | if (params) { 719 | return req.withParams(params, traditional).send(); 720 | } 721 | 722 | return req.send(); 723 | } 724 | 725 | head(url) { 726 | return this.createRequest(url).asHead().send(); 727 | } 728 | 729 | jsonp(url, callbackParameterName = 'jsoncallback') { 730 | return this.createRequest(url).asJsonp(callbackParameterName).send(); 731 | } 732 | 733 | options(url) { 734 | return this.createRequest(url).asOptions().send(); 735 | } 736 | 737 | put(url, content) { 738 | return this.createRequest(url).asPut().withContent(content).send(); 739 | } 740 | 741 | patch(url, content) { 742 | return this.createRequest(url).asPatch().withContent(content).send(); 743 | } 744 | 745 | post(url, content) { 746 | return this.createRequest(url).asPost().withContent(content).send(); 747 | } 748 | }; -------------------------------------------------------------------------------- /dist/es2015/index.js: -------------------------------------------------------------------------------- 1 | export * from './aurelia-http-client'; -------------------------------------------------------------------------------- /dist/index.d.ts: -------------------------------------------------------------------------------- 1 | export * from 'aurelia-http-client/aurelia-http-client'; -------------------------------------------------------------------------------- /dist/native-modules/aurelia-http-client.js: -------------------------------------------------------------------------------- 1 | var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); 2 | 3 | function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } 4 | 5 | function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } 6 | 7 | 8 | 9 | import { join, buildQueryString } from 'aurelia-path'; 10 | import { PLATFORM, DOM } from 'aurelia-pal'; 11 | 12 | export var Headers = function () { 13 | function Headers() { 14 | var headers = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; 15 | 16 | 17 | 18 | this.headers = {}; 19 | 20 | for (var _key in headers) { 21 | this.headers[_key.toLowerCase()] = { key: _key, value: headers[_key] }; 22 | } 23 | } 24 | 25 | Headers.prototype.add = function add(key, value) { 26 | this.headers[key.toLowerCase()] = { key: key, value: value }; 27 | }; 28 | 29 | Headers.prototype.get = function get(key) { 30 | var header = this.headers[key.toLowerCase()]; 31 | return header ? header.value : undefined; 32 | }; 33 | 34 | Headers.prototype.clear = function clear() { 35 | this.headers = {}; 36 | }; 37 | 38 | Headers.prototype.has = function has(header) { 39 | return this.headers.hasOwnProperty(header.toLowerCase()); 40 | }; 41 | 42 | Headers.prototype.configureXHR = function configureXHR(xhr) { 43 | for (var name in this.headers) { 44 | if (this.headers.hasOwnProperty(name)) { 45 | xhr.setRequestHeader(this.headers[name].key, this.headers[name].value); 46 | } 47 | } 48 | }; 49 | 50 | Headers.parse = function parse(headerStr) { 51 | var headers = new Headers(); 52 | if (!headerStr) { 53 | return headers; 54 | } 55 | 56 | var headerPairs = headerStr.split('\r\n'); 57 | for (var i = 0; i < headerPairs.length; i++) { 58 | var headerPair = headerPairs[i]; 59 | 60 | var index = headerPair.indexOf(': '); 61 | if (index > 0) { 62 | var _key2 = headerPair.substring(0, index); 63 | var val = headerPair.substring(index + 2); 64 | headers.add(_key2, val); 65 | } 66 | } 67 | 68 | return headers; 69 | }; 70 | 71 | return Headers; 72 | }(); 73 | 74 | export var RequestMessage = function () { 75 | function RequestMessage(method, url, content, headers) { 76 | 77 | 78 | this.method = method; 79 | this.url = url; 80 | this.content = content; 81 | this.headers = headers || new Headers(); 82 | this.baseUrl = ''; 83 | } 84 | 85 | RequestMessage.prototype.buildFullUrl = function buildFullUrl() { 86 | var absoluteUrl = /^([a-z][a-z0-9+\-.]*:)?\/\//i; 87 | var url = absoluteUrl.test(this.url) ? this.url : join(this.baseUrl, this.url); 88 | 89 | if (this.params) { 90 | var qs = buildQueryString(this.params, this.traditional); 91 | url = qs ? url + (this.url.indexOf('?') < 0 ? '?' : '&') + qs : url; 92 | } 93 | 94 | return url; 95 | }; 96 | 97 | return RequestMessage; 98 | }(); 99 | 100 | export var HttpResponseMessage = function () { 101 | function HttpResponseMessage(requestMessage, xhr, responseType, reviver) { 102 | 103 | 104 | this.requestMessage = requestMessage; 105 | this.statusCode = xhr.status; 106 | this.response = xhr.response || xhr.responseText; 107 | this.isSuccess = xhr.status >= 200 && xhr.status < 400; 108 | this.statusText = xhr.statusText; 109 | this.reviver = reviver; 110 | this.mimeType = null; 111 | 112 | if (xhr.getAllResponseHeaders) { 113 | this.headers = Headers.parse(xhr.getAllResponseHeaders()); 114 | } else { 115 | this.headers = new Headers(); 116 | } 117 | 118 | var contentType = void 0; 119 | 120 | if (this.headers && this.headers.headers) { 121 | contentType = this.headers.get('Content-Type'); 122 | } 123 | 124 | if (contentType) { 125 | this.mimeType = responseType = contentType.split(';')[0].trim(); 126 | if (mimeTypes.hasOwnProperty(this.mimeType)) responseType = mimeTypes[this.mimeType]; 127 | } 128 | 129 | this.responseType = responseType; 130 | } 131 | 132 | _createClass(HttpResponseMessage, [{ 133 | key: 'content', 134 | get: function get() { 135 | try { 136 | if (this._content !== undefined) { 137 | return this._content; 138 | } 139 | 140 | if (this.response === undefined || this.response === null || this.response === '') { 141 | this._content = this.response; 142 | return this._content; 143 | } 144 | 145 | if (this.responseType === 'json') { 146 | this._content = JSON.parse(this.response, this.reviver); 147 | return this._content; 148 | } 149 | 150 | if (this.reviver) { 151 | this._content = this.reviver(this.response); 152 | return this._content; 153 | } 154 | 155 | this._content = this.response; 156 | return this._content; 157 | } catch (e) { 158 | if (this.isSuccess) { 159 | throw e; 160 | } 161 | 162 | this._content = null; 163 | return this._content; 164 | } 165 | } 166 | }]); 167 | 168 | return HttpResponseMessage; 169 | }(); 170 | 171 | export var mimeTypes = { 172 | 'text/html': 'html', 173 | 'text/javascript': 'js', 174 | 'application/javascript': 'js', 175 | 'text/json': 'json', 176 | 'application/json': 'json', 177 | 'application/rss+xml': 'rss', 178 | 'application/atom+xml': 'atom', 179 | 'application/xhtml+xml': 'xhtml', 180 | 'text/markdown': 'md', 181 | 'text/xml': 'xml', 182 | 'text/mathml': 'mml', 183 | 'application/xml': 'xml', 184 | 'text/yml': 'yml', 185 | 'text/csv': 'csv', 186 | 'text/css': 'css', 187 | 'text/less': 'less', 188 | 'text/stylus': 'styl', 189 | 'text/scss': 'scss', 190 | 'text/sass': 'sass', 191 | 'text/plain': 'txt' 192 | }; 193 | 194 | function applyXhrTransformers(xhrTransformers, client, processor, message, xhr) { 195 | var i = void 0; 196 | var ii = void 0; 197 | 198 | for (i = 0, ii = xhrTransformers.length; i < ii; ++i) { 199 | xhrTransformers[i](client, processor, message, xhr); 200 | } 201 | } 202 | 203 | export var RequestMessageProcessor = function () { 204 | function RequestMessageProcessor(xhrType, xhrTransformers) { 205 | 206 | 207 | this.XHRType = xhrType; 208 | this.xhrTransformers = xhrTransformers; 209 | this.isAborted = false; 210 | } 211 | 212 | RequestMessageProcessor.prototype.abort = function abort() { 213 | if (this.xhr && this.xhr.readyState !== PLATFORM.XMLHttpRequest.UNSENT) { 214 | this.xhr.abort(); 215 | } 216 | 217 | this.isAborted = true; 218 | }; 219 | 220 | RequestMessageProcessor.prototype.process = function process(client, requestMessage) { 221 | var _this = this; 222 | 223 | var promise = new Promise(function (resolve, reject) { 224 | var rejectResponse = void 0; 225 | if (client.rejectPromiseWithErrorObject) { 226 | rejectResponse = function rejectResponse(resp) { 227 | var errorResp = new ErrorHttpResponseMessage(resp); 228 | reject(errorResp); 229 | }; 230 | } else { 231 | rejectResponse = function rejectResponse(resp) { 232 | reject(resp); 233 | }; 234 | } 235 | 236 | var xhr = _this.xhr = new _this.XHRType(); 237 | xhr.onload = function (e) { 238 | var response = new HttpResponseMessage(requestMessage, xhr, requestMessage.responseType, requestMessage.reviver); 239 | if (response.isSuccess) { 240 | resolve(response); 241 | } else { 242 | rejectResponse(response); 243 | } 244 | }; 245 | 246 | xhr.ontimeout = function (e) { 247 | rejectResponse(new HttpResponseMessage(requestMessage, { 248 | response: e, 249 | status: xhr.status, 250 | statusText: xhr.statusText 251 | }, 'timeout')); 252 | }; 253 | 254 | xhr.onerror = function (e) { 255 | rejectResponse(new HttpResponseMessage(requestMessage, { 256 | response: e, 257 | status: xhr.status, 258 | statusText: xhr.statusText 259 | }, 'error')); 260 | }; 261 | 262 | xhr.onabort = function (e) { 263 | rejectResponse(new HttpResponseMessage(requestMessage, { 264 | response: e, 265 | status: xhr.status, 266 | statusText: xhr.statusText 267 | }, 'abort')); 268 | }; 269 | }); 270 | 271 | return Promise.resolve(requestMessage).then(function (message) { 272 | var processRequest = function processRequest() { 273 | if (_this.isAborted) { 274 | _this.xhr.abort(); 275 | } else { 276 | _this.xhr.open(message.method, message.buildFullUrl(), true, message.user, message.password); 277 | applyXhrTransformers(_this.xhrTransformers, client, _this, message, _this.xhr); 278 | if (typeof message.content === 'undefined') { 279 | _this.xhr.send(); 280 | } else { 281 | _this.xhr.send(message.content); 282 | } 283 | } 284 | 285 | return promise; 286 | }; 287 | 288 | var chain = [[processRequest, undefined]]; 289 | 290 | var interceptors = message.interceptors || []; 291 | interceptors.forEach(function (interceptor) { 292 | if (interceptor.request || interceptor.requestError) { 293 | chain.unshift([interceptor.request ? interceptor.request.bind(interceptor) : undefined, interceptor.requestError ? interceptor.requestError.bind(interceptor) : undefined]); 294 | } 295 | 296 | if (interceptor.response || interceptor.responseError) { 297 | chain.push([interceptor.response ? interceptor.response.bind(interceptor) : undefined, interceptor.responseError ? interceptor.responseError.bind(interceptor) : undefined]); 298 | } 299 | }); 300 | 301 | var interceptorsPromise = Promise.resolve(message); 302 | 303 | while (chain.length) { 304 | var _interceptorsPromise; 305 | 306 | interceptorsPromise = (_interceptorsPromise = interceptorsPromise).then.apply(_interceptorsPromise, chain.shift()); 307 | } 308 | 309 | return interceptorsPromise; 310 | }); 311 | }; 312 | 313 | return RequestMessageProcessor; 314 | }(); 315 | 316 | export function timeoutTransformer(client, processor, message, xhr) { 317 | if (message.timeout !== undefined) { 318 | xhr.timeout = message.timeout; 319 | } 320 | } 321 | 322 | export function callbackParameterNameTransformer(client, processor, message, xhr) { 323 | if (message.callbackParameterName !== undefined) { 324 | xhr.callbackParameterName = message.callbackParameterName; 325 | } 326 | } 327 | 328 | export function credentialsTransformer(client, processor, message, xhr) { 329 | if (message.withCredentials !== undefined) { 330 | xhr.withCredentials = message.withCredentials; 331 | } 332 | } 333 | 334 | export function progressTransformer(client, processor, message, xhr) { 335 | if (message.progressCallback) { 336 | xhr.upload.onprogress = message.progressCallback; 337 | } 338 | } 339 | 340 | export function downloadProgressTransformer(client, processor, message, xhr) { 341 | if (message.downloadProgressCallback) { 342 | xhr.onprogress = message.downloadProgressCallback; 343 | } 344 | } 345 | 346 | export function responseTypeTransformer(client, processor, message, xhr) { 347 | var responseType = message.responseType; 348 | 349 | if (responseType === 'json') { 350 | responseType = 'text'; 351 | } 352 | 353 | xhr.responseType = responseType; 354 | } 355 | 356 | export function headerTransformer(client, processor, message, xhr) { 357 | message.headers.configureXHR(xhr); 358 | } 359 | 360 | export function contentTransformer(client, processor, message, xhr) { 361 | if (message.skipContentProcessing) { 362 | return; 363 | } 364 | 365 | if (PLATFORM.global.FormData && message.content instanceof FormData) { 366 | return; 367 | } 368 | 369 | if (PLATFORM.global.Blob && message.content instanceof Blob) { 370 | return; 371 | } 372 | 373 | if (PLATFORM.global.ArrayBuffer && message.content instanceof ArrayBuffer) { 374 | return; 375 | } 376 | 377 | if (message.content instanceof Document) { 378 | return; 379 | } 380 | 381 | if (typeof message.content === 'string') { 382 | return; 383 | } 384 | 385 | if (message.content === null || message.content === undefined) { 386 | return; 387 | } 388 | 389 | message.content = JSON.stringify(message.content, message.replacer); 390 | 391 | if (!message.headers.has('Content-Type')) { 392 | message.headers.add('Content-Type', 'application/json'); 393 | } 394 | } 395 | 396 | export var JSONPRequestMessage = function (_RequestMessage) { 397 | _inherits(JSONPRequestMessage, _RequestMessage); 398 | 399 | function JSONPRequestMessage(url, callbackParameterName) { 400 | 401 | 402 | var _this2 = _possibleConstructorReturn(this, _RequestMessage.call(this, 'JSONP', url)); 403 | 404 | _this2.responseType = 'jsonp'; 405 | _this2.callbackParameterName = callbackParameterName; 406 | return _this2; 407 | } 408 | 409 | return JSONPRequestMessage; 410 | }(RequestMessage); 411 | 412 | var JSONPXHR = function () { 413 | function JSONPXHR() { 414 | 415 | } 416 | 417 | JSONPXHR.prototype.open = function open(method, url) { 418 | this.method = method; 419 | this.url = url; 420 | this.callbackName = 'jsonp_callback_' + Math.round(100000 * Math.random()); 421 | }; 422 | 423 | JSONPXHR.prototype.send = function send() { 424 | var _this3 = this; 425 | 426 | var url = this.url + (this.url.indexOf('?') >= 0 ? '&' : '?') + encodeURIComponent(this.callbackParameterName) + '=' + this.callbackName; 427 | var script = DOM.createElement('script'); 428 | 429 | script.src = url; 430 | script.onerror = function (e) { 431 | cleanUp(); 432 | 433 | _this3.status = 0; 434 | _this3.onerror(new Error('error')); 435 | }; 436 | 437 | var cleanUp = function cleanUp() { 438 | delete PLATFORM.global[_this3.callbackName]; 439 | DOM.removeNode(script); 440 | }; 441 | 442 | PLATFORM.global[this.callbackName] = function (data) { 443 | cleanUp(); 444 | 445 | if (_this3.status === undefined) { 446 | _this3.status = 200; 447 | _this3.statusText = 'OK'; 448 | _this3.response = data; 449 | _this3.onload(_this3); 450 | } 451 | }; 452 | 453 | DOM.appendNode(script); 454 | 455 | if (this.timeout !== undefined) { 456 | setTimeout(function () { 457 | if (_this3.status === undefined) { 458 | _this3.status = 0; 459 | _this3.ontimeout(new Error('timeout')); 460 | } 461 | }, this.timeout); 462 | } 463 | }; 464 | 465 | JSONPXHR.prototype.abort = function abort() { 466 | if (this.status === undefined) { 467 | this.status = 0; 468 | this.onabort(new Error('abort')); 469 | } 470 | }; 471 | 472 | JSONPXHR.prototype.setRequestHeader = function setRequestHeader() {}; 473 | 474 | return JSONPXHR; 475 | }(); 476 | 477 | export function createJSONPRequestMessageProcessor() { 478 | return new RequestMessageProcessor(JSONPXHR, [timeoutTransformer, callbackParameterNameTransformer]); 479 | } 480 | 481 | export var HttpRequestMessage = function (_RequestMessage2) { 482 | _inherits(HttpRequestMessage, _RequestMessage2); 483 | 484 | function HttpRequestMessage(method, url, content, headers) { 485 | 486 | 487 | var _this4 = _possibleConstructorReturn(this, _RequestMessage2.call(this, method, url, content, headers)); 488 | 489 | _this4.responseType = 'json';return _this4; 490 | } 491 | 492 | return HttpRequestMessage; 493 | }(RequestMessage); 494 | 495 | export function createHttpRequestMessageProcessor() { 496 | return new RequestMessageProcessor(PLATFORM.XMLHttpRequest, [timeoutTransformer, credentialsTransformer, progressTransformer, downloadProgressTransformer, responseTypeTransformer, contentTransformer, headerTransformer]); 497 | } 498 | 499 | export var ErrorHttpResponseMessage = function (_HttpResponseMessage) { 500 | _inherits(ErrorHttpResponseMessage, _HttpResponseMessage); 501 | 502 | function ErrorHttpResponseMessage(responseMessage) { 503 | 504 | 505 | var _this5 = _possibleConstructorReturn(this, _HttpResponseMessage.call(this, responseMessage.requestMessage, { 506 | response: responseMessage.response, 507 | status: responseMessage.statusCode, 508 | statusText: responseMessage.statusText 509 | }, responseMessage.responseType)); 510 | 511 | _this5.name = responseMessage.responseType; 512 | _this5.message = 'Error: ' + responseMessage.statusCode + ' Status: ' + responseMessage.statusText; 513 | return _this5; 514 | } 515 | 516 | return ErrorHttpResponseMessage; 517 | }(HttpResponseMessage); 518 | 519 | export var RequestBuilder = function () { 520 | function RequestBuilder(client) { 521 | 522 | 523 | this.client = client; 524 | this.transformers = client.requestTransformers.slice(0); 525 | this.useJsonp = false; 526 | } 527 | 528 | RequestBuilder.prototype.asDelete = function asDelete() { 529 | return this._addTransformer(function (client, processor, message) { 530 | message.method = 'DELETE'; 531 | }); 532 | }; 533 | 534 | RequestBuilder.prototype.asGet = function asGet() { 535 | return this._addTransformer(function (client, processor, message) { 536 | message.method = 'GET'; 537 | }); 538 | }; 539 | 540 | RequestBuilder.prototype.asHead = function asHead() { 541 | return this._addTransformer(function (client, processor, message) { 542 | message.method = 'HEAD'; 543 | }); 544 | }; 545 | 546 | RequestBuilder.prototype.asOptions = function asOptions() { 547 | return this._addTransformer(function (client, processor, message) { 548 | message.method = 'OPTIONS'; 549 | }); 550 | }; 551 | 552 | RequestBuilder.prototype.asPatch = function asPatch() { 553 | return this._addTransformer(function (client, processor, message) { 554 | message.method = 'PATCH'; 555 | }); 556 | }; 557 | 558 | RequestBuilder.prototype.asPost = function asPost() { 559 | return this._addTransformer(function (client, processor, message) { 560 | message.method = 'POST'; 561 | }); 562 | }; 563 | 564 | RequestBuilder.prototype.asPut = function asPut() { 565 | return this._addTransformer(function (client, processor, message) { 566 | message.method = 'PUT'; 567 | }); 568 | }; 569 | 570 | RequestBuilder.prototype.asJsonp = function asJsonp(callbackParameterName) { 571 | this.useJsonp = true; 572 | return this._addTransformer(function (client, processor, message) { 573 | message.callbackParameterName = callbackParameterName; 574 | }); 575 | }; 576 | 577 | RequestBuilder.prototype.withUrl = function withUrl(url) { 578 | return this._addTransformer(function (client, processor, message) { 579 | message.url = url; 580 | }); 581 | }; 582 | 583 | RequestBuilder.prototype.withContent = function withContent(content) { 584 | return this._addTransformer(function (client, processor, message) { 585 | message.content = content; 586 | }); 587 | }; 588 | 589 | RequestBuilder.prototype.withBaseUrl = function withBaseUrl(baseUrl) { 590 | return this._addTransformer(function (client, processor, message) { 591 | message.baseUrl = baseUrl; 592 | }); 593 | }; 594 | 595 | RequestBuilder.prototype.withParams = function withParams(params, traditional) { 596 | return this._addTransformer(function (client, processor, message) { 597 | message.traditional = traditional; 598 | message.params = params; 599 | }); 600 | }; 601 | 602 | RequestBuilder.prototype.withResponseType = function withResponseType(responseType) { 603 | return this._addTransformer(function (client, processor, message) { 604 | message.responseType = responseType; 605 | }); 606 | }; 607 | 608 | RequestBuilder.prototype.withTimeout = function withTimeout(timeout) { 609 | return this._addTransformer(function (client, processor, message) { 610 | message.timeout = timeout; 611 | }); 612 | }; 613 | 614 | RequestBuilder.prototype.withHeader = function withHeader(key, value) { 615 | return this._addTransformer(function (client, processor, message) { 616 | message.headers.add(key, value); 617 | }); 618 | }; 619 | 620 | RequestBuilder.prototype.withCredentials = function withCredentials(value) { 621 | return this._addTransformer(function (client, processor, message) { 622 | message.withCredentials = value; 623 | }); 624 | }; 625 | 626 | RequestBuilder.prototype.withLogin = function withLogin(user, password) { 627 | return this._addTransformer(function (client, processor, message) { 628 | message.user = user;message.password = password; 629 | }); 630 | }; 631 | 632 | RequestBuilder.prototype.withReviver = function withReviver(reviver) { 633 | return this._addTransformer(function (client, processor, message) { 634 | message.reviver = reviver; 635 | }); 636 | }; 637 | 638 | RequestBuilder.prototype.withReplacer = function withReplacer(replacer) { 639 | return this._addTransformer(function (client, processor, message) { 640 | message.replacer = replacer; 641 | }); 642 | }; 643 | 644 | RequestBuilder.prototype.withProgressCallback = function withProgressCallback(progressCallback) { 645 | return this._addTransformer(function (client, processor, message) { 646 | message.progressCallback = progressCallback; 647 | }); 648 | }; 649 | 650 | RequestBuilder.prototype.withDownloadProgressCallback = function withDownloadProgressCallback(downloadProgressCallback) { 651 | return this._addTransformer(function (client, processor, message) { 652 | message.downloadProgressCallback = downloadProgressCallback; 653 | }); 654 | }; 655 | 656 | RequestBuilder.prototype.withCallbackParameterName = function withCallbackParameterName(callbackParameterName) { 657 | return this._addTransformer(function (client, processor, message) { 658 | message.callbackParameterName = callbackParameterName; 659 | }); 660 | }; 661 | 662 | RequestBuilder.prototype.withInterceptor = function withInterceptor(interceptor) { 663 | return this._addTransformer(function (client, processor, message) { 664 | message.interceptors = message.interceptors || []; 665 | message.interceptors.unshift(interceptor); 666 | }); 667 | }; 668 | 669 | RequestBuilder.prototype.skipContentProcessing = function skipContentProcessing() { 670 | return this._addTransformer(function (client, processor, message) { 671 | message.skipContentProcessing = true; 672 | }); 673 | }; 674 | 675 | RequestBuilder.prototype._addTransformer = function _addTransformer(fn) { 676 | this.transformers.push(fn); 677 | return this; 678 | }; 679 | 680 | RequestBuilder.addHelper = function addHelper(name, fn) { 681 | RequestBuilder.prototype[name] = function () { 682 | return this._addTransformer(fn.apply(this, arguments)); 683 | }; 684 | }; 685 | 686 | RequestBuilder.prototype.send = function send() { 687 | var message = this.useJsonp ? new JSONPRequestMessage() : new HttpRequestMessage(); 688 | return this.client.send(message, this.transformers); 689 | }; 690 | 691 | return RequestBuilder; 692 | }(); 693 | 694 | function trackRequestStart(client, processor) { 695 | client.pendingRequests.push(processor); 696 | client.isRequesting = true; 697 | } 698 | 699 | function trackRequestEnd(client, processor) { 700 | var index = client.pendingRequests.indexOf(processor); 701 | 702 | client.pendingRequests.splice(index, 1); 703 | client.isRequesting = client.pendingRequests.length > 0; 704 | 705 | if (!client.isRequesting) { 706 | var evt = DOM.createCustomEvent('aurelia-http-client-requests-drained', { bubbles: true, cancelable: true }); 707 | setTimeout(function () { 708 | return DOM.dispatchEvent(evt); 709 | }, 1); 710 | } 711 | } 712 | 713 | export var HttpClient = function () { 714 | function HttpClient() { 715 | 716 | 717 | this.isRequesting = false; 718 | 719 | this.rejectPromiseWithErrorObject = false; 720 | this.requestTransformers = []; 721 | this.requestProcessorFactories = new Map(); 722 | this.requestProcessorFactories.set(HttpRequestMessage, createHttpRequestMessageProcessor); 723 | this.requestProcessorFactories.set(JSONPRequestMessage, createJSONPRequestMessageProcessor); 724 | this.pendingRequests = []; 725 | } 726 | 727 | HttpClient.prototype.configure = function configure(fn) { 728 | var builder = new RequestBuilder(this); 729 | fn(builder); 730 | this.requestTransformers = builder.transformers; 731 | return this; 732 | }; 733 | 734 | HttpClient.prototype.createRequest = function createRequest(url) { 735 | var builder = new RequestBuilder(this); 736 | 737 | if (url) { 738 | builder.withUrl(url); 739 | } 740 | 741 | return builder; 742 | }; 743 | 744 | HttpClient.prototype.send = function send(requestMessage, transformers) { 745 | var _this6 = this; 746 | 747 | var createProcessor = this.requestProcessorFactories.get(requestMessage.constructor); 748 | var processor = void 0; 749 | var promise = void 0; 750 | var i = void 0; 751 | var ii = void 0; 752 | 753 | if (!createProcessor) { 754 | throw new Error('No request message processor factory for ' + requestMessage.constructor + '.'); 755 | } 756 | 757 | processor = createProcessor(); 758 | trackRequestStart(this, processor); 759 | 760 | transformers = transformers || this.requestTransformers; 761 | 762 | promise = Promise.resolve(requestMessage).then(function (message) { 763 | for (i = 0, ii = transformers.length; i < ii; ++i) { 764 | transformers[i](_this6, processor, message); 765 | } 766 | 767 | return processor.process(_this6, message).then(function (response) { 768 | trackRequestEnd(_this6, processor); 769 | return response; 770 | }).catch(function (response) { 771 | trackRequestEnd(_this6, processor); 772 | throw response; 773 | }); 774 | }); 775 | 776 | promise.abort = promise.cancel = function () { 777 | processor.abort(); 778 | }; 779 | 780 | return promise; 781 | }; 782 | 783 | HttpClient.prototype.delete = function _delete(url) { 784 | return this.createRequest(url).asDelete().send(); 785 | }; 786 | 787 | HttpClient.prototype.get = function get(url, params, traditional) { 788 | var req = this.createRequest(url).asGet(); 789 | 790 | if (params) { 791 | return req.withParams(params, traditional).send(); 792 | } 793 | 794 | return req.send(); 795 | }; 796 | 797 | HttpClient.prototype.head = function head(url) { 798 | return this.createRequest(url).asHead().send(); 799 | }; 800 | 801 | HttpClient.prototype.jsonp = function jsonp(url) { 802 | var callbackParameterName = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'jsoncallback'; 803 | 804 | return this.createRequest(url).asJsonp(callbackParameterName).send(); 805 | }; 806 | 807 | HttpClient.prototype.options = function options(url) { 808 | return this.createRequest(url).asOptions().send(); 809 | }; 810 | 811 | HttpClient.prototype.put = function put(url, content) { 812 | return this.createRequest(url).asPut().withContent(content).send(); 813 | }; 814 | 815 | HttpClient.prototype.patch = function patch(url, content) { 816 | return this.createRequest(url).asPatch().withContent(content).send(); 817 | }; 818 | 819 | HttpClient.prototype.post = function post(url, content) { 820 | return this.createRequest(url).asPost().withContent(content).send(); 821 | }; 822 | 823 | return HttpClient; 824 | }(); -------------------------------------------------------------------------------- /dist/native-modules/index.js: -------------------------------------------------------------------------------- 1 | export * from './aurelia-http-client'; -------------------------------------------------------------------------------- /dist/system/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | System.register(['./aurelia-http-client'], function (_export, _context) { 4 | "use strict"; 5 | 6 | return { 7 | setters: [function (_aureliaHttpClient) { 8 | var _exportObj = {}; 9 | 10 | for (var _key in _aureliaHttpClient) { 11 | if (_key !== "default" && _key !== "__esModule") _exportObj[_key] = _aureliaHttpClient[_key]; 12 | } 13 | 14 | _export(_exportObj); 15 | }], 16 | execute: function () {} 17 | }; 18 | }); -------------------------------------------------------------------------------- /doc/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | 2 | ## [1.3.2](https://github.com/aurelia/http-client/compare/1.3.0...1.3.2) (2019-03-27) 3 | 4 | 5 | 6 | 7 | ## [1.3.1](https://github.com/aurelia/http-client/compare/1.3.0...1.3.1) (2019-02-04) 8 | 9 | 10 | 11 | 12 | # [1.3.0](https://github.com/aurelia/http-client/compare/1.2.1...1.3.0) (2018-06-14) 13 | 14 | * Fix unit tests. 15 | * Enable error responses to be shaped like Error objects, addressing some issues with BlueBird. 16 | 17 | 18 | ## [1.2.1](https://github.com/aurelia/http-client/compare/1.2.0...1.2.1) (2017-10-07) 19 | 20 | 21 | ### Bug Fixes 22 | 23 | * **http-client:** don't call withParams if there are no params ([2cd076a](https://github.com/aurelia/http-client/commit/2cd076a)) 24 | 25 | 26 | 27 | 28 | # [1.2.0](https://github.com/aurelia/http-client/compare/1.1.1...1.2.0) (2017-10-01) 29 | 30 | ### Features 31 | 32 | * Added support for traditional query string parameter construction. 33 | 34 | 35 | ## [1.1.1](https://github.com/aurelia/http-client/compare/1.1.0...v1.1.1) (2017-04-05) 36 | 37 | 38 | ### Bug Fixes 39 | 40 | * **xhr-transformers:** fix sending ArrayBuffer type ([4d957f8](https://github.com/aurelia/http-client/commit/4d957f8)), closes [#161](https://github.com/aurelia/http-client/issues/161) 41 | 42 | 43 | 44 | 45 | # [1.1.0](https://github.com/aurelia/http-client/compare/1.0.4...v1.1.0) (2017-03-23) 46 | 47 | 48 | ### Features 49 | 50 | * **request-builder:** add download progress callback ([a53869b](https://github.com/aurelia/http-client/commit/a53869b)), closes [#163](https://github.com/aurelia/http-client/issues/163) 51 | * **request-builder:** add download progress callback ([73165dd](https://github.com/aurelia/http-client/commit/73165dd)) 52 | 53 | 54 | 55 | 56 | ## [1.0.4](https://github.com/aurelia/http-client/compare/1.0.3...v1.0.4) (2017-01-06) 57 | 58 | ### Bug Fixes 59 | 60 | * Improve dts files. 61 | 62 | 63 | ## [1.0.3](https://github.com/aurelia/http-client/compare/1.0.2...v1.0.3) (2016-11-08) 64 | 65 | 66 | ### Bug Fixes 67 | 68 | * **http-response-message:** handle empty responses ([bae5188](https://github.com/aurelia/http-client/commit/bae5188)) 69 | 70 | 71 | 72 | ## 1.0.2 73 | 74 | ### Bug Fixes 75 | 76 | * **headers:** #152 - Made header treatment case insensitive 77 | 78 | 79 | ## [1.0.1](https://github.com/aurelia/http-client/compare/1.0.0...v1.0.1) (2016-10-06) 80 | 81 | 82 | ### Bug Fixes 83 | 84 | * Add responseType field in HttpResponseMessage ([2c0f437](https://github.com/aurelia/http-client/commit/2c0f437)), closes [#149](https://github.com/aurelia/http-client/issues/149) 85 | 86 | 87 | 88 | 89 | # [1.0.0](https://github.com/aurelia/http-client/compare/1.0.0-rc.1.0.1...v1.0.0) (2016-07-27) 90 | 91 | 92 | ### Bug Fixes 93 | 94 | * **request-message-processor:** do not send undefined message.content ([ede329f](https://github.com/aurelia/http-client/commit/ede329f)) 95 | 96 | 97 | 98 | 99 | # [1.0.0-rc.1.0.1](https://github.com/aurelia/http-client/compare/1.0.0-rc.1.0.0...v1.0.0-rc.1.0.1) (2016-07-12) 100 | 101 | 102 | 103 | 104 | # [1.0.0-rc.1.0.0](https://github.com/aurelia/http-client/compare/1.0.0-beta.2.0.1...v1.0.0-rc.1.0.0) (2016-06-22) 105 | 106 | 107 | 108 | ### 1.0.0-beta.1.2.2 (2016-05-24) 109 | 110 | 111 | ### 1.0.0-beta.1.2.1 (2016-05-10) 112 | 113 | 114 | ### 1.0.0-beta.1.2.0 (2016-03-22) 115 | 116 | * Update to Babel 6 117 | 118 | ### 1.0.0-beta.1.1.2 (2016-03-01) 119 | 120 | 121 | #### Bug Fixes 122 | 123 | * **all:** remove core-js dependency ([9fbd7413](http://github.com/aurelia/http-client/commit/9fbd7413058b53c2b98e51b952e869ca0b1c4b26)) 124 | * **http-client:** declare replacer property ([0080eaae](http://github.com/aurelia/http-client/commit/0080eaae426073a065bbf5f3d87b05f225c2a65c)) 125 | 126 | 127 | ### 1.0.0-beta.1.1.1 (2016-02-08) 128 | 129 | 130 | #### Bug Fixes 131 | 132 | * **http-response:** status code type ([2afe081e](http://github.com/aurelia/http-client/commit/2afe081e36bfbaedab1541291cfd4ea492b69ca8), closes [#114](http://github.com/aurelia/http-client/issues/114)) 133 | 134 | 135 | ## 1.0.0-beta.1.1.0 (2016-01-29) 136 | 137 | 138 | #### Features 139 | 140 | * **package:** update metadata for jspm; core-js; deps ([9b0e4272](http://github.com/aurelia/http-client/commit/9b0e4272ac4b784642d49aba87f206a949023868)) 141 | 142 | 143 | ### 1.0.0-beta.1.0.1 (2016-01-08) 144 | 145 | #### Bug Fixes 146 | 147 | * **http-client:** declare headers property ([e64dba37](http://github.com/aurelia/http-client/commit/e64dba37c9539091fcc0e71a07e49aec60dda53f)) 148 | 149 | 150 | ### 1.0.0-beta.1 (2015-11-16) 151 | 152 | 153 | ## 0.13.0 (2015-11-09) 154 | 155 | 156 | #### Features 157 | 158 | * **headers:** case-insensitve has ([494918d5](http://github.com/aurelia/http-client/commit/494918d54784ee0c214e21f60546c17a533f9bcf), closes [#92](http://github.com/aurelia/http-client/issues/92)) 159 | * **request-builder:** enable opening xhr with login info ([a9adff68](http://github.com/aurelia/http-client/commit/a9adff68a58813bd24c2f35b465929a2bc469ebc), closes [#52](http://github.com/aurelia/http-client/issues/52)) 160 | 161 | 162 | ## 0.12.0 (2015-10-13) 163 | 164 | 165 | #### Bug Fixes 166 | 167 | * ***:** switch http client to use path.join ([6954c4fe](http://github.com/aurelia/http-client/commit/6954c4fe74de49739cdab92e3753f418136d3b7f)) 168 | * **all:** 169 | * add types for promises of send methods ([1911331d](http://github.com/aurelia/http-client/commit/1911331de6ca0325ceedcbff2db7a160110d0595)) 170 | * update compiler and fix core-js references ([c0334447](http://github.com/aurelia/http-client/commit/c0334447b4a2f794b0428a54f5e43eebd663780c)) 171 | * errors should still return HttpResponseMessage ([43f3cf4f](http://github.com/aurelia/http-client/commit/43f3cf4ffdd9690bebb99cffe699eacf3477de94)) 172 | * **bower:** correct semver ranges ([892535e1](http://github.com/aurelia/http-client/commit/892535e1ecea50c0cabd6fa9be25ebb1249632fd)) 173 | * **build:** 174 | * update linting, testing and tools ([f808d92c](http://github.com/aurelia/http-client/commit/f808d92c5140e60783f298dac1a8552048bb47dc)) 175 | * correct order of files for concat ([5a6418de](http://github.com/aurelia/http-client/commit/5a6418de752733167dca716bb5aefac682286de4)) 176 | * add missing bower bump ([59e8a734](http://github.com/aurelia/http-client/commit/59e8a734b555440ee26737040778dd18b2189d86)) 177 | * **event:** switch from hook to event ([01628f42](http://github.com/aurelia/http-client/commit/01628f42884c899ea5b6cc64ac118e5c8f9413c1)) 178 | * **http-client:** 179 | * Use correct import for core-js We were previously using `import core from core-j ([694fe35c](http://github.com/aurelia/http-client/commit/694fe35cc010623855be565ac550ce2d2dc147bc)) 180 | * abort/cancel attached to wrong promise instance ([add453c7](http://github.com/aurelia/http-client/commit/add453c79fd7a1fd0e2cb94f6ee49aecac715c64), closes [#32](http://github.com/aurelia/http-client/issues/32)) 181 | * rename withJsonpParameter to asJsonp ([c70eedd8](http://github.com/aurelia/http-client/commit/c70eedd83380a0f5ea474f35b4f32a3bd5c40078)) 182 | * take advantage of fixed path.join behavior ([29ed73a1](http://github.com/aurelia/http-client/commit/29ed73a1f7cc118bc1f9740486081cad270b37c5)) 183 | * handle empty base url with join ([d1968323](http://github.com/aurelia/http-client/commit/d1968323c557e59bdad46e1a7d43e14941a46089)) 184 | * **http-client-spec:** call send method ([7689a195](http://github.com/aurelia/http-client/commit/7689a195e344d0762445f0bfe2fb8b64b0e4790c)) 185 | * **http-request-message:** 186 | * reject on onerror and ontimeout ([e0880c39](http://github.com/aurelia/http-client/commit/e0880c39b1447c054cdf5c908b4e991ad3ed28d3)) 187 | * do not reject promise; instead return response with isSuccess as false ([561ca9cb](http://github.com/aurelia/http-client/commit/561ca9cbc7f8a445d76c18b26707442e4a4cdf78)) 188 | * **http-response-message:** 189 | * prevent throws on content with unsuccessful requests ([a8a0c3d7](http://github.com/aurelia/http-client/commit/a8a0c3d7df437783f768771981fa327d9bbbd9cd)) 190 | * account for null or undefined response ([63639cf6](http://github.com/aurelia/http-client/commit/63639cf66d6abee4e44d7bfc02d5d4f8a4f5e44f)) 191 | * **package:** 192 | * update aurelia tools and dts generator ([2704444f](http://github.com/aurelia/http-client/commit/2704444ffe3a0f09db9bd9a1cbc1cad37a4647ec)) 193 | * change jspm directories ([191dde2d](http://github.com/aurelia/http-client/commit/191dde2d3dbff814f6bfb784acee519f99b3984d)) 194 | * update dependencies ([382e877b](http://github.com/aurelia/http-client/commit/382e877bd7e5f71d22863ab97df0f472db32c7ac)) 195 | * update dependencies ([3697fb76](http://github.com/aurelia/http-client/commit/3697fb76f9be018823ad2a46d2c9136debd4d401)) 196 | * update path to the latest version ([0e3eb0a7](http://github.com/aurelia/http-client/commit/0e3eb0a79a63fef5fdafa1d7bb820b25b636eaa9)) 197 | * update dependencies to latest ([562b00ba](http://github.com/aurelia/http-client/commit/562b00bac62bb46e94d7db9df196d04350ee0f42)) 198 | * add es6-shim polyfill ([bd25426f](http://github.com/aurelia/http-client/commit/bd25426f85c98ee6ad0f24abcd6d666e71c43106)) 199 | * **processor:** modify processor to apply xhr transformers after running request interceptors ([8b515859](http://github.com/aurelia/http-client/commit/8b51585984190b8d9e625469a87c814b8b80dd49), closes [#60](http://github.com/aurelia/http-client/issues/60)) 200 | * **request-builder:** 201 | * incorrect jsonp callback property name ([267ec3ec](http://github.com/aurelia/http-client/commit/267ec3ecd3721583a493653178689a539c873a5d)) 202 | * rename baseUrl to baseUri for consistency ([1fd381e9](http://github.com/aurelia/http-client/commit/1fd381e957b32d9e69966a5b60ed48f0943dcb99)) 203 | * **request-message-processor:** apply transforms after hr.open ([d5893dfd](http://github.com/aurelia/http-client/commit/d5893dfd737b8782bbd2bd7bc9915d01dfe928f2)) 204 | 205 | 206 | #### Features 207 | 208 | * **all:** 209 | * update to use PAL ([f289d14c](http://github.com/aurelia/http-client/commit/f289d14c2afea90f1a7513f446d3a5c2db984dbc)) 210 | * improvements to type info ([bad7bb5b](http://github.com/aurelia/http-client/commit/bad7bb5bf6026046f9b900e4bda9d4c085b783b1)) 211 | * more type info added ([d7775b87](http://github.com/aurelia/http-client/commit/d7775b87704a6f6b188ec14befc2eddc05af37b5)) 212 | * add support for query string building ([3c80d9e0](http://github.com/aurelia/http-client/commit/3c80d9e04ca9cfe56aa48ce53a2616b2fe32f4dc)) 213 | * massive re-design of http client ([f344819f](http://github.com/aurelia/http-client/commit/f344819f9c01ed6a7aba589d5bb56d01b0a832dd)) 214 | * **build:** update compile, switch to register modules, switch to core-js ([68f4665e](http://github.com/aurelia/http-client/commit/68f4665ed69c35a9ecb94b4a661fa753eb30f79b)) 215 | * **docs:** generate api.json from .d.ts file ([9ef8112d](http://github.com/aurelia/http-client/commit/9ef8112ded6a2a94712d30b296ba84d6832cb95b)) 216 | * **http-client:** 217 | * improve type information on HttpClient.configure ([a7dd0cf6](http://github.com/aurelia/http-client/commit/a7dd0cf6d4accb3c193fb4539d63e0ae3eba606f)) 218 | * ignore baseUrl when using absolute URLs ([ab4d2666](http://github.com/aurelia/http-client/commit/ab4d266647b24a6a61087daa5127ddfb2fb9c71b)) 219 | * automatically set the content type for JSON content ([c2768802](http://github.com/aurelia/http-client/commit/c276880205597ddaa76d3d644e10866b0cd1fd26), closes [#39](http://github.com/aurelia/http-client/issues/39)) 220 | * content-type detection ([c60b4727](http://github.com/aurelia/http-client/commit/c60b4727a56a0226cebe28ed91f183d2bfd5dc78)) 221 | * improve API for creating new requests ([93a6e38a](http://github.com/aurelia/http-client/commit/93a6e38a151b85926d2acf069cfcee0221c6b23b), closes [#27](http://github.com/aurelia/http-client/issues/27)) 222 | * add support for options ([8d10c4a1](http://github.com/aurelia/http-client/commit/8d10c4a115c674222c3b7af4870d9511a368a825)) 223 | * add onRequestsComplete event ([dab95e6f](http://github.com/aurelia/http-client/commit/dab95e6fe89a1fd847b2000eb80b6ac4f4c3237b)) 224 | * new client configuration api ([6c1b0e96](http://github.com/aurelia/http-client/commit/6c1b0e9671fb97286545ab6cdd706127b7ba2159)) 225 | * add HttpBuilder API. ([831c45fa](http://github.com/aurelia/http-client/commit/831c45faa3168e71d7abd2a65c6772a25516865e), closes [#4](http://github.com/aurelia/http-client/issues/4)) 226 | * add http patch method ([77cfc39a](http://github.com/aurelia/http-client/commit/77cfc39a780683d77f29037612bd9d18853a4e98)) 227 | * **interceptors:** 228 | * moved interceptors from client to message ([206d05d7](http://github.com/aurelia/http-client/commit/206d05d7a64b196a46daa14452ca3e017214cc0a)) 229 | * apply all transformers before interceptors ([ee4561c3](http://github.com/aurelia/http-client/commit/ee4561c37b2a6101123aecb4c1ad8490ee05ba59)) 230 | * added support for interceptors ([8ed41762](http://github.com/aurelia/http-client/commit/8ed4176276e604c2334bc4b2d3471ecb8ebfda0f)) 231 | * **jsonp:** fail JSONP requests on script load errors ([fc98ecc5](http://github.com/aurelia/http-client/commit/fc98ecc5fcb00c16140b1524ba0a5ba16bb702e7)) 232 | * **request-builder:** allow message parameters to be fully specified without sending the message ([06d84947](http://github.com/aurelia/http-client/commit/06d84947dd9e3e19028571ce0134639113ce5410), closes [#29](http://github.com/aurelia/http-client/issues/29)) 233 | * **transformers:** handle content via a transformer ([5d3f4c02](http://github.com/aurelia/http-client/commit/5d3f4c02494aafd06418298ba3bf50c2e6307626)) 234 | * **type annotation:** 235 | * added type info ([14eab7fd](http://github.com/aurelia/http-client/commit/14eab7fdc6de5b4a361e1babbfe445636b1e319f)) 236 | * added type info ([7a468549](http://github.com/aurelia/http-client/commit/7a468549f566fdd12fc81d7f12ecee394ac370a6)) 237 | * added type info ([1608675d](http://github.com/aurelia/http-client/commit/1608675d086c069da63e35545ddf1826cc958dbf)) 238 | * added type info ([2c59d7bb](http://github.com/aurelia/http-client/commit/2c59d7bb49cd9f4305b200e019a192250ccf270a)) 239 | * added type info ([496f4003](http://github.com/aurelia/http-client/commit/496f4003da642486e6c74689e0fff390d541ad0d)) 240 | 241 | 242 | #### Breaking Changes 243 | 244 | * This is a breaking API change to HttpRequestBuilder and HttpRequestMessage. To update, replace uses of `withUri`, `withBaseUri`, and `uri` with `withUrl`, `withBaseUrl`, and `url`, as appropriate. 245 | 246 | ([150a2f72](http://github.com/aurelia/http-client/commit/150a2f721166516d50699ea5da8a074a5792a238)) 247 | 248 | 249 | ## 0.11.0 (2015-09-04) 250 | 251 | 252 | #### Bug Fixes 253 | 254 | * **build:** update linting, testing and tools ([f808d92c](http://github.com/aurelia/http-client/commit/f808d92c5140e60783f298dac1a8552048bb47dc)) 255 | 256 | 257 | #### Features 258 | 259 | * **docs:** generate api.json from .d.ts file ([9ef8112d](http://github.com/aurelia/http-client/commit/9ef8112ded6a2a94712d30b296ba84d6832cb95b)) 260 | * **type annotation:** added type info ([14eab7fd](http://github.com/aurelia/http-client/commit/14eab7fdc6de5b4a361e1babbfe445636b1e319f)) 261 | 262 | 263 | ### 0.10.3 (2015-08-14) 264 | 265 | 266 | #### Bug Fixes 267 | 268 | * **http-client:** Use correct import for core-js We were previously using `import core from core-j ([694fe35c](http://github.com/aurelia/http-client/commit/694fe35cc010623855be565ac550ce2d2dc147bc)) 269 | 270 | 271 | #### Features 272 | 273 | * **all:** 274 | * improvements to type info ([bad7bb5b](http://github.com/aurelia/http-client/commit/bad7bb5bf6026046f9b900e4bda9d4c085b783b1)) 275 | * more type info added ([d7775b87](http://github.com/aurelia/http-client/commit/d7775b87704a6f6b188ec14befc2eddc05af37b5)) 276 | * **type annotation:** 277 | * added type info ([7a468549](http://github.com/aurelia/http-client/commit/7a468549f566fdd12fc81d7f12ecee394ac370a6)) 278 | * added type info ([1608675d](http://github.com/aurelia/http-client/commit/1608675d086c069da63e35545ddf1826cc958dbf)) 279 | * added type info ([2c59d7bb](http://github.com/aurelia/http-client/commit/2c59d7bb49cd9f4305b200e019a192250ccf270a)) 280 | * added type info ([496f4003](http://github.com/aurelia/http-client/commit/496f4003da642486e6c74689e0fff390d541ad0d)) 281 | 282 | 283 | ### 0.10.2 (2015-07-29) 284 | 285 | 286 | #### Bug Fixes 287 | 288 | * **build:** correct order of files for concat ([5a6418de](http://github.com/aurelia/http-client/commit/5a6418de752733167dca716bb5aefac682286de4)) 289 | 290 | 291 | ### 0.10.1 (2015-07-29) 292 | 293 | 294 | #### Bug Fixes 295 | 296 | * **processor:** modify processor to apply xhr transformers after running request interceptors ([8b515859](http://github.com/aurelia/http-client/commit/8b51585984190b8d9e625469a87c814b8b80dd49), closes [#60](http://github.com/aurelia/http-client/issues/60)) 297 | 298 | 299 | ## 0.10.0 (2015-07-01) 300 | 301 | 302 | #### Bug Fixes 303 | 304 | * **package:** update aurelia tools and dts generator ([2704444f](http://github.com/aurelia/http-client/commit/2704444ffe3a0f09db9bd9a1cbc1cad37a4647ec)) 305 | 306 | 307 | ### 0.9.1 (2015-06-08) 308 | 309 | 310 | #### Features 311 | 312 | * **http-client:** automatically set the content type for JSON content ([c2768802](http://github.com/aurelia/http-client/commit/c276880205597ddaa76d3d644e10866b0cd1fd26), closes [#39](http://github.com/aurelia/http-client/issues/39)) 313 | 314 | 315 | ## 0.9.0 (2015-06-08) 316 | 317 | 318 | #### Features 319 | 320 | * **interceptors:** 321 | * moved interceptors from client to message ([206d05d7](http://github.com/aurelia/http-client/commit/206d05d7a64b196a46daa14452ca3e017214cc0a)) 322 | * apply all transformers before interceptors ([ee4561c3](http://github.com/aurelia/http-client/commit/ee4561c37b2a6101123aecb4c1ad8490ee05ba59)) 323 | * added support for interceptors ([8ed41762](http://github.com/aurelia/http-client/commit/8ed4176276e604c2334bc4b2d3471ecb8ebfda0f)) 324 | * **jsonp:** fail JSONP requests on script load errors ([fc98ecc5](http://github.com/aurelia/http-client/commit/fc98ecc5fcb00c16140b1524ba0a5ba16bb702e7)) 325 | 326 | 327 | ### 0.8.1 (2015-05-09) 328 | 329 | #### Bug Fixes 330 | 331 | * **http-response-message**: fix issue with IE9 not supporting the response property ([6a2acfe4](http://github.com/aurelia/http-client/commit/6a2acfe44a96ba49557d70b37920f34224980784 332 | 333 | 334 | ## 0.8.0 (2015-04-30) 335 | 336 | 337 | #### Breaking Changes 338 | 339 | * This is a breaking API change to HttpRequestBuilder and HttpRequestMessage. To update, replace uses of `withUri`, `withBaseUri`, and `uri` with `withUrl`, `withBaseUrl`, and `url`, as appropriate. 340 | 341 | ([150a2f72](http://github.com/aurelia/http-client/commit/150a2f721166516d50699ea5da8a074a5792a238)) 342 | 343 | 344 | ## 0.7.0 (2015-04-09) 345 | 346 | 347 | #### Bug Fixes 348 | 349 | * **all:** update compiler and fix core-js references ([c0334447](http://github.com/aurelia/http-client/commit/c0334447b4a2f794b0428a54f5e43eebd663780c)) 350 | 351 | 352 | #### Features 353 | 354 | * **http-client:** content-type detection ([c60b4727](http://github.com/aurelia/http-client/commit/c60b4727a56a0226cebe28ed91f183d2bfd5dc78)) 355 | 356 | 357 | ### 0.6.1 (2015-03-27) 358 | 359 | 360 | #### Bug Fixes 361 | 362 | * **http-client:** abort/cancel attached to wrong promise instance ([add453c7](http://github.com/aurelia/http-client/commit/add453c79fd7a1fd0e2cb94f6ee49aecac715c64), closes [#32](http://github.com/aurelia/http-client/issues/32)) 363 | 364 | 365 | ## 0.6.0 (2015-03-24) 366 | 367 | 368 | #### Bug Fixes 369 | 370 | * **http-client:** rename withJsonpParameter to asJsonp ([c70eedd8](http://github.com/aurelia/http-client/commit/c70eedd83380a0f5ea474f35b4f32a3bd5c40078)) 371 | * **request-builder:** 372 | * incorrect jsonp callback property name ([267ec3ec](http://github.com/aurelia/http-client/commit/267ec3ecd3721583a493653178689a539c873a5d)) 373 | * rename baseUrl to baseUri for consistency ([1fd381e9](http://github.com/aurelia/http-client/commit/1fd381e957b32d9e69966a5b60ed48f0943dcb99)) 374 | 375 | 376 | #### Features 377 | 378 | * **http-client:** improve API for creating new requests ([93a6e38a](http://github.com/aurelia/http-client/commit/93a6e38a151b85926d2acf069cfcee0221c6b23b), closes [#27](http://github.com/aurelia/http-client/issues/27)) 379 | * **request-builder:** allow message parameters to be fully specified without sending the message ([06d84947](http://github.com/aurelia/http-client/commit/06d84947dd9e3e19028571ce0134639113ce5410), closes [#29](http://github.com/aurelia/http-client/issues/29)) 380 | 381 | 382 | ### 0.5.5 (2015-02-28) 383 | 384 | 385 | #### Bug Fixes 386 | 387 | * **package:** change jspm directories ([191dde2d](http://github.com/aurelia/http-client/commit/191dde2d3dbff814f6bfb784acee519f99b3984d)) 388 | 389 | 390 | ### 0.5.4 (2015-02-27) 391 | 392 | 393 | #### Bug Fixes 394 | 395 | * **package:** update dependencies ([382e877b](http://github.com/aurelia/http-client/commit/382e877bd7e5f71d22863ab97df0f472db32c7ac)) 396 | 397 | 398 | ### 0.5.3 (2015-02-18) 399 | 400 | 401 | ### 0.5.2 (2015-02-12) 402 | 403 | 404 | #### Bug Fixes 405 | 406 | * **event:** switch from hook to event ([01628f42](http://github.com/aurelia/http-client/commit/01628f42884c899ea5b6cc64ac118e5c8f9413c1)) 407 | 408 | 409 | ### 0.5.1 (2015-02-12) 410 | 411 | 412 | #### Bug Fixes 413 | 414 | * **http-response-message:** prevent throws on content with unsuccessful requests ([a8a0c3d7](http://github.com/aurelia/http-client/commit/a8a0c3d7df437783f768771981fa327d9bbbd9cd)) 415 | 416 | 417 | ## 0.5.0 (2015-02-12) 418 | 419 | 420 | #### Bug Fixes 421 | 422 | * **build:** add missing bower bump ([59e8a734](http://github.com/aurelia/http-client/commit/59e8a734b555440ee26737040778dd18b2189d86)) 423 | * **http-response-message:** account for null or undefined response ([63639cf6](http://github.com/aurelia/http-client/commit/63639cf66d6abee4e44d7bfc02d5d4f8a4f5e44f)) 424 | * **request-message-processor:** apply transforms after hr.open ([d5893dfd](http://github.com/aurelia/http-client/commit/d5893dfd737b8782bbd2bd7bc9915d01dfe928f2)) 425 | 426 | 427 | #### Features 428 | 429 | * **all:** 430 | * add support for query string building ([3c80d9e0](http://github.com/aurelia/http-client/commit/3c80d9e04ca9cfe56aa48ce53a2616b2fe32f4dc)) 431 | * massive re-design of http client ([f344819f](http://github.com/aurelia/http-client/commit/f344819f9c01ed6a7aba589d5bb56d01b0a832dd)) 432 | * **http-client:** 433 | * add support for options ([8d10c4a1](http://github.com/aurelia/http-client/commit/8d10c4a115c674222c3b7af4870d9511a368a825)) 434 | * add onRequestsComplete event ([dab95e6f](http://github.com/aurelia/http-client/commit/dab95e6fe89a1fd847b2000eb80b6ac4f4c3237b)) 435 | * new client configuration api ([6c1b0e96](http://github.com/aurelia/http-client/commit/6c1b0e9671fb97286545ab6cdd706127b7ba2159)) 436 | * add HttpBuilder API. ([831c45fa](http://github.com/aurelia/http-client/commit/831c45faa3168e71d7abd2a65c6772a25516865e), closes [#4](http://github.com/aurelia/http-client/issues/4)) 437 | * **transformers:** handle content via a transformer ([5d3f4c02](http://github.com/aurelia/http-client/commit/5d3f4c02494aafd06418298ba3bf50c2e6307626)) 438 | 439 | 440 | ### 0.4.4 (2015-02-03) 441 | 442 | 443 | #### Bug Fixes 444 | 445 | * **all:** errors should still return HttpResponseMessage ([43f3cf4f](http://github.com/aurelia/http-client/commit/43f3cf4ffdd9690bebb99cffe699eacf3477de94)) 446 | 447 | 448 | ### 0.4.3 (2015-01-24) 449 | 450 | 451 | #### Bug Fixes 452 | 453 | * **bower:** correct semver ranges ([892535e1](http://github.com/aurelia/http-client/commit/892535e1ecea50c0cabd6fa9be25ebb1249632fd)) 454 | 455 | 456 | ### 0.4.2 (2015-01-22) 457 | 458 | 459 | #### Bug Fixes 460 | 461 | * **http-client-spec:** call send method ([7689a195](http://github.com/aurelia/http-client/commit/7689a195e344d0762445f0bfe2fb8b64b0e4790c)) 462 | * **http-request-message:** reject on onerror and ontimeout ([e0880c39](http://github.com/aurelia/http-client/commit/e0880c39b1447c054cdf5c908b4e991ad3ed28d3)) 463 | * **package:** update dependencies ([3697fb76](http://github.com/aurelia/http-client/commit/3697fb76f9be018823ad2a46d2c9136debd4d401)) 464 | 465 | 466 | ### 0.4.1 (2015-01-12) 467 | 468 | 469 | #### Features 470 | 471 | * **http-client:** add http patch method ([77cfc39a](http://github.com/aurelia/http-client/commit/77cfc39a780683d77f29037612bd9d18853a4e98)) 472 | 473 | 474 | ## 0.4.0 (2015-01-06) 475 | 476 | 477 | #### Bug Fixes 478 | 479 | * **http-request-message:** do not reject promise; instead return response with isSuccess as false ([561ca9cb](http://github.com/aurelia/http-client/commit/561ca9cbc7f8a445d76c18b26707442e4a4cdf78)) 480 | 481 | 482 | #### Features 483 | 484 | * **build:** update compile, switch to register modules, switch to core-js ([68f4665e](http://github.com/aurelia/http-client/commit/68f4665ed69c35a9ecb94b4a661fa753eb30f79b)) 485 | 486 | 487 | ### 0.3.2 (2015-01-06) 488 | 489 | 490 | #### Bug Fixes 491 | 492 | * **http-request-message:** do not reject promise; instead return response with isSuccess as false ([561ca9cb](http://github.com/aurelia/http-client/commit/561ca9cbc7f8a445d76c18b26707442e4a4cdf78)) 493 | 494 | 495 | #### Features 496 | 497 | * **build:** update compile, switch to register modules, switch to core-js ([68f4665e](http://github.com/aurelia/http-client/commit/68f4665ed69c35a9ecb94b4a661fa753eb30f79b)) 498 | 499 | 500 | ### 0.3.1 (2014-12-18) 501 | 502 | 503 | #### Bug Fixes 504 | 505 | * **http-client:** take advantage of fixed path.join behavior ([29ed73a1](http://github.com/aurelia/http-client/commit/29ed73a1f7cc118bc1f9740486081cad270b37c5)) 506 | * **package:** update path to the latest version ([0e3eb0a7](http://github.com/aurelia/http-client/commit/0e3eb0a79a63fef5fdafa1d7bb820b25b636eaa9)) 507 | 508 | 509 | ## 0.3.0 (2014-12-17) 510 | 511 | 512 | #### Bug Fixes 513 | 514 | * **http-client:** handle empty base url with join ([d1968323](http://github.com/aurelia/http-client/commit/d1968323c557e59bdad46e1a7d43e14941a46089)) 515 | 516 | 517 | ## 0.2.0 (2014-12-17) 518 | 519 | 520 | #### Bug Fixes 521 | 522 | * **http-client:** switch http client to use path.join ([6954c4fe](http://github.com/aurelia/http-client/commit/6954c4fe74de49739cdab92e3753f418136d3b7f)) 523 | * **package:** update dependencies to latest ([562b00ba](http://github.com/aurelia/http-client/commit/562b00bac62bb46e94d7db9df196d04350ee0f42)) 524 | 525 | 526 | ## 0.1.0 (2014-12-11) 527 | 528 | 529 | #### Bug Fixes 530 | 531 | * **package:** add es6-shim polyfill ([bd25426f](http://github.com/aurelia/http-client/commit/bd25426f85c98ee6ad0f24abcd6d666e71c43106)) 532 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | require('require-dir')('build/tasks'); 2 | -------------------------------------------------------------------------------- /karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration 2 | // Generated on Fri Dec 05 2014 16:49:29 GMT-0500 (EST) 3 | 4 | module.exports = function(config) { 5 | config.set({ 6 | 7 | // base path that will be used to resolve all patterns (eg. files, exclude) 8 | basePath: '', 9 | 10 | 11 | // frameworks to use 12 | // available frameworks: https://npmjs.org/browse/keyword/karma-adapter 13 | frameworks: ['jspm', 'source-map-support', 'jasmine-ajax', 'jasmine'], 14 | 15 | jspm: { 16 | // Edit this to your needs 17 | loadFiles: ['test/**/*.js'], 18 | serveFiles : ['src/**/*.js'] 19 | }, 20 | 21 | 22 | // list of files / patterns to load in the browser 23 | files: [], 24 | 25 | 26 | // list of files to exclude 27 | exclude: [ 28 | ], 29 | 30 | 31 | // preprocess matching files before serving them to the browser 32 | // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor 33 | preprocessors: { 34 | 'test/**/*.js': ['babel'], 35 | 'src/**/*.js': ['babel'] 36 | }, 37 | 'babelPreprocessor': { 38 | options: { 39 | sourceMap: 'inline', 40 | presets: [ 'es2015-loose', 'stage-1'], 41 | plugins: [ 42 | 'syntax-flow', 43 | 'transform-decorators-legacy', 44 | 'transform-flow-strip-types' 45 | ] 46 | } 47 | }, 48 | 49 | 50 | // test results reporter to use 51 | // possible values: 'dots', 'progress' 52 | // available reporters: https://npmjs.org/browse/keyword/karma-reporter 53 | reporters: ['progress'], 54 | 55 | 56 | // web server port 57 | port: 9876, 58 | 59 | 60 | // enable / disable colors in the output (reporters and logs) 61 | colors: true, 62 | 63 | 64 | // level of logging 65 | // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG 66 | logLevel: config.LOG_INFO, 67 | 68 | 69 | // enable / disable watching file and executing tests whenever any file changes 70 | autoWatch: true, 71 | 72 | 73 | // start these browsers 74 | // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher 75 | browsers: ['Chrome'], 76 | 77 | 78 | // Continuous Integration mode 79 | // if true, Karma captures browsers, runs the tests and exits 80 | singleRun: false 81 | }); 82 | }; 83 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "aurelia-http-client", 3 | "version": "1.3.2", 4 | "description": "A simple, restful, message-based wrapper around XMLHttpRequest.", 5 | "keywords": [ 6 | "aurelia", 7 | "http", 8 | "ajax" 9 | ], 10 | "homepage": "http://aurelia.io", 11 | "bugs": { 12 | "url": "https://github.com/aurelia/http-client/issues" 13 | }, 14 | "license": "MIT", 15 | "author": "Rob Eisenberg (http://robeisenberg.com/)", 16 | "main": "dist/commonjs/aurelia-http-client.js", 17 | "typings": "dist/aurelia-http-client.d.ts", 18 | "repository": { 19 | "type": "git", 20 | "url": "http://github.com/aurelia/http-client" 21 | }, 22 | "scripts": { 23 | "test": "karma start --single-run", 24 | "build": "gulp build", 25 | "cut-release": "gulp prepare-release" 26 | }, 27 | "files": [ 28 | "dist", 29 | "doc", 30 | "src", 31 | "typings.json", 32 | "README.md", 33 | "LICENSE" 34 | ], 35 | "jspm": { 36 | "registry": "npm", 37 | "main": "aurelia-http-client", 38 | "format": "amd", 39 | "directories": { 40 | "dist": "dist/amd" 41 | }, 42 | "dependencies": { 43 | "aurelia-pal": "^1.0.0", 44 | "aurelia-path": "^1.1.1" 45 | }, 46 | "devDependencies": { 47 | "aurelia-pal-browser": "^1.0.0-beta.1.1.2", 48 | "babel": "babel-core@^5.1.13", 49 | "babel-runtime": "^5.1.13", 50 | "core-js": "^2.0.3" 51 | } 52 | }, 53 | "dependencies": { 54 | "aurelia-pal": "^1.0.0", 55 | "aurelia-path": "^1.1.1" 56 | }, 57 | "devDependencies": { 58 | "aurelia-tools": "^0.2.4", 59 | "babel-dts-generator": "^0.6.1", 60 | "babel-eslint": "^6.1.2", 61 | "babel-plugin-syntax-flow": "^6.8.0", 62 | "babel-plugin-transform-decorators-legacy": "^1.3.4", 63 | "babel-plugin-transform-es2015-modules-amd": "^6.8.0", 64 | "babel-plugin-transform-es2015-modules-commonjs": "^6.11.5", 65 | "babel-plugin-transform-es2015-modules-systemjs": "^6.11.6", 66 | "babel-plugin-transform-flow-strip-types": "^6.8.0", 67 | "babel-preset-es2015": "^6.9.0", 68 | "babel-preset-es2015-loose": "^7.0.0", 69 | "babel-preset-es2015-loose-native-modules": "^1.0.0", 70 | "babel-preset-stage-1": "^6.5.0", 71 | "del": "^2.2.1", 72 | "gulp": "^3.9.1", 73 | "gulp-babel": "^6.1.2", 74 | "gulp-bump": "^2.2.0", 75 | "gulp-concat": "^2.6.0", 76 | "gulp-conventional-changelog": "^1.1.0", 77 | "gulp-eslint": "^3.0.1", 78 | "gulp-ignore": "^2.0.1", 79 | "gulp-insert": "^0.5.0", 80 | "gulp-rename": "^1.2.2", 81 | "gulp-typedoc": "^2.0.0", 82 | "gulp-typedoc-extractor": "^0.0.8", 83 | "gulp-typescript": "^2.13.6", 84 | "gulp-util": "^3.0.7", 85 | "jasmine-core": "^2.4.1", 86 | "jspm": "^0.16.53", 87 | "karma": "^1.1.2", 88 | "karma-babel-preprocessor": "^6.0.1", 89 | "karma-chrome-launcher": "^1.0.1", 90 | "karma-coverage": "^1.1.1", 91 | "karma-jasmine": "^1.0.2", 92 | "karma-jasmine-ajax": "^0.1.13", 93 | "karma-jspm": "^2.2.0", 94 | "karma-source-map-support": "^1.1.0", 95 | "merge2": "^1.0.2", 96 | "object.assign": "^4.0.4", 97 | "require-dir": "^0.3.0", 98 | "run-sequence": "^1.2.2", 99 | "through2": "^2.0.1", 100 | "typedoc": "^0.4.4", 101 | "typescript": "^1.9.0-dev.20160622-1.0", 102 | "vinyl": "^1.1.1", 103 | "vinyl-paths": "^2.1.0", 104 | "yargs": "^4.8.1" 105 | }, 106 | "aurelia": { 107 | "documentation": { 108 | "articles": [] 109 | } 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /src/error-http-response-message.js: -------------------------------------------------------------------------------- 1 | import {HttpResponseMessage} from './http-response-message'; 2 | 3 | /** 4 | * Represents an error like object response message from an HTTP or JSONP request. 5 | */ 6 | export class ErrorHttpResponseMessage extends HttpResponseMessage { 7 | 8 | /** 9 | * Error like name 10 | */ 11 | name: string; 12 | 13 | /** 14 | * Error like message 15 | */ 16 | message: string; 17 | 18 | /** 19 | * Instanciate a new error response message 20 | * ErrorHttpResponseMessage instanceof Error is false but with two members 'name' and 'message' we have an error like object 21 | * @param responseMessage response message 22 | */ 23 | constructor(responseMessage: HttpResponseMessage) { 24 | super(responseMessage.requestMessage, { 25 | response: responseMessage.response, 26 | status: responseMessage.statusCode, 27 | statusText: responseMessage.statusText 28 | }, responseMessage.responseType); 29 | //error like properties : name and message 30 | this.name = responseMessage.responseType; 31 | this.message = `Error: ${responseMessage.statusCode} Status: ${responseMessage.statusText}`; 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/headers.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Represents http request/response headers. 3 | */ 4 | export class Headers { 5 | /** 6 | * Creates an instance of the headers class. 7 | * @param headers A set of key/values to initialize the headers with. 8 | */ 9 | constructor(headers?: Object = {}) { 10 | this.headers = {}; 11 | // Convert object to set with case insensitive keys 12 | for (let key in headers) { 13 | this.headers[key.toLowerCase()] = {key, value: headers[key]}; 14 | } 15 | } 16 | 17 | /** 18 | * Adds a header. 19 | * @param key The header key. 20 | * @param value The header value. 21 | */ 22 | add(key: string, value: string): void { 23 | this.headers[key.toLowerCase()] = {key, value}; 24 | } 25 | 26 | /** 27 | * Gets a header value. 28 | * @param key The header key. 29 | * @return The header value. 30 | */ 31 | get(key: string): string { 32 | let header = this.headers[key.toLowerCase()]; 33 | return header ? header.value : undefined; 34 | } 35 | 36 | /** 37 | * Clears the headers. 38 | */ 39 | clear(): void { 40 | this.headers = {}; 41 | } 42 | 43 | /** 44 | * Determines whether or not the indicated header exists in the collection. 45 | * @param header The header key to check. 46 | * @return True if it exists, false otherwise. 47 | */ 48 | has(header: string): boolean { 49 | return this.headers.hasOwnProperty(header.toLowerCase()); 50 | } 51 | 52 | /** 53 | * Configures an XMR object with the headers. 54 | * @param xhr The XHRT instance to configure. 55 | */ 56 | configureXHR(xhr: XHR): void { 57 | for (let name in this.headers) { 58 | if (this.headers.hasOwnProperty(name)) { 59 | xhr.setRequestHeader(this.headers[name].key, this.headers[name].value); 60 | } 61 | } 62 | } 63 | 64 | /** 65 | * XmlHttpRequest's getAllResponseHeaders() method returns a string of response 66 | * headers according to the format described here: 67 | * http://www.w3.org/TR/XMLHttpRequest/#the-getallresponseheaders-method 68 | * This method parses that string into a user-friendly key/value pair object. 69 | * @param headerStr The string from the XHR. 70 | * @return A Headers instance containing the parsed headers. 71 | */ 72 | static parse(headerStr: string): Headers { 73 | let headers = new Headers(); 74 | if (!headerStr) { 75 | return headers; 76 | } 77 | 78 | let headerPairs = headerStr.split('\u000d\u000a'); 79 | for (let i = 0; i < headerPairs.length; i++) { 80 | let headerPair = headerPairs[i]; 81 | // Can't use split() here because it does the wrong thing 82 | // if the header value has the string ": " in it. 83 | let index = headerPair.indexOf('\u003a\u0020'); 84 | if (index > 0) { 85 | let key = headerPair.substring(0, index); 86 | let val = headerPair.substring(index + 2); 87 | headers.add(key, val); 88 | } 89 | } 90 | 91 | return headers; 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/http-client.js: -------------------------------------------------------------------------------- 1 | /*eslint no-unused-vars:0*/ 2 | import {DOM} from 'aurelia-pal'; 3 | import {RequestMessage} from './request-message'; 4 | import {RequestBuilder} from './request-builder'; 5 | import {HttpRequestMessage, createHttpRequestMessageProcessor} from './http-request-message'; 6 | import {HttpResponseMessage} from './http-response-message'; 7 | import {JSONPRequestMessage, createJSONPRequestMessageProcessor} from './jsonp-request-message'; 8 | import {RequestMessageProcessor} from './request-message-processor'; 9 | 10 | function trackRequestStart(client: HttpClient, processor: RequestMessageProcessor) { 11 | client.pendingRequests.push(processor); 12 | client.isRequesting = true; 13 | } 14 | 15 | function trackRequestEnd(client: HttpClient, processor: RequestMessageProcessor) { 16 | let index = client.pendingRequests.indexOf(processor); 17 | 18 | client.pendingRequests.splice(index, 1); 19 | client.isRequesting = client.pendingRequests.length > 0; 20 | 21 | if (!client.isRequesting) { 22 | let evt = DOM.createCustomEvent('aurelia-http-client-requests-drained', { bubbles: true, cancelable: true }); 23 | setTimeout(() => DOM.dispatchEvent(evt), 1); 24 | } 25 | } 26 | 27 | /** 28 | * The main HTTP client object. 29 | */ 30 | export class HttpClient { 31 | 32 | /** 33 | * Return true if promises are rejected with an error like object. Default false 34 | */ 35 | rejectPromiseWithErrorObject: boolean; 36 | 37 | /** 38 | * Indicates whether or not the client is in the process of requesting resources. 39 | */ 40 | isRequesting: boolean = false; 41 | 42 | /** 43 | * Creates an instance of HttpClient. 44 | */ 45 | constructor() { 46 | this.rejectPromiseWithErrorObject = false; 47 | this.requestTransformers = []; 48 | this.requestProcessorFactories = new Map(); 49 | this.requestProcessorFactories.set(HttpRequestMessage, createHttpRequestMessageProcessor); 50 | this.requestProcessorFactories.set(JSONPRequestMessage, createJSONPRequestMessageProcessor); 51 | this.pendingRequests = []; 52 | } 53 | 54 | /** 55 | * Configure this HttpClient with default settings to be used by all requests. 56 | * @param fn A function that takes a RequestBuilder as an argument. 57 | */ 58 | configure(fn: (builder: RequestBuilder) => void): HttpClient { 59 | let builder = new RequestBuilder(this); 60 | fn(builder); 61 | this.requestTransformers = builder.transformers; 62 | return this; 63 | } 64 | 65 | /** 66 | * Returns a new RequestBuilder for this HttpClient instance that can be used to build and send HTTP requests. 67 | * @param url The target URL. 68 | */ 69 | createRequest(url: string): RequestBuilder { 70 | let builder = new RequestBuilder(this); 71 | 72 | if (url) { 73 | builder.withUrl(url); 74 | } 75 | 76 | return builder; 77 | } 78 | 79 | /** 80 | * Sends a message using the underlying networking stack. 81 | * @param message A configured HttpRequestMessage or JSONPRequestMessage. 82 | * @param transformers A collection of transformers to apply to the HTTP request. 83 | * @return A cancellable promise object. 84 | */ 85 | send(requestMessage: RequestMessage, transformers: Array): Promise { 86 | let createProcessor = this.requestProcessorFactories.get(requestMessage.constructor); 87 | let processor: RequestMessageProcessor; 88 | let promise; 89 | let i; 90 | let ii; 91 | 92 | if (!createProcessor) { 93 | throw new Error(`No request message processor factory for ${requestMessage.constructor}.`); 94 | } 95 | 96 | processor = createProcessor(); 97 | trackRequestStart(this, processor); 98 | 99 | transformers = transformers || this.requestTransformers; 100 | 101 | promise = Promise.resolve(requestMessage) 102 | .then(message => { 103 | // First apply transformers passed to the client.send() 104 | for (i = 0, ii = transformers.length; i < ii; ++i) { 105 | transformers[i](this, processor, message); 106 | } 107 | 108 | return processor.process(this, message).then(response => { 109 | trackRequestEnd(this, processor); 110 | return response; 111 | }).catch(response => { 112 | trackRequestEnd(this, processor); 113 | throw response; 114 | }); 115 | }); 116 | 117 | promise.abort = promise.cancel = function() { 118 | processor.abort(); 119 | }; 120 | 121 | return promise; 122 | } 123 | 124 | /** 125 | * Sends an HTTP DELETE request. 126 | * @param url The target URL. 127 | * @return A cancellable promise object. 128 | */ 129 | delete(url: string): Promise { 130 | return this.createRequest(url).asDelete().send(); 131 | } 132 | 133 | /** 134 | * Sends an HTTP GET request. 135 | * @param url The target URL. 136 | * @return {Promise} A cancellable promise object. 137 | */ 138 | get(url: string, params?: Object, traditional?: boolean): Promise { 139 | let req = this.createRequest(url).asGet(); 140 | 141 | if (params) { 142 | return req.withParams(params, traditional).send(); 143 | } 144 | 145 | return req.send(); 146 | } 147 | 148 | /** 149 | * Sends an HTTP HEAD request. 150 | * @param url The target URL. 151 | * @return A cancellable promise object. 152 | */ 153 | head(url: string): Promise { 154 | return this.createRequest(url).asHead().send(); 155 | } 156 | 157 | /** 158 | * Sends a JSONP request. 159 | * @param url The target URL. 160 | * @return A cancellable promise object. 161 | */ 162 | jsonp(url: string, callbackParameterName: string = 'jsoncallback'): Promise { 163 | return this.createRequest(url).asJsonp(callbackParameterName).send(); 164 | } 165 | 166 | /** 167 | * Sends an HTTP OPTIONS request. 168 | * @param url The target URL. 169 | * @return A cancellable promise object. 170 | */ 171 | options(url: string): Promise { 172 | return this.createRequest(url).asOptions().send(); 173 | } 174 | 175 | /** 176 | * Sends an HTTP PUT request. 177 | * @param url The target URL. 178 | * @param content The request payload. 179 | * @return A cancellable promise object. 180 | */ 181 | put(url: string, content: any): Promise { 182 | return this.createRequest(url).asPut().withContent(content).send(); 183 | } 184 | 185 | /** 186 | * Sends an HTTP PATCH request. 187 | * @param url The target URL. 188 | * @param content The request payload. 189 | * @return A cancellable promise object. 190 | */ 191 | patch(url: string, content: any): Promise { 192 | return this.createRequest(url).asPatch().withContent(content).send(); 193 | } 194 | 195 | /** 196 | * Sends an HTTP POST request. 197 | * @param url The target URL. 198 | * @param content The request payload. 199 | * @return A cancellable promise object. 200 | */ 201 | post(url: string, content: any): Promise { 202 | return this.createRequest(url).asPost().withContent(content).send(); 203 | } 204 | } 205 | -------------------------------------------------------------------------------- /src/http-request-message.js: -------------------------------------------------------------------------------- 1 | import {PLATFORM} from 'aurelia-pal'; 2 | import {RequestMessage} from './request-message'; 3 | import {RequestMessageProcessor} from './request-message-processor'; 4 | import { 5 | timeoutTransformer, 6 | credentialsTransformer, 7 | progressTransformer, 8 | downloadProgressTransformer, 9 | responseTypeTransformer, 10 | headerTransformer, 11 | contentTransformer 12 | } from './xhr-transformers'; 13 | 14 | /** 15 | * Represents an HTTP request message. 16 | */ 17 | export class HttpRequestMessage extends RequestMessage { 18 | 19 | /** 20 | * A replacer function to use in transforming the content. 21 | */ 22 | replacer: (key: string, value: any) => any; 23 | 24 | /** 25 | * Creates an instance of HttpRequestMessage. 26 | * @param method The http method. 27 | * @param url The url to submit the request to. 28 | * @param content The content of the request. 29 | * @param headers The headers to send along with the request. 30 | */ 31 | constructor(method: string, url: string, content: any, headers?: Headers) { 32 | super(method, url, content, headers); 33 | this.responseType = 'json'; //text, arraybuffer, blob, document 34 | } 35 | } 36 | 37 | /** 38 | * Creates a RequestMessageProcessor for handling HTTP request messages. 39 | * @return A processor instance for HTTP request messages. 40 | */ 41 | export function createHttpRequestMessageProcessor(): RequestMessageProcessor { 42 | return new RequestMessageProcessor(PLATFORM.XMLHttpRequest, [ 43 | timeoutTransformer, 44 | credentialsTransformer, 45 | progressTransformer, 46 | downloadProgressTransformer, 47 | responseTypeTransformer, 48 | contentTransformer, 49 | headerTransformer 50 | ]); 51 | } 52 | -------------------------------------------------------------------------------- /src/http-response-message.js: -------------------------------------------------------------------------------- 1 | import {Headers} from './headers'; 2 | import {RequestMessage} from './request-message'; 3 | 4 | /** 5 | * Represents a response message from an HTTP or JSONP request. 6 | */ 7 | export class HttpResponseMessage { 8 | /** 9 | * The request message that resulted in this response. 10 | */ 11 | requestMessage: RequestMessage; 12 | 13 | /** 14 | * The status code of the response. 15 | */ 16 | statusCode: number; 17 | 18 | /** 19 | * The raw response. 20 | */ 21 | response: any; 22 | 23 | /** 24 | * The type of the response. 25 | */ 26 | responseType: string; 27 | 28 | /** 29 | * The success status of the request based on status code. 30 | */ 31 | isSuccess: boolean; 32 | 33 | /** 34 | * The status text. 35 | */ 36 | statusText: string; 37 | 38 | /** 39 | * A reviver function to use in transforming the content. 40 | */ 41 | reviver: (key: string, value: any) => any; 42 | 43 | /** 44 | * The mime type of the response. 45 | */ 46 | mimeType: string; 47 | 48 | /** 49 | * The headers received with the response. 50 | */ 51 | headers: Headers; 52 | 53 | /** 54 | * Creates an instance of HttpResponseMessage. 55 | * @param requestMessage The request message that resulted in this response. 56 | * @param xhr The XHR instance that made the request. 57 | * @param responseType The type of the response. 58 | * @param reviver? A reviver function to use in transforming the content. 59 | */ 60 | constructor(requestMessage: RequestMessage, xhr: XHR, responseType: string, reviver?: (key: string, value: any) => any) { 61 | this.requestMessage = requestMessage; 62 | this.statusCode = xhr.status; 63 | this.response = xhr.response || xhr.responseText; 64 | this.isSuccess = xhr.status >= 200 && xhr.status < 400; 65 | this.statusText = xhr.statusText; 66 | this.reviver = reviver; 67 | this.mimeType = null; 68 | 69 | if (xhr.getAllResponseHeaders) { 70 | this.headers = Headers.parse(xhr.getAllResponseHeaders()); 71 | } else { 72 | this.headers = new Headers(); 73 | } 74 | 75 | let contentType; 76 | 77 | if (this.headers && this.headers.headers) { 78 | contentType = this.headers.get('Content-Type'); 79 | } 80 | 81 | if (contentType) { 82 | this.mimeType = responseType = contentType.split(';')[0].trim(); 83 | if (mimeTypes.hasOwnProperty(this.mimeType)) responseType = mimeTypes[this.mimeType]; 84 | } 85 | 86 | this.responseType = responseType; 87 | } 88 | 89 | /** 90 | * Gets the content of the response. 91 | * @return the response content. 92 | */ 93 | get content(): any { 94 | try { 95 | if (this._content !== undefined) { 96 | return this._content; 97 | } 98 | 99 | if (this.response === undefined || this.response === null || this.response === '') { 100 | this._content = this.response; 101 | return this._content; 102 | } 103 | 104 | if (this.responseType === 'json') { 105 | this._content = JSON.parse(this.response, this.reviver); 106 | return this._content; 107 | } 108 | 109 | if (this.reviver) { 110 | this._content = this.reviver(this.response); 111 | return this._content; 112 | } 113 | 114 | this._content = this.response; 115 | return this._content; 116 | } catch (e) { 117 | if (this.isSuccess) { 118 | throw e; 119 | } 120 | 121 | this._content = null; 122 | return this._content; 123 | } 124 | } 125 | } 126 | 127 | /** 128 | * MimeTypes mapped to responseTypes 129 | * 130 | * @type {Object} 131 | */ 132 | export let mimeTypes = { 133 | 'text/html': 'html', 134 | 'text/javascript': 'js', 135 | 'application/javascript': 'js', 136 | 'text/json': 'json', 137 | 'application/json': 'json', 138 | 'application/rss+xml': 'rss', 139 | 'application/atom+xml': 'atom', 140 | 'application/xhtml+xml': 'xhtml', 141 | 'text/markdown': 'md', 142 | 'text/xml': 'xml', 143 | 'text/mathml': 'mml', 144 | 'application/xml': 'xml', 145 | 'text/yml': 'yml', 146 | 'text/csv': 'csv', 147 | 'text/css': 'css', 148 | 'text/less': 'less', 149 | 'text/stylus': 'styl', 150 | 'text/scss': 'scss', 151 | 'text/sass': 'sass', 152 | 'text/plain': 'txt' 153 | }; 154 | -------------------------------------------------------------------------------- /src/jsonp-request-message.js: -------------------------------------------------------------------------------- 1 | import {DOM, PLATFORM} from 'aurelia-pal'; 2 | import {RequestMessage} from './request-message'; 3 | import {RequestMessageProcessor} from './request-message-processor'; 4 | import { 5 | timeoutTransformer, 6 | callbackParameterNameTransformer 7 | } from './xhr-transformers'; 8 | 9 | /** 10 | * Represents an JSONP request message. 11 | */ 12 | export class JSONPRequestMessage extends RequestMessage { 13 | /** 14 | * Creates an instance of JSONPRequestMessage. 15 | * @param url The url to submit the request to. 16 | * @param callbackParameterName The name of the callback parameter that the api expects. 17 | */ 18 | constructor(url: string, callbackParameterName: string) { 19 | super('JSONP', url); 20 | this.responseType = 'jsonp'; 21 | this.callbackParameterName = callbackParameterName; 22 | } 23 | } 24 | 25 | class JSONPXHR { 26 | open(method: string, url: string): void { 27 | this.method = method; 28 | this.url = url; 29 | this.callbackName = 'jsonp_callback_' + Math.round(100000 * Math.random()); 30 | } 31 | 32 | send(): void { 33 | let url = this.url + (this.url.indexOf('?') >= 0 ? '&' : '?') + encodeURIComponent(this.callbackParameterName) + '=' + this.callbackName; 34 | let script = DOM.createElement('script'); 35 | 36 | script.src = url; 37 | script.onerror = (e) => { 38 | cleanUp(); 39 | 40 | this.status = 0; 41 | this.onerror(new Error('error')); 42 | }; 43 | 44 | let cleanUp = () => { 45 | delete PLATFORM.global[this.callbackName]; 46 | DOM.removeNode(script); 47 | }; 48 | 49 | PLATFORM.global[this.callbackName] = (data) => { 50 | cleanUp(); 51 | 52 | if (this.status === undefined) { 53 | this.status = 200; 54 | this.statusText = 'OK'; 55 | this.response = data; 56 | this.onload(this); 57 | } 58 | }; 59 | 60 | DOM.appendNode(script); 61 | 62 | if (this.timeout !== undefined) { 63 | setTimeout(() => { 64 | if (this.status === undefined) { 65 | this.status = 0; 66 | this.ontimeout(new Error('timeout')); 67 | } 68 | }, this.timeout); 69 | } 70 | } 71 | 72 | abort() : void { 73 | if (this.status === undefined) { 74 | this.status = 0; 75 | this.onabort(new Error('abort')); 76 | } 77 | } 78 | 79 | setRequestHeader() {} 80 | } 81 | 82 | /** 83 | * Creates a RequestMessageProcessor for handling JSONP request messages. 84 | * @return A processor instance for JSONP request messages. 85 | */ 86 | export function createJSONPRequestMessageProcessor(): RequestMessageProcessor { 87 | return new RequestMessageProcessor(JSONPXHR, [ 88 | timeoutTransformer, 89 | callbackParameterNameTransformer 90 | ]); 91 | } 92 | -------------------------------------------------------------------------------- /src/request-builder.js: -------------------------------------------------------------------------------- 1 | import {HttpRequestMessage} from './http-request-message'; 2 | import {JSONPRequestMessage} from './jsonp-request-message'; 3 | import {HttpResponseMessage} from './http-response-message'; 4 | import {HttpClient} from './http-client'; 5 | 6 | /** 7 | * Intercepts requests, responses and errors. 8 | */ 9 | interface Interceptor { 10 | /** 11 | * Intercepts the response. 12 | */ 13 | response?: (message: HttpResponseMessage) => HttpResponseMessage | Promise; 14 | /** 15 | * Intercepts a response error. 16 | */ 17 | responseError?: (error: HttpResponseMessage) => HttpResponseMessage | Promise; 18 | /** 19 | * Intercepts the request. 20 | */ 21 | request?: (message: RequestMessage) => RequestMessage | Promise; 22 | /** 23 | * Intercepts a request error. 24 | */ 25 | requestError?: (error: Error) => RequestMessage | Promise; 26 | } 27 | 28 | /** 29 | * Transforms a request. 30 | */ 31 | interface RequestTransformer { 32 | (client: HttpClient, processor: RequestMessageProcessor, message: RequestMessage): void; 33 | } 34 | 35 | /** 36 | * A builder class allowing fluent composition of HTTP requests. 37 | */ 38 | export class RequestBuilder { 39 | 40 | /** 41 | * The HttpClient instance. 42 | */ 43 | client: HttpClient; 44 | 45 | /** 46 | * Creates an instance of RequestBuilder 47 | * @param client An instance of HttpClient 48 | */ 49 | constructor(client: HttpClient) { 50 | this.client = client; 51 | this.transformers = client.requestTransformers.slice(0); 52 | this.useJsonp = false; 53 | } 54 | 55 | /** 56 | * Makes the request a DELETE request. 57 | * @return The chainable RequestBuilder to use in further configuration of the request. 58 | */ 59 | asDelete(): RequestBuilder { 60 | return this._addTransformer(function(client, processor, message) { 61 | message.method = 'DELETE'; 62 | }); 63 | } 64 | 65 | /** 66 | * Makes the request a GET request. 67 | * @return The chainable RequestBuilder to use in further configuration of the request. 68 | */ 69 | asGet(): RequestBuilder { 70 | return this._addTransformer(function(client, processor, message) { 71 | message.method = 'GET'; 72 | }); 73 | } 74 | 75 | /** 76 | * Makes the request a HEAD request. 77 | * @return The chainable RequestBuilder to use in further configuration of the request. 78 | */ 79 | asHead(): RequestBuilder { 80 | return this._addTransformer(function(client, processor, message) { 81 | message.method = 'HEAD'; 82 | }); 83 | } 84 | 85 | /** 86 | * Makes the request a OPTIONS request. 87 | * @return The chainable RequestBuilder to use in further configuration of the request. 88 | */ 89 | asOptions(): RequestBuilder { 90 | return this._addTransformer(function(client, processor, message) { 91 | message.method = 'OPTIONS'; 92 | }); 93 | } 94 | 95 | /** 96 | * Makes the request a PATCH request. 97 | * @return The chainable RequestBuilder to use in further configuration of the request. 98 | */ 99 | asPatch(): RequestBuilder { 100 | return this._addTransformer(function(client, processor, message) { 101 | message.method = 'PATCH'; 102 | }); 103 | } 104 | 105 | /** 106 | * Makes the request a POST request. 107 | * @return The chainable RequestBuilder to use in further configuration of the request. 108 | */ 109 | asPost(): RequestBuilder { 110 | return this._addTransformer(function(client, processor, message) { 111 | message.method = 'POST'; 112 | }); 113 | } 114 | 115 | /** 116 | * Makes the request a PUT request. 117 | * @return The chainable RequestBuilder to use in further configuration of the request. 118 | */ 119 | asPut(): RequestBuilder { 120 | return this._addTransformer(function(client, processor, message) { 121 | message.method = 'PUT'; 122 | }); 123 | } 124 | 125 | /** 126 | * Makes the request a JSONP request. 127 | * @param callbackParameterName The name of the callback to use. 128 | * @return The chainable RequestBuilder to use in further configuration of the request. 129 | */ 130 | asJsonp(callbackParameterName: string): RequestBuilder { 131 | this.useJsonp = true; 132 | return this._addTransformer(function(client, processor, message) { 133 | message.callbackParameterName = callbackParameterName; 134 | }); 135 | } 136 | 137 | /** 138 | * Sets the request url. 139 | * @param url The url to use. 140 | * @return The chainable RequestBuilder to use in further configuration of the request. 141 | */ 142 | withUrl(url: string): RequestBuilder { 143 | return this._addTransformer(function(client, processor, message) { 144 | message.url = url; 145 | }); 146 | } 147 | 148 | /** 149 | * Sets the request content. 150 | * @param The content to send. 151 | * @return The chainable RequestBuilder to use in further configuration of the request. 152 | */ 153 | withContent(content: any): RequestBuilder { 154 | return this._addTransformer(function(client, processor, message) { 155 | message.content = content; 156 | }); 157 | } 158 | 159 | /** 160 | * Sets the base url that will be prepended to the url. 161 | * @param baseUrl The base url to use. 162 | * @return The chainable RequestBuilder to use in further configuration of the request. 163 | */ 164 | withBaseUrl(baseUrl: string): RequestBuilder { 165 | return this._addTransformer(function(client, processor, message) { 166 | message.baseUrl = baseUrl; 167 | }); 168 | } 169 | 170 | /** 171 | * Sets params that will be added to the request url as a query string. 172 | * @param params The key/value pairs to use to build the query string. 173 | * @return The chainable RequestBuilder to use in further configuration of the request. 174 | */ 175 | withParams(params: Object, traditional ?: boolean): RequestBuilder { 176 | return this._addTransformer(function(client, processor, message) { 177 | message.traditional = traditional; 178 | message.params = params; 179 | }); 180 | } 181 | 182 | /** 183 | * Sets the response type. 184 | * @param responseType The response type to expect. 185 | * @return The chainable RequestBuilder to use in further configuration of the request. 186 | */ 187 | withResponseType(responseType: string): RequestBuilder { 188 | return this._addTransformer(function(client, processor, message) { 189 | message.responseType = responseType; 190 | }); 191 | } 192 | 193 | /** 194 | * Sets a timeout for the request. 195 | * @param timeout The timeout for the request. 196 | * @return The chainable RequestBuilder to use in further configuration of the request. 197 | */ 198 | withTimeout(timeout: number): RequestBuilder { 199 | return this._addTransformer(function(client, processor, message) { 200 | message.timeout = timeout; 201 | }); 202 | } 203 | 204 | /** 205 | * Sets a header on the request. 206 | * @param key The header key to add. 207 | * @param value The header value to add. 208 | * @return The chainable RequestBuilder to use in further configuration of the request. 209 | */ 210 | withHeader(key: string, value: string): RequestBuilder { 211 | return this._addTransformer(function(client, processor, message) { 212 | message.headers.add(key, value); 213 | }); 214 | } 215 | 216 | /** 217 | * Sets the withCredentials flag on the request. 218 | * @param value The value of the withCredentials flag to set. 219 | * @return The chainable RequestBuilder to use in further configuration of the request. 220 | */ 221 | withCredentials(value: boolean): RequestBuilder { 222 | return this._addTransformer(function(client, processor, message) { 223 | message.withCredentials = value; 224 | }); 225 | } 226 | 227 | /** 228 | * Sets the user and password to use in opening the request. 229 | * @param user The username to send. 230 | * @param password The password to send. 231 | * @return The chainable RequestBuilder to use in further configuration of the request. 232 | */ 233 | withLogin(user: string, password: string): RequestBuilder { 234 | return this._addTransformer(function(client, processor, message) { 235 | message.user = user; message.password = password; 236 | }); 237 | } 238 | 239 | /** 240 | * Sets a reviver to transform the response content. 241 | * @param reviver The reviver to use in processing the response. 242 | * @return The chainable RequestBuilder to use in further configuration of the request. 243 | */ 244 | withReviver(reviver: (key: string, value: any) => any): RequestBuilder { 245 | return this._addTransformer(function(client, processor, message) { 246 | message.reviver = reviver; 247 | }); 248 | } 249 | 250 | /** 251 | * Sets a replacer to transform the request content. 252 | * @param replacer The replacer to use in preparing the request. 253 | * @return The chainable RequestBuilder to use in further configuration of the request. 254 | */ 255 | withReplacer(replacer: (key: string, value: any) => any): RequestBuilder { 256 | return this._addTransformer(function(client, processor, message) { 257 | message.replacer = replacer; 258 | }); 259 | } 260 | 261 | /** 262 | * Sets an upload progress callback. 263 | * @param progressCallback The progress callback function. 264 | * @return The chainable RequestBuilder to use in further configuration of the request. 265 | */ 266 | withProgressCallback(progressCallback: Function): RequestBuilder { 267 | return this._addTransformer(function(client, processor, message) { 268 | message.progressCallback = progressCallback; 269 | }); 270 | } 271 | 272 | /** 273 | * Sets an download progress callback. 274 | * @param progressCallback The progress callback function. 275 | * @return The chainable RequestBuilder to use in further configuration of the request. 276 | */ 277 | withDownloadProgressCallback(downloadProgressCallback: Function): RequestBuilder { 278 | return this._addTransformer(function(client, processor, message) { 279 | message.downloadProgressCallback = downloadProgressCallback; 280 | }); 281 | } 282 | 283 | /** 284 | * Sets a callback parameter name for JSONP. 285 | * @param callbackParameterName The name of the callback parameter that the JSONP request requires. 286 | * @return The chainable RequestBuilder to use in further configuration of the request. 287 | */ 288 | withCallbackParameterName(callbackParameterName: string): RequestBuilder { 289 | return this._addTransformer(function(client, processor, message) { 290 | message.callbackParameterName = callbackParameterName; 291 | }); 292 | } 293 | 294 | /** 295 | * Adds an interceptor to the request. 296 | * @param interceptor The interceptor to add. 297 | * @return The chainable RequestBuilder to use in further configuration of the request. 298 | */ 299 | withInterceptor(interceptor: Interceptor): RequestBuilder { 300 | return this._addTransformer(function(client, processor, message) { 301 | // NOTE: Interceptors are stored in reverse order. Inner interceptors before outer interceptors. 302 | // This reversal is needed so that we can build up the interception chain around the 303 | // server request. 304 | message.interceptors = message.interceptors || []; 305 | message.interceptors.unshift(interceptor); 306 | }); 307 | } 308 | 309 | /** 310 | * Skips the request content processing transform. 311 | * @return The chainable RequestBuilder to use in further configuration of the request. 312 | */ 313 | skipContentProcessing(): RequestBuilder { 314 | return this._addTransformer(function(client, processor, message) { 315 | message.skipContentProcessing = true; 316 | }); 317 | } 318 | 319 | _addTransformer(fn) { 320 | this.transformers.push(fn); 321 | return this; 322 | } 323 | 324 | /** 325 | * Adds a user-defined request transformer to the RequestBuilder. 326 | * @param name The name of the helper to add. 327 | * @param fn The helper function. 328 | */ 329 | static addHelper(name: string, fn: () => RequestTransformer): void { 330 | RequestBuilder.prototype[name] = function() { 331 | return this._addTransformer(fn.apply(this, arguments)); 332 | }; 333 | } 334 | 335 | /** 336 | * Sends the request. 337 | * @return {Promise} A cancellable promise object. 338 | */ 339 | send(): Promise { 340 | let message = this.useJsonp ? new JSONPRequestMessage() : new HttpRequestMessage(); 341 | return this.client.send(message, this.transformers); 342 | } 343 | } 344 | -------------------------------------------------------------------------------- /src/request-message-processor.js: -------------------------------------------------------------------------------- 1 | /*eslint no-unused-vars:0*/ 2 | import {PLATFORM} from 'aurelia-pal'; 3 | import {RequestMessage} from './request-message'; 4 | import {HttpResponseMessage} from './http-response-message'; 5 | import {ErrorHttpResponseMessage} from './error-http-response-message'; 6 | 7 | function applyXhrTransformers(xhrTransformers, client, processor, message, xhr) { 8 | let i; 9 | let ii; 10 | 11 | for (i = 0, ii = xhrTransformers.length; i < ii; ++i) { 12 | xhrTransformers[i](client, processor, message, xhr); 13 | } 14 | } 15 | 16 | /** 17 | * Creates an XHR implementation. 18 | */ 19 | interface XHRConstructor { 20 | //new():XHR; 21 | } 22 | 23 | /** 24 | * Represents an XHR. 25 | */ 26 | interface XHR { 27 | /** 28 | * The status code of the response. 29 | */ 30 | status: number; 31 | /** 32 | * The status text. 33 | */ 34 | statusText: string; 35 | /** 36 | * The raw response. 37 | */ 38 | response: any; 39 | /** 40 | * The raw response text. 41 | */ 42 | responseText: string; 43 | /** 44 | * The load callback. 45 | */ 46 | onload: Function; 47 | /** 48 | * The timeout callback. 49 | */ 50 | ontimeout: Function; 51 | /** 52 | * The error callback. 53 | */ 54 | onerror: Function; 55 | /** 56 | * The abort callback. 57 | */ 58 | onabort: Function; 59 | /** 60 | * Aborts the request. 61 | */ 62 | abort(): void; 63 | /** 64 | * Opens the XHR channel. 65 | */ 66 | open(method: string, url: string, isAsync: boolean, user?: string, password?: string): void; 67 | /** 68 | * Sends the request. 69 | */ 70 | send(content? : any): void; 71 | } 72 | 73 | /** 74 | * Represents an XHR transformer. 75 | */ 76 | interface XHRTransformer { 77 | (client: HttpClient, processor: RequestMessageProcessor, message: RequestMessage, xhr: XHR): void; 78 | } 79 | 80 | /** 81 | * Processes request messages. 82 | */ 83 | export class RequestMessageProcessor { 84 | /** 85 | * Creates an instance of RequestMessageProcessor. 86 | */ 87 | constructor(xhrType: XHRConstructor, xhrTransformers: XHRTransformer[]) { 88 | this.XHRType = xhrType; 89 | this.xhrTransformers = xhrTransformers; 90 | this.isAborted = false; 91 | } 92 | 93 | /** 94 | * Aborts the request. 95 | */ 96 | abort(): void { 97 | // The logic here is if the xhr object is not set then there is nothing to abort so the intent was carried out 98 | // Also test if the XHR is UNSENT - if not, it will be aborted in the process() phase 99 | if (this.xhr && this.xhr.readyState !== PLATFORM.XMLHttpRequest.UNSENT) { 100 | this.xhr.abort(); 101 | } 102 | 103 | this.isAborted = true; 104 | } 105 | 106 | /** 107 | * Processes the request. 108 | * @param client The HttpClient making the request. 109 | * @param requestMessage The message to process. 110 | * @return A promise for an HttpResponseMessage. 111 | */ 112 | process(client: HttpClient, requestMessage: RequestMessage): Promise { 113 | let promise = new Promise((resolve, reject) => { 114 | let rejectResponse: (resp: HttpResponseMessage) => void; 115 | if (client.rejectPromiseWithErrorObject) { 116 | rejectResponse = (resp: HttpResponseMessage) => { 117 | const errorResp = new ErrorHttpResponseMessage(resp); 118 | reject(errorResp); 119 | }; 120 | } else { 121 | rejectResponse = (resp: HttpResponseMessage) => { 122 | reject(resp); 123 | }; 124 | } 125 | 126 | let xhr = this.xhr = new this.XHRType(); 127 | xhr.onload = (e) => { 128 | let response = new HttpResponseMessage(requestMessage, xhr, requestMessage.responseType, requestMessage.reviver); 129 | if (response.isSuccess) { 130 | resolve(response); 131 | } else { 132 | rejectResponse(response); 133 | } 134 | }; 135 | 136 | xhr.ontimeout = (e) => { 137 | rejectResponse(new HttpResponseMessage(requestMessage, { 138 | response: e, 139 | status: xhr.status, 140 | statusText: xhr.statusText 141 | }, 'timeout')); 142 | }; 143 | 144 | xhr.onerror = (e) => { 145 | rejectResponse(new HttpResponseMessage(requestMessage, { 146 | response: e, 147 | status: xhr.status, 148 | statusText: xhr.statusText 149 | }, 'error')); 150 | }; 151 | 152 | xhr.onabort = (e) => { 153 | rejectResponse(new HttpResponseMessage(requestMessage, { 154 | response: e, 155 | status: xhr.status, 156 | statusText: xhr.statusText 157 | }, 'abort')); 158 | }; 159 | }); 160 | 161 | return Promise.resolve(requestMessage) 162 | .then(message => { 163 | let processRequest = () => { 164 | if (this.isAborted) { 165 | // Some interceptors can delay sending of XHR, so when abort is called 166 | // before XHR is actually sent we abort() instead send() 167 | this.xhr.abort(); 168 | } else { 169 | this.xhr.open(message.method, message.buildFullUrl(), true, message.user, message.password); 170 | applyXhrTransformers(this.xhrTransformers, client, this, message, this.xhr); 171 | if (typeof message.content === 'undefined') { 172 | // IE serializes undefined as "undefined" 173 | // some servers reject such requests because of unexpected payload, e.g. in case of DELETE requests 174 | this.xhr.send(); 175 | } else { 176 | this.xhr.send(message.content); 177 | } 178 | } 179 | 180 | return promise; 181 | }; 182 | 183 | // [ onFullfilled, onReject ] pairs 184 | let chain = [[processRequest, undefined]]; 185 | // Apply interceptors chain from the message.interceptors 186 | let interceptors = message.interceptors || []; 187 | interceptors.forEach(function(interceptor) { 188 | if (interceptor.request || interceptor.requestError) { 189 | chain.unshift([ 190 | interceptor.request ? interceptor.request.bind(interceptor) : undefined, 191 | interceptor.requestError ? interceptor.requestError.bind(interceptor) : undefined 192 | ]); 193 | } 194 | 195 | if (interceptor.response || interceptor.responseError) { 196 | chain.push([ 197 | interceptor.response ? interceptor.response.bind(interceptor) : undefined, 198 | interceptor.responseError ? interceptor.responseError.bind(interceptor) : undefined 199 | ]); 200 | } 201 | }); 202 | 203 | let interceptorsPromise = Promise.resolve(message); 204 | 205 | while (chain.length) { 206 | interceptorsPromise = interceptorsPromise.then(...chain.shift()); 207 | } 208 | 209 | return interceptorsPromise; 210 | }); 211 | } 212 | } 213 | -------------------------------------------------------------------------------- /src/request-message.js: -------------------------------------------------------------------------------- 1 | import {join, buildQueryString} from 'aurelia-path'; 2 | import {Headers} from './headers'; 3 | 4 | /** 5 | * Represents a request message. 6 | */ 7 | export class RequestMessage { 8 | /** 9 | * The HTTP method. 10 | */ 11 | method: string; 12 | 13 | /** 14 | * The url to submit the request to. 15 | */ 16 | url: string; 17 | 18 | /** 19 | * The content of the request. 20 | */ 21 | content: any; 22 | 23 | /** 24 | * The headers to send along with the request. 25 | */ 26 | headers: Headers; 27 | 28 | /** 29 | * Use tradional style for param serialization. 30 | */ 31 | traditional: boolean; 32 | 33 | /** 34 | * The base url that the request url is joined with. 35 | */ 36 | baseUrl: string; 37 | 38 | /** 39 | * Creates an instance of RequestMessage. 40 | * @param method The HTTP method. 41 | * @param url The url to submit the request to. 42 | * @param content The content of the request. 43 | * @param headers The headers to send along with the request. 44 | */ 45 | constructor(method: string, url: string, content: any, headers?: Headers) { 46 | this.method = method; 47 | this.url = url; 48 | this.content = content; 49 | this.headers = headers || new Headers(); 50 | this.baseUrl = ''; 51 | } 52 | 53 | /** 54 | * Builds the url to make the request from. 55 | * @return The constructed url. 56 | */ 57 | buildFullUrl(): string { 58 | let absoluteUrl = /^([a-z][a-z0-9+\-.]*:)?\/\//i; 59 | let url = absoluteUrl.test(this.url) ? this.url : join(this.baseUrl, this.url); 60 | 61 | if (this.params) { 62 | let qs = buildQueryString(this.params, this.traditional); 63 | url = qs ? url + (this.url.indexOf('?') < 0 ? '?' : '&') + qs : url; 64 | } 65 | 66 | return url; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/xhr-transformers.js: -------------------------------------------------------------------------------- 1 | import {PLATFORM} from 'aurelia-pal'; 2 | 3 | /** 4 | * Adds a timeout to the request. 5 | * @param client The http client. 6 | * @param processor The request message processor. 7 | * @param message The request message. 8 | * @param xhr The xhr instance. 9 | */ 10 | export function timeoutTransformer(client: HttpClient, processor: RequestMessageProcessor, message: RequestMessage, xhr: XHR) { 11 | if (message.timeout !== undefined) { 12 | xhr.timeout = message.timeout; 13 | } 14 | } 15 | 16 | /** 17 | * Adds a callback parameter name to the request. 18 | * @param client The http client. 19 | * @param processor The request message processor. 20 | * @param message The request message. 21 | * @param xhr The xhr instance. 22 | */ 23 | export function callbackParameterNameTransformer(client: HttpClient, processor: RequestMessageProcessor, message: RequestMessage, xhr: XHR) { 24 | if (message.callbackParameterName !== undefined) { 25 | xhr.callbackParameterName = message.callbackParameterName; 26 | } 27 | } 28 | 29 | /** 30 | * Sets withCredentials on the request. 31 | * @param client The http client. 32 | * @param processor The request message processor. 33 | * @param message The request message. 34 | * @param xhr The xhr instance. 35 | */ 36 | export function credentialsTransformer(client: HttpClient, processor: RequestMessageProcessor, message: RequestMessage, xhr: XHR) { 37 | if (message.withCredentials !== undefined) { 38 | xhr.withCredentials = message.withCredentials; 39 | } 40 | } 41 | 42 | /** 43 | * Adds an upload.onprogress callback to the request. 44 | * @param client The http client. 45 | * @param processor The request message processor. 46 | * @param message The request message. 47 | * @param xhr The xhr instance. 48 | */ 49 | export function progressTransformer(client: HttpClient, processor: RequestMessageProcessor, message: RequestMessage, xhr: XHR) { 50 | if (message.progressCallback) { 51 | xhr.upload.onprogress = message.progressCallback; 52 | } 53 | } 54 | 55 | /** 56 | * Adds an onprogress callback to the request. 57 | * @param client The http client. 58 | * @param processor The request message processor. 59 | * @param message The request message. 60 | * @param xhr The xhr instance. 61 | */ 62 | export function downloadProgressTransformer(client: HttpClient, processor: RequestMessageProcessor, message: RequestMessage, xhr: XHR) { 63 | if (message.downloadProgressCallback) { 64 | xhr.onprogress = message.downloadProgressCallback; 65 | } 66 | } 67 | 68 | /** 69 | * Adds a response type transformer to the request. 70 | * @param client The http client. 71 | * @param processor The request message processor. 72 | * @param message The request message. 73 | * @param xhr The xhr instance. 74 | */ 75 | export function responseTypeTransformer(client: HttpClient, processor: RequestMessageProcessor, message: RequestMessage, xhr: XHR) { 76 | let responseType = message.responseType; 77 | 78 | if (responseType === 'json') { 79 | responseType = 'text'; //IE does not support json 80 | } 81 | 82 | xhr.responseType = responseType; 83 | } 84 | 85 | /** 86 | * Adds headers to the request. 87 | * @param client The http client. 88 | * @param processor The request message processor. 89 | * @param message The request message. 90 | * @param xhr The xhr instance. 91 | */ 92 | export function headerTransformer(client: HttpClient, processor: RequestMessageProcessor, message: RequestMessage, xhr: XHR) { 93 | message.headers.configureXHR(xhr); 94 | } 95 | 96 | /** 97 | * Transforms the content of the request. 98 | * @param client The http client. 99 | * @param processor The request message processor. 100 | * @param message The request message. 101 | * @param xhr The xhr instance. 102 | */ 103 | export function contentTransformer(client: HttpClient, processor: RequestMessageProcessor, message: RequestMessage, xhr: XHR) { 104 | if (message.skipContentProcessing) { 105 | return; 106 | } 107 | 108 | if (PLATFORM.global.FormData && message.content instanceof FormData) { 109 | return; 110 | } 111 | 112 | if (PLATFORM.global.Blob && message.content instanceof Blob) { 113 | return; 114 | } 115 | 116 | if (PLATFORM.global.ArrayBuffer && message.content instanceof ArrayBuffer) { 117 | return; 118 | } 119 | 120 | if (message.content instanceof Document) { 121 | return; 122 | } 123 | 124 | if (typeof message.content === 'string') { 125 | return; 126 | } 127 | 128 | if (message.content === null || message.content === undefined) { 129 | return; 130 | } 131 | 132 | message.content = JSON.stringify(message.content, message.replacer); 133 | 134 | if (!message.headers.has('Content-Type')) { 135 | message.headers.add('Content-Type', 'application/json'); 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /test/headers.spec.js: -------------------------------------------------------------------------------- 1 | import './setup'; 2 | import {Headers} from '../src/headers'; 3 | 4 | describe('headers', () => { 5 | it('can add header value', () => { 6 | var headers = new Headers(); 7 | headers.add('Authorization', '123'); 8 | 9 | expect(headers.headers['authorization'].value).toBe('123'); 10 | }); 11 | 12 | it('can get header value', () => { 13 | var headers = new Headers(); 14 | headers.add('Authorization', '123'); 15 | // The header should be resolved case insensitively 16 | expect(headers.get('Authorization')).toBe('123'); 17 | expect(headers.get('aUtHorIzaTion')).toBe('123'); 18 | }); 19 | 20 | it('will clear headers on clear', () => { 21 | var headers = new Headers(); 22 | headers.add('Authorization', '123'); 23 | 24 | expect(headers.get('Authorization')).toBe('123'); 25 | expect(Object.keys(headers.headers).length).toBe(1); 26 | 27 | headers.clear(); 28 | 29 | expect(headers.get('Authorization')).toBeUndefined(); 30 | expect(headers.get('AUthoRIZatioN')).toBeUndefined(); 31 | expect(headers.headers).toEqual({}); 32 | }); 33 | 34 | it('configureXHR should add the headers', () => { 35 | var headers = new Headers(); 36 | headers.add('Authorization', '123'); 37 | headers.add('Content-Type', 'application/json'); 38 | headers.add('ETag', 'some-value'); 39 | 40 | jasmine.Ajax.withMock(() => { 41 | var xhr = new XMLHttpRequest(); 42 | //headers.configureXHR works only if xhr is in Open state 43 | xhr.open('test', 'http://dummy.com', true); 44 | headers.configureXHR(xhr); 45 | 46 | expect(xhr.requestHeaders['Authorization']).toBe('123'); 47 | expect(xhr.requestHeaders['Content-Type']).toBe('application/json'); 48 | expect(xhr.requestHeaders['ETag']).toBe('some-value'); 49 | }); 50 | }); 51 | 52 | describe("parse()", () => { 53 | it("should return a new instance on undefined, null or empty string", () => { 54 | expect(Headers.parse()).toEqual(jasmine.any(Headers)); 55 | expect(Headers.parse(null)).toEqual(jasmine.any(Headers)); 56 | expect(Headers.parse("")).toEqual(jasmine.any(Headers)); 57 | }); 58 | 59 | it("should parse header strings", () => { 60 | let headers = Headers.parse('key1: value1\u000d\u000akey2: value2'); 61 | 62 | expect(headers.get('key1')).toBe('value1'); 63 | expect(headers.get('key2')).toBe('value2'); 64 | }); 65 | 66 | it("should parse headers with values of ': '", () => { 67 | let headers = Headers.parse('key1: value1\u000d\u000akey2: key: value'); 68 | 69 | expect(headers.get('key1')).toBe('value1'); 70 | expect(headers.get('key2')).toBe('key: value'); 71 | }); 72 | }); 73 | }); 74 | -------------------------------------------------------------------------------- /test/http-request-message.spec.js: -------------------------------------------------------------------------------- 1 | import './setup'; 2 | import {HttpRequestMessage, createHttpRequestMessageProcessor} from '../src/http-request-message'; 3 | import {Headers} from '../src/headers'; 4 | import {RequestMessageProcessor} from '../src/request-message-processor'; 5 | import { 6 | timeoutTransformer, 7 | credentialsTransformer, 8 | progressTransformer, 9 | responseTypeTransformer, 10 | headerTransformer, 11 | contentTransformer 12 | } from '../src/xhr-transformers'; 13 | 14 | describe("http-request-message", () => { 15 | it("should have a constructor that takes in the method, url, content and headers", () => { 16 | let method = {}, url = {}, content = {}, headers = {}; 17 | let httpRequestMessage = new HttpRequestMessage(method, url, content, headers); 18 | 19 | expect(httpRequestMessage.method).toBe(method); 20 | expect(httpRequestMessage.url).toBe(url); 21 | expect(httpRequestMessage.content).toBe(content); 22 | expect(httpRequestMessage.headers).toBe(headers); 23 | expect(httpRequestMessage.responseType).toBe('json'); 24 | }); 25 | 26 | it("have a constructor should default the headers if not provided", () => { 27 | let method = {}, url = {}, content = {}; 28 | let httpRequestMessage = new HttpRequestMessage(method, url, content); 29 | expect(httpRequestMessage.headers).toEqual(jasmine.any(Headers)); 30 | }); 31 | 32 | describe("createHttpRequestMessageProcessor",() => { 33 | it("should create a RequestMessageProcessor with an XMLHttpRequest and the correct xhrTransformers", () => { 34 | let httpProcessor = createHttpRequestMessageProcessor(); 35 | 36 | expect(httpProcessor).toEqual(jasmine.any(RequestMessageProcessor)); 37 | expect(httpProcessor.XHRType).toBe(XMLHttpRequest); 38 | expect(httpProcessor.xhrTransformers).toContain(timeoutTransformer); 39 | expect(httpProcessor.xhrTransformers).toContain(credentialsTransformer); 40 | expect(httpProcessor.xhrTransformers).toContain(progressTransformer); 41 | expect(httpProcessor.xhrTransformers).toContain(responseTypeTransformer); 42 | expect(httpProcessor.xhrTransformers).toContain(headerTransformer); 43 | expect(httpProcessor.xhrTransformers).toContain(contentTransformer); 44 | }); 45 | }); 46 | 47 | }); 48 | -------------------------------------------------------------------------------- /test/http-response-message.spec.js: -------------------------------------------------------------------------------- 1 | import './setup'; 2 | import {HttpResponseMessage, mimeTypes} from '../src/http-response-message'; 3 | import {Headers} from '../src/headers'; 4 | 5 | describe("HttpResponseMessage", () => { 6 | describe("constructor", () => { 7 | it("should have a isSuccess defined by the xhr.status", () => { 8 | //1xx informal 9 | expect(new HttpResponseMessage({}, {status: 100}).isSuccess).toBeFalsy(); 10 | expect(new HttpResponseMessage({}, {status: 199}).isSuccess).toBeFalsy(); 11 | 12 | //2xx success 13 | expect(new HttpResponseMessage({}, {status: 200}).isSuccess).toBeTruthy(); 14 | expect(new HttpResponseMessage({}, {status: 299}).isSuccess).toBeTruthy(); 15 | 16 | //3xx redirection 17 | expect(new HttpResponseMessage({}, {status: 300}).isSuccess).toBeTruthy(); 18 | expect(new HttpResponseMessage({}, {status: 399}).isSuccess).toBeTruthy(); 19 | 20 | //4xx client error 21 | expect(new HttpResponseMessage({}, {status: 400}).isSuccess).toBeFalsy(); 22 | expect(new HttpResponseMessage({}, {status: 499}).isSuccess).toBeFalsy(); 23 | 24 | //5xx server error 25 | expect(new HttpResponseMessage({}, {status: 400}).isSuccess).toBeFalsy(); 26 | expect(new HttpResponseMessage({}, {status: 499}).isSuccess).toBeFalsy(); 27 | }); 28 | 29 | //This may seem superfluous but it's surprising how many bugs stem from constructors no longer working as expected 30 | it("have the xhr.status, xhr.response, xhr.statusText, responseType, reviver and headers as fields", () => { 31 | //Everything that can be an object is set to one for reference checking 32 | let xhr = {status: 200, response: {}, statusText: {}}; 33 | let responseType = {}, reviver = {}, requestMessage = {}; 34 | let httpResponse = new HttpResponseMessage(requestMessage, xhr, responseType, reviver); 35 | 36 | expect(httpResponse.requestMessage).toBe(requestMessage); 37 | expect(httpResponse.statusCode).toBe(xhr.status); 38 | expect(httpResponse.response).toBe(xhr.response); 39 | expect(httpResponse.statusText).toBe(xhr.statusText); 40 | expect(httpResponse.responseType).toBe(responseType); 41 | expect(httpResponse.reviver).toBe(reviver); 42 | expect(httpResponse.headers).toEqual(jasmine.any(Headers)); 43 | }); 44 | 45 | it("will call the Headers.parse() if xhr.getAllResponseHeaders() is defined", () => { 46 | let parseSpy = spyOn(Headers, 'parse'); 47 | let xhrMock = jasmine.createSpyObj("xhr", ["getAllResponseHeaders"]); 48 | xhrMock.getAllResponseHeaders.and.returnValue(""); 49 | 50 | new HttpResponseMessage({}, xhrMock); 51 | expect(xhrMock.getAllResponseHeaders).toHaveBeenCalled(); 52 | expect(parseSpy).toHaveBeenCalledWith(""); 53 | }); 54 | 55 | it("will set responseType to an aliased version of the mimetype detected in the Content-Type header, will also store the original mimetype as mimeType", () => { 56 | runContentTypeExpectations([ 57 | {contentType:"text/yml",mimeType:"text/yml",type:"yml"}, 58 | {contentType:"text/xml",mimeType:"text/xml",type:"xml"}, 59 | {contentType:"text/markdown",mimeType:"text/markdown",type:"md"} 60 | ]); 61 | }); 62 | 63 | it("will split the Content-Type using the ; delimeter and use the first part as the mimeType", () => { 64 | runContentTypeExpectations([ 65 | {contentType:"text/html; charset=utf-8",mimeType:"text/html",type:"html"}, 66 | {contentType:"application/atom+xml; type=feed",mimeType:"application/atom+xml",type:"atom"}, 67 | {contentType:"application/json; odata=verbose",mimeType:"application/json",type:"json"} 68 | ]); 69 | }); 70 | 71 | it("will use the mimeType as the responseType if there is no alias for it in the mimeTypes map", () => { 72 | runContentTypeExpectations([ 73 | {contentType:"text/foo",mimeType:"text/foo",type:"text/foo"}, 74 | {contentType:"text/foo; charset=utf-8",mimeType:"text/foo",type:"text/foo"} 75 | ]); 76 | }); 77 | 78 | it("will set responseType to the responseType specified in the request if no Content-Type header was found, mimeType will be null", () => { 79 | runContentTypeExpectations([ 80 | {contentType:undefined,mimeType:null,type:"json",requestType:"json"}, 81 | {contentType:undefined,mimeType:null,type:"html",requestType:"html"}, 82 | {contentType:undefined,mimeType:null,type:"text/html",requestType:"text/html"}, 83 | {contentType:undefined,mimeType:null,type:"something",requestType:"something"} 84 | ]); 85 | }); 86 | 87 | it("will use json if no responseType for the request was null", () => { 88 | runContentTypeExpectations([ 89 | {contentType:undefined,mimeType:null,type:null,requestType:"json"} 90 | ]); 91 | }); 92 | }); 93 | 94 | describe("content", () => { 95 | it("will return _content if defined", () => { 96 | let httpResponse = new HttpResponseMessage(null,{}); 97 | let _content = httpResponse._content = {}; 98 | 99 | expect(httpResponse.content).toBe(_content); 100 | }); 101 | 102 | it("will return undefined if response is undefined", () => { 103 | let httpResponse = new HttpResponseMessage(null, {}); 104 | expect(httpResponse.content).toBeUndefined(); 105 | }); 106 | 107 | it("will return null if response is null", () => { 108 | let httpResponse = new HttpResponseMessage(null, {response: null,responseText:null}); 109 | expect(httpResponse.content).toBeNull(); 110 | }); 111 | 112 | it("will return a blank string if response is blank", () => { 113 | let httpResponse = new HttpResponseMessage(null, {response: '',responseText:''}); 114 | expect(httpResponse.content).toBe(''); 115 | }); 116 | 117 | it("will JSON.parse if the response type is 'json'", () => { 118 | let response = {}, reviver = {}, content = {}; 119 | let parseSpy = spyOn(JSON, 'parse').and.returnValue(content); 120 | let httpResponse = new HttpResponseMessage(null, {response: response}, 'json', reviver); 121 | 122 | expect(httpResponse.content).toBe(content); 123 | expect(parseSpy).toHaveBeenCalledWith(response, reviver); 124 | }); 125 | 126 | it("will call the reviver if the response type is not 'json' and the reviver is defined", () => { 127 | let response = {}; 128 | let reviverSpy = jasmine.createSpy("reviver").and.returnValue(response); 129 | let httpResponse = new HttpResponseMessage(null, {response: response}, 'notJson', reviverSpy); 130 | 131 | expect(httpResponse.content).toBe(response); 132 | expect(reviverSpy).toHaveBeenCalledWith(response); 133 | }); 134 | 135 | it("will return the response if the reviver is not set and the response type is not json", () => { 136 | let response = {}; 137 | let httpResponse = new HttpResponseMessage(null, {response: response}, 'notJson'); 138 | 139 | expect(httpResponse.content).toBe(response); 140 | }); 141 | 142 | it("will catch expections on content if the response was not successful", () => { 143 | let reviverSpy = jasmine.createSpy("reviver").and.throwError(); 144 | let httpResponse = new HttpResponseMessage(null, {status : 404, response : {}}, 'notJson', reviverSpy); 145 | 146 | expect(httpResponse.content).toBeNull(); 147 | expect(reviverSpy).toHaveBeenCalled(); 148 | }); 149 | 150 | it("will throw on content if the response was successful", () => { 151 | let reviverSpy = jasmine.createSpy("reviver").and.throwError(); 152 | let httpResponse = new HttpResponseMessage(null, {status : 200, response : {}}, 'notJson', reviverSpy); 153 | 154 | expect(() => httpResponse.content).toThrow(); 155 | expect(reviverSpy).toHaveBeenCalled(); 156 | }); 157 | }); 158 | }); 159 | 160 | /** 161 | * Run an array of expectations for testing content-type headers and responseTypes 162 | * 163 | * Sets up a request to have a response with a certain Content-Type and checks if the 164 | * response has the correct `responseType` set. 165 | * Will check if the `responseType` has been aliased according to the `mimeTypes` map. 166 | * Will check if the response has the original mimetype stored in the `mimeType` property. 167 | * 168 | * an expectation object should be the following format: 169 | * { 170 | * contentType:"text/html; charset=utf-8", 171 | * mimeType:"text/html", 172 | * type:"html", 173 | * requestType:"json" 174 | * }, 175 | * 176 | * 177 | * With these settings it will set the Content-Type header to "text/html; charset=utf-8" in the mock response, 178 | * and expect it to be resolved to a `mimeType` of "text/html" and aliased with the responseType "html" 179 | * 180 | * The requestType property is optional and will be set as the `responseType` in the request message. 181 | */ 182 | function runContentTypeExpectations(expectations){ 183 | let reviver = {}; 184 | expectations.map((expectation)=>{ 185 | var headers = new Headers(); 186 | //use json as the default `responseType` in the request message 187 | expectation.requestType = expectation.requestType||"json"; 188 | //set a content-type in the response header 189 | if(expectation.contentType) headers.add('Content-Type', expectation.contentType); 190 | 191 | jasmine.Ajax.withMock(() => { 192 | var xhr = new XMLHttpRequest(); 193 | //begin config mock 194 | //headers.configureXHR works only if xhr is in Open state 195 | xhr.open('test', 'http://dummy.com', true); 196 | //patch getAllResponseHeaders to return requestHeaders 197 | //monkey patch getAllResponseHeaders function : rebuild responseheaders from request headers 198 | xhr.getAllResponseHeaders = function () { 199 | if (!this.requestHeaders) { return null; } 200 | 201 | var result = []; 202 | for (var key in this.requestHeaders) { 203 | result.push(key + ': ' + this.requestHeaders[key]); 204 | } 205 | return result.join('\r\n') + '\r\n'; 206 | } 207 | //end config mock 208 | headers.configureXHR(xhr); 209 | 210 | //check if content-type was correctly set in the xhr headers 211 | expect(xhr.requestHeaders['Content-Type']).toBe(expectation.contentType); 212 | 213 | let httpResponse = new HttpResponseMessage(null, xhr, expectation.requestType, reviver); 214 | 215 | //expect mimetype to be converted to it's alias if it is defined in the `mimeTypes` list 216 | if(mimeTypes[expectation.mimeType]) expect(httpResponse.responseType).toBe(expectation.type); 217 | //expect original mimetype to be stored in `mimeType` 218 | expect(httpResponse.mimeType).toBe(expectation.mimeType); 219 | }); 220 | }); 221 | } 222 | -------------------------------------------------------------------------------- /test/jsonp-request-message.spec.js: -------------------------------------------------------------------------------- 1 | import './setup'; 2 | import {JSONPRequestMessage, createJSONPRequestMessageProcessor} from '../src/jsonp-request-message'; 3 | import {Headers} from '../src/headers'; 4 | import {RequestMessageProcessor} from '../src/request-message-processor'; 5 | import { timeoutTransformer, callbackParameterNameTransformer } from '../src/xhr-transformers'; 6 | 7 | describe("JSONPRequestMessage", () => { 8 | it("should have a constructor that correctly sets the methods and response type", () => { 9 | let url = {}, callbackName = {}; 10 | let jsonpRequest = new JSONPRequestMessage(url, callbackName); 11 | expect(jsonpRequest.method).toBe('JSONP'); 12 | expect(jsonpRequest.url).toBe(url); 13 | expect(jsonpRequest.content).toBeUndefined(); 14 | expect(jsonpRequest.headers).toEqual(jasmine.any(Headers)); 15 | expect(jsonpRequest.responseType).toBe('jsonp'); 16 | expect(jsonpRequest.callbackParameterName).toBe(callbackName); 17 | }); 18 | 19 | describe("createJSONPRequestMessageProcessor",() => { 20 | it("should create a RequestMessageProcessor with an JSONPXHR and the correct xhrTransformers", () => { 21 | let httpProcessor = createJSONPRequestMessageProcessor(); 22 | 23 | expect(httpProcessor).toEqual(jasmine.any(RequestMessageProcessor)); 24 | expect(httpProcessor.XHRType).toBeDefined(); 25 | expect(httpProcessor.xhrTransformers).toContain(timeoutTransformer); 26 | expect(httpProcessor.xhrTransformers).toContain(callbackParameterNameTransformer); 27 | }); 28 | }); 29 | 30 | //TODO : Will have to create a jsonp preprocessor for karma to test an actual request 31 | }); 32 | -------------------------------------------------------------------------------- /test/request-builder.spec.js: -------------------------------------------------------------------------------- 1 | import './setup'; 2 | import {RequestBuilder} from '../src/request-builder'; 3 | import {HttpClient} from '../src/http-client'; 4 | 5 | describe('request builder', () => { 6 | var requestBuilder; 7 | 8 | beforeEach(() => { 9 | var client = new HttpClient(); 10 | requestBuilder = new RequestBuilder(client); 11 | }); 12 | 13 | describe('add helper', () => { 14 | it('adds a function prototype to the request builder class', () => { 15 | RequestBuilder.addHelper('helper1', () => { }); 16 | expect(typeof requestBuilder.helper1).toBe('function'); 17 | }); 18 | }); 19 | 20 | describe('helper method', () => { 21 | it('is called with the given argument', () => { 22 | var helper2 = jasmine.createSpy('helper2'); 23 | RequestBuilder.addHelper('helper2', helper2); 24 | requestBuilder.helper2('testarg'); 25 | expect(helper2).toHaveBeenCalledWith('testarg'); 26 | }); 27 | 28 | it('has it\'s return value appended to transformers', () => { 29 | var transformerFunction = new Function(); 30 | RequestBuilder.addHelper('helper3', () => { return transformerFunction }); 31 | requestBuilder.helper3(); 32 | expect(requestBuilder.transformers).toContain(transformerFunction); 33 | }); 34 | }); 35 | }); 36 | -------------------------------------------------------------------------------- /test/request-message-processor.spec.js: -------------------------------------------------------------------------------- 1 | import './setup'; 2 | import { RequestMessageProcessor } from '../src/request-message-processor'; 3 | import { HttpResponseMessage } from '../src/http-response-message'; 4 | import { ErrorHttpResponseMessage } from '../src/error-http-response-message'; 5 | import { RequestMessage } from '../src/request-message'; 6 | import { PLATFORM } from 'aurelia-pal'; 7 | 8 | describe("Request message processor", () => { 9 | it("constructor() correctly setup the xhrType and the xhrTransformers", () => { 10 | var xhrType = {}; 11 | var xhrTransformers = {}; 12 | var processor = new RequestMessageProcessor(xhrType, xhrTransformers); 13 | 14 | expect(processor.XHRType).toBe(xhrType); 15 | expect(processor.xhrTransformers).toBe(xhrTransformers); 16 | }); 17 | 18 | it("abort() tell the request to abort", () => { 19 | var processor = new RequestMessageProcessor(); 20 | let xhr = processor.xhr = jasmine.createSpyObj('xhr', ['abort']); 21 | processor.abort(); 22 | expect(xhr.abort).toHaveBeenCalled(); 23 | }); 24 | 25 | it("abort() won't throw if the request hasn't started yet", () => { 26 | var processor = new RequestMessageProcessor(); 27 | expect(() => processor.abort()).not.toThrow(); 28 | }); 29 | 30 | describe("process()", () => { 31 | let openSpy, sendSpy, xhrTransformers, reqProcessor, message, client; 32 | 33 | class MockXhrType { 34 | constructor() { 35 | this.open = openSpy = jasmine.createSpy("open"); 36 | this.send = sendSpy = jasmine.createSpy("send"); 37 | } 38 | 39 | fakeResponse(status = 200, statusText, response) { 40 | this.status = status; 41 | this.statusText = statusText; 42 | this.response = response; 43 | this.onload(); 44 | } 45 | } 46 | 47 | 48 | beforeEach(() => { 49 | xhrTransformers = []; 50 | reqProcessor = new RequestMessageProcessor(MockXhrType, xhrTransformers); 51 | message = new RequestMessage('get', 'some', {}); 52 | message.responseType = "test"; 53 | client = {}; 54 | }); 55 | 56 | it("should create a new instance of the XHRType", () => { 57 | reqProcessor.process(client, message); 58 | expect(reqProcessor.xhr).toEqual(jasmine.any(MockXhrType)); 59 | }); 60 | 61 | // promise only resolves 62 | it("should call xhr.open with the method, full url and ajax set to true", (done) => { 63 | reqProcessor.process(client, message).then(() => { 64 | expect(openSpy).toHaveBeenCalledWith(message.method, message.url, true, undefined, undefined); 65 | done(); 66 | }); 67 | reqProcessor.xhr.fakeResponse(); 68 | }); 69 | 70 | it("should call xhr.send with the message content", (done) => { 71 | reqProcessor.process(client, message).then(() => { 72 | expect(sendSpy).toHaveBeenCalledWith(message.content); 73 | done(); 74 | }); 75 | reqProcessor.xhr.fakeResponse(); 76 | }); 77 | 78 | it("should run through all the xhr transformers", (done) => { 79 | let transformSpy = jasmine.createSpy("transformSpy"); 80 | reqProcessor.xhrTransformers.push(transformSpy); 81 | reqProcessor.xhrTransformers.push(transformSpy); 82 | reqProcessor.process(client, message).then(() => { 83 | expect(transformSpy).toHaveBeenCalledWith(client, reqProcessor, message, reqProcessor.xhr); 84 | expect(transformSpy.calls.count()).toBe(2); 85 | done(); 86 | }); 87 | reqProcessor.xhr.fakeResponse(); 88 | }); 89 | 90 | //The next couple of functions are breaking the idea of a unit test and treading 91 | //into integration tests but spying on the constructor of HttpResponseMessage isn't possible 92 | it("will resolve if the onload response is successful", (done) => { 93 | let responseObj = {}; 94 | 95 | reqProcessor.process(client, message) 96 | .then((response) => { 97 | expect(response).toEqual(jasmine.any(HttpResponseMessage)); 98 | expect(response.requestMessage).toBe(message); 99 | expect(response.statusCode).toBe(200); 100 | expect(response.response).toBe(responseObj); 101 | expect(response.responseType).toBe("test"); 102 | expect(response.statusText).toBe("status test"); 103 | expect(response.reviver).toBe(message.reviver); 104 | }) 105 | .catch(() => expect(false).toBeTruthy("Should have failed")) 106 | .then(done); 107 | 108 | reqProcessor.xhr.fakeResponse(200, "status test", responseObj) 109 | }); 110 | 111 | //test boilerplate 112 | let itHelper: (testMsg: string, rejectPromiseWithErrorObjectOption?: boolean) => (testMsg: string, done: () => void) => void; 113 | //end 114 | 115 | itHelper = (testMsg: string, rejectPromiseWithErrorObjectOption?: boolean) => { 116 | it(testMsg, (done) => { 117 | let responseObj = {}; 118 | let clientCpy = Object.assign({}, client, { rejectPromiseWithErrorObject: rejectPromiseWithErrorObjectOption }); 119 | reqProcessor.process(clientCpy, message) 120 | .then((response) => expect(false).toBeTruthy("This should have failed")) 121 | .catch((response) => { 122 | expect(response).toEqual(jasmine.any(HttpResponseMessage)); 123 | if (rejectPromiseWithErrorObjectOption) { 124 | expect(response).toEqual(jasmine.any(ErrorHttpResponseMessage)); 125 | } 126 | expect(response.requestMessage).toBe(message); 127 | expect(response.statusCode).toBe(401); 128 | expect(response.response).toBe(responseObj); 129 | expect(response.responseType).toBe("test"); 130 | expect(response.statusText).toBe("status test"); 131 | expect(response.reviver).toBe(message.reviver); 132 | }) 133 | .then(done); 134 | 135 | reqProcessor.xhr.fakeResponse(401, "status test", responseObj); 136 | }); 137 | }; 138 | itHelper("will reject if the onload response has failed"); 139 | itHelper("will reject if the onload response has failed with rejectPromiseWithErrorObject option", true); 140 | 141 | itHelper = (testMsg: string, rejectPromiseWithErrorObjectOption?: boolean) => { 142 | it(testMsg, (done) => { 143 | 144 | let errorResponse = {}; 145 | let clientCpy = Object.assign({}, client, { rejectPromiseWithErrorObject: rejectPromiseWithErrorObjectOption }); 146 | reqProcessor.process(clientCpy, message) 147 | .then((response) => expect(false).toBeTruthy("This should have failed")) 148 | .catch((response) => { 149 | expect(response).toEqual(jasmine.any(HttpResponseMessage)); 150 | if (rejectPromiseWithErrorObjectOption) { 151 | expect(response).toEqual(jasmine.any(ErrorHttpResponseMessage)); 152 | } 153 | expect(response.requestMessage).toBe(message); 154 | expect(response.response).toBe(errorResponse); 155 | expect(response.responseType).toBe("timeout"); 156 | }) 157 | .then(done); 158 | 159 | let xhr = reqProcessor.xhr; 160 | xhr.ontimeout(errorResponse); 161 | }); 162 | }; 163 | itHelper("will reject if the ontimeout was called"); 164 | itHelper("will reject if the ontimeout was called with rejectPromiseWithErrorObject option", true); 165 | 166 | itHelper = (testMsg: string, rejectPromiseWithErrorObjectOption?: boolean) => { 167 | it(testMsg, (done) => { 168 | 169 | let errorResponse = {}; 170 | let clientCpy = Object.assign({}, client, { rejectPromiseWithErrorObject: rejectPromiseWithErrorObjectOption }); 171 | reqProcessor.process(clientCpy, message) 172 | .then((response) => expect(false).toBeTruthy("This should have failed")) 173 | .catch((response) => { 174 | expect(response).toEqual(jasmine.any(HttpResponseMessage)); 175 | if (rejectPromiseWithErrorObjectOption) { 176 | expect(response).toEqual(jasmine.any(ErrorHttpResponseMessage)); 177 | } 178 | expect(response.requestMessage).toBe(message); 179 | expect(response.response).toBe(errorResponse); 180 | expect(response.responseType).toBe("error"); 181 | }) 182 | .then(done); 183 | 184 | let xhr = reqProcessor.xhr; 185 | xhr.onerror(errorResponse); 186 | }); 187 | }; 188 | itHelper("will reject if the onerror was called"); 189 | itHelper("will reject if the onerror was called with rejectPromiseWithErrorObject option", true); 190 | 191 | itHelper = (testMsg: string, rejectPromiseWithErrorObjectOption?: boolean) => { 192 | it(testMsg, (done) => { 193 | let errorResponse = {}; 194 | let clientCpy = Object.assign({}, client, { rejectPromiseWithErrorObject: rejectPromiseWithErrorObjectOption }); 195 | reqProcessor.process(clientCpy, message) 196 | .then((response) => expect(false).toBeTruthy("This should have failed")) 197 | .catch((response) => { 198 | expect(response).toEqual(jasmine.any(HttpResponseMessage)); 199 | if (rejectPromiseWithErrorObjectOption) { 200 | expect(response).toEqual(jasmine.any(ErrorHttpResponseMessage)); 201 | } 202 | expect(response.requestMessage).toBe(message); 203 | expect(response.response).toBe(errorResponse); 204 | expect(response.responseType).toBe("abort"); 205 | }) 206 | .then(done); 207 | 208 | let xhr = reqProcessor.xhr; 209 | xhr.status = 200; 210 | xhr.onabort(errorResponse); 211 | }); 212 | }; 213 | itHelper("will reject if the onabort was called"); 214 | itHelper("will reject if the onabort was called with rejectPromiseWithErrorObject option", true); 215 | 216 | it('applies xhr transformers after calling request interceptors', (done) => { 217 | class RequestInterceptor { 218 | request(message) { 219 | return message; 220 | } 221 | } 222 | 223 | var interceptor = new RequestInterceptor(); 224 | spyOn(interceptor, 'request').and.callThrough(); 225 | 226 | function mockTransformer() { 227 | expect(interceptor.request).toHaveBeenCalled(); 228 | } 229 | 230 | message.interceptors = [interceptor] 231 | reqProcessor.xhrTransformers.push(mockTransformer); 232 | reqProcessor.process(client, message).then((response) => { done() }); 233 | reqProcessor.xhr.fakeResponse(); 234 | }); 235 | }); 236 | }); 237 | -------------------------------------------------------------------------------- /test/request-message.spec.js: -------------------------------------------------------------------------------- 1 | import './setup'; 2 | import {RequestMessage} from '../src/request-message'; 3 | 4 | describe('RequestMessage', () => { 5 | describe('buildFullUrl', () => { 6 | var message; 7 | beforeEach(() => { 8 | message = new RequestMessage('get', '/anotherurl'); 9 | message.baseUrl = 'example.com'; 10 | }); 11 | 12 | it('combines baseUrl and url', () => { 13 | expect(message.buildFullUrl()).toBe('example.com/anotherurl'); 14 | }); 15 | 16 | it('appends query parameters', () => { 17 | message.params = { 'a': 'this', 'b': 'that' }; 18 | expect(message.buildFullUrl()).toBe('example.com/anotherurl?a=this&b=that'); 19 | }); 20 | 21 | it('build query parameters as traditional style', () => { 22 | message.traditional = true; 23 | message.params = { 'a': [ 'this', 'that' ] }; 24 | expect(message.buildFullUrl()).toBe('example.com/anotherurl?a=this&a=that'); 25 | }); 26 | 27 | it('build query parameters as not traditional style', () => { 28 | message.traditional = false; 29 | message.params = { 'a': [ 'this', 'that' ] }; 30 | expect(message.buildFullUrl()).toBe('example.com/anotherurl?a%5B%5D=this&a%5B%5D=that'); 31 | }); 32 | }); 33 | }); 34 | -------------------------------------------------------------------------------- /test/setup.js: -------------------------------------------------------------------------------- 1 | import {initialize} from 'aurelia-pal-browser'; 2 | initialize(); -------------------------------------------------------------------------------- /test/xhr-transformers.spec.js: -------------------------------------------------------------------------------- 1 | import './setup'; 2 | import * as XhrTransforms from '../src/xhr-transformers'; 3 | import {Headers} from '../src/headers'; 4 | 5 | describe('transformers', () => { 6 | it("timeout should set the xhr timeout to the message timeout if defined in the message", () => { 7 | let xhr = {}; 8 | 9 | XhrTransforms.timeoutTransformer(null, null, {}, xhr); 10 | expect(xhr.timeout).toBeUndefined(); 11 | 12 | XhrTransforms.timeoutTransformer(null, null, {timeout: 200}, xhr); 13 | expect(xhr.timeout).toBe(200); 14 | }); 15 | 16 | it("callbackParameterNameTransformer should set the xhr.callbackParameterName if defined in the message", () => { 17 | let xhr = {}; 18 | 19 | XhrTransforms.callbackParameterNameTransformer(null, null, {}, xhr); 20 | expect(xhr.callbackParameterName).toBeUndefined(); 21 | 22 | XhrTransforms.callbackParameterNameTransformer(null, null, {callbackParameterName: 'foo'}, xhr); 23 | expect(xhr.callbackParameterName).toBe('foo'); 24 | }); 25 | 26 | it("credentialsTransformer should set the xhr.withCredentials if defined in the message", () => { 27 | let xhr = {}, credentials = {}; 28 | 29 | XhrTransforms.credentialsTransformer(null, null, {}, xhr); 30 | expect(xhr.withCredentials).toBeUndefined(); 31 | 32 | XhrTransforms.credentialsTransformer(null, null, {withCredentials: credentials}, xhr); 33 | expect(xhr.withCredentials).toBe(credentials); 34 | }); 35 | 36 | 37 | it("responseTypeTransformer should change the responseType to text when it's json", () => { 38 | let xhr = {upload: {}}, 39 | progressCallback = {}; 40 | 41 | XhrTransforms.progressTransformer(null, null, {}, xhr); 42 | expect(xhr.upload.onprogress).toBeUndefined(); 43 | 44 | XhrTransforms.progressTransformer(null, null, {progressCallback: progressCallback}, xhr); 45 | expect(xhr.upload.onprogress).toBe(progressCallback); 46 | }); 47 | 48 | it("contentTransformer should set the Content-Type header to application/json when it serializes the content to JSON", () => { 49 | let message = {headers: new Headers()}; 50 | 51 | XhrTransforms.contentTransformer(null, null, message, {}); 52 | expect(message.headers.get("Content-Type")).toBeUndefined(); 53 | 54 | message.content = "test"; 55 | XhrTransforms.contentTransformer(null, null, message, {}); 56 | expect(message.headers.get("Content-Type")).toBeUndefined(); 57 | 58 | message.content = {test:"content"}; 59 | message.headers.add("Content-Type", "text/test"); 60 | XhrTransforms.contentTransformer(null, null, message, {}); 61 | expect(message.headers.get("Content-Type")).toBe("text/test"); 62 | 63 | message.headers.clear(); 64 | message.content = {test:"content"}; 65 | XhrTransforms.contentTransformer(null, null, message, {}); 66 | expect(message.headers.get("Content-Type")).toBe("application/json"); 67 | }); 68 | }); 69 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2015", 4 | "module": "es2015", 5 | "experimentalDecorators": true, 6 | "emitDecoratorMetadata": false, 7 | "moduleResolution": "node", 8 | "stripInternal": true, 9 | "preserveConstEnums": true, 10 | "listFiles": true, 11 | "declaration": true, 12 | "removeComments": true, 13 | "lib": ["es2015", "dom"] 14 | }, 15 | "exclude": [ 16 | "node_modules", 17 | "dist", 18 | "build", 19 | "doc", 20 | "test", 21 | "config.js", 22 | "gulpfile.js", 23 | "karma.conf.js" 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /typings.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "aurelia-http-client", 3 | "main": "dist/aurelia-http-client.d.ts" 4 | } 5 | --------------------------------------------------------------------------------