├── .prettierignore
├── .eslintignore
├── services
├── service-helpers
│ ├── __tests__
│ │ ├── fixtures
│ │ │ └── foo.txt
│ │ ├── validator-browser.test.js
│ │ └── validator.test.js
│ ├── object-clean.js
│ ├── object-map.js
│ ├── stringify-booleans.js
│ ├── generic-types.js
│ ├── create-service-factory.js
│ ├── pick.js
│ └── validator.js
├── __tests__
│ ├── tilequery.test.js
│ ├── uploads.test.js
│ ├── geocoding.test.js
│ ├── matrix.test.js
│ ├── geocoding-v6.test.js
│ ├── directions.test.js
│ ├── map-matching.test.js
│ ├── isochrone.test.js
│ ├── optimization.test.js
│ └── datasets.test.js
├── tilequery.js
├── matrix.js
├── isochrone.js
├── uploads.js
├── optimization.js
├── map-matching.js
├── tokens.js
└── geocoding.js
├── .gitignore
├── lib
├── browser
│ ├── .eslintrc.json
│ ├── browser-client.js
│ ├── __tests__
│ │ └── browser-layer.test.js
│ └── browser-layer.js
├── node
│ ├── .eslintrc.json
│ ├── node-client.js
│ └── node-layer.js
├── client.js
├── constants.js
├── helpers
│ ├── parse-headers.js
│ ├── __tests__
│ │ ├── parse-headers.test.js
│ │ ├── url-utils.test.js
│ │ └── parse-link-header.test.js
│ ├── parse-link-header.js
│ └── url-utils.js
└── classes
│ ├── mapi-client.js
│ ├── mapi-response.js
│ ├── mapi-error.js
│ └── __tests__
│ ├── mapi-client.test.js
│ ├── mapi-response.test.js
│ └── mapi-error.test.js
├── index.js
├── test
├── try-browser
│ ├── .eslintrc.json
│ ├── index.html
│ └── try-browser.js
├── bundle.test.js
├── browser-interface.test.js
├── try-node.js
├── node-interface.test.js
└── test-utils.js
├── conf
├── classes-documentation.yml
└── service-documentation.yml
├── .travis.yml
├── rollup.config.js
├── .eslintrc.json
├── LICENSE
├── bundle.js
├── CODE_OF_CONDUCT.md
├── docs
├── development.md
└── classes.md
├── package.json
├── CHANGELOG.md
└── README.md
/.prettierignore:
--------------------------------------------------------------------------------
1 | .eslintignore
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | coverage
2 | umd
3 |
--------------------------------------------------------------------------------
/services/service-helpers/__tests__/fixtures/foo.txt:
--------------------------------------------------------------------------------
1 | foo
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | coverage
3 | umd
4 | .DS_Store
5 | .nyc_output
6 |
--------------------------------------------------------------------------------
/lib/browser/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "browser": true,
4 | "node": false
5 | }
6 | }
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var client = require('./lib/client');
4 |
5 | module.exports = client;
6 |
--------------------------------------------------------------------------------
/test/try-browser/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "browser": true,
4 | "node": false
5 | }
6 | }
--------------------------------------------------------------------------------
/conf/classes-documentation.yml:
--------------------------------------------------------------------------------
1 | toc:
2 | - MapiRequest
3 | - MapiResponse
4 | - MapiError
5 | - MapiClient
6 |
--------------------------------------------------------------------------------
/lib/node/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "node": true,
4 | "browser": false,
5 | "es6": true
6 | }
7 | }
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | dist: focal
2 | language: node_js
3 | node_js:
4 | - lts/*
5 | cache:
6 | directories:
7 | - node_modules
8 | script:
9 | - npm run test
10 | - npm run bundle
11 |
--------------------------------------------------------------------------------
/lib/client.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | // The "browser" field in "package.json" instructs browser
4 | // bundlers to override this an load browser/browser-client, instead.
5 | var nodeClient = require('./node/node-client');
6 |
7 | module.exports = nodeClient;
8 |
--------------------------------------------------------------------------------
/services/service-helpers/object-clean.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var pick = require('./pick');
4 |
5 | function objectClean(obj) {
6 | return pick(obj, function(_, val) {
7 | return val != null;
8 | });
9 | }
10 |
11 | module.exports = objectClean;
12 |
--------------------------------------------------------------------------------
/services/service-helpers/object-map.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | function objectMap(obj, cb) {
4 | return Object.keys(obj).reduce(function(result, key) {
5 | result[key] = cb(key, obj[key]);
6 | return result;
7 | }, {});
8 | }
9 |
10 | module.exports = objectMap;
11 |
--------------------------------------------------------------------------------
/lib/constants.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = {
4 | API_ORIGIN: 'https://api.mapbox.com',
5 | EVENT_PROGRESS_DOWNLOAD: 'downloadProgress',
6 | EVENT_PROGRESS_UPLOAD: 'uploadProgress',
7 | EVENT_ERROR: 'error',
8 | EVENT_RESPONSE: 'response',
9 | ERROR_HTTP: 'HttpError',
10 | ERROR_REQUEST_ABORTED: 'RequestAbortedError'
11 | };
12 |
--------------------------------------------------------------------------------
/services/service-helpers/stringify-booleans.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var objectMap = require('./object-map');
4 |
5 | /**
6 | * Stringify all the boolean values in an object, so true becomes "true".
7 | *
8 | * @param {Object} obj
9 | * @returns {Object}
10 | */
11 | function stringifyBoolean(obj) {
12 | return objectMap(obj, function(_, value) {
13 | return typeof value === 'boolean' ? JSON.stringify(value) : value;
14 | });
15 | }
16 |
17 | module.exports = stringifyBoolean;
18 |
--------------------------------------------------------------------------------
/rollup.config.js:
--------------------------------------------------------------------------------
1 | /* eslint-env node */
2 | 'use strict';
3 |
4 | var path = require('path');
5 | var commonjs = require('rollup-plugin-commonjs');
6 | var nodeResolve = require('rollup-plugin-node-resolve');
7 |
8 | module.exports = {
9 | input: path.join(__dirname, './bundle.js'),
10 | output: {
11 | file: path.join(__dirname, './umd/mapbox-sdk.js'),
12 | format: 'umd',
13 | name: 'mapboxSdk'
14 | },
15 | plugins: [
16 | nodeResolve({
17 | browser: true
18 | }),
19 | commonjs()
20 | ]
21 | };
22 |
--------------------------------------------------------------------------------
/services/service-helpers/generic-types.js:
--------------------------------------------------------------------------------
1 | /**
2 | * `[longitude, latitude]`
3 | *
4 | * @typedef {Array} Coordinates
5 | */
6 |
7 | /**
8 | * `[minLongitude, minLatitude, maxLongitude, maxLatitude]`
9 | *
10 | * @typedef {Array} BoundingBox
11 | */
12 |
13 | /**
14 | * In Node, files must be `ReadableStream`s or paths pointing for the file in the filesystem.
15 | *
16 | * In the browser, files must be `Blob`s or `ArrayBuffer`s.
17 | *
18 | * @typedef {Blob|ArrayBuffer|string|ReadableStream} UploadableFile
19 | */
20 |
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "eslint:recommended",
3 | "plugins": ["node"],
4 | "env": {
5 | "commonjs": true,
6 | "es6": false
7 | },
8 | "globals": {
9 | "Promise": true
10 | },
11 | "parserOptions": {
12 | "sourceType": "script"
13 | },
14 | "rules": {
15 | "strict": ["error"],
16 | "eqeqeq": ["error", "smart"],
17 | "node/no-unsupported-features": ["error"],
18 | "node/no-missing-require": "error"
19 | },
20 | "overrides": [
21 | {
22 | "files": ["test/*.js", "**/__tests__/*.js"],
23 | "env": {
24 | "jest": true,
25 | "es6": true,
26 | "node": true
27 | }
28 | }
29 | ]
30 | }
31 |
--------------------------------------------------------------------------------
/conf/service-documentation.yml:
--------------------------------------------------------------------------------
1 | toc:
2 | - Styles
3 | - Static
4 | - Uploads
5 | - Datasets
6 | - Tilequery
7 | - Tilesets
8 | - Geocoding
9 | - Directions
10 | - MapMatching
11 | - Matrix
12 | - Optimization
13 | - Tokens
14 | - name: Data structures
15 | description: |
16 | Data structures used in service method configuration.
17 | children:
18 | - DirectionsWaypoint
19 | - MapMatchingPoint
20 | - MatrixPoint
21 | - OptimizationWaypoint
22 | - SimpleMarkerOverlay
23 | - CustomMarkerOverlay
24 | - PathOverlay
25 | - GeoJsonOverlay
26 | - UploadableFile
27 | - Coordinates
28 | - BoundingBox
29 |
--------------------------------------------------------------------------------
/services/service-helpers/create-service-factory.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var MapiClient = require('../../lib/classes/mapi-client');
4 | // This will create the environment-appropriate client.
5 | var createClient = require('../../lib/client');
6 |
7 | function createServiceFactory(ServicePrototype) {
8 | return function(clientOrConfig) {
9 | var client;
10 | if (MapiClient.prototype.isPrototypeOf(clientOrConfig)) {
11 | client = clientOrConfig;
12 | } else {
13 | client = createClient(clientOrConfig);
14 | }
15 | var service = Object.create(ServicePrototype);
16 | service.client = client;
17 | return service;
18 | };
19 | }
20 |
21 | module.exports = createServiceFactory;
22 |
--------------------------------------------------------------------------------
/lib/node/node-client.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var node = require('./node-layer');
4 | var MapiClient = require('../classes/mapi-client');
5 |
6 | function NodeClient(options) {
7 | MapiClient.call(this, options);
8 | }
9 | NodeClient.prototype = Object.create(MapiClient.prototype);
10 | NodeClient.prototype.constructor = NodeClient;
11 |
12 | NodeClient.prototype.sendRequest = node.nodeSend;
13 | NodeClient.prototype.abortRequest = node.nodeAbort;
14 |
15 | /**
16 | * Create a client for Node.
17 | *
18 | * @param {Object} options
19 | * @param {string} options.accessToken
20 | * @param {string} [options.origin]
21 | * @returns {MapiClient}
22 | */
23 | function createNodeClient(options) {
24 | return new NodeClient(options);
25 | }
26 |
27 | module.exports = createNodeClient;
28 |
--------------------------------------------------------------------------------
/test/bundle.test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const fs = require('fs');
4 | const path = require('path');
5 | const camelcase = require('camelcase');
6 | const mapboxSdk = require('../bundle');
7 |
8 | describe('includes all services', () => {
9 | const services = fs
10 | .readdirSync(path.join(__dirname, '../services'))
11 | .filter(filename => path.extname(filename) === '.js')
12 | .map(filename => camelcase(path.basename(filename, '.js')));
13 |
14 | // Mock token lifted from parse-mapbox-token tests.
15 | const client = mapboxSdk({
16 | accessToken:
17 | 'pk.eyJ1IjoiZmFrZXVzZXIiLCJhIjoicHBvb2xsIn0.sbihZCZJ56-fsFNKHXF8YQ'
18 | });
19 |
20 | services.forEach(service => {
21 | test(`includes ${service}`, () => {
22 | expect(client[service]).toBeTruthy();
23 | });
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/lib/browser/browser-client.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var browser = require('./browser-layer');
4 | var MapiClient = require('../classes/mapi-client');
5 |
6 | function BrowserClient(options) {
7 | MapiClient.call(this, options);
8 | }
9 | BrowserClient.prototype = Object.create(MapiClient.prototype);
10 | BrowserClient.prototype.constructor = BrowserClient;
11 |
12 | BrowserClient.prototype.sendRequest = browser.browserSend;
13 | BrowserClient.prototype.abortRequest = browser.browserAbort;
14 |
15 | /**
16 | * Create a client for the browser.
17 | *
18 | * @param {Object} options
19 | * @param {string} options.accessToken
20 | * @param {string} [options.origin]
21 | * @returns {MapiClient}
22 | */
23 | function createBrowserClient(options) {
24 | return new BrowserClient(options);
25 | }
26 |
27 | module.exports = createBrowserClient;
28 |
--------------------------------------------------------------------------------
/services/service-helpers/pick.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * Create a new object by picking properties off an existing object.
5 | * The second param can be overloaded as a callback for
6 | * more fine grained picking of properties.
7 | * @param {Object} source
8 | * @param {Array|function(string, Object):boolean} keys
9 | * @returns {Object}
10 | */
11 | function pick(source, keys) {
12 | var filter = function(key, val) {
13 | return keys.indexOf(key) !== -1 && val !== undefined;
14 | };
15 |
16 | if (typeof keys === 'function') {
17 | filter = keys;
18 | }
19 |
20 | return Object.keys(source)
21 | .filter(function(key) {
22 | return filter(key, source[key]);
23 | })
24 | .reduce(function(result, key) {
25 | result[key] = source[key];
26 | return result;
27 | }, {});
28 | }
29 |
30 | module.exports = pick;
31 |
--------------------------------------------------------------------------------
/lib/helpers/parse-headers.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | function parseSingleHeader(raw) {
4 | var boundary = raw.indexOf(':');
5 | var name = raw
6 | .substring(0, boundary)
7 | .trim()
8 | .toLowerCase();
9 | var value = raw.substring(boundary + 1).trim();
10 | return {
11 | name: name,
12 | value: value
13 | };
14 | }
15 |
16 | /**
17 | * Parse raw headers into an object with lowercase properties.
18 | * Does not fully parse headings into more complete data structure,
19 | * as larger libraries might do. Also does not deal with duplicate
20 | * headers because Node doesn't seem to deal with those well, so
21 | * we shouldn't let the browser either, for consistency.
22 | *
23 | * @param {string} raw
24 | * @returns {Object}
25 | */
26 | function parseHeaders(raw) {
27 | var headers = {};
28 | if (!raw) {
29 | return headers;
30 | }
31 |
32 | raw
33 | .trim()
34 | .split(/[\r|\n]+/)
35 | .forEach(function(rawHeader) {
36 | var parsed = parseSingleHeader(rawHeader);
37 | headers[parsed.name] = parsed.value;
38 | });
39 |
40 | return headers;
41 | }
42 |
43 | module.exports = parseHeaders;
44 |
--------------------------------------------------------------------------------
/lib/helpers/__tests__/parse-headers.test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const parseHeaders = require('../parse-headers');
4 |
5 | test('works', () => {
6 | const raw = `date: Fri, 08 Dec 2017 21:04:30 GMT
7 | content-encoding: gzip
8 | x-content-type-options: nosniff
9 | server: meinheld/0.6.1
10 | x-frame-options: DENY
11 | content-type: text/html; charset=utf-8
12 | connection: keep-alive
13 |
14 |
15 | strict-transport-security: max-age=63072000
16 | vary: Cookie, Accept-Encoding
17 | content-length: 6502
18 | x-xss-protection: 1; mode=block`;
19 |
20 | expect(parseHeaders(raw)).toEqual({
21 | connection: 'keep-alive',
22 | 'content-encoding': 'gzip',
23 | 'content-length': '6502',
24 | 'content-type': 'text/html; charset=utf-8',
25 | date: 'Fri, 08 Dec 2017 21:04:30 GMT',
26 | server: 'meinheld/0.6.1',
27 | 'strict-transport-security': 'max-age=63072000',
28 | vary: 'Cookie, Accept-Encoding',
29 | 'x-content-type-options': 'nosniff',
30 | 'x-frame-options': 'DENY',
31 | 'x-xss-protection': '1; mode=block'
32 | });
33 | });
34 |
35 | test('given empty input, returns empty object', () => {
36 | expect(parseHeaders()).toEqual({});
37 | expect(parseHeaders(undefined)).toEqual({});
38 | expect(parseHeaders(null)).toEqual({});
39 | expect(parseHeaders('')).toEqual({});
40 | });
41 |
--------------------------------------------------------------------------------
/services/service-helpers/validator.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var xtend = require('xtend');
4 | var v = require('@mapbox/fusspot');
5 |
6 | function file(value) {
7 | // If we're in a browser so Blob is available, the file must be that.
8 | // In Node, however, it could be a filepath or a pipeable (Readable) stream.
9 | if (typeof window !== 'undefined') {
10 | if (value instanceof global.Blob || value instanceof global.ArrayBuffer) {
11 | return;
12 | }
13 | return 'Blob or ArrayBuffer';
14 | }
15 | if (typeof value === 'string' || value.pipe !== undefined) {
16 | return;
17 | }
18 | return 'Filename or Readable stream';
19 | }
20 |
21 | function assertShape(validatorObj, apiName) {
22 | return v.assert(v.strictShape(validatorObj), apiName);
23 | }
24 |
25 | function date(value) {
26 | var msg = 'date';
27 | if (typeof value === 'boolean') {
28 | return msg;
29 | }
30 | try {
31 | var date = new Date(value);
32 | if (date.getTime && isNaN(date.getTime())) {
33 | return msg;
34 | }
35 | } catch (e) {
36 | return msg;
37 | }
38 | }
39 |
40 | function coordinates(value) {
41 | return v.tuple(v.number, v.number)(value);
42 | }
43 |
44 | module.exports = xtend(v, {
45 | file: file,
46 | date: date,
47 | coordinates: coordinates,
48 | assertShape: assertShape
49 | });
50 |
--------------------------------------------------------------------------------
/lib/classes/mapi-client.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var parseToken = require('@mapbox/parse-mapbox-token');
4 | var MapiRequest = require('./mapi-request');
5 | var constants = require('../constants');
6 |
7 | /**
8 | * A low-level Mapbox API client. Use it to create service clients
9 | * that share the same configuration.
10 | *
11 | * Services and `MapiRequest`s use the underlying `MapiClient` to
12 | * determine how to create, send, and abort requests in a way
13 | * that is appropriate to the configuration and environment
14 | * (Node or the browser).
15 | *
16 | * @class MapiClient
17 | * @property {string} accessToken - The Mapbox access token assigned
18 | * to this client.
19 | * @property {string} [origin] - The origin
20 | * to use for API requests. Defaults to https://api.mapbox.com.
21 | */
22 |
23 | function MapiClient(options) {
24 | if (!options || !options.accessToken) {
25 | throw new Error('Cannot create a client without an access token');
26 | }
27 | // Try parsing the access token to determine right away if it's valid.
28 | parseToken(options.accessToken);
29 |
30 | this.accessToken = options.accessToken;
31 | this.origin = options.origin || constants.API_ORIGIN;
32 | }
33 |
34 | MapiClient.prototype.createRequest = function createRequest(requestOptions) {
35 | return new MapiRequest(this, requestOptions);
36 | };
37 |
38 | module.exports = MapiClient;
39 |
--------------------------------------------------------------------------------
/services/service-helpers/__tests__/validator-browser.test.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @jest-environment jsdom
3 | */
4 | 'use strict';
5 |
6 | const v = require('../validator');
7 |
8 | var t = function(rootcheck) {
9 | return function(value) {
10 | var messages = v.validate(rootcheck, value);
11 | return messages;
12 | };
13 | };
14 |
15 | var req = v.required;
16 |
17 | describe('v.file in the browser', () => {
18 | const check = t(v.shape({ prop: req(v.file) }));
19 |
20 | test('rejects strings', () => {
21 | expect(check({ prop: 'path/to/file.txt' })).toEqual([
22 | 'prop',
23 | 'Blob or ArrayBuffer'
24 | ]);
25 | });
26 |
27 | test('rejects numbers', () => {
28 | expect(check({ prop: 4 })).toEqual(['prop', 'Blob or ArrayBuffer']);
29 | });
30 | test('rejects booleans', () => {
31 | expect(check({ prop: false })).toEqual(['prop', 'Blob or ArrayBuffer']);
32 | });
33 | test('rejects objects', () => {
34 | expect(check({ prop: {} })).toEqual(['prop', 'Blob or ArrayBuffer']);
35 | });
36 | test('rejects arrays', () => {
37 | expect(check({ prop: [] })).toEqual(['prop', 'Blob or ArrayBuffer']);
38 | });
39 |
40 | test('accepts Blobs', () => {
41 | expect(check({ prop: new global.Blob(['blobbbbb']) })).toBeUndefined();
42 | });
43 | test('accepts ArrayBuffers', () => {
44 | expect(check({ prop: new global.ArrayBuffer(3) })).toBeUndefined();
45 | });
46 | });
47 |
--------------------------------------------------------------------------------
/test/try-browser/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Test the SDK in the browser
4 |
21 |
22 |
23 | Test the SDK in the browser
24 |
25 | Open the console and use the global method tryServiceMethod.
26 | It should log responses and errors to the console.
27 | Here's its signature:
28 |
29 |
30 |
tryServiceMethod(
31 | service: string,
32 | method: string,
33 | config: ?Object,
34 | accessToken: ?string,
35 | callback: ?(response) => void,
36 | )
37 |
38 |
39 | You can paste an access token as the last argument or set it as the global variable MAPBOX_ACCESS_TOKEN.
40 |
41 |
42 | Only use public or very temporary access tokens! This is not a secure place to put a secret token.
43 |
44 |
45 | An example:
46 |
47 | MAPBOX_ACCESS_TOKEN = 'pk....';
48 | tryServiceMethod('styles', 'getStyle', {
49 | styleId: 'cjgzfhs7k00072rnkgjlcuxsu'
50 | });
51 |
52 |
53 |
54 |