├── .eslintignore ├── .eslintrc.json ├── .gitignore ├── .prettierrc.json ├── README.md ├── index.ts ├── package.json ├── rollup.config.mjs └── yarn.lock /.eslintignore: -------------------------------------------------------------------------------- 1 | /dist/ -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "commonjs": true, 5 | "es6": true, 6 | "node": true, 7 | "jest": true 8 | }, 9 | "parser": "@typescript-eslint/parser", 10 | "parserOptions": { 11 | "ecmaVersion": 9 12 | }, 13 | "globals": { 14 | "JSX": true 15 | }, 16 | "extends": [ 17 | "plugin:@typescript-eslint/recommended", 18 | "eslint:recommended", 19 | "plugin:import/errors", 20 | "plugin:import/typescript", 21 | "prettier" 22 | ], 23 | "plugins": [ 24 | "@typescript-eslint", 25 | "prettier", 26 | "promise" 27 | ], 28 | "rules": { 29 | "jsx-no-lambda": "off", 30 | "linebreak-style": ["error", "unix"], 31 | "max-len": [ 32 | "error", 33 | { 34 | "code": 100, 35 | "ignoreComments": true 36 | } 37 | ], 38 | "no-unused-vars": "off", 39 | "import/no-unresolved": "off", 40 | "react/prop-types": "off", 41 | "react/react-in-jsx-scope": "off", 42 | "@typescript-eslint/no-explicit-any": "off", 43 | "@typescript-eslint/no-unused-vars": ["error"], 44 | "@typescript-eslint/no-var-requires": "off", 45 | "@typescript-eslint/no-empty-function": "off" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # dependencies 2 | /node_modules 3 | 4 | # production 5 | /dist 6 | 7 | # misc 8 | .DS_Store 9 | .idea 10 | *.iml 11 | 12 | # debug 13 | npm-debug.log* 14 | yarn-debug.log* 15 | yarn-error.log* 16 | 17 | # local env files 18 | .env 19 | .env.development.local 20 | .env.test.local 21 | .env.production.local 22 | -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "arrowParens": "avoid", 3 | "endOfLine": "lf", 4 | "printWidth": 100, 5 | "singleQuote": true, 6 | "trailingComma": "all" 7 | } 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # @umami/node 2 | 3 | ## Overview 4 | 5 | The Umami node client allows you to send data to Umami on the server side. 6 | 7 | ## Installation 8 | 9 | ```shell 10 | npm install @umami/node 11 | ``` 12 | 13 | This command will install api client [npm package](https://www.npmjs.com/package/@umami/node). 14 | ## Usage 15 | 16 | ```js 17 | import umami from '@umami/node'; 18 | 19 | //~ init 20 | let umamiClient = new umami.Umami({ 21 | websiteId: '50429a93-8479-4073-be80-d5d29c09c2ec', // Your website id 22 | hostUrl: 'https://umami.mywebsite.com' // URL to your Umami instance 23 | // ,userAgent // (optional) agent specifications ( OS / Browser / Device ) 24 | }); 25 | 26 | //~ (optional) identify : update with you own session attributes 27 | const sessionId = Date.now(); 28 | const identifyOptions = { 29 | "attribute": "11.23", 30 | "sessionId": sessionId 31 | } 32 | await umamiClient.identify(identifyOptions); 33 | 34 | //~ track a page 35 | const url = `/home`; 36 | const title = "title of /home"; 37 | let event = {url, title} 38 | await umamiClient.track(event); 39 | console.log(`✮ Page ${JSON.stringify(event)}`); 40 | 41 | //~ track an event - an event has a *name* 42 | const data = {"color": "red"}; 43 | event = {url, title, "name": "button-click", data}; 44 | await umamiClient.track(event); 45 | console.log(`✮ Event ${JSON.stringify(event)}`); 46 | ``` 47 | 48 | If you're using Umami Cloud, then you can use `https://cloud.umami.is` as `hostUrl`. 49 | 50 | As `.track` function event argument, the properties you can send are: 51 | 52 | - **hostname**: Hostname of server 53 | - **language**: Client language (eg. en-US) 54 | - **referrer**: Page referrer 55 | - **screen**: Screen dimensions (eg. 1920x1080) 56 | - **title**: Page title 57 | - **url**: Page url 58 | 59 | And to track event: 60 | - **name**: Event name 61 | - **data**: Event data custom properties 62 | -------------------------------------------------------------------------------- /index.ts: -------------------------------------------------------------------------------- 1 | export interface UmamiOptions { 2 | hostUrl?: string; 3 | websiteId?: string; 4 | sessionId?: string; 5 | userAgent?: string; 6 | } 7 | 8 | export interface UmamiPayload { 9 | website: string; 10 | session?: string; 11 | hostname?: string; 12 | language?: string; 13 | referrer?: string; 14 | screen?: string; 15 | title?: string; 16 | url?: string; 17 | name?: string; 18 | data?: { 19 | [key: string]: string | number | Date; 20 | }; 21 | } 22 | 23 | export interface UmamiEventData { 24 | [key: string]: string | number | Date; 25 | } 26 | 27 | export class Umami { 28 | options: UmamiOptions; 29 | properties: object; 30 | 31 | constructor(options: UmamiOptions = {}) { 32 | this.options = options; 33 | this.properties = {}; 34 | } 35 | 36 | init(options: UmamiOptions) { 37 | this.options = { ...this.options, ...options }; 38 | } 39 | 40 | send(payload: UmamiPayload, type: 'event' | 'identify' = 'event') { 41 | const { hostUrl, userAgent } = this.options; 42 | 43 | return fetch(`${hostUrl}/api/send`, { 44 | method: 'POST', 45 | headers: { 46 | 'Content-Type': 'application/json', 47 | 'User-Agent': userAgent || `Mozilla/5.0 Umami/${process.version}`, 48 | }, 49 | body: JSON.stringify({ type, payload }), 50 | }); 51 | } 52 | 53 | track(event: object | string, eventData?: UmamiEventData) { 54 | const type = typeof event; 55 | const { websiteId } = this.options; 56 | 57 | switch (type) { 58 | case 'string': 59 | return this.send({ 60 | website: websiteId, 61 | name: event as string, 62 | data: eventData, 63 | }); 64 | case 'object': 65 | return this.send({ website: websiteId, ...(event as UmamiPayload) }); 66 | } 67 | 68 | return Promise.reject('Invalid payload.'); 69 | } 70 | 71 | identify(properties: object = {}) { 72 | this.properties = { ...this.properties, ...properties }; 73 | const { websiteId, sessionId } = this.options; 74 | 75 | return this.send( 76 | { website: websiteId, session: sessionId, data: { ...this.properties } }, 77 | 'identify', 78 | ); 79 | } 80 | 81 | reset() { 82 | this.properties = {}; 83 | } 84 | } 85 | 86 | const umami = new Umami(); 87 | 88 | export default umami; 89 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@umami/node", 3 | "version": "0.4.0", 4 | "description": "Node client for Umami", 5 | "keywords": [ 6 | "node", 7 | "client", 8 | "umami", 9 | "analytics", 10 | "track" 11 | ], 12 | "license": "MIT", 13 | "repository": { 14 | "type": "git", 15 | "url": "git+https://github.com/umami-software/node.git" 16 | }, 17 | "main": "dist/index.js", 18 | "module": "dist/index.mjs", 19 | "types": "dist/index.d.ts", 20 | "files": [ 21 | "dist" 22 | ], 23 | "publishConfig": { 24 | "access": "public" 25 | }, 26 | "scripts": { 27 | "build": "rollup -c", 28 | "types": "tsc && tsc-alias", 29 | "check-types": "tsc", 30 | "lint": "eslint src --ext .ts,.tsx" 31 | }, 32 | "lint-staged": { 33 | "src/**/*.{js,ts}": [ 34 | "prettier --write" 35 | ] 36 | }, 37 | "devDependencies": { 38 | "@rollup/plugin-alias": "^4.0.3", 39 | "@rollup/plugin-commonjs": "^24.0.1", 40 | "@rollup/plugin-node-resolve": "^15.0.1", 41 | "@testing-library/react": "^12.1.1", 42 | "@types/node": "^18.11.9", 43 | "@typescript-eslint/eslint-plugin": "^5.17.0", 44 | "@typescript-eslint/parser": "^5.17.0", 45 | "esbuild": "^0.19.11", 46 | "eslint": "^7.23.0", 47 | "eslint-config-prettier": "^8.1.0", 48 | "eslint-plugin-css-modules": "^2.11.0", 49 | "eslint-plugin-import": "^2.22.1", 50 | "eslint-plugin-jest": "^24.3.4", 51 | "eslint-plugin-prettier": "^4.0.0", 52 | "eslint-plugin-promise": "^5.1.0", 53 | "eslint-plugin-react": "^7.23.1", 54 | "eslint-plugin-react-hooks": "^4.2.0", 55 | "eslint-plugin-storybook": "^0.6.12", 56 | "husky": "^7.0.4", 57 | "jest": "^27.2.4", 58 | "lint-staged": "^11.1.2", 59 | "prettier": "^2.2.1", 60 | "prettier-eslint": "^13.0.0", 61 | "rollup": "^4.9.2", 62 | "rollup-plugin-delete": "^2.0.0", 63 | "rollup-plugin-dts": "^6.1.0", 64 | "rollup-plugin-esbuild": "^6.1.0", 65 | "rollup-plugin-postcss": "^4.0.2", 66 | "tsc-alias": "^1.8.2", 67 | "typescript": "^4.7.4" 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /rollup.config.mjs: -------------------------------------------------------------------------------- 1 | import resolve from '@rollup/plugin-node-resolve'; 2 | import commonjs from '@rollup/plugin-commonjs'; 3 | import del from 'rollup-plugin-delete'; 4 | import esbuild from 'rollup-plugin-esbuild'; 5 | import dts from 'rollup-plugin-dts'; 6 | 7 | const jsBundle = { 8 | input: 'index.ts', 9 | output: [ 10 | { 11 | file: 'dist/index.js', 12 | format: 'cjs', 13 | sourcemap: true, 14 | exports: 'named', 15 | }, 16 | { 17 | file: 'dist/index.mjs', 18 | format: 'es', 19 | sourcemap: true, 20 | exports: 'named', 21 | }, 22 | ], 23 | plugins: [del({ targets: 'dist/*', runOnce: true }), resolve(), commonjs(), esbuild()], 24 | }; 25 | 26 | const dtsBundle = { 27 | input: 'index.ts', 28 | output: { 29 | file: 'dist/index.d.ts', 30 | format: 'es', 31 | exports: 'named', 32 | }, 33 | plugins: [resolve(), commonjs(), dts()], 34 | }; 35 | 36 | export default [jsBundle, dtsBundle]; 37 | --------------------------------------------------------------------------------