diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index e80dc3a7..b8cb55d9 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -14,5 +14,5 @@ "streetsidesoftware.code-spell-checker" ], "forwardPorts": [3000], - "postCreateCommand": "npm install", + "postCreateCommand": "npm install" } diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 00000000..6e852e3f --- /dev/null +++ b/.eslintignore @@ -0,0 +1,8 @@ +.DS_Store +.eslintrc.js +minver +node_modules +example/svelte-kit/.svelte-kit + +# Ignore files for PNPM, NPM and YARN +package-lock.json diff --git a/.eslintrc.js b/.eslintrc.js index 89d7e09d..fdcb3fb8 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -11,6 +11,7 @@ module.exports = { "plugin:@typescript-eslint/eslint-recommended", "plugin:@typescript-eslint/recommended", "plugin:@typescript-eslint/recommended-requiring-type-checking", + "prettier" ], globals: { MutationObserver: "readonly", @@ -18,31 +19,24 @@ module.exports = { Atomics: "readonly", BigInt: "readonly", BigInt64Array: "readonly", - BigUint64Array: "readonly", + BigUint64Array: "readonly" }, parser: "@typescript-eslint/parser", parserOptions: { - ecmaVersion: 2020, + ecmaVersion: 2022, sourceType: "module", - project: ["./tsconfig.eslint.json"], + project: ["./tsconfig.json"], tsconfigRootDir: __dirname }, - plugins: [ - "@typescript-eslint", - "import", - "jest" - ], - ignorePatterns: [ - "dist", - "node_modules", - "example" - ], + plugins: ["@typescript-eslint", "import", "jest"], + ignorePatterns: ["dist", "node_modules", "example"], rules: { "@typescript-eslint/no-inferrable-types": "off", "@typescript-eslint/no-unsafe-assignment": "off", "@typescript-eslint/no-unsafe-call": "off", "@typescript-eslint/no-unsafe-member-access": "off", "@typescript-eslint/no-unsafe-return": "off", - "@typescript-eslint/restrict-plus-operands": "off", + "@typescript-eslint/no-redundant-type-constituents": "off", + "@typescript-eslint/restrict-plus-operands": "off" } }; diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..6704eb6c --- /dev/null +++ b/.gitattributes @@ -0,0 +1,3 @@ +# Auto detect text files and perform LF normalization +* text=auto eol=lf +*.sh text eol=lf diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 00000000..25f88763 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +github: exceptionless diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index cdb149ba..f36bd6c6 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,5 +1,5 @@ name: Build -on: [ push, pull_request ] +on: [push, pull_request] jobs: build: @@ -11,33 +11,36 @@ jobs: - macos-latest - windows-latest node_version: - - 18 + - 20 name: Node ${{ matrix.node_version }} on ${{ matrix.os }} steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 - name: Build Reason run: "echo ref: ${{github.ref}} event: ${{github.event_name}}" - name: Setup Node.js environment - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version: ${{ matrix.node_version }} - registry-url: 'https://registry.npmjs.org' - - name: Install latest NPM - run: npm install -g npm@9 + registry-url: "https://registry.npmjs.org" + - name: Cache node_modules + uses: actions/cache@v4 + with: + path: node_modules + key: ${{ matrix.node_version }}-${{ runner.os }}-node-modules-${{ hashFiles('package-lock.json') }} - name: Set Min Version uses: Stelzi79/action-minver@3.0.1 id: version with: - minimum-major-minor: 2.0 + minimum-major-minor: 3.0 tag-prefix: v - name: Build Version run: | npm install --global replace-in-files-cli - replace-in-files --string="2.0.0-dev" --replacement=${{steps.version.outputs.version}} packages/core/src/configuration/Configuration.ts - replace-in-files --string="2.0.0-dev" --replacement=${{steps.version.outputs.version}} **/package*.json + replace-in-files --string="3.0.0-dev" --replacement=${{steps.version.outputs.version}} packages/core/src/configuration/Configuration.ts + replace-in-files --string="3.0.0-dev" --replacement=${{steps.version.outputs.version}} **/package*.json npm install - name: Build run: npm run build @@ -52,11 +55,11 @@ jobs: NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}} - name: Setup GitHub CI Node.js environment if: github.event_name != 'pull_request' && matrix.os == 'ubuntu-latest' - uses: actions/setup-node@v2 + uses: actions/setup-node@v4 with: node-version: ${{ matrix.node_version }} - registry-url: 'https://npm.pkg.github.com' - scope: '@exceptionless' + registry-url: "https://npm.pkg.github.com" + scope: "@exceptionless" - name: Push GitHub CI Packages if: github.event_name != 'pull_request' && matrix.os == 'ubuntu-latest' run: npm publish --workspaces --access public diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 00000000..8e5af294 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,7 @@ +.DS_Store +node_modules +minver +example/svelte-kit/.svelte-kit + +# Ignore files for PNPM, NPM and YARN +package-lock.json diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 00000000..a4937b33 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,4 @@ +{ + "printWidth": 160, + "trailingComma": "none" +} diff --git a/.vscode/launch.json b/.vscode/launch.json index 80c3ca13..b66abe35 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -7,9 +7,7 @@ "request": "launch", "preLaunchTask": "npm: build", "cwd": "${workspaceRoot}/example/express", - "skipFiles": [ - "/**" - ], + "skipFiles": ["/**"], "type": "pwa-node" }, { @@ -17,14 +15,12 @@ "request": "launch", "type": "node", "program": "${workspaceFolder}/node_modules/.bin/jest", - "args": [ - "--runInBand" - ], + "args": ["--runInBand"], "console": "integratedTerminal", "internalConsoleOptions": "neverOpen", "disableOptimisticBPs": true, "windows": { - "program": "${workspaceFolder}/node_modules/jest/bin/jest", + "program": "${workspaceFolder}/node_modules/jest/bin/jest" }, "cwd": "${workspaceRoot}" }, @@ -33,14 +29,12 @@ "request": "launch", "type": "node", "program": "${workspaceFolder}/node_modules/.bin/jest", - "args": [ - "${fileBasenameNoExtension}", - ], + "args": ["${fileBasenameNoExtension}"], "console": "integratedTerminal", "internalConsoleOptions": "neverOpen", "disableOptimisticBPs": true, "windows": { - "program": "${workspaceFolder}/node_modules/jest/bin/jest", + "program": "${workspaceFolder}/node_modules/jest/bin/jest" }, "cwd": "${workspaceRoot}" } diff --git a/.vscode/settings.json b/.vscode/settings.json index 8c6cbc0a..682d952c 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -9,7 +9,7 @@ "test-data": true }, "search.exclude": { - "**/node_modules": true, + "**/node_modules": true }, "prettier.eslintIntegration": true, "javascript.preferences.quoteStyle": "double", @@ -23,7 +23,6 @@ "esbuild", "eslintignore", "jsdelivr", - "lage", "localstorage", "maxcdn", "ncaught", @@ -36,9 +35,7 @@ "vitejs", "webcompat" ], - "eslint.validate": [ - "javascript", - "typescript" - ], - "deno.enable": false + "eslint.validate": ["javascript", "typescript"], + "deno.enable": false, + "jest.jestCommandLine": "npm test --" } diff --git a/README.md b/README.md index 1c5f2708..dc9962d7 100644 --- a/README.md +++ b/README.md @@ -3,14 +3,13 @@ [![Build status](https://github.com/Exceptionless/Exceptionless.JavaScript/workflows/Build/badge.svg)](https://github.com/Exceptionless/Exceptionless.JavaScript/actions) [![Discord](https://img.shields.io/discord/715744504891703319)](https://discord.gg/6HxgFCx) [![NPM version](https://img.shields.io/npm/v/@exceptionless/core.svg)](https://www.npmjs.org/package/@exceptionless/core) -[![Donate](https://img.shields.io/badge/donorbox-donate-blue.svg)](https://donorbox.org/exceptionless?recurring=true) The definition of the word exceptionless is: to be without exception. Exceptionless provides real-time error reporting for your JavaScript applications in the browser or in Node.js. It organizes the gathered information into simple actionable data that will help your app become exceptionless! ## Browser You can install the npm package via `npm install @exceptionless/browser --save` -or via cdn [`https://unpkg.com/@exceptionless/browser](https://unpkg.com/@exceptionless/browser). +or via cdn [`https://unpkg.com/@exceptionless/browser`](https://unpkg.com/@exceptionless/browser). Next, you just need to call startup during your apps startup to automatically capture unhandled errors. @@ -22,7 +21,7 @@ await Exceptionless.startup((c) => { c.setUserIdentity("12345678", "Blake"); // set some default data - c.defaultData["mydata"] = { + c.defaultData["mydata"] = { myGreeting: "Hello World" }; @@ -77,9 +76,9 @@ Use one of the following methods to install Exceptionless into your browser appl ##### CDN - Add the following script tag at the very beginning of your page: +Add the following script tag at the very beginning of your page: - ```html +```html - ``` +``` ##### npm - 1. Install the package by running `npm install @exceptionless/browser --save`. - 2. Import Exceptionless and call startup during app startup. +1. Install the package by running `npm install @exceptionless/browser --save`. +2. Import Exceptionless and call startup during app startup. - ```js - import { Exceptionless } from "@exceptionless/browser"; +```js +import { Exceptionless } from "@exceptionless/browser"; - await Exceptionless.startup((c) => { - c.apiKey = "API_KEY_HERE"; - }); - ``` +await Exceptionless.startup((c) => { + c.apiKey = "API_KEY_HERE"; +}); +``` #### Node.js @@ -109,13 +108,13 @@ Use this method to install Exceptionless into your Node application: 1. Install the package by running `npm install @exceptionless/node --save`. 2. Import the Exceptionless module in your application: - ```js - import { Exceptionless } from "@exceptionless/node"; +```js +import { Exceptionless } from "@exceptionless/node"; - await Exceptionless.startup((c) => { - c.apiKey = "API_KEY_HERE"; - }); - ``` +await Exceptionless.startup((c) => { + c.apiKey = "API_KEY_HERE"; +}); +``` ### Configuring the client @@ -224,7 +223,7 @@ instance. This is configured by setting the `serverUrl` on the default ```js await Exceptionless.startup((c) => { c.apiKey = "API_KEY_HERE"; - c.serverUrl = "http://localhost:5000"; + c.serverUrl = "https://localhost:5100"; }); ``` @@ -249,28 +248,28 @@ If you find a bug or want to contribute a feature, feel free to create a pull re 1. Clone this repository: - ```sh - git clone https://github.com/exceptionless/Exceptionless.JavaScript.git - ``` + ```sh + git clone https://github.com/exceptionless/Exceptionless.JavaScript.git + ``` 2. Install [Node.js](https://nodejs.org). Node is used for building and testing purposes. 3. Install the development dependencies using [npm](https://www.npmjs.com). - ```sh - npm install - ``` + ```sh + npm install + ``` 4. Build the project by running the following command. - ```sh - npm run build - ``` + ```sh + npm run build + ``` 5. Test the project by running the following command. - ```sh - npm test - ``` + ```sh + npm test + ``` ## Thanks diff --git a/example/browser/index.html b/example/browser/index.html index cfc0b90c..9da74706 100644 --- a/example/browser/index.html +++ b/example/browser/index.html @@ -1,4 +1,4 @@ - + @@ -13,6 +13,9 @@

