diff --git a/README.md b/README.md index 509ec373..3b4d5314 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ Use this method to install exceptionless into your node app. 1. Install the package by running `npm install exceptionless --save-dev`. 2. Add the exceptionless client to your app: ```javascript -var client = require('exceptionless').ExceptionlessClient.default; +var client = require('exceptionless.node').ExceptionlessClient.default; ``` ### Configuring the client. @@ -61,13 +61,13 @@ var client = new exceptionless.ExceptionlessClient({ #### Node.js 1. You can set the `apiKey` or `serverUrl` on the default ExceptionlessClient instance. ```javascript -var client = require('exceptionless').ExceptionlessClient.default; +var client = require('exceptionless.node').ExceptionlessClient.default; client.config.apiKey = 'API_KEY_HERE'; client.config.serverUrl = 'http://localhost:50000'; ``` 2. You can create a new instance of the ExceptionlessClient and specify the `apiKey`, `serverUrl` or [configuration object](https://github.com/exceptionless/Exceptionless.JavaScript/blob/master/src/configuration/IConfigurationSettings.ts). ```javascript -var exceptionless = require('exceptionless'); +var exceptionless = require('exceptionless.node'); var client = new exceptionless.ExceptionlessClient('API_KEY_HERE'); // or with a api key and server url. @@ -91,7 +91,7 @@ You may also want to send us log messages, feature usages or other kinds of even // javascript var client = exceptionless.ExceptionlessClient.default; // Node.Js -// var client = require('exceptionless').ExceptionlessClient.default; +// var client = require('exceptionless.node').ExceptionlessClient.default; client.submitLog('Logging made easy'); @@ -119,7 +119,7 @@ In addition to automatically sending all unhandled exceptions, you may want to m // javascript var client = exceptionless.ExceptionlessClient.default; // Node.Js -// var client = require('exceptionless').ExceptionlessClient.default; +// var client = require('exceptionless.node').ExceptionlessClient.default; try { throw new Error('test'); @@ -135,7 +135,7 @@ You can easily include additional information in your error reports using our fl // javascript var client = exceptionless.ExceptionlessClient.default; // Node.Js -// var client = require('exceptionless').ExceptionlessClient.default; +// var client = require('exceptionless.node').ExceptionlessClient.default; try { throw new Error('Unable to create order from quote.'); diff --git a/bower.json b/bower.json index fc7e8b61..e1743ac7 100644 --- a/bower.json +++ b/bower.json @@ -1,6 +1,6 @@ { "name": "exceptionless", - "version": "0.4.1", + "version": "0.5.0", "description": "JavaScript client for Exceptionless", "license": "Apache", "main": "dist/exceptionless.js", diff --git a/dist/exceptionless.d.ts b/dist/exceptionless.d.ts index 2299cff6..4b40e7a2 100644 --- a/dist/exceptionless.d.ts +++ b/dist/exceptionless.d.ts @@ -1,6 +1,3 @@ -export interface IBootstrapper { - register(): void; -} export interface ILastReferenceIdManager { getLast(): string; clearLast(): void; @@ -68,12 +65,9 @@ export interface IRequestInfoCollector { getRequestInfo(context: EventPluginContext): IRequestInfo; } export interface IStorage { - save(path: string, value: T): boolean; + save(path: string, value: T): boolean; get(path: string): T; - getList(searchPattern?: string, limit?: number): { - path: string; - value: T; - }[]; + getList(searchPattern?: string, limit?: number): IStorageItem[]; remove(path: string): void; } export interface ISubmissionClient { @@ -168,12 +162,11 @@ export declare class DefaultEventQueue implements IEventQueue { } export declare class InMemoryStorage implements IStorage { private _items; - save(path: string, value: T): boolean; + private _maxItems; + constructor(maxItems?: number); + save(path: string, value: T): boolean; get(path: string): T; - getList(searchPattern?: string, limit?: number): { - path: string; - value: T; - }[]; + getList(searchPattern?: string, limit?: number): IStorageItem[]; remove(path: string): void; } export declare class Utils { @@ -384,6 +377,11 @@ export declare class SubmissionMethodPlugin implements IEventPlugin { name: string; run(context: EventPluginContext, next?: () => void): void; } +export interface IStorageItem { + created: number; + path: string; + value: T; +} export declare class SettingsResponse { success: boolean; settings: any; @@ -392,33 +390,10 @@ export declare class SettingsResponse { exception: any; constructor(success: boolean, settings: any, settingsVersion?: number, exception?: any, message?: string); } -export declare class NodeEnvironmentInfoCollector implements IEnvironmentInfoCollector { - getEnvironmentInfo(context: EventPluginContext): IEnvironmentInfo; -} -export declare class NodeErrorParser implements IErrorParser { - parse(context: EventPluginContext, exception: Error): IError; -} -export declare class NodeRequestInfoCollector implements IRequestInfoCollector { - getRequestInfo(context: EventPluginContext): IRequestInfo; -} export interface IClientConfiguration { settings: Object; version: number; } -export declare class DefaultSubmissionClient implements ISubmissionClient { - configurationVersionHeader: string; - postEvents(events: IEvent[], config: Configuration, callback: (response: SubmissionResponse) => void): void; - postUserDescription(referenceId: string, description: IUserDescription, config: Configuration, callback: (response: SubmissionResponse) => void): void; - getSettings(config: Configuration, callback: (response: SettingsResponse) => void): void; - sendRequest(config: Configuration, method: string, path: string, data: string, callback: (status: number, message: string, data?: string, headers?: Object) => void): void; -} -export declare class NodeSubmissionClient extends DefaultSubmissionClient { - constructor(); - sendRequest(config: Configuration, method: string, path: string, data: string, callback: (status: number, message: string, data?: string, headers?: Object) => void): void; -} -export declare class NodeBootstrapper implements IBootstrapper { - register(): void; -} export declare class DefaultErrorParser implements IErrorParser { parse(context: EventPluginContext, exception: Error): IError; } @@ -428,6 +403,10 @@ export declare class DefaultModuleCollector implements IModuleCollector { export declare class DefaultRequestInfoCollector implements IRequestInfoCollector { getRequestInfo(context: EventPluginContext): IRequestInfo; } -export declare class DefaultBootstrapper implements IBootstrapper { - register(): void; +export declare class DefaultSubmissionClient implements ISubmissionClient { + configurationVersionHeader: string; + postEvents(events: IEvent[], config: Configuration, callback: (response: SubmissionResponse) => void): void; + postUserDescription(referenceId: string, description: IUserDescription, config: Configuration, callback: (response: SubmissionResponse) => void): void; + getSettings(config: Configuration, callback: (response: SettingsResponse) => void): void; + sendRequest(config: Configuration, method: string, path: string, data: string, callback: (status: number, message: string, data?: string, headers?: Object) => void): void; } diff --git a/dist/exceptionless.js b/dist/exceptionless.js index 84c2afa3..026b45de 100644 --- a/dist/exceptionless.js +++ b/dist/exceptionless.js @@ -1162,17 +1162,8 @@ window.TraceKit = TraceKit; if (!exports) { var exports = {}; } -if (!require) { - var require = function(){}; -} -var __extends = this.__extends || function (d, b) { - for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; - function __() { this.constructor = d; } - __.prototype = b.prototype; - d.prototype = new __(); -}; var ContextData = (function () { function ContextData() { } @@ -1536,32 +1527,47 @@ var DefaultEventQueue = (function () { })(); exports.DefaultEventQueue = DefaultEventQueue; var InMemoryStorage = (function () { - function InMemoryStorage() { - this._items = {}; + function InMemoryStorage(maxItems) { + this._items = []; + this._maxItems = maxItems > 0 ? maxItems : 250; } InMemoryStorage.prototype.save = function (path, value) { - this._items[path] = value; + if (!path || !value) { + return false; + } + this.remove(path); + if (this._items.push({ created: new Date().getTime(), path: path, value: value }) > this._maxItems) { + this._items.shift(); + } return true; }; InMemoryStorage.prototype.get = function (path) { - return this._items[path] || null; + var item = path ? this.getList("^" + path + "$", 1)[0] : null; + return item ? item.value : null; }; InMemoryStorage.prototype.getList = function (searchPattern, limit) { - var regex = new RegExp(searchPattern || '.*'); + var items = this._items; + if (!searchPattern) { + return items.slice(0, limit); + } + var regex = new RegExp(searchPattern); var results = []; - for (var key in this._items) { - if (results.length >= limit) { - break; - } - if (regex.test(key)) { - results.push({ path: key, value: this._items[key] }); + for (var index = 0; index < items.length; index++) { + if (regex.test(items[index].path)) { + results.push(items[index]); + if (results.length >= limit) { + break; + } } } return results; }; InMemoryStorage.prototype.remove = function (path) { if (path) { - delete this._items[path]; + var item = this.getList("^" + path + "$", 1)[0]; + if (item) { + this._items.splice(this._items.indexOf(item), 1); + } } }; return InMemoryStorage; @@ -1855,7 +1861,7 @@ var Configuration = (function () { }; Object.defineProperty(Configuration.prototype, "userAgent", { get: function () { - return 'exceptionless-js/0.4.1'; + return 'exceptionless-js/0.5.0'; }, enumerable: true, configurable: true @@ -2295,120 +2301,105 @@ var SettingsResponse = (function () { return SettingsResponse; })(); exports.SettingsResponse = SettingsResponse; -var os = require('os'); -var NodeEnvironmentInfoCollector = (function () { - function NodeEnvironmentInfoCollector() { +var DefaultErrorParser = (function () { + function DefaultErrorParser() { } - NodeEnvironmentInfoCollector.prototype.getEnvironmentInfo = function (context) { - function getIpAddresses() { - var ips = []; - var interfaces = os.networkInterfaces(); - Object.keys(interfaces).forEach(function (name) { - interfaces[name].forEach(function (iface) { - if ('IPv4' === iface.family && !iface.internal) { - ips.push(iface.address); - } - }); - }); - return ips.join(', '); - } - if (!os) { - return null; - } - var environmentInfo = { - processor_count: os.cpus().length, - total_physical_memory: os.totalmem(), - available_physical_memory: os.freemem(), - command_line: process.argv.join(' '), - process_name: process.title, - process_id: process.pid + '', - process_memory_size: process.memoryUsage().heapTotal, - architecture: os.arch(), - o_s_name: os.type(), - o_s_version: os.release(), - ip_address: getIpAddresses(), - machine_name: os.hostname(), - runtime_version: process.version, - data: { - loadavg: os.loadavg(), - platform: os.platform(), - tmpdir: os.tmpdir(), - uptime: os.uptime() + DefaultErrorParser.prototype.parse = function (context, exception) { + function getParameters(parameters) { + var params = (typeof parameters === 'string' ? [parameters] : parameters) || []; + var result = []; + for (var index = 0; index < params.length; index++) { + result.push({ name: params[index] }); } - }; - if (os.endianness) { - environmentInfo.data.endianness = os.endianness(); + return result; } - return environmentInfo; - }; - return NodeEnvironmentInfoCollector; -})(); -exports.NodeEnvironmentInfoCollector = NodeEnvironmentInfoCollector; -var nodestacktrace = require('stack-trace'); -var NodeErrorParser = (function () { - function NodeErrorParser() { - } - NodeErrorParser.prototype.parse = function (context, exception) { function getStackFrames(context, stackFrames) { + var anonymous = ''; var frames = []; for (var index = 0; index < stackFrames.length; index++) { var frame = stackFrames[index]; frames.push({ - name: frame.getMethodName() || frame.getFunctionName(), - file_name: frame.getFileName(), - line_number: frame.getLineNumber(), - column: frame.getColumnNumber(), - declaring_type: frame.getTypeName(), - data: { - is_native: frame.isNative() || (!!frame.filename && frame.filename[0] !== '/' && frame.filename[0] !== '.') - } + name: (frame.func || anonymous).replace('?', anonymous), + parameters: getParameters(frame.args), + file_name: frame.url, + line_number: frame.line, + column: frame.column }); } return frames; } - if (!nodestacktrace) { - throw new Error('Unable to load the stack trace library.'); + var traceKitStackTrace = '@@_TraceKit.StackTrace'; + var stackTrace = !!context.contextData[traceKitStackTrace] + ? context.contextData[traceKitStackTrace] + : TraceKit.computeStackTrace(exception, 25); + if (!stackTrace) { + throw new Error('Unable to parse the exceptions stack trace.'); } - var stackFrames = nodestacktrace.parse(exception) || []; return { - type: exception.name, - message: exception.message, - stack_trace: getStackFrames(context, stackFrames) + type: stackTrace.name, + message: stackTrace.message || exception.message, + stack_trace: getStackFrames(context, stackTrace.stack || []) }; }; - return NodeErrorParser; + return DefaultErrorParser; +})(); +exports.DefaultErrorParser = DefaultErrorParser; +var DefaultModuleCollector = (function () { + function DefaultModuleCollector() { + } + DefaultModuleCollector.prototype.getModules = function (context) { + if (document && document.getElementsByTagName) { + return null; + } + var modules = []; + var scripts = document.getElementsByTagName('script'); + if (scripts && scripts.length > 0) { + for (var index = 0; index < scripts.length; index++) { + if (scripts[index].src) { + modules.push({ + module_id: index, + name: scripts[index].src, + version: Utils.parseVersion(scripts[index].src) + }); + } + else if (!!scripts[index].innerHTML) { + modules.push({ + module_id: index, + name: 'Script Tag', + version: Utils.getHashCode(scripts[index].innerHTML) + }); + } + } + } + return modules; + }; + return DefaultModuleCollector; })(); -exports.NodeErrorParser = NodeErrorParser; -var NodeRequestInfoCollector = (function () { - function NodeRequestInfoCollector() { +exports.DefaultModuleCollector = DefaultModuleCollector; +var DefaultRequestInfoCollector = (function () { + function DefaultRequestInfoCollector() { } - NodeRequestInfoCollector.prototype.getRequestInfo = function (context) { - var requestKey = '@request'; - if (!context.contextData[requestKey]) { + DefaultRequestInfoCollector.prototype.getRequestInfo = function (context) { + if (!document || !navigator || !location) { return null; } - var request = context.contextData[requestKey]; var requestInfo = { - client_ip_address: request.ip, - user_agent: request.headers['user-agent'], - is_secure: request.secure, - http_method: request.method, - host: request.hostname || request.host, - path: request.path, - post_data: request.body, - cookies: Utils.getCookies((request || {}).headers['cookie']), - query_string: request.params + user_agent: navigator.userAgent, + is_secure: location.protocol === 'https:', + host: location.hostname, + port: location.port && location.port !== '' ? parseInt(location.port) : 80, + path: location.pathname, + cookies: Utils.getCookies(document.cookie), + query_string: Utils.parseQueryString(location.search.substring(1)) }; - var host = request.headers['host']; - var port = host && parseInt(host.slice(host.indexOf(':') + 1)); - if (port > 0) { - requestInfo.port = port; + if (document.referrer && document.referrer !== '') { + requestInfo.referrer = document.referrer; } return requestInfo; }; - return NodeRequestInfoCollector; + return DefaultRequestInfoCollector; })(); -exports.NodeRequestInfoCollector = NodeRequestInfoCollector; +exports.DefaultRequestInfoCollector = DefaultRequestInfoCollector; var DefaultSubmissionClient = (function () { function DefaultSubmissionClient() { this.configurationVersionHeader = 'X-Exceptionless-ConfigVersion'; @@ -2442,10 +2433,10 @@ var DefaultSubmissionClient = (function () { catch (e) { config.log.error("Unable to parse settings: '" + data + "'"); } - if (!settings || !settings.settings || !settings.version) { + if (!settings || isNaN(settings.version)) { return callback(new SettingsResponse(false, null, -1, null, 'Invalid configuration settings.')); } - callback(new SettingsResponse(true, settings.settings, settings.version)); + callback(new SettingsResponse(true, settings.settings || {}, settings.version)); }); }; DefaultSubmissionClient.prototype.sendRequest = function (config, method, path, data, callback) { @@ -2533,281 +2524,52 @@ var DefaultSubmissionClient = (function () { return DefaultSubmissionClient; })(); exports.DefaultSubmissionClient = DefaultSubmissionClient; -var http = require('http'); -var https = require('https'); -var url = require('url'); -var NodeSubmissionClient = (function (_super) { - __extends(NodeSubmissionClient, _super); - function NodeSubmissionClient() { - _super.call(this); - this.configurationVersionHeader = this.configurationVersionHeader.toLowerCase(); - } - NodeSubmissionClient.prototype.sendRequest = function (config, method, path, data, callback) { - function complete(response, data, headers) { - var message; - if (response.statusCode === 0) { - message = 'Unable to connect to server.'; - } - else if (response.statusCode < 200 || response.statusCode > 299) { - message = response.statusMessage || response.message; - } - callback(response.statusCode || 500, message, data, headers); - } - var parsedHost = url.parse(config.serverUrl); - var options = { - auth: "client:" + config.apiKey, - headers: {}, - hostname: parsedHost.hostname, - method: method, - port: parsedHost.port && parseInt(parsedHost.port), - path: path, - }; - if (method === 'POST') { - options.headers = { - 'Content-Type': 'application/json', - 'Content-Length': data.length - }; - } - options.headers['User-Agent'] = config.userAgent; - var request = (parsedHost.protocol === 'https' ? https : http).request(options, function (response) { - var body = ''; - response.on('data', function (chunk) { return body += chunk; }); - response.on('end', function () { - complete(response, body, response.headers); - }); - }); - request.on('error', function (e) { - callback(500, e.message); - }); - !!data && request.write(data); - request.end(); - }; - return NodeSubmissionClient; -})(DefaultSubmissionClient); -exports.NodeSubmissionClient = NodeSubmissionClient; -var NodeBootstrapper = (function () { - function NodeBootstrapper() { - } - NodeBootstrapper.prototype.register = function () { - var beforeExit = 'beforeExit'; - var uncaughtException = 'uncaughtException'; - if (!(typeof window === 'undefined' && typeof global !== 'undefined' && {}.toString.call(global) === '[object global]')) { - return; - } - var defaults = Configuration.defaults; - defaults.environmentInfoCollector = new NodeEnvironmentInfoCollector(); - defaults.errorParser = new NodeErrorParser(); - defaults.requestInfoCollector = new NodeRequestInfoCollector(); - defaults.submissionClient = new NodeSubmissionClient(); - process.on(uncaughtException, function (error) { - ExceptionlessClient.default.submitUnhandledException(error, uncaughtException); - }); - process.on(beforeExit, function (code) { - function getExitCodeReason(code) { - if (code === 1) { - return 'Uncaught Fatal Exception'; - } - if (code === 3) { - return 'Internal JavaScript Parse Error'; - } - if (code === 4) { - return 'Internal JavaScript Evaluation Failure'; - } - if (code === 5) { - return 'Fatal Exception'; - } - if (code === 6) { - return 'Non-function Internal Exception Handler '; - } - if (code === 7) { - return 'Internal Exception Handler Run-Time Failure'; - } - if (code === 8) { - return 'Uncaught Exception'; - } - if (code === 9) { - return 'Invalid Argument'; - } - if (code === 10) { - return 'Internal JavaScript Run-Time Failure'; - } - if (code === 12) { - return 'Invalid Debug Argument'; - } - if (code > 128) { - return 'Signal Exits'; - } - return null; - } - var client = ExceptionlessClient.default; - var message = getExitCodeReason(code); - if (message !== null) { - client.submitLog(beforeExit, message, 'Error'); - } - client.config.queue.process(); - }); - }; - return NodeBootstrapper; -})(); -exports.NodeBootstrapper = NodeBootstrapper; -var DefaultErrorParser = (function () { - function DefaultErrorParser() { +function getDefaultsSettingsFromScriptTag() { + if (!document || !document.getElementsByTagName) { + return null; } - DefaultErrorParser.prototype.parse = function (context, exception) { - function getParameters(parameters) { - var params = (typeof parameters === 'string' ? [parameters] : parameters) || []; - var result = []; - for (var index = 0; index < params.length; index++) { - result.push({ name: params[index] }); - } - return result; - } - function getStackFrames(context, stackFrames) { - var anonymous = ''; - var frames = []; - for (var index = 0; index < stackFrames.length; index++) { - var frame = stackFrames[index]; - frames.push({ - name: (frame.func || anonymous).replace('?', anonymous), - parameters: getParameters(frame.args), - file_name: frame.url, - line_number: frame.line, - column: frame.column - }); - } - return frames; + var scripts = document.getElementsByTagName('script'); + for (var index = 0; index < scripts.length; index++) { + if (scripts[index].src && scripts[index].src.indexOf('/exceptionless') > -1) { + return Utils.parseQueryString(scripts[index].src.split('?').pop()); } - var traceKitStackTrace = '@@_TraceKit.StackTrace'; - var stackTrace = !!context.contextData[traceKitStackTrace] - ? context.contextData[traceKitStackTrace] - : TraceKit.computeStackTrace(exception, 25); - if (!stackTrace) { - throw new Error('Unable to parse the exceptions stack trace.'); - } - return { - type: stackTrace.name, - message: stackTrace.message || exception.message, - stack_trace: getStackFrames(context, stackTrace.stack || []) - }; - }; - return DefaultErrorParser; -})(); -exports.DefaultErrorParser = DefaultErrorParser; -var DefaultModuleCollector = (function () { - function DefaultModuleCollector() { } - DefaultModuleCollector.prototype.getModules = function (context) { - if (document && document.getElementsByTagName) { - return null; - } - var modules = []; - var scripts = document.getElementsByTagName('script'); - if (scripts && scripts.length > 0) { - for (var index = 0; index < scripts.length; index++) { - if (scripts[index].src) { - modules.push({ - module_id: index, - name: scripts[index].src, - version: Utils.parseVersion(scripts[index].src) - }); - } - else if (!!scripts[index].innerHTML) { - modules.push({ - module_id: index, - name: 'Script Tag', - version: Utils.getHashCode(scripts[index].innerHTML) - }); - } - } - } - return modules; - }; - return DefaultModuleCollector; -})(); -exports.DefaultModuleCollector = DefaultModuleCollector; -var DefaultRequestInfoCollector = (function () { - function DefaultRequestInfoCollector() { + return null; +} +function processUnhandledException(stackTrace, options) { + var builder = ExceptionlessClient.default.createUnhandledException(new Error(stackTrace.message || (options || {}).status || 'Script error'), 'onerror'); + builder.pluginContextData['@@_TraceKit.StackTrace'] = stackTrace; + builder.submit(); +} +function processJQueryAjaxError(event, xhr, settings, error) { + var client = ExceptionlessClient.default; + if (xhr.status === 404) { + client.submitNotFound(settings.url); } - DefaultRequestInfoCollector.prototype.getRequestInfo = function (context) { - if (!document || !navigator || !location) { - return null; - } - var requestInfo = { - user_agent: navigator.userAgent, - is_secure: location.protocol === 'https:', - host: location.hostname, - port: location.port && location.port !== '' ? parseInt(location.port) : 80, - path: location.pathname, - cookies: Utils.getCookies(document.cookie), - query_string: Utils.parseQueryString(location.search.substring(1)) - }; - if (document.referrer && document.referrer !== '') { - requestInfo.referrer = document.referrer; - } - return requestInfo; - }; - return DefaultRequestInfoCollector; -})(); -exports.DefaultRequestInfoCollector = DefaultRequestInfoCollector; -var DefaultBootstrapper = (function () { - function DefaultBootstrapper() { + else if (xhr.status !== 401) { + client.createUnhandledException(error, 'JQuery.ajaxError') + .setSource(settings.url) + .setProperty('status', xhr.status) + .setProperty('request', settings.data) + .setProperty('response', xhr.responseText && xhr.responseText.slice && xhr.responseText.slice(0, 1024)) + .submit(); } - DefaultBootstrapper.prototype.register = function () { - function getDefaultsSettingsFromScriptTag() { - if (!document || !document.getElementsByTagName) { - return null; - } - var scripts = document.getElementsByTagName('script'); - for (var index = 0; index < scripts.length; index++) { - if (scripts[index].src && scripts[index].src.indexOf('/exceptionless') > -1) { - return Utils.parseQueryString(scripts[index].src.split('?').pop()); - } - } - return null; - } - function processUnhandledException(stackTrace, options) { - var builder = ExceptionlessClient.default.createUnhandledException(new Error(stackTrace.message || (options || {}).status || 'Script error'), 'onerror'); - builder.pluginContextData['@@_TraceKit.StackTrace'] = stackTrace; - builder.submit(); - } - function processJQueryAjaxError(event, xhr, settings, error) { - var client = ExceptionlessClient.default; - if (xhr.status === 404) { - client.submitNotFound(settings.url); - } - else if (xhr.status !== 401) { - client.createUnhandledException(error, 'JQuery.ajaxError') - .setSource(settings.url) - .setProperty('status', xhr.status) - .setProperty('request', settings.data) - .setProperty('response', xhr.responseText && xhr.responseText.slice && xhr.responseText.slice(0, 1024)) - .submit(); - } - } - if (typeof window === 'undefined' || typeof document === 'undefined') { - return; - } - var defaults = Configuration.defaults; - var settings = getDefaultsSettingsFromScriptTag(); - if (settings && (settings.apiKey || settings.serverUrl)) { - defaults.apiKey = settings.apiKey; - defaults.serverUrl = settings.serverUrl; - } - defaults.errorParser = new DefaultErrorParser(); - defaults.moduleCollector = new DefaultModuleCollector(); - defaults.requestInfoCollector = new DefaultRequestInfoCollector(); - defaults.submissionClient = new DefaultSubmissionClient(); - TraceKit.report.subscribe(processUnhandledException); - TraceKit.extendToAsynchronousCallbacks(); - if (typeof $ !== 'undefined' && $(document)) { - $(document).ajaxError(processJQueryAjaxError); - } - }; - return DefaultBootstrapper; -})(); -exports.DefaultBootstrapper = DefaultBootstrapper; -new NodeBootstrapper().register(); -new DefaultBootstrapper().register(); +} +var defaults = Configuration.defaults; +var settings = getDefaultsSettingsFromScriptTag(); +if (settings && (settings.apiKey || settings.serverUrl)) { + defaults.apiKey = settings.apiKey; + defaults.serverUrl = settings.serverUrl; +} +defaults.errorParser = new DefaultErrorParser(); +defaults.moduleCollector = new DefaultModuleCollector(); +defaults.requestInfoCollector = new DefaultRequestInfoCollector(); +defaults.submissionClient = new DefaultSubmissionClient(); +TraceKit.report.subscribe(processUnhandledException); +TraceKit.extendToAsynchronousCallbacks(); +if (typeof $ !== 'undefined' && $(document)) { + $(document).ajaxError(processJQueryAjaxError); +} Error.stackTraceLimit = Infinity; return exports; diff --git a/dist/exceptionless.js.map b/dist/exceptionless.js.map index 5dd6e888..4cb46cc8 100644 --- a/dist/exceptionless.js.map +++ b/dist/exceptionless.js.map @@ -1 +1 @@ -{"version":3,"sources":["tracekit.js","/source/exceptionless.ts"],"names":[],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AC7nCA,iCAAiC,8BAA8B,CAAC,CAAA;AAChE,oCAAoC,iCAAiC,CAAC,CAAA;AAuDtE,IAAI,mCAAgB,EAAE,CAAC,QAAQ,EAAE,CAAC;AAClC,IAAI,yCAAmB,EAAE,CAAC,QAAQ,EAAE,CAAC;AAC/B,KAAM,CAAC,eAAe,GAAG,QAAQ,CAAC","file":"exceptionless.js","sourcesContent":["/*\n TraceKit - Cross brower stack traces - github.com/csnover/TraceKit\n MIT license\n*/\n\n(function(window, undefined) {\nif (!window) {\n return;\n}\n\nvar TraceKit = {};\nvar _oldTraceKit = window.TraceKit;\n\n// global reference to slice\nvar _slice = [].slice;\nvar UNKNOWN_FUNCTION = '?';\n\n\n/**\n * _has, a better form of hasOwnProperty\n * Example: _has(MainHostObject, property) === true/false\n *\n * @param {Object} object to check property\n * @param {string} key to check\n */\nfunction _has(object, key) {\n return Object.prototype.hasOwnProperty.call(object, key);\n}\n\nfunction _isUndefined(what) {\n return typeof what === 'undefined';\n}\n\n/**\n * TraceKit.noConflict: Export TraceKit out to another variable\n * Example: var TK = TraceKit.noConflict()\n */\nTraceKit.noConflict = function noConflict() {\n window.TraceKit = _oldTraceKit;\n return TraceKit;\n};\n\n/**\n * TraceKit.wrap: Wrap any function in a TraceKit reporter\n * Example: func = TraceKit.wrap(func);\n *\n * @param {Function} func Function to be wrapped\n * @return {Function} The wrapped func\n */\nTraceKit.wrap = function traceKitWrapper(func) {\n function wrapped() {\n try {\n return func.apply(this, arguments);\n } catch (e) {\n TraceKit.report(e);\n throw e;\n }\n }\n return wrapped;\n};\n\n/**\n * TraceKit.report: cross-browser processing of unhandled exceptions\n *\n * Syntax:\n * TraceKit.report.subscribe(function(stackInfo) { ... })\n * TraceKit.report.unsubscribe(function(stackInfo) { ... })\n * TraceKit.report(exception)\n * try { ...code... } catch(ex) { TraceKit.report(ex); }\n *\n * Supports:\n * - Firefox: full stack trace with line numbers, plus column number\n * on top frame; column number is not guaranteed\n * - Opera: full stack trace with line and column numbers\n * - Chrome: full stack trace with line and column numbers\n * - Safari: line and column number for the top frame only; some frames\n * may be missing, and column number is not guaranteed\n * - IE: line and column number for the top frame only; some frames\n * may be missing, and column number is not guaranteed\n *\n * In theory, TraceKit should work on all of the following versions:\n * - IE5.5+ (only 8.0 tested)\n * - Firefox 0.9+ (only 3.5+ tested)\n * - Opera 7+ (only 10.50 tested; versions 9 and earlier may require\n * Exceptions Have Stacktrace to be enabled in opera:config)\n * - Safari 3+ (only 4+ tested)\n * - Chrome 1+ (only 5+ tested)\n * - Konqueror 3.5+ (untested)\n *\n * Requires TraceKit.computeStackTrace.\n *\n * Tries to catch all unhandled exceptions and report them to the\n * subscribed handlers. Please note that TraceKit.report will rethrow the\n * exception. This is REQUIRED in order to get a useful stack trace in IE.\n * If the exception does not reach the top of the browser, you will only\n * get a stack trace from the point where TraceKit.report was called.\n *\n * Handlers receive a stackInfo object as described in the\n * TraceKit.computeStackTrace docs.\n */\nTraceKit.report = (function reportModuleWrapper() {\n var handlers = [],\n lastException = null,\n lastExceptionStack = null;\n\n /**\n * Add a crash handler.\n * @param {Function} handler\n */\n function subscribe(handler) {\n installGlobalHandler();\n handlers.push(handler);\n }\n\n /**\n * Remove a crash handler.\n * @param {Function} handler\n */\n function unsubscribe(handler) {\n for (var i = handlers.length - 1; i >= 0; --i) {\n if (handlers[i] === handler) {\n handlers.splice(i, 1);\n }\n }\n }\n\n /**\n * Dispatch stack information to all handlers.\n * @param {Object.} stack\n */\n function notifyHandlers(stack, isWindowError) {\n var exception = null;\n if (isWindowError && !TraceKit.collectWindowErrors) {\n return;\n }\n for (var i in handlers) {\n if (_has(handlers, i)) {\n try {\n handlers[i].apply(null, [stack].concat(_slice.call(arguments, 2)));\n } catch (inner) {\n exception = inner;\n }\n }\n }\n\n if (exception) {\n throw exception;\n }\n }\n\n var _oldOnerrorHandler, _onErrorHandlerInstalled;\n\n /**\n * Ensures all global unhandled exceptions are recorded.\n * Supported by Gecko and IE.\n * @param {string} message Error message.\n * @param {string} url URL of script that generated the exception.\n * @param {(number|string)} lineNo The line number at which the error\n * occurred.\n * @param {?(number|string)} columnNo The column number at which the error\n * occurred.\n * @param {?Error} errorObj The actual Error object.\n */\n function traceKitWindowOnError(message, url, lineNo, columnNo, errorObj) {\n var stack = null;\n\n if (errorObj) {\n stack = TraceKit.computeStackTrace(errorObj);\n }\n else\n {\n if (lastExceptionStack) {\n TraceKit.computeStackTrace.augmentStackTraceWithInitialElement(lastExceptionStack, url, lineNo, message);\n stack = lastExceptionStack;\n lastExceptionStack = null;\n lastException = null;\n } else {\n var location = {\n 'url': url,\n 'line': lineNo,\n 'column': columnNo\n };\n location.func = TraceKit.computeStackTrace.guessFunctionName(location.url, location.line);\n location.context = TraceKit.computeStackTrace.gatherContext(location.url, location.line);\n stack = {\n 'mode': 'onerror',\n 'message': message,\n 'url': document.location.href,\n 'stack': [location],\n 'useragent': navigator.userAgent\n };\n }\n }\n\n notifyHandlers(stack, 'from window.onerror');\n\n if (_oldOnerrorHandler) {\n return _oldOnerrorHandler.apply(this, arguments);\n }\n\n return false;\n }\n\n function installGlobalHandler ()\n {\n if (_onErrorHandlerInstalled === true) {\n return;\n }\n _oldOnerrorHandler = window.onerror;\n window.onerror = traceKitWindowOnError;\n _onErrorHandlerInstalled = true;\n }\n\n /**\n * Reports an unhandled Error to TraceKit.\n * @param {Error} ex\n */\n function report(ex) {\n var args = _slice.call(arguments, 1);\n if (lastExceptionStack) {\n if (lastException === ex) {\n return; // already caught by an inner catch block, ignore\n } else {\n var s = lastExceptionStack;\n lastExceptionStack = null;\n lastException = null;\n notifyHandlers.apply(null, [s, null].concat(args));\n }\n }\n\n var stack = TraceKit.computeStackTrace(ex);\n lastExceptionStack = stack;\n lastException = ex;\n\n // If the stack trace is incomplete, wait for 2 seconds for\n // slow slow IE to see if onerror occurs or not before reporting\n // this exception; otherwise, we will end up with an incomplete\n // stack trace\n window.setTimeout(function () {\n if (lastException === ex) {\n lastExceptionStack = null;\n lastException = null;\n notifyHandlers.apply(null, [stack, null].concat(args));\n }\n }, (stack.incomplete ? 2000 : 0));\n\n throw ex; // re-throw to propagate to the top level (and cause window.onerror)\n }\n\n report.subscribe = subscribe;\n report.unsubscribe = unsubscribe;\n return report;\n}());\n\n/**\n * TraceKit.computeStackTrace: cross-browser stack traces in JavaScript\n *\n * Syntax:\n * s = TraceKit.computeStackTrace.ofCaller([depth])\n * s = TraceKit.computeStackTrace(exception) // consider using TraceKit.report instead (see below)\n * Returns:\n * s.name - exception name\n * s.message - exception message\n * s.stack[i].url - JavaScript or HTML file URL\n * s.stack[i].func - function name, or empty for anonymous functions (if guessing did not work)\n * s.stack[i].args - arguments passed to the function, if known\n * s.stack[i].line - line number, if known\n * s.stack[i].column - column number, if known\n * s.stack[i].context - an array of source code lines; the middle element corresponds to the correct line#\n * s.mode - 'stack', 'stacktrace', 'multiline', 'callers', 'onerror', or 'failed' -- method used to collect the stack trace\n *\n * Supports:\n * - Firefox: full stack trace with line numbers and unreliable column\n * number on top frame\n * - Opera 10: full stack trace with line and column numbers\n * - Opera 9-: full stack trace with line numbers\n * - Chrome: full stack trace with line and column numbers\n * - Safari: line and column number for the topmost stacktrace element\n * only\n * - IE: no line numbers whatsoever\n *\n * Tries to guess names of anonymous functions by looking for assignments\n * in the source code. In IE and Safari, we have to guess source file names\n * by searching for function bodies inside all page scripts. This will not\n * work for scripts that are loaded cross-domain.\n * Here be dragons: some function names may be guessed incorrectly, and\n * duplicate functions may be mismatched.\n *\n * TraceKit.computeStackTrace should only be used for tracing purposes.\n * Logging of unhandled exceptions should be done with TraceKit.report,\n * which builds on top of TraceKit.computeStackTrace and provides better\n * IE support by utilizing the window.onerror event to retrieve information\n * about the top of the stack.\n *\n * Note: In IE and Safari, no stack trace is recorded on the Error object,\n * so computeStackTrace instead walks its *own* chain of callers.\n * This means that:\n * * in Safari, some methods may be missing from the stack trace;\n * * in IE, the topmost function in the stack trace will always be the\n * caller of computeStackTrace.\n *\n * This is okay for tracing (because you are likely to be calling\n * computeStackTrace from the function you want to be the topmost element\n * of the stack trace anyway), but not okay for logging unhandled\n * exceptions (because your catch block will likely be far away from the\n * inner function that actually caused the exception).\n *\n * Tracing example:\n * function trace(message) {\n * var stackInfo = TraceKit.computeStackTrace.ofCaller();\n * var data = message + \"\\n\";\n * for(var i in stackInfo.stack) {\n * var item = stackInfo.stack[i];\n * data += (item.func || '[anonymous]') + \"() in \" + item.url + \":\" + (item.line || '0') + \"\\n\";\n * }\n * if (window.console)\n * console.info(data);\n * else\n * alert(data);\n * }\n */\nTraceKit.computeStackTrace = (function computeStackTraceWrapper() {\n var debug = false,\n sourceCache = {};\n\n /**\n * Attempts to retrieve source code via XMLHttpRequest, which is used\n * to look up anonymous function names.\n * @param {string} url URL of source code.\n * @return {string} Source contents.\n */\n function loadSource(url) {\n if (!TraceKit.remoteFetching) { //Only attempt request if remoteFetching is on.\n return '';\n }\n try {\n var getXHR = function() {\n try {\n return new window.XMLHttpRequest();\n } catch (e) {\n // explicitly bubble up the exception if not found\n return new window.ActiveXObject('Microsoft.XMLHTTP');\n }\n };\n\n var request = getXHR();\n request.open('GET', url, false);\n request.send('');\n return request.responseText;\n } catch (e) {\n return '';\n }\n }\n\n /**\n * Retrieves source code from the source code cache.\n * @param {string} url URL of source code.\n * @return {Array.} Source contents.\n */\n function getSource(url) {\n if (typeof url !== 'string') {\n return [];\n }\n\n if (!_has(sourceCache, url)) {\n // URL needs to be able to fetched within the acceptable domain. Otherwise,\n // cross-domain errors will be triggered.\n var source = '';\n\n url = url || '';\n if (url.indexOf && url.indexOf(document.domain) !== -1) {\n source = loadSource(url);\n }\n sourceCache[url] = source ? source.split('\\n') : [];\n }\n\n return sourceCache[url];\n }\n\n /**\n * Tries to use an externally loaded copy of source code to determine\n * the name of a function by looking at the name of the variable it was\n * assigned to, if any.\n * @param {string} url URL of source code.\n * @param {(string|number)} lineNo Line number in source code.\n * @return {string} The function name, if discoverable.\n */\n function guessFunctionName(url, lineNo) {\n var reFunctionArgNames = /function ([^(]*)\\(([^)]*)\\)/,\n reGuessFunction = /['\"]?([0-9A-Za-z$_]+)['\"]?\\s*[:=]\\s*(function|eval|new Function)/,\n line = '',\n maxLines = 10,\n source = getSource(url),\n m;\n\n if (!source.length) {\n return UNKNOWN_FUNCTION;\n }\n\n // Walk backwards from the first line in the function until we find the line which\n // matches the pattern above, which is the function definition\n for (var i = 0; i < maxLines; ++i) {\n line = source[lineNo - i] + line;\n\n if (!_isUndefined(line)) {\n if ((m = reGuessFunction.exec(line))) {\n return m[1];\n } else if ((m = reFunctionArgNames.exec(line))) {\n return m[1];\n }\n }\n }\n\n return UNKNOWN_FUNCTION;\n }\n\n /**\n * Retrieves the surrounding lines from where an exception occurred.\n * @param {string} url URL of source code.\n * @param {(string|number)} line Line number in source code to centre\n * around for context.\n * @return {?Array.} Lines of source code.\n */\n function gatherContext(url, line) {\n var source = getSource(url);\n\n if (!source.length) {\n return null;\n }\n\n var context = [],\n // linesBefore & linesAfter are inclusive with the offending line.\n // if linesOfContext is even, there will be one extra line\n // *before* the offending line.\n linesBefore = Math.floor(TraceKit.linesOfContext / 2),\n // Add one extra line if linesOfContext is odd\n linesAfter = linesBefore + (TraceKit.linesOfContext % 2),\n start = Math.max(0, line - linesBefore - 1),\n end = Math.min(source.length, line + linesAfter - 1);\n\n line -= 1; // convert to 0-based index\n\n for (var i = start; i < end; ++i) {\n if (!_isUndefined(source[i])) {\n context.push(source[i]);\n }\n }\n\n return context.length > 0 ? context : null;\n }\n\n /**\n * Escapes special characters, except for whitespace, in a string to be\n * used inside a regular expression as a string literal.\n * @param {string} text The string.\n * @return {string} The escaped string literal.\n */\n function escapeRegExp(text) {\n return text.replace(/[\\-\\[\\]{}()*+?.,\\\\\\^$|#]/g, '\\\\$&');\n }\n\n /**\n * Escapes special characters in a string to be used inside a regular\n * expression as a string literal. Also ensures that HTML entities will\n * be matched the same as their literal friends.\n * @param {string} body The string.\n * @return {string} The escaped string.\n */\n function escapeCodeAsRegExpForMatchingInsideHTML(body) {\n return escapeRegExp(body).replace('<', '(?:<|<)').replace('>', '(?:>|>)').replace('&', '(?:&|&)').replace('\"', '(?:\"|")').replace(/\\s+/g, '\\\\s+');\n }\n\n /**\n * Determines where a code fragment occurs in the source code.\n * @param {RegExp} re The function definition.\n * @param {Array.} urls A list of URLs to search.\n * @return {?Object.} An object containing\n * the url, line, and column number of the defined function.\n */\n function findSourceInUrls(re, urls) {\n var source, m;\n for (var i = 0, j = urls.length; i < j; ++i) {\n // console.log('searching', urls[i]);\n if ((source = getSource(urls[i])).length) {\n source = source.join('\\n');\n if ((m = re.exec(source))) {\n // console.log('Found function in ' + urls[i]);\n\n return {\n 'url': urls[i],\n 'line': source.substring(0, m.index).split('\\n').length,\n 'column': m.index - source.lastIndexOf('\\n', m.index) - 1\n };\n }\n }\n }\n\n // console.log('no match');\n\n return null;\n }\n\n /**\n * Determines at which column a code fragment occurs on a line of the\n * source code.\n * @param {string} fragment The code fragment.\n * @param {string} url The URL to search.\n * @param {(string|number)} line The line number to examine.\n * @return {?number} The column number.\n */\n function findSourceInLine(fragment, url, line) {\n var source = getSource(url),\n re = new RegExp('\\\\b' + escapeRegExp(fragment) + '\\\\b'),\n m;\n\n line -= 1;\n\n if (source && source.length > line && (m = re.exec(source[line]))) {\n return m.index;\n }\n\n return null;\n }\n\n /**\n * Determines where a function was defined within the source code.\n * @param {(Function|string)} func A function reference or serialized\n * function definition.\n * @return {?Object.} An object containing\n * the url, line, and column number of the defined function.\n */\n function findSourceByFunctionBody(func) {\n var urls = [window.location.href],\n scripts = document.getElementsByTagName('script'),\n body,\n code = '' + func,\n codeRE = /^function(?:\\s+([\\w$]+))?\\s*\\(([\\w\\s,]*)\\)\\s*\\{\\s*(\\S[\\s\\S]*\\S)\\s*\\}\\s*$/,\n eventRE = /^function on([\\w$]+)\\s*\\(event\\)\\s*\\{\\s*(\\S[\\s\\S]*\\S)\\s*\\}\\s*$/,\n re,\n parts,\n result;\n\n for (var i = 0; i < scripts.length; ++i) {\n var script = scripts[i];\n if (script.src) {\n urls.push(script.src);\n }\n }\n\n if (!(parts = codeRE.exec(code))) {\n re = new RegExp(escapeRegExp(code).replace(/\\s+/g, '\\\\s+'));\n }\n\n // not sure if this is really necessary, but I don’t have a test\n // corpus large enough to confirm that and it was in the original.\n else {\n var name = parts[1] ? '\\\\s+' + parts[1] : '',\n args = parts[2].split(',').join('\\\\s*,\\\\s*');\n\n body = escapeRegExp(parts[3]).replace(/;$/, ';?'); // semicolon is inserted if the function ends with a comment.replace(/\\s+/g, '\\\\s+');\n re = new RegExp('function' + name + '\\\\s*\\\\(\\\\s*' + args + '\\\\s*\\\\)\\\\s*{\\\\s*' + body + '\\\\s*}');\n }\n\n // look for a normal function definition\n if ((result = findSourceInUrls(re, urls))) {\n return result;\n }\n\n // look for an old-school event handler function\n if ((parts = eventRE.exec(code))) {\n var event = parts[1];\n body = escapeCodeAsRegExpForMatchingInsideHTML(parts[2]);\n\n // look for a function defined in HTML as an onXXX handler\n re = new RegExp('on' + event + '=[\\\\\\'\"]\\\\s*' + body + '\\\\s*[\\\\\\'\"]', 'i');\n\n if ((result = findSourceInUrls(re, urls[0]))) {\n return result;\n }\n\n // look for ???\n re = new RegExp(body);\n\n if ((result = findSourceInUrls(re, urls))) {\n return result;\n }\n }\n\n return null;\n }\n\n // Contents of Exception in various browsers.\n //\n // SAFARI:\n // ex.message = Can't find variable: qq\n // ex.line = 59\n // ex.sourceId = 580238192\n // ex.sourceURL = http://...\n // ex.expressionBeginOffset = 96\n // ex.expressionCaretOffset = 98\n // ex.expressionEndOffset = 98\n // ex.name = ReferenceError\n //\n // FIREFOX:\n // ex.message = qq is not defined\n // ex.fileName = http://...\n // ex.lineNumber = 59\n // ex.columnNumber = 69\n // ex.stack = ...stack trace... (see the example below)\n // ex.name = ReferenceError\n //\n // CHROME:\n // ex.message = qq is not defined\n // ex.name = ReferenceError\n // ex.type = not_defined\n // ex.arguments = ['aa']\n // ex.stack = ...stack trace...\n //\n // INTERNET EXPLORER:\n // ex.message = ...\n // ex.name = ReferenceError\n //\n // OPERA:\n // ex.message = ...message... (see the example below)\n // ex.name = ReferenceError\n // ex.opera#sourceloc = 11 (pretty much useless, duplicates the info in ex.message)\n // ex.stacktrace = n/a; see 'opera:config#UserPrefs|Exceptions Have Stacktrace'\n\n /**\n * Computes stack trace information from the stack property.\n * Chrome and Gecko use this property.\n * @param {Error} ex\n * @return {?Object.} Stack trace information.\n */\n function computeStackTraceFromStackProp(ex) {\n if (!ex.stack) {\n return null;\n }\n\n var chrome = /^\\s*at (.*?) ?\\(?((?:file|https?|chrome-extension):.*?):(\\d+)(?::(\\d+))?\\)?\\s*$/i,\n gecko = /^\\s*(.*?)(?:\\((.*?)\\))?@?((?:file|https?|chrome):.*?):(\\d+)(?::(\\d+))?\\s*$/i,\n winjs = /^\\s*at (?:((?:\\[object object\\])?.+) )?\\(?((?:ms-appx|http|https):.*?):(\\d+)(?::(\\d+))?\\)?\\s*$/i,\n lines = ex.stack.split('\\n'),\n stack = [],\n parts,\n element,\n reference = /^(.*) is undefined$/.exec(ex.message);\n\n for (var i = 0, j = lines.length; i < j; ++i) {\n if ((parts = gecko.exec(lines[i]))) {\n element = {\n 'url': parts[3],\n 'func': parts[1] || UNKNOWN_FUNCTION,\n 'args': parts[2] ? parts[2].split(',') : '',\n 'line': +parts[4],\n 'column': parts[5] ? +parts[5] : null\n };\n } else if ((parts = chrome.exec(lines[i]))) {\n element = {\n 'url': parts[2],\n 'func': parts[1] || UNKNOWN_FUNCTION,\n 'line': +parts[3],\n 'column': parts[4] ? +parts[4] : null\n };\n } else if ((parts = winjs.exec(lines[i]))) {\n element = {\n 'url': parts[2],\n 'func': parts[1] || UNKNOWN_FUNCTION,\n 'line': +parts[3],\n 'column': parts[4] ? +parts[4] : null\n };\n } else {\n continue;\n }\n\n if (!element.func && element.line) {\n element.func = guessFunctionName(element.url, element.line);\n }\n\n if (element.line) {\n element.context = gatherContext(element.url, element.line);\n }\n\n stack.push(element);\n }\n\n if (!stack.length) {\n return null;\n }\n\n if (stack[0] && stack[0].line && !stack[0].column && reference) {\n stack[0].column = findSourceInLine(reference[1], stack[0].url, stack[0].line);\n } else if (!stack[0].column && !_isUndefined(ex.columnNumber)) {\n // FireFox uses this awesome columnNumber property for its top frame\n // Also note, Firefox's column number is 0-based and everything else expects 1-based,\n // so adding 1\n stack[0].column = ex.columnNumber + 1;\n }\n\n return {\n 'mode': 'stack',\n 'name': ex.name,\n 'message': ex.message,\n 'url': document.location.href,\n 'stack': stack,\n 'useragent': navigator.userAgent\n };\n }\n\n /**\n * Computes stack trace information from the stacktrace property.\n * Opera 10 uses this property.\n * @param {Error} ex\n * @return {?Object.} Stack trace information.\n */\n function computeStackTraceFromStacktraceProp(ex) {\n // Access and store the stacktrace property before doing ANYTHING\n // else to it because Opera is not very good at providing it\n // reliably in other circumstances.\n var stacktrace = ex.stacktrace;\n\n var testRE = / line (\\d+), column (\\d+) in (?:]+)>|([^\\)]+))\\((.*)\\) in (.*):\\s*$/i,\n lines = stacktrace.split('\\n'),\n stack = [],\n parts;\n\n for (var i = 0, j = lines.length; i < j; i += 2) {\n if ((parts = testRE.exec(lines[i]))) {\n var element = {\n 'line': +parts[1],\n 'column': +parts[2],\n 'func': parts[3] || parts[4],\n 'args': parts[5] ? parts[5].split(',') : [],\n 'url': parts[6]\n };\n\n if (!element.func && element.line) {\n element.func = guessFunctionName(element.url, element.line);\n }\n if (element.line) {\n try {\n element.context = gatherContext(element.url, element.line);\n } catch (exc) {}\n }\n\n if (!element.context) {\n element.context = [lines[i + 1]];\n }\n\n stack.push(element);\n }\n }\n\n if (!stack.length) {\n return null;\n }\n\n return {\n 'mode': 'stacktrace',\n 'name': ex.name,\n 'message': ex.message,\n 'url': document.location.href,\n 'stack': stack,\n 'useragent': navigator.userAgent\n };\n }\n\n /**\n * NOT TESTED.\n * Computes stack trace information from an error message that includes\n * the stack trace.\n * Opera 9 and earlier use this method if the option to show stack\n * traces is turned on in opera:config.\n * @param {Error} ex\n * @return {?Object.} Stack information.\n */\n function computeStackTraceFromOperaMultiLineMessage(ex) {\n // Opera includes a stack trace into the exception message. An example is:\n //\n // Statement on line 3: Undefined variable: undefinedFunc\n // Backtrace:\n // Line 3 of linked script file://localhost/Users/andreyvit/Projects/TraceKit/javascript-client/sample.js: In function zzz\n // undefinedFunc(a);\n // Line 7 of inline#1 script in file://localhost/Users/andreyvit/Projects/TraceKit/javascript-client/sample.html: In function yyy\n // zzz(x, y, z);\n // Line 3 of inline#1 script in file://localhost/Users/andreyvit/Projects/TraceKit/javascript-client/sample.html: In function xxx\n // yyy(a, a, a);\n // Line 1 of function script\n // try { xxx('hi'); return false; } catch(ex) { TraceKit.report(ex); }\n // ...\n\n var lines = ex.message.split('\\n');\n if (lines.length < 4) {\n return null;\n }\n\n var lineRE1 = /^\\s*Line (\\d+) of linked script ((?:file|https?)\\S+)(?:: in function (\\S+))?\\s*$/i,\n lineRE2 = /^\\s*Line (\\d+) of inline#(\\d+) script in ((?:file|https?)\\S+)(?:: in function (\\S+))?\\s*$/i,\n lineRE3 = /^\\s*Line (\\d+) of function script\\s*$/i,\n stack = [],\n scripts = document.getElementsByTagName('script'),\n inlineScriptBlocks = [],\n parts,\n i,\n len,\n source;\n\n for (i in scripts) {\n if (_has(scripts, i) && !scripts[i].src) {\n inlineScriptBlocks.push(scripts[i]);\n }\n }\n\n for (i = 2, len = lines.length; i < len; i += 2) {\n var item = null;\n if ((parts = lineRE1.exec(lines[i]))) {\n item = {\n 'url': parts[2],\n 'func': parts[3],\n 'line': +parts[1]\n };\n } else if ((parts = lineRE2.exec(lines[i]))) {\n item = {\n 'url': parts[3],\n 'func': parts[4]\n };\n var relativeLine = (+parts[1]); // relative to the start of the + + + diff --git a/example/TypeScript/index.html b/example/TypeScript/index.html new file mode 100644 index 00000000..531242a9 --- /dev/null +++ b/example/TypeScript/index.html @@ -0,0 +1,11 @@ + + + + + Exceptionless Test + + + + + + diff --git a/example/TypeScript/index.ts b/example/TypeScript/index.ts new file mode 100644 index 00000000..3551bc62 --- /dev/null +++ b/example/TypeScript/index.ts @@ -0,0 +1,22 @@ +import * as exceptionless from '../../dist/exceptionless' + +var client = exceptionless.ExceptionlessClient.default; +client.config.serverUrl = 'http://localhost:50000'; +client.config.useDebugLogger(); + +// set some default data +client.config.defaultData['SampleUser'] = { + id:1, + name: 'Blake', + password: '123456', + passwordResetToken: 'a reset token', + myPasswordValue: '123456', + myPassword: '123456', + customValue: 'Password', + value: { + Password: '123456' + } +}; + +client.config.defaultTags.push('Example', 'JavaScript', 'TypeScript'); +client.submitLog('Testing'); diff --git a/example/TypeScript/tsconfig.json b/example/TypeScript/tsconfig.json new file mode 100644 index 00000000..fac851fd --- /dev/null +++ b/example/TypeScript/tsconfig.json @@ -0,0 +1,20 @@ +{ + "compilerOptions": { + "module": "commonjs", + "removeComments": true, + "sourceMap": false, + "target": "es5" + }, + "files": [ + "../../dist/exceptionless.d.ts", + "index.ts" + ], + "bundles": { + "angular": { + "files": [ + "../../dist/exceptionless.d.ts", + "index.ts" + ] + } + } +} diff --git a/example/express/app.js b/example/express/app.js index 5541e2c1..905d871f 100644 --- a/example/express/app.js +++ b/example/express/app.js @@ -1,7 +1,7 @@ var express = require('express'); var app = express(); -var client = require('../../dist/exceptionless').ExceptionlessClient.default; +var client = require('../../dist/exceptionless.node').ExceptionlessClient.default; client.config.apiKey = 'LhhP1C9gijpSKCslHHCvwdSIz298twx271n1l6xw'; client.config.serverUrl = 'http://localhost:50000'; client.config.useDebugLogger(); diff --git a/example/express/package.json b/example/express/package.json index 33f253c4..cd300cd7 100644 --- a/example/express/package.json +++ b/example/express/package.json @@ -6,7 +6,7 @@ "author": "Exceptionless", "license": "Apache", "dependencies": { - "express": "^4.12.3", + "express": "^4.12.4", "stack-trace": "0.0.9" } } diff --git a/gulpfile.js b/gulpfile.js index 33c7380d..f36ebb2f 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -13,15 +13,19 @@ gulp.task('clean', function () { del.sync(['dist'], { force: true }); }); -gulp.task('typescript.es5', function() { - return tsProject.src('src/tsconfig.es5.json').pipe(gulp.dest('dist/temp')); +gulp.task('typescript', function() { + return tsProject.src('src/tsconfig.json').pipe(gulp.dest('dist/temp')); }); -gulp.task('typescript.es5.integrations', function() { +gulp.task('typescript.integrations', ['typescript'], function() { return tsProject.src('src/integrations/tsconfig.json').pipe(gulp.dest('dist/temp')); }); -gulp.task('exceptionless.es5.umd', ['typescript.es5', 'typescript.es5.integrations'], function() { +gulp.task('typescript.node', function() { + return tsProject.src('src/tsconfig.node.json').pipe(gulp.dest('dist/temp')); +}); + +gulp.task('exceptionless.umd', ['typescript', 'typescript.integrations'], function() { return gulp.src('dist/temp/src/exceptionless.js') .pipe(sourcemaps.init({ loadMaps: true })) .pipe(umd({ @@ -29,12 +33,12 @@ gulp.task('exceptionless.es5.umd', ['typescript.es5', 'typescript.es5.integratio globalName: 'exceptionless', namespace: 'exceptionless' })) - .pipe(replace('}(this, function(require, exports, module) {', '}(this, function(require, exports, module) {\nif (!exports) {\n\tvar exports = {};\n}\nif (!require) {\n\tvar require = function(){};\n}\n')) + .pipe(replace('}(this, function(require, exports, module) {', '}(this, function(require, exports, module) {\nif (!exports) {\n\tvar exports = {};\n}\n')) .pipe(sourcemaps.write('.')) .pipe(gulp.dest('dist/temp')); }); -gulp.task('exceptionless.es5', ['exceptionless.es5.umd'], function() { +gulp.task('exceptionless', ['exceptionless.umd'], function() { gulp.src('dist/temp/src/exceptionless.d.ts') .pipe(gulp.dest('dist')); @@ -54,7 +58,6 @@ gulp.task('exceptionless.es5', ['exceptionless.es5.umd'], function() { .pipe(sourcemaps.init({ loadMaps: true })) .pipe(concat('exceptionless.js')) .pipe(replace('exceptionless-js/1.0.0.0', 'exceptionless-js/' + package.version)) - .pipe(replace('require(\'source-map/lib/source-map/source-map-consumer\')', 'null')) // needed for node until source maps dependency is fixed .pipe(sourcemaps.write('.')) .pipe(gulp.dest('dist')); @@ -62,27 +65,14 @@ gulp.task('exceptionless.es5', ['exceptionless.es5.umd'], function() { .pipe(sourcemaps.init({ loadMaps: true })) .pipe(concat('exceptionless.min.js')) .pipe(replace('exceptionless-js/1.0.0.0', 'exceptionless-js/' + package.version)) - .pipe(replace('require(\'source-map/lib/source-map/source-map-consumer\')', 'null')) // needed for node until source maps dependency is fixed .pipe(uglify({ output: { beautify: false }})) .pipe(sourcemaps.write('.')) .pipe(gulp.dest('dist')) }); -gulp.task('typescript.es6', function() { - return tsProject.src('src/tsconfig.es6.json').pipe(gulp.dest('dist/temp')); -}); - -gulp.task('exceptionless.es6', ['typescript.es6'], function() { - gulp.src('dist/temp/src/exceptionless.es6.d.ts').pipe(gulp.dest('dist')); - - var files = [ - 'node_modules/tracekit/tracekit.js', - 'dist/temp/src/exceptionless.es6.js' - ]; - - gulp.src(files) +gulp.task('exceptionless.node', ['typescript.node'], function() { + gulp.src('dist/temp/src/exceptionless.node.js') .pipe(sourcemaps.init({ loadMaps: true })) - .pipe(concat('exceptionless.es6.js')) .pipe(replace('exceptionless-js/1.0.0.0', 'exceptionless-js/' + package.version)) .pipe(sourcemaps.write('.')) .pipe(gulp.dest('dist')); @@ -92,7 +82,7 @@ gulp.task('watch', ['build'], function() { gulp.watch('*.ts', ['build']); }); -gulp.task('build', ['clean', 'exceptionless.es5']); +gulp.task('build', ['clean', 'exceptionless', 'exceptionless.node']); gulp.task('typescript.test', function() { return tsProject.src('src/tsconfig.test.json').pipe(gulp.dest('dist/temp')); @@ -106,7 +96,7 @@ gulp.task('exceptionless.test.umd', ['typescript.test'], function() { globalName: 'exceptionless', namespace: 'exceptionless' })) - .pipe(replace('}(this, function(require, exports, module) {', '}(this, function(require, exports, module) {\nif (!exports) {\n\tvar exports = {};\n}\nif (!require) {\n\tvar require = function(){};\n}\n')) + .pipe(replace('}(this, function(require, exports, module) {', '}(this, function(require, exports, module) {\nif (!exports) {\n\tvar exports = {};\n}\n')) .pipe(sourcemaps.write('.')) .pipe(gulp.dest('dist/temp')); }); diff --git a/karma.conf.js b/karma.conf.js index e2e8e8bc..ee49d825 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -4,7 +4,7 @@ module.exports = function (config) { config.set({ basePath: 'src', browserNoActivityTimeout: 100000, - browsers: ['Chrome'], + browsers: ['PhantomJS'], frameworks: ['jasmine'], files: [ '../node_modules/es5-shim/es5-shim.js', diff --git a/package.json b/package.json index 63f8cfff..10515416 100644 --- a/package.json +++ b/package.json @@ -1,9 +1,9 @@ { "name": "exceptionless", - "version": "0.4.1", + "version": "0.5.0", "description": "JavaScript client for Exceptionless", "license": "Apache", - "main": "dist/exceptionless.es5.js", + "main": "dist/exceptionless.node.js", "scripts": { "prepublish": "tsd reinstall -s", "start": "gulp", @@ -20,8 +20,8 @@ ], "devDependencies": { "bower": "~1.4.1", - "del": "^1.1.1", - "es5-shim": "^4.1.1", + "del": "^1.2.0", + "es5-shim": "^4.1.4", "es6-shim": "^0.31.2", "gulp": "^3.8.11", "gulp-concat": "^2.5.2", @@ -31,11 +31,11 @@ "gulp-uglify": "^1.2.0", "gulp-wrap-umd": "^0.2.1", "jasmine-core": "^2.3.4", - "karma": "~0.12.31", - "karma-chrome-launcher": "^0.1.12", + "karma": "~0.12.35", "karma-cli": "0.0.4", "karma-jasmine": "~0.3.5", - "rimraf": "^2.3.3", + "karma-phantomjs-launcher": "^0.2.0", + "rimraf": "^2.3.4", "tracekit": "^0.2.0", "tsproject": "^0.8.10", "typescript": "1.5.0-beta" diff --git a/src/bootstrap/DefaultBootstrapper.ts b/src/bootstrap/DefaultBootstrapper.ts deleted file mode 100644 index eb81a333..00000000 --- a/src/bootstrap/DefaultBootstrapper.ts +++ /dev/null @@ -1,73 +0,0 @@ -import { IBootstrapper } from 'IBootstrapper'; -import { Configuration } from '../configuration/Configuration'; -import { IConfigurationSettings } from '../configuration/IConfigurationSettings'; -import { IError } from '../models/IError'; -import { DefaultErrorParser } from '../services/DefaultErrorParser'; -import { DefaultModuleCollector } from '../services/DefaultModuleCollector'; -import { DefaultRequestInfoCollector } from '../services/DefaultRequestInfoCollector'; -import { DefaultSubmissionClient } from '../submission/DefaultSubmissionClient'; -import { ExceptionlessClient } from '../ExceptionlessClient'; -import { Utils } from '../Utils'; - -export class DefaultBootstrapper implements IBootstrapper { - public register(): void { - function getDefaultsSettingsFromScriptTag(): IConfigurationSettings { - if (!document || !document.getElementsByTagName) { - return null; - } - - var scripts = document.getElementsByTagName('script'); - for (var index = 0; index < scripts.length; index++) { - if (scripts[index].src && scripts[index].src.indexOf('/exceptionless') > -1) { - return Utils.parseQueryString(scripts[index].src.split('?').pop()); - } - } - return null; - } - - function processUnhandledException(stackTrace:TraceKit.StackTrace, options?:any): void { - var builder = ExceptionlessClient.default.createUnhandledException(new Error(stackTrace.message || (options || {}).status || 'Script error'), 'onerror'); - builder.pluginContextData['@@_TraceKit.StackTrace'] = stackTrace; - builder.submit(); - } - - function processJQueryAjaxError(event, xhr, settings, error:Error): void { - var client = ExceptionlessClient.default; - if (xhr.status === 404) { - client.submitNotFound(settings.url); - } else if (xhr.status !== 401) { - client.createUnhandledException(error, 'JQuery.ajaxError') - .setSource(settings.url) - .setProperty('status', xhr.status) - .setProperty('request', settings.data) - .setProperty('response', xhr.responseText && xhr.responseText.slice && xhr.responseText.slice(0, 1024)) - .submit(); - } - } - - if (typeof window === 'undefined' || typeof document === 'undefined') { - return; - } - - var defaults = Configuration.defaults; - var settings = getDefaultsSettingsFromScriptTag(); - if (settings && (settings.apiKey || settings.serverUrl)) { - defaults.apiKey = settings.apiKey; - defaults.serverUrl = settings.serverUrl; - } - - defaults.errorParser = new DefaultErrorParser(); - defaults.moduleCollector = new DefaultModuleCollector(); - defaults.requestInfoCollector = new DefaultRequestInfoCollector(); - defaults.submissionClient = new DefaultSubmissionClient(); - - TraceKit.report.subscribe(processUnhandledException); - TraceKit.extendToAsynchronousCallbacks(); - - if (typeof $ !== 'undefined' && $(document)) { - $(document).ajaxError(processJQueryAjaxError); - } - } -} - -declare var $; diff --git a/src/bootstrap/IBootstrapper.ts b/src/bootstrap/IBootstrapper.ts deleted file mode 100644 index 2bda0b21..00000000 --- a/src/bootstrap/IBootstrapper.ts +++ /dev/null @@ -1,3 +0,0 @@ -export interface IBootstrapper { - register():void; -} diff --git a/src/bootstrap/NodeBootstrapper.ts b/src/bootstrap/NodeBootstrapper.ts deleted file mode 100644 index 3e251c4b..00000000 --- a/src/bootstrap/NodeBootstrapper.ts +++ /dev/null @@ -1,92 +0,0 @@ -import { IBootstrapper } from 'IBootstrapper'; -import { Configuration } from '../configuration/Configuration'; -import { IConfigurationSettings } from '../configuration/IConfigurationSettings'; -import { IError } from '../models/IError'; -import { NodeEnvironmentInfoCollector } from '../services/NodeEnvironmentInfoCollector'; -import { NodeErrorParser } from '../services/NodeErrorParser'; -import { NodeRequestInfoCollector } from '../services/NodeRequestInfoCollector'; -import { NodeSubmissionClient } from '../submission/NodeSubmissionClient'; -import { ExceptionlessClient } from '../ExceptionlessClient'; -import { Utils } from '../Utils'; - -export class NodeBootstrapper implements IBootstrapper { - public register(): void { - const beforeExit:string = 'beforeExit'; - const uncaughtException:string = 'uncaughtException'; - - if (!(typeof window === 'undefined' && typeof global !== 'undefined' && {}.toString.call(global) === '[object global]')) { - return; - } - - var defaults = Configuration.defaults; - defaults.environmentInfoCollector = new NodeEnvironmentInfoCollector(); - defaults.errorParser = new NodeErrorParser(); - defaults.requestInfoCollector = new NodeRequestInfoCollector(); - defaults.submissionClient = new NodeSubmissionClient(); - - process.on(uncaughtException, function (error:Error) { - ExceptionlessClient.default.submitUnhandledException(error, uncaughtException); - }); - - process.on(beforeExit, function (code:number) { - /** - * exit codes: https://nodejs.org/api/process.html#process_event_exit - */ - function getExitCodeReason(code:number): string { - if (code === 1) { - return 'Uncaught Fatal Exception'; - } - - if (code === 3) { - return 'Internal JavaScript Parse Error'; - } - - if (code === 4) { - return 'Internal JavaScript Evaluation Failure'; - } - - if (code === 5) { - return 'Fatal Exception'; - } - - if (code === 6) { - return 'Non-function Internal Exception Handler '; - } - - if (code === 7) { - return 'Internal Exception Handler Run-Time Failure'; - } - - if (code === 8) { - return 'Uncaught Exception'; - } - - if (code === 9) { - return 'Invalid Argument'; - } - - if (code === 10) { - return 'Internal JavaScript Run-Time Failure'; - } - - if (code === 12) { - return 'Invalid Debug Argument'; - } - - if (code > 128) { - return 'Signal Exits'; - } - - return null; - } - - var client = ExceptionlessClient.default; - var message = getExitCodeReason(code); - if (message !== null) { - client.submitLog(beforeExit, message, 'Error') - } - - client.config.queue.process() - }); - } -} diff --git a/src/exceptionless.node.ts b/src/exceptionless.node.ts new file mode 100644 index 00000000..c7d5f7f9 --- /dev/null +++ b/src/exceptionless.node.ts @@ -0,0 +1,127 @@ +import { Configuration } from './configuration/Configuration'; +import { IConfigurationSettings } from './configuration/IConfigurationSettings'; +import { SettingsManager } from './configuration/SettingsManager'; +import { ILastReferenceIdManager } from './lastReferenceIdManager/ILastReferenceIdManager'; +import { DefaultLastReferenceIdManager } from './lastReferenceIdManager/DefaultLastReferenceIdManager'; +import { ConsoleLog } from './logging/ConsoleLog'; +import { ILog } from './logging/ILog'; +import { NullLog } from './logging/NullLog'; +import { IClientConfiguration } from './models/IClientConfiguration'; +import { IEnvironmentInfo } from './models/IEnvironmentInfo'; +import { IError } from './models/IError'; +import { IEvent } from './models/IEvent'; +import { IInnerError } from './models/IInnerError'; +import { IMethod } from './models/IMethod'; +import { IModule } from './models/IModule'; +import { IParameter } from './models/IParameter'; +import { IRequestInfo } from './models/IRequestInfo'; +import { IStackFrame } from './models/IStackFrame'; +import { IUserDescription } from './models/IUserDescription'; +import { IUserInfo } from './models/IUserInfo'; +import { ContextData } from './plugins/ContextData'; +import { EventPluginContext } from './plugins/EventPluginContext'; +import { EventPluginManager } from './plugins/EventPluginManager'; +import { IEventPlugin } from './plugins/IEventPlugin'; +import { ConfigurationDefaultsPlugin } from './plugins/default/ConfigurationDefaultsPlugin'; +import { ErrorPlugin } from './plugins/default/ErrorPlugin'; +import { ModuleInfoPlugin } from './plugins/default/ModuleInfoPlugin'; +import { ReferenceIdPlugin } from './plugins/default/ReferenceIdPlugin'; +import { RequestInfoPlugin } from './plugins/default/RequestInfoPlugin'; +import { EnvironmentInfoPlugin } from 'plugins/default/EnvironmentInfoPlugin'; +import { SubmissionMethodPlugin } from './plugins/default/SubmissionMethodPlugin'; +import { DefaultEventQueue } from './queue/DefaultEventQueue'; +import { IEventQueue } from './queue/IEventQueue'; +import { IEnvironmentInfoCollector } from './services/IEnvironmentInfoCollector'; +import { IErrorParser } from './services/IErrorParser'; +import { IModuleCollector } from './services/IModuleCollector'; +import { IRequestInfoCollector } from './services/IRequestInfoCollector'; +import { NodeEnvironmentInfoCollector } from './services/NodeEnvironmentInfoCollector'; +import { NodeErrorParser } from './services/NodeErrorParser'; +import { NodeRequestInfoCollector } from './services/NodeRequestInfoCollector'; +import { InMemoryStorage } from './storage/InMemoryStorage'; +import { IStorage } from './storage/IStorage'; +import { IStorageItem } from './storage/IStorageItem'; +import { DefaultSubmissionClient } from './submission/DefaultSubmissionClient'; +import { ISubmissionClient } from './submission/ISubmissionClient'; +import { NodeSubmissionClient } from './submission/NodeSubmissionClient'; +import { SettingsResponse } from './submission/SettingsResponse'; +import { SubmissionResponse } from './submission/SubmissionResponse'; +import { EventBuilder } from 'EventBuilder'; +import { ExceptionlessClient } from 'ExceptionlessClient'; +import { Utils } from 'Utils'; + +const beforeExit:string = 'beforeExit'; +const uncaughtException:string = 'uncaughtException'; + +var defaults = Configuration.defaults; +defaults.environmentInfoCollector = new NodeEnvironmentInfoCollector(); +defaults.errorParser = new NodeErrorParser(); +defaults.requestInfoCollector = new NodeRequestInfoCollector(); +defaults.submissionClient = new NodeSubmissionClient(); + +process.on(uncaughtException, function (error:Error) { + ExceptionlessClient.default.submitUnhandledException(error, uncaughtException); +}); + +process.on(beforeExit, function (code:number) { + /** + * exit codes: https://nodejs.org/api/process.html#process_event_exit + */ + function getExitCodeReason(code:number): string { + if (code === 1) { + return 'Uncaught Fatal Exception'; + } + + if (code === 3) { + return 'Internal JavaScript Parse Error'; + } + + if (code === 4) { + return 'Internal JavaScript Evaluation Failure'; + } + + if (code === 5) { + return 'Fatal Exception'; + } + + if (code === 6) { + return 'Non-function Internal Exception Handler '; + } + + if (code === 7) { + return 'Internal Exception Handler Run-Time Failure'; + } + + if (code === 8) { + return 'Uncaught Exception'; + } + + if (code === 9) { + return 'Invalid Argument'; + } + + if (code === 10) { + return 'Internal JavaScript Run-Time Failure'; + } + + if (code === 12) { + return 'Invalid Debug Argument'; + } + + if (code > 128) { + return 'Signal Exits'; + } + + return null; + } + + var client = ExceptionlessClient.default; + var message = getExitCodeReason(code); + if (message !== null) { + client.submitLog(beforeExit, message, 'Error') + } + + client.config.queue.process() +}); + +(Error).stackTraceLimit = Infinity; diff --git a/src/exceptionless.ts b/src/exceptionless.ts index 53ca44b8..ffc61331 100644 --- a/src/exceptionless.ts +++ b/src/exceptionless.ts @@ -1,6 +1,3 @@ -import { IBootstrapper } from './bootstrap/IBootstrapper'; -import { NodeBootstrapper } from './bootstrap/NodeBootstrapper'; -import { DefaultBootstrapper } from './bootstrap/DefaultBootstrapper'; import { Configuration } from './configuration/Configuration'; import { IConfigurationSettings } from './configuration/IConfigurationSettings'; import { SettingsManager } from './configuration/SettingsManager'; @@ -38,23 +35,73 @@ import { IEnvironmentInfoCollector } from './services/IEnvironmentInfoCollector' import { IErrorParser } from './services/IErrorParser'; import { IModuleCollector } from './services/IModuleCollector'; import { IRequestInfoCollector } from './services/IRequestInfoCollector'; -import { NodeEnvironmentInfoCollector } from './services/NodeEnvironmentInfoCollector'; -import { NodeErrorParser } from './services/NodeErrorParser'; -import { NodeRequestInfoCollector } from './services/NodeRequestInfoCollector'; import { DefaultErrorParser } from './services/DefaultErrorParser'; import { DefaultModuleCollector } from './services/DefaultModuleCollector'; import { DefaultRequestInfoCollector } from './services/DefaultRequestInfoCollector'; import { InMemoryStorage } from './storage/InMemoryStorage'; import { IStorage } from './storage/IStorage'; +import { IStorageItem } from './storage/IStorageItem'; import { DefaultSubmissionClient } from './submission/DefaultSubmissionClient'; import { ISubmissionClient } from './submission/ISubmissionClient'; -import { NodeSubmissionClient } from './submission/NodeSubmissionClient'; import { SettingsResponse } from './submission/SettingsResponse'; import { SubmissionResponse } from './submission/SubmissionResponse'; import { EventBuilder } from 'EventBuilder'; import { ExceptionlessClient } from 'ExceptionlessClient'; import { Utils } from 'Utils'; -new NodeBootstrapper().register(); -new DefaultBootstrapper().register(); +function getDefaultsSettingsFromScriptTag(): IConfigurationSettings { + if (!document || !document.getElementsByTagName) { + return null; + } + + var scripts = document.getElementsByTagName('script'); + for (var index = 0; index < scripts.length; index++) { + if (scripts[index].src && scripts[index].src.indexOf('/exceptionless') > -1) { + return Utils.parseQueryString(scripts[index].src.split('?').pop()); + } + } + return null; +} + +function processUnhandledException(stackTrace:TraceKit.StackTrace, options?:any): void { + var builder = ExceptionlessClient.default.createUnhandledException(new Error(stackTrace.message || (options || {}).status || 'Script error'), 'onerror'); + builder.pluginContextData['@@_TraceKit.StackTrace'] = stackTrace; + builder.submit(); +} + +function processJQueryAjaxError(event, xhr, settings, error:Error): void { + var client = ExceptionlessClient.default; + if (xhr.status === 404) { + client.submitNotFound(settings.url); + } else if (xhr.status !== 401) { + client.createUnhandledException(error, 'JQuery.ajaxError') + .setSource(settings.url) + .setProperty('status', xhr.status) + .setProperty('request', settings.data) + .setProperty('response', xhr.responseText && xhr.responseText.slice && xhr.responseText.slice(0, 1024)) + .submit(); + } +} + +var defaults = Configuration.defaults; +var settings = getDefaultsSettingsFromScriptTag(); +if (settings && (settings.apiKey || settings.serverUrl)) { + defaults.apiKey = settings.apiKey; + defaults.serverUrl = settings.serverUrl; +} + +defaults.errorParser = new DefaultErrorParser(); +defaults.moduleCollector = new DefaultModuleCollector(); +defaults.requestInfoCollector = new DefaultRequestInfoCollector(); +defaults.submissionClient = new DefaultSubmissionClient(); + +TraceKit.report.subscribe(processUnhandledException); +TraceKit.extendToAsynchronousCallbacks(); + +if (typeof $ !== 'undefined' && $(document)) { + $(document).ajaxError(processJQueryAjaxError); +} + (Error).stackTraceLimit = Infinity; + +declare var $; diff --git a/src/integrations/angular.ts b/src/integrations/angular.ts index 9be0114a..538d7236 100644 --- a/src/integrations/angular.ts +++ b/src/integrations/angular.ts @@ -1,3 +1,7 @@ +// Update once ts issue is fixed: https://github.com/Microsoft/TypeScript/issues/2192 +//import * as exceptionless from '../../dist/exceptionless' +declare var exceptionless; + angular.module('exceptionless', []) .constant('$ExceptionlessClient', exceptionless.ExceptionlessClient.default) .factory('exceptionlessHttpInterceptor', ['$q', '$ExceptionlessClient', function ($q, $ExceptionlessClient) { @@ -43,6 +47,10 @@ angular.module('exceptionless', []) }]) .run(['$rootScope', '$ExceptionlessClient', function($rootScope, $ExceptionlessClient) { $rootScope.$on('$routeChangeSuccess', function(event, next, current) { + if (!current) { + return; + } + $ExceptionlessClient.createFeatureUsage(current.name) .setProperty('next', next) .setProperty('current', current) @@ -57,7 +65,7 @@ angular.module('exceptionless', []) }); $rootScope.$on('$stateChangeSuccess', function(event, toState, toParams, fromState, fromParams) { - if (toState.name === 'otherwise') { + if (!toState || toState.name === 'otherwise') { return; } @@ -70,6 +78,10 @@ angular.module('exceptionless', []) }); $rootScope.$on('$stateNotFound', function(event, unfoundState, fromState, fromParams) { + if (!unfoundState) { + return; + } + $ExceptionlessClient.createNotFound(unfoundState.to) .setProperty('unfoundState', unfoundState) .setProperty('fromState', fromState) @@ -94,5 +106,3 @@ angular.module('exceptionless', []) $ExceptionlessClient.config.queue.process(); }); }]); - -declare var exceptionless; diff --git a/src/queue/DefaultEventQueue-spec.ts b/src/queue/DefaultEventQueue-spec.ts index 0314b19d..78685274 100644 --- a/src/queue/DefaultEventQueue-spec.ts +++ b/src/queue/DefaultEventQueue-spec.ts @@ -2,17 +2,25 @@ import { Configuration } from '../configuration/Configuration'; import { IEvent } from '../models/IEvent'; describe('DefaultEventQueue', () => { - it('should enqueue event', () => { - var config = new Configuration({ apiKey:'LhhP1C9gijpSKCslHHCvwdSIz298twx271n1l6xw', serverUrl:'http://localhost:50000'}); + function getConfiguration(): Configuration { + var config:Configuration = new Configuration({ + apiKey:'LhhP1C9gijpSKCslHHCvwdSIz298twx271n1l6xw', + serverUrl:'http://localhost:50000' + }); + expect(config.storage.getList().length).toBe(0); + return config; + } + + it('should enqueue event', () => { + var config:Configuration = getConfiguration(); var event:IEvent = { type: 'log', reference_id: '123454321' }; config.queue.enqueue(event); expect(config.storage.getList().length).toBe(1); }); it('should process queue', () => { - var config = new Configuration({ apiKey:'LhhP1C9gijpSKCslHHCvwdSIz298twx271n1l6xw', serverUrl:'http://localhost:50000'}); - expect(config.storage.getList().length).toBe(0); + var config:Configuration = getConfiguration(); var event:IEvent = { type: 'log', reference_id: '123454321' }; config.queue.enqueue(event); expect(config.storage.getList().length).toBe(1); @@ -26,8 +34,7 @@ describe('DefaultEventQueue', () => { }); it('should discard event submission', () => { - var config = new Configuration({ apiKey:'LhhP1C9gijpSKCslHHCvwdSIz298twx271n1l6xw', serverUrl:'http://localhost:50000'}); - expect(config.storage.getList().length).toBe(0); + var config:Configuration = getConfiguration(); config.queue.suspendProcessing(1, true); var event:IEvent = { type: 'log', reference_id: '123454321' }; @@ -36,8 +43,7 @@ describe('DefaultEventQueue', () => { }); it('should suspend processing', (done) => { - var config = new Configuration({ apiKey:'LhhP1C9gijpSKCslHHCvwdSIz298twx271n1l6xw', serverUrl:'http://localhost:50000'}); - expect(config.storage.getList().length).toBe(0); + var config:Configuration = getConfiguration(); config.queue.suspendProcessing(.0001); var event:IEvent = { type: 'log', reference_id: '123454321' }; diff --git a/src/storage/IStorage.ts b/src/storage/IStorage.ts index c1d243ae..118eee29 100644 --- a/src/storage/IStorage.ts +++ b/src/storage/IStorage.ts @@ -1,6 +1,8 @@ -export interface IStorage{ - save(path:string, value:T):boolean; +import { IStorageItem } from './IStorageItem'; + +export interface IStorage { + save(path:string, value:T):boolean; get(path:string):T; - getList(searchPattern?:string, limit?:number):{ path:string, value:T }[]; + getList(searchPattern?:string, limit?:number):IStorageItem[]; remove(path:string):void; } diff --git a/src/storage/IStorageItem.ts b/src/storage/IStorageItem.ts new file mode 100644 index 00000000..5d3a167f --- /dev/null +++ b/src/storage/IStorageItem.ts @@ -0,0 +1,5 @@ +export interface IStorageItem { + created:number; + path:string; + value:T; +} diff --git a/src/storage/InMemoryStorage-spec.ts b/src/storage/InMemoryStorage-spec.ts index e4576611..eb056b2f 100644 --- a/src/storage/InMemoryStorage-spec.ts +++ b/src/storage/InMemoryStorage-spec.ts @@ -1,5 +1,6 @@ import { IEvent } from '../models/IEvent'; import { InMemoryStorage } from './InMemoryStorage'; +import { IStorageItem } from './IStorageItem'; describe('InMemoryStorage', () => { it('should save events', () => { @@ -17,6 +18,23 @@ describe('InMemoryStorage', () => { expect(storage.getList(key + '1').length).toBe(1); }); + it('should save once', () => { + var storage = new InMemoryStorage(); + storage.save('one', 1); + expect(storage.getList().length).toBe(1); + storage.save('one', 1); + expect(storage.getList().length).toBe(1); + }); + + it('should get by key', () => { + var storage = new InMemoryStorage(); + storage.save('ex-server-settings.json-version', 1); + storage.save('ex-server-settings.json', { exist: true }); + expect(storage.getList().length).toBe(2); + expect(storage.get('ex-server-settings.json-version')).toBe(1); + expect(storage.get('ex-server-settings.json')).toEqual({ exist: true }); + }); + it('should get saved events', () => { var storage = new InMemoryStorage(); var key = 'ex-q-'; @@ -90,4 +108,61 @@ describe('InMemoryStorage', () => { expect(storage.getList().length).toBe(0); }); + + it('should get with limit', () => { + var storage = new InMemoryStorage(250); + for (var index:number = 0; index < 260; index++) { + storage.save('ex-q-' + index, { type: 'log', reference_id: index.toString() }); + } + + expect(storage.getList().length).toBe(250); + expect(storage.getList(null).length).toBe(250); + expect(storage.getList(null, 1).length).toBe(1) + }); + + it('should get the oldest events', () => { + function getDate(baseDate:Date, offset:number) { + return new Date(baseDate.getTime() + (offset * 60000)); + } + + const date:Date = new Date(); + var storage = new InMemoryStorage(); + for (var index:number = 0; index < 10; index++) { + storage.save('ex-q-' + index, { + date: getDate(date, index), + type: 'log', + reference_id: index.toString() + }); + + expect(storage.getList().length).toBe(index + 1); + } + + var offset:number = 0; + var events:IStorageItem[] = storage.getList('ex-q-', 2); + while (events && events.length > 0) { + expect(2).toBe(events.length); + for (var ei = 0; ei < 2; ei++) { + expect(getDate(date, offset++)).toEqual(events[ei].value.date); + storage.remove(events[ei].path); + } + + events = storage.getList('ex-q-', 2); + } + }); + + it('should respect max items limit', () => { + var storage = new InMemoryStorage(5); + for (var index:number = 0; index < 5; index++) { + storage.save('ex-q-' + index, { type: 'log', reference_id: index.toString() }); + } + + var events:IStorageItem[] = storage.getList(); + expect(events.length).toBe(5); + expect(events[0].path).toBe('ex-q-0'); + storage.save('ex-q-6', { type: 'log', reference_id: '6' }); + + events = storage.getList(); + expect(events.length).toBe(5); + expect(events[0].path).toBe('ex-q-1'); + }); }); diff --git a/src/storage/InMemoryStorage.ts b/src/storage/InMemoryStorage.ts index 4e2dabbc..83656536 100644 --- a/src/storage/InMemoryStorage.ts +++ b/src/storage/InMemoryStorage.ts @@ -1,28 +1,48 @@ +import { IEvent } from '../models/IEvent'; import { IStorage } from './IStorage'; +import { IStorageItem } from './IStorageItem'; export class InMemoryStorage implements IStorage { - private _items = {}; + private _items:IStorageItem[] = []; + private _maxItems:number; + + constructor(maxItems?:number) { + this._maxItems = maxItems > 0 ? maxItems : 250; + } + + public save(path:string, value:T):boolean { + if (!path || !value) { + return false; + } + + this.remove(path); + if (this._items.push({ created: new Date().getTime(), path: path, value: value }) > this._maxItems) { + this._items.shift(); + } - public save(path:string, value:T):boolean { - this._items[path] = value; return true; } public get(path:string):T { - return this._items[path] || null; + var item:IStorageItem = path ? this.getList(`^${path}$`, 1)[0] : null; + return item ? item.value : null; } - public getList(searchPattern?:string, limit?:number):{ path:string, value:T }[] { - var regex = new RegExp(searchPattern || '.*'); + public getList(searchPattern?:string, limit?:number):IStorageItem[] { + var items = this._items; // Optimization for minifier + if (!searchPattern) { + return items.slice(0, limit); + } - var results:{ path:string, value:T }[] = []; - for (var key in this._items) { - if (results.length >= limit) { - break; - } + var regex = new RegExp(searchPattern); + var results:IStorageItem[] = []; + for (var index = 0; index < items.length; index++) { + if (regex.test(items[index].path)) { + results.push(items[index]); - if (regex.test(key)) { - results.push({ path: key, value: this._items[key] }); + if (results.length >= limit) { + break; + } } } @@ -31,7 +51,10 @@ export class InMemoryStorage implements IStorage { public remove(path:string):void { if (path) { - delete this._items[path]; + var item = this.getList(`^${path}$`, 1)[0]; + if (item) { + this._items.splice(this._items.indexOf(item), 1); + } } } } diff --git a/src/submission/DefaultSubmissionClient.ts b/src/submission/DefaultSubmissionClient.ts index 9597b4c6..0b3dec98 100644 --- a/src/submission/DefaultSubmissionClient.ts +++ b/src/submission/DefaultSubmissionClient.ts @@ -45,11 +45,11 @@ export class DefaultSubmissionClient implements ISubmissionClient { config.log.error(`Unable to parse settings: '${data}'`); } - if (!settings || !settings.settings || !settings.version) { + if (!settings || isNaN(settings.version)) { return callback(new SettingsResponse(false, null, -1, null, 'Invalid configuration settings.')); } - callback(new SettingsResponse(true, settings.settings, settings.version)); + callback(new SettingsResponse(true, settings.settings || {}, settings.version)); }); } diff --git a/src/tsconfig.es6.json b/src/tsconfig.json similarity index 56% rename from src/tsconfig.es6.json rename to src/tsconfig.json index d12b93c8..642e549b 100644 --- a/src/tsconfig.es6.json +++ b/src/tsconfig.json @@ -1,18 +1,17 @@ { "compilerOptions": { "declaration": true, + "module": "commonjs", "removeComments": true, "sourceMap": true, - "target": "es6" + "target": "es5" }, "bundles": { - "exceptionless.es6": { + "exceptionless": { "files": [ - "typings/node/node.d.ts", - "typings/stack-trace/stack-trace.d.ts", "typings/tracekit/tracekit.d.ts", - "Exceptionless.ts" + "exceptionless.ts" ] } } diff --git a/src/tsconfig.es5.json b/src/tsconfig.node.json similarity index 75% rename from src/tsconfig.es5.json rename to src/tsconfig.node.json index 19057f66..533b4be7 100644 --- a/src/tsconfig.es5.json +++ b/src/tsconfig.node.json @@ -7,13 +7,12 @@ "target": "es5" }, "bundles": { - "exceptionless": { + "exceptionless.node": { "files": [ "typings/node/node.d.ts", "typings/stack-trace/stack-trace.d.ts", - "typings/tracekit/tracekit.d.ts", - "Exceptionless.ts" + "exceptionless.node.ts" ] } }