Error Submission

+ + +

Log Submission

@@ -21,12 +24,16 @@

Log Submission

+

Diagnostics

+

Benchmark

+ +

Client Logs

- + diff --git a/example/browser/index.js b/example/browser/index.js index 1650624a..df524fbb 100644 --- a/example/browser/index.js +++ b/example/browser/index.js @@ -1,4 +1,6 @@ -import { Exceptionless } from "../../node_modules/@exceptionless/browser/dist/index.bundle.js"; +import { Exceptionless, prune } from "../../node_modules/@exceptionless/browser/dist/index.bundle.js"; +import "/node_modules/jquery/dist/jquery.js"; + import { divide } from "./math.js"; import { TextAreaLogger } from "./text-area-logger.js"; @@ -6,11 +8,12 @@ await Exceptionless.startup((c) => { c.useDebugLogger(); c.services.log = new TextAreaLogger("logs", c.services.log); - c.apiKey = "LhhP1C9gijpSKCslHHCvwdSIz298twx271n1l6xw"; - c.serverUrl = "http://localhost:5000"; + c.apiKey = "LhhP1C9gijpSKCslHHCvwdSIz298twx271nTest"; + c.serverUrl = "https://localhost:5100"; c.updateSettingsWhenIdleInterval = 15000; c.usePersistedQueueStorage = true; c.setUserIdentity("12345678", "Blake"); + c.useSessions(); // set some default data c.defaultData["SampleUser"] = { @@ -22,71 +25,182 @@ await Exceptionless.startup((c) => { myPassword: "123456", customValue: "Password", value: { - Password: "123456", - }, + Password: "123456" + } }; c.defaultTags.push("Example", "JavaScript"); c.settings["@@error:MediaError"] = "Off"; }); -document.addEventListener("DOMContentLoaded", () => { +const registerEventHandlers = () => { const elements = document.querySelectorAll(".submit-log"); for (const element of elements) { element.addEventListener("click", (event) => { const level = event.target.attributes["data-level"]?.value; - Exceptionless.submitLog( - "sendEvents", - `This is a log message with level: ${level || ""}`, - level - ); + Exceptionless.submitLog("sendEvents", `This is a log message with level: ${level || ""}`, level); }); } - document - .querySelector("#throw-custom-error") - .addEventListener("click", () => { - throw new CustomError("A Custom Error", 500); - }); + document.querySelector("#submit-error-log-with-error").addEventListener("click", async () => { + const builder = Exceptionless.createLog("Button Click", "Error Log with Error"); + builder.context.setException(new Error("test")); + await builder.submit(); + }); - document - .querySelector("#throw-division-by-zero-error") - .addEventListener("click", () => { - divide(10, 0); - }); + document.querySelector("#throw-browser-extension-error").addEventListener("click", () => { + const error = new Error("A Browser Extension Error"); + error.stack = "at () in chrome-extension://bmagokdooijbeehmkpknfglimnifench/firebug-lite.js:line 9716:col 29"; - document - .querySelector("#throw-index-out-of-range") - .addEventListener("click", () => { - throwIndexOutOfRange(); - }); + throw error; + }); - document - .querySelector("#throw-index-out-of-range-custom-stacking") - .addEventListener("click", () => { - throwIndexOutOfRange(1, true); - }); + document.querySelector("#throw-custom-error").addEventListener("click", () => { + throw new CustomError("A Custom Error", 500); + }); - document - .querySelector("#throw-string-error") - .addEventListener("click", () => { - throwStringError(); - }); + document.querySelector("#throw-division-by-zero-error").addEventListener("click", () => { + divide(10, 0); + }); - document - .querySelector("#throw-ignored-error") - .addEventListener("click", () => { - throw new MediaError("An Ignored Exception Type"); - }); + document.querySelector("#throw-index-out-of-range").addEventListener("click", () => { + throwIndexOutOfRange(); + }); + + document.querySelector("#throw-index-out-of-range-custom-stacking").addEventListener("click", () => { + throwIndexOutOfRange(1, true); + }); + + document.querySelector("#throw-string-error").addEventListener("click", () => { + throwStringError(); + }); - document - .querySelector("#config-settings-log") - .addEventListener("click", () => { - Exceptionless.config.services.log.info( - JSON.stringify(Exceptionless.config.settings) - ); + document.querySelector("#throw-ignored-error").addEventListener("click", () => { + throw new MediaError("An Ignored Exception Type"); + }); + + document.querySelector("#throw-jquery-ajax-error").addEventListener("click", () => { + $.ajax("http://notexistenturlthrowserror", { + type: "POST", + success: (data, textStatus, jqXHR) => { + console.log({ message: "jQuery.ajax.success", data, textStatus, jqXHR }); + }, + error: (jqXHR, textStatus, errorThrown) => { + console.log({ message: "jQuery.ajax.error", jqXHR, textStatus, errorThrown }); + } }); -}); + }); + + document.querySelector("#throw-promise-unhandled-rejection").addEventListener("click", () => { + const promiseFn = () => + new Promise(function (_, reject) { + switch (Math.floor(Math.random() * 5)) { + case 0: + reject(0); + break; + case 1: + reject(new Error("Promise rejected error")); + break; + case 2: + reject("Promise rejected string"); + break; + case 3: + reject(); + break; + case 4: + throw new Error("Error thrown from promise"); + } + }); + + promiseFn(); + }); + + document.querySelector("#config-settings-log").addEventListener("click", () => { + Exceptionless.config.services.log.info(JSON.stringify(Exceptionless.config.settings)); + }); + + document.querySelector("#prune-large-object-benchmark").addEventListener("click", () => { + const data = { + str: "hello", + num: 123, + bool: true, + nullVal: null, + undefinedVal: undefined, + arr: [ + "foo", + 42, + { + prop1: "bar", + prop2: true, + prop3: [ + 1, + 2, + { + nestedProp1: "baz", + nestedProp2: false, + nestedObj: {} + } + ] + } + ], + person: { + name: "John", + age: 30, + address: { + street: "123 Main St", + city: "Anytown", + state: "TX", + country: { + name: "United States", + region: { + north: { + name: "North Region", + states: ["New York", "Vermont", "New Hampshire", "Maine"] + }, + south: { + name: "South Region", + states: ["Texas", "Florida", "Georgia", "North Carolina"] + }, + east: { + name: "East Region", + states: ["New York", "Massachusetts", "Connecticut", "New Jersey"] + }, + west: { + name: "West Region", + states: ["California", "Oregon", "Washington", "Arizona"] + } + } + } + } + }, + func: function (x) { + return x * 2; + }, + date: new Date(), + regex: /foo(bar)?/i, + symbol: Symbol("my symbol"), + bigint: 9007199254740991n, + map: new Map([ + [{ id: 1 }, "value associated with an object key"], + ["string key", "value associated with a string key"], + [123, "value associated with a number key"], + [Symbol("symbol key"), "value associated with a symbol key"] + ]), + set: new Set(["foo", 42, { prop: "value" }]) + }; + + const { log } = Exceptionless.config.services; + log.info("Starting pruning of large object"); + + const start = performance.now(); + for (let i = 0; i < 1000; i++) { + prune(data, 3); + } + const end = performance.now(); + + log.info(`Pruning large object took ${end - start} milliseconds`); + }); +}; async function throwIndexOutOfRange(indexer, withCustomStacking) { try { @@ -94,15 +208,13 @@ async function throwIndexOutOfRange(indexer, withCustomStacking) { } catch (e) { if (withCustomStacking) { if (Math.random() < 0.5) { - await Exceptionless.createException(e) - .setManualStackingKey("MyCustomStackingKey") - .submit(); + await Exceptionless.createException(e).setManualStackingKey("MyCustomStackingKey").submit(); } else { await Exceptionless.createException(e) .setManualStackingInfo( { File: "index.js", - Function: "throwIndexOutOfRange", + Function: "throwIndexOutOfRange" }, "Custom Index Out Of Range Exception" ) @@ -145,3 +257,9 @@ class CustomError extends Error { throw new Error("Not Implemented"); } } + +if (document.readyState === "loading") { + document.addEventListener("DOMContentLoaded", registerEventHandlers); +} else { + registerEventHandlers(); +} diff --git a/example/browser/package.json b/example/browser/package.json index 17f98ecb..41fdeda7 100644 --- a/example/browser/package.json +++ b/example/browser/package.json @@ -1,7 +1,7 @@ { "name": "browser-sample", "private": true, - "version": "2.0.0-dev", + "version": "3.0.0-dev", "description": "Exceptionless Sample Browser App", "main": "index.js", "type": "module", @@ -11,6 +11,7 @@ "access": "restricted" }, "dependencies": { - "@exceptionless/browser": "2.0.0-dev" + "@exceptionless/browser": "3.0.0-dev", + "jquery": "^3.7.1" } } diff --git a/example/browser/text-area-logger.js b/example/browser/text-area-logger.js index a0982828..6d20ae04 100644 --- a/example/browser/text-area-logger.js +++ b/example/browser/text-area-logger.js @@ -39,7 +39,7 @@ export class TextAreaLogger { if (this.element) { this.element.innerHTML += `\n${formattedMessage}`; } else { - this.messageBuffer.push(formattedMessage) + this.messageBuffer.push(formattedMessage); } } } diff --git a/example/express/app.js b/example/express/app.js index faa4a40e..05fd946c 100644 --- a/example/express/app.js +++ b/example/express/app.js @@ -1,12 +1,11 @@ import express from "express"; const app = express(); -import { KnownEventDataKeys } from "@exceptionless/core"; -import { Exceptionless } from "@exceptionless/node"; +import { Exceptionless, KnownEventDataKeys } from "@exceptionless/node"; await Exceptionless.startup((c) => { - c.apiKey = "LhhP1C9gijpSKCslHHCvwdSIz298twx271n1l6xw"; - c.serverUrl = "http://localhost:5000"; + c.apiKey = "LhhP1C9gijpSKCslHHCvwdSIz298twx271nTest"; + c.serverUrl = "https://localhost:5100"; c.useDebugLogger(); c.useLocalStorage(); c.usePersistedQueueStorage = true; @@ -23,8 +22,8 @@ await Exceptionless.startup((c) => { myPassword: "123456", customValue: "Password", value: { - Password: "123456", - }, + Password: "123456" + } }; }); @@ -46,9 +45,7 @@ app.get("/trycatch", async (req, res) => { try { throw new Error("Caught in try/catch"); } catch (error) { - await Exceptionless.createException(error) - .setContextProperty(KnownEventDataKeys.RequestInfo, req) - .submit(); + await Exceptionless.createException(error).setContextProperty(KnownEventDataKeys.RequestInfo, req).submit(); res.status(500).send("Error caught in try/catch"); } @@ -56,12 +53,10 @@ app.get("/trycatch", async (req, res) => { app.use(async (err, req, res, next) => { if (res.headersSent) { - return next(err) + return next(err); } - await Exceptionless.createUnhandledException(err, "express") - .setContextProperty(KnownEventDataKeys.RequestInfo, req) - .submit(); + await Exceptionless.createUnhandledException(err, "express").setContextProperty(KnownEventDataKeys.RequestInfo, req).submit(); res.status(500).send("Something broke!"); }); diff --git a/example/express/package.json b/example/express/package.json index 13c7b77d..f8cb1630 100644 --- a/example/express/package.json +++ b/example/express/package.json @@ -1,21 +1,21 @@ { "name": "express-sample", "private": true, - "version": "2.0.0-dev", + "version": "3.0.0-dev", "description": "Exceptionless Sample Node App", "main": "app.js", "type": "module", "author": "Exceptionless", "license": "Apache-2.0", "scripts": { - "dev": "nodemon app.js", - "start": "node app.js" + "dev": "node app.js --enable-source-maps --watch", + "start": "node app.js --enable-source-maps" }, "publishConfig": { "access": "restricted" }, "dependencies": { - "express": "^4.18.2", - "@exceptionless/node": "2.0.0-dev" + "express": "^4.19.2", + "@exceptionless/node": "3.0.0-dev" } } diff --git a/example/react/README.md b/example/react/README.md index 6612e012..c667d694 100644 --- a/example/react/README.md +++ b/example/react/README.md @@ -1,15 +1,15 @@ -## Exceptionless React Example +## Exceptionless React Example -This example shows how to use the `@exceptionless/react` package. There is both a class component example (App.js) and a function component example with hooks (HooksExampleApp.js). +This example shows how to use the `@exceptionless/react` package. There is both a class component example (App.js) and a function component example with hooks (HooksExampleApp.js). -The package includes [error boundary support](https://reactjs.org/docs/error-boundaries.html) which means uncaught errors inside your components will automatically be sent to Exceptionless. +The package includes [error boundary support](https://reactjs.org/docs/error-boundaries.html) which means uncaught errors inside your components will automatically be sent to Exceptionless. -To run locally, follow these steps: +To run locally, follow these steps: -1. `git clone https://github.com/exceptionless/Exceptionless.JavaScript` +1. `git clone https://github.com/exceptionless/Exceptionless.JavaScript` 2. `cd Exceptionless.Javascript` -3. `npm install` +3. `npm install` 4. `cd example/react` -5. `npm start` +5. `npm start` -Reference the main `@exceptionless/react` [README](../../packages/react/README.md) here when building your own React app. +Reference the main `@exceptionless/react` [README](../../packages/react/README.md) here when building your own React app. diff --git a/example/react/public/index.html b/example/react/index.html similarity index 55% rename from example/react/public/index.html rename to example/react/index.html index aa069f27..a12035c4 100644 --- a/example/react/public/index.html +++ b/example/react/index.html @@ -1,29 +1,17 @@ - + - + - - + + - - + React App @@ -39,5 +27,6 @@ To begin the development, run `npm start` or `yarn start`. To create a production bundle, use `npm run build` or `yarn build`. --> + diff --git a/example/react/package.json b/example/react/package.json index 9267d49e..ba7a7abb 100644 --- a/example/react/package.json +++ b/example/react/package.json @@ -1,11 +1,11 @@ { "name": "react-example", "private": true, - "version": "2.0.0-dev", + "version": "3.0.0-dev", "scripts": { - "start": "react-scripts start", - "build": "react-scripts build", - "eject": "react-scripts eject" + "start": "vite", + "build": "vite build", + "preview": "vite preview" }, "eslintConfig": { "extends": [ @@ -26,17 +26,18 @@ ] }, "devDependencies": { - "@testing-library/jest-dom": "^5.16.5", - "@testing-library/react": "^14.0.0", - "@testing-library/user-event": "^14.4.3", - "react-scripts": "^5.0.1" + "@testing-library/jest-dom": "^6.4.2", + "@testing-library/react": "^14.2.2", + "@testing-library/user-event": "^14.5.2", + "@vitejs/plugin-react": "^4.2.1", + "vite": "^5.2.6" }, "dependencies": { - "@exceptionless/react": "2.0.0-dev", + "@exceptionless/react": "3.0.0-dev", "react": "^18.2.0", - "react-dom": "^18.2.0", - "web-vitals": "^3.1.1" + "react-dom": "^18.2.0" }, + "type": "module", "publishConfig": { "access": "restricted" } diff --git a/example/react/src/App.js b/example/react/src/App.jsx similarity index 60% rename from example/react/src/App.js rename to example/react/src/App.jsx index 04ec0de4..b98186e3 100644 --- a/example/react/src/App.js +++ b/example/react/src/App.jsx @@ -1,9 +1,6 @@ import React, { Component } from "react"; import "./App.css"; -import { - Exceptionless, - ExceptionlessErrorBoundary, -} from "@exceptionless/react"; +import { Exceptionless, ExceptionlessErrorBoundary } from "@exceptionless/react"; class App extends Component { constructor(props) { @@ -11,13 +8,13 @@ class App extends Component { this.state = { error: false, message: "", - errorInfo: "", + errorInfo: "" }; } async componentDidMount() { await Exceptionless.startup((c) => { - c.apiKey = "LhhP1C9gijpSKCslHHCvwdSIz298twx271n1l6xw"; - c.serverUrl = "http://localhost:5000"; + c.apiKey = "LhhP1C9gijpSKCslHHCvwdSIz298twx271nTest"; + c.serverUrl = "https://localhost:5100"; c.useDebugLogger(); c.defaultTags.push("Example", "React"); @@ -58,38 +55,23 @@ class App extends Component {

Exceptionless React Sample

-

- By pressing the button below, an uncaught error will be thrown - inside your component. This will automatically be sent to - Exceptionless. -

- +

By pressing the button below, an uncaught error will be thrown inside your component. This will automatically be sent to Exceptionless.

+
-

- Throw an uncaught error and make sure Exceptionless tracks it. -

- +

Throw an uncaught error and make sure Exceptionless tracks it.

+
-

- The following buttons simulated handled events outside the - component. -

+

The following buttons simulated handled events outside the component.

{this.state.message && (

- Message sent to Exceptionless:{" "} - {this.state.message} + Message sent to Exceptionless: {this.state.message}

)} {this.state.errorInfo && (

- Error message sent to Exceptionless:{" "} - {this.state.errorInfo} + Error message sent to Exceptionless: {this.state.errorInfo}

)}
@@ -100,11 +82,7 @@ class App extends Component { }; render() { - return ( - - {this.renderExample()} - - ); + return {this.renderExample()}; } } diff --git a/example/react/src/HooksExampleApp.js b/example/react/src/HooksExampleApp.js index cafaee49..02297eb1 100644 --- a/example/react/src/HooksExampleApp.js +++ b/example/react/src/HooksExampleApp.js @@ -1,9 +1,6 @@ import React, { useEffect, useState } from "react"; import "./App.css"; -import { - Exceptionless, - ExceptionlessErrorBoundary, -} from "@exceptionless/react"; +import { Exceptionless, ExceptionlessErrorBoundary } from "@exceptionless/react"; const HooksExampleApp = () => { const [error, setError] = useState(false); @@ -13,8 +10,8 @@ const HooksExampleApp = () => { const startExceptionless = async () => { await Exceptionless.startup((c) => { - c.apiKey = "LhhP1C9gijpSKCslHHCvwdSIz298twx271n1l6xw"; - c.serverUrl = "http://localhost:5000"; + c.apiKey = "LhhP1C9gijpSKCslHHCvwdSIz298twx271nTest"; + c.serverUrl = "https://localhost:5100"; c.useDebugLogger(); c.defaultTags.push("Example", "React"); @@ -46,16 +43,9 @@ const HooksExampleApp = () => {

Exceptionless React Sample

-

- By pressing the button below, an uncaught error will be thrown - inside your component. This will automatically be sent to - Exceptionless. -

+

By pressing the button below, an uncaught error will be thrown inside your component. This will automatically be sent to Exceptionless.

-

- The following buttons simulated handled events outside the - component. -

+

The following buttons simulated handled events outside the component.

@@ -65,9 +55,7 @@ const HooksExampleApp = () => { } }; - return ( - {renderExample()} - ); + return {renderExample()}; }; export default HooksExampleApp; diff --git a/example/react/src/index.css b/example/react/src/index.css index ec2585e8..8285d2a3 100644 --- a/example/react/src/index.css +++ b/example/react/src/index.css @@ -1,13 +1,10 @@ body { margin: 0; - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', - 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', - sans-serif; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } code { - font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', - monospace; + font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace; } diff --git a/example/react/src/index.js b/example/react/src/index.js deleted file mode 100644 index 2d212261..00000000 --- a/example/react/src/index.js +++ /dev/null @@ -1,19 +0,0 @@ -import React from 'react'; -import { createRoot } from 'react-dom/client'; -import './index.css'; -import App from './App'; -import reportWebVitals from './reportWebVitals'; - -const container = document.getElementById('root'); -const root = createRoot(container); - -root.render( - - - -); - -// If you want to start measuring performance in your app, pass a function -// to log results (for example: reportWebVitals(console.log)) -// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals -reportWebVitals(); diff --git a/example/react/src/index.jsx b/example/react/src/index.jsx new file mode 100644 index 00000000..2d72d610 --- /dev/null +++ b/example/react/src/index.jsx @@ -0,0 +1,13 @@ +import React from "react"; +import { createRoot } from "react-dom/client"; +import "./index.css"; +import App from "./App"; + +const container = document.getElementById("root"); +const root = createRoot(container); + +root.render( + + + +); diff --git a/example/react/src/reportWebVitals.js b/example/react/src/reportWebVitals.js deleted file mode 100644 index 5253d3ad..00000000 --- a/example/react/src/reportWebVitals.js +++ /dev/null @@ -1,13 +0,0 @@ -const reportWebVitals = onPerfEntry => { - if (onPerfEntry && onPerfEntry instanceof Function) { - import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { - getCLS(onPerfEntry); - getFID(onPerfEntry); - getFCP(onPerfEntry); - getLCP(onPerfEntry); - getTTFB(onPerfEntry); - }); - } -}; - -export default reportWebVitals; diff --git a/example/react/src/setupTests.js b/example/react/src/setupTests.js index 8f2609b7..1dd407a6 100644 --- a/example/react/src/setupTests.js +++ b/example/react/src/setupTests.js @@ -2,4 +2,4 @@ // allows you to do things like: // expect(element).toHaveTextContent(/react/i) // learn more: https://github.com/testing-library/jest-dom -import '@testing-library/jest-dom'; +import "@testing-library/jest-dom"; diff --git a/example/react/vite.config.js b/example/react/vite.config.js new file mode 100644 index 00000000..8af32310 --- /dev/null +++ b/example/react/vite.config.js @@ -0,0 +1,11 @@ +import { defineConfig } from "vite"; +import react from "@vitejs/plugin-react"; + +export default defineConfig({ + base: "", + plugins: [react()], + server: { + open: true, + port: 5174 + } +}); diff --git a/example/svelte-kit/README.md b/example/svelte-kit/README.md index 06152b8e..27acad82 100644 --- a/example/svelte-kit/README.md +++ b/example/svelte-kit/README.md @@ -1,4 +1,4 @@ -## Exceptionless Svelte Kit Example +## Exceptionless Svelte Kit Example This example shows how to use the `@exceptionless/browser` package for client side Svelte Kit and `@exceptionless/node` for server side Svelte Kit. These is both a client side error hook `hooks.client.js` and a server side error hook `hooks.server.js`. diff --git a/example/svelte-kit/jsconfig.json b/example/svelte-kit/jsconfig.json index fe45e13f..f1da068b 100644 --- a/example/svelte-kit/jsconfig.json +++ b/example/svelte-kit/jsconfig.json @@ -1,17 +1,17 @@ { - "extends": "./.svelte-kit/tsconfig.json", - "compilerOptions": { - "allowJs": true, - "checkJs": true, - "esModuleInterop": true, - "forceConsistentCasingInFileNames": true, - "resolveJsonModule": true, - "skipLibCheck": true, - "sourceMap": true, - "strict": true - } - // Path aliases are handled by https://kit.svelte.dev/docs/configuration#alias and https://kit.svelte.dev/docs/configuration#files - // - // If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes - // from the referenced tsconfig.json - TypeScript does not merge them in + "extends": "./.svelte-kit/tsconfig.json", + "compilerOptions": { + "allowJs": true, + "checkJs": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "skipLibCheck": true, + "sourceMap": true, + "strict": true + } + // Path aliases are handled by https://kit.svelte.dev/docs/configuration#alias and https://kit.svelte.dev/docs/configuration#files + // + // If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes + // from the referenced tsconfig.json - TypeScript does not merge them in } diff --git a/example/svelte-kit/package.json b/example/svelte-kit/package.json index d7492984..02346316 100644 --- a/example/svelte-kit/package.json +++ b/example/svelte-kit/package.json @@ -10,15 +10,15 @@ "check:watch": "svelte-kit sync && svelte-check --tsconfig ./jsconfig.json --watch" }, "dependencies": { - "@exceptionless/browser": "2.0.0-dev" + "@exceptionless/browser": "3.0.0-dev" }, "devDependencies": { - "@sveltejs/adapter-auto": "^2.0.0", - "@sveltejs/kit": "^1.7.1", - "svelte": "^3.54.0", - "svelte-check": "^3.0.1", - "typescript": "^4.9.3", - "vite": "^4.0.0" + "@sveltejs/adapter-auto": "^3.2.0", + "@sveltejs/kit": "^2.5.5", + "svelte": "^4.2.12", + "svelte-check": "^3.6.8", + "typescript": "^5.4.3", + "vite": "^5.2.6" }, "type": "module", "publishConfig": { diff --git a/example/svelte-kit/src/app.d.ts b/example/svelte-kit/src/app.d.ts index f59b884c..899c7e8f 100644 --- a/example/svelte-kit/src/app.d.ts +++ b/example/svelte-kit/src/app.d.ts @@ -1,12 +1,12 @@ // See https://kit.svelte.dev/docs/types#app // for information about these interfaces declare global { - namespace App { - // interface Error {} - // interface Locals {} - // interface PageData {} - // interface Platform {} - } + namespace App { + // interface Error {} + // interface Locals {} + // interface PageData {} + // interface Platform {} + } } export {}; diff --git a/example/svelte-kit/src/app.html b/example/svelte-kit/src/app.html index effe0d0d..b009ee26 100644 --- a/example/svelte-kit/src/app.html +++ b/example/svelte-kit/src/app.html @@ -1,12 +1,12 @@ - + - - - - - %sveltekit.head% - - -
%sveltekit.body%
- + + + + + %sveltekit.head% + + +
%sveltekit.body%
+ diff --git a/example/svelte-kit/src/hooks.client.js b/example/svelte-kit/src/hooks.client.js index 63e47773..ebb426fb 100644 --- a/example/svelte-kit/src/hooks.client.js +++ b/example/svelte-kit/src/hooks.client.js @@ -1,8 +1,8 @@ -import { Exceptionless } from "@exceptionless/browser"; +import { Exceptionless, toError } from "@exceptionless/browser"; -Exceptionless.startup(c => { - c.apiKey = "LhhP1C9gijpSKCslHHCvwdSIz298twx271n1l6xw"; - c.serverUrl = "http://localhost:5000"; +Exceptionless.startup((c) => { + c.apiKey = "LhhP1C9gijpSKCslHHCvwdSIz298twx271nTest"; + c.serverUrl = "https://localhost:5100"; c.useDebugLogger(); c.defaultTags.push("Example", "svelte-kit", "client"); @@ -10,6 +10,6 @@ Exceptionless.startup(c => { /** @type {import('@sveltejs/kit').HandleClientError} */ export async function handleError({ error, event }) { - console.log('client error handler'); - await Exceptionless.submitException(error); + console.log("client error handler"); + await Exceptionless.submitException(toError(error)); } diff --git a/example/svelte-kit/src/hooks.server.js b/example/svelte-kit/src/hooks.server.js index 6cc6181f..822bc091 100644 --- a/example/svelte-kit/src/hooks.server.js +++ b/example/svelte-kit/src/hooks.server.js @@ -1,15 +1,15 @@ -import { Exceptionless } from "@exceptionless/node"; +import { Exceptionless, toError } from "@exceptionless/node"; -Exceptionless.startup(c => { - c.apiKey = "LhhP1C9gijpSKCslHHCvwdSIz298twx271n1l6xw"; - c.serverUrl = "http://localhost:5000"; +Exceptionless.startup((c) => { + c.apiKey = "LhhP1C9gijpSKCslHHCvwdSIz298twx271nTest"; + c.serverUrl = "https://localhost:5100"; c.useDebugLogger(); c.defaultTags.push("Example", "svelte-kit", "server"); }); -/** @type {import('@sveltejs/kit').HandleServerError} */ +/** @type {import("@sveltejs/kit").HandleServerError} */ export async function handleError({ error, event }) { - console.log('server error handler'); - await Exceptionless.submitException(error); + console.log("server error handler"); + await Exceptionless.submitException(toError(error)); } diff --git a/example/svelte-kit/svelte.config.js b/example/svelte-kit/svelte.config.js index 301e785e..c2b930dc 100644 --- a/example/svelte-kit/svelte.config.js +++ b/example/svelte-kit/svelte.config.js @@ -1,10 +1,10 @@ -import adapter from '@sveltejs/adapter-auto'; +import adapter from "@sveltejs/adapter-auto"; /** @type {import('@sveltejs/kit').Config} */ const config = { - kit: { - adapter: adapter() - } + kit: { + adapter: adapter() + } }; export default config; diff --git a/example/svelte-kit/vite.config.js b/example/svelte-kit/vite.config.js index bbf8c7da..78e127f5 100644 --- a/example/svelte-kit/vite.config.js +++ b/example/svelte-kit/vite.config.js @@ -1,6 +1,6 @@ -import { sveltekit } from '@sveltejs/kit/vite'; -import { defineConfig } from 'vite'; +import { sveltekit } from "@sveltejs/kit/vite"; +import { defineConfig } from "vite"; export default defineConfig({ - plugins: [sveltekit()] + plugins: [sveltekit()] }); diff --git a/example/vue/index.html b/example/vue/index.html index 2f3335c8..81e28f3d 100644 --- a/example/vue/index.html +++ b/example/vue/index.html @@ -1,4 +1,4 @@ - + diff --git a/example/vue/package.json b/example/vue/package.json index 1110aba3..5809e3ab 100644 --- a/example/vue/package.json +++ b/example/vue/package.json @@ -1,21 +1,22 @@ { "name": "vue-example", "private": true, - "version": "2.0.0-dev", + "version": "3.0.0-dev", "scripts": { "dev": "vite", "build": "vite build", "serve": "vite preview" }, "dependencies": { - "vue": "^3.2.47", - "@exceptionless/vue": "2.0.0-dev" + "vue": "^3.4.21", + "@exceptionless/vue": "3.0.0-dev" }, "devDependencies": { - "@vitejs/plugin-vue": "^4.0.0", - "@vue/compiler-sfc": "^3.2.47", - "vite": "^4.1.1" + "@vitejs/plugin-vue": "^5.0.4", + "@vue/compiler-sfc": "^3.4.21", + "vite": "^5.2.6" }, + "type": "module", "publishConfig": { "access": "restricted" } diff --git a/example/vue/src/App.vue b/example/vue/src/App.vue index c34ed3d3..72b3d88e 100644 --- a/example/vue/src/App.vue +++ b/example/vue/src/App.vue @@ -1,5 +1,5 @@