@openpanel/express
Advanced tools
+85
| # Express | ||
| The Express middleware is a basic wrapper around Javascript SDK. It provides a simple way to add the SDK to your Express application. | ||
| > 📖 **Full documentation:** [https://openpanel.dev/docs/sdks/express](https://openpanel.dev/docs/sdks/express) | ||
| --- | ||
| Looking for a step-by-step tutorial? Check out the [Express analytics guide](https://openpanel.dev/guides/express-analytics). | ||
| ## Installation | ||
| ```bash | ||
| pnpm install @openpanel/express | ||
| ``` | ||
| ## Usage | ||
| The default export of `@openpanel/express` is a function that returns an Express middleware. It will also append the Openpanel SDK to the `req` object. | ||
| You can access it via `req.op`. | ||
| ```ts | ||
| import express from 'express'; | ||
| import createOpenpanelMiddleware from '@openpanel/express'; | ||
| const app = express(); | ||
| app.use( | ||
| createOpenpanelMiddleware({ | ||
| clientId: 'xxx', | ||
| clientSecret: 'xxx', | ||
| // trackRequest(url) { | ||
| // return url.includes('/v1') | ||
| // }, | ||
| // getProfileId(req) { | ||
| // return req.user.id | ||
| // } | ||
| }) | ||
| ); | ||
| app.get('/sign-up', (req, res) => { | ||
| // track sign up events | ||
| req.op.track('sign-up', { | ||
| email: req.body.email, | ||
| }); | ||
| res.send('Hello World'); | ||
| }); | ||
| app.listen(3000, () => { | ||
| console.log('Server is running on http://localhost:3000'); | ||
| }); | ||
| ``` | ||
| ### Options | ||
| ##### Common options | ||
| - `apiUrl` - The url of the openpanel API or your self-hosted instance | ||
| - `clientId` - The client id of your application | ||
| - `clientSecret` - The client secret of your application (**only required for server-side events**) | ||
| - `filter` - A function that will be called before sending an event. If it returns false, the event will not be sent | ||
| - `disabled` - If true, the library will not send any events | ||
| #### Express options | ||
| - `trackRequest` - A function that returns `true` if the request should be tracked. | ||
| - `getProfileId` - A function that returns the profile ID of the user making the request. | ||
| ## Typescript | ||
| If `req.op` is not typed you can extend the `Request` interface. | ||
| ```ts | ||
| import { OpenPanel } from '@openpanel/express'; | ||
| declare global { | ||
| namespace Express { | ||
| export interface Request { | ||
| op: OpenPanel; | ||
| } | ||
| } | ||
| } | ||
| ``` |
@@ -1,1 +0,1 @@ | ||
| "use strict";(()=>{var f=["openpanel-client-ip","cf-connecting-ip","true-client-ip","x-client-ip","x-forwarded-for","x-real-ip","fastly-client-ip","x-cluster-client-ip","x-appengine-user-ip","do-connecting-ip","x-nf-client-connection-ip","x-forwarded","forwarded"];function l(e){if(e.startsWith("::ffff:")){let t=e.substring(7);return l(t)}if(e==="::1"||e.startsWith("fc00:")||e.startsWith("fd00:")||e.startsWith("fe80:")||e.startsWith("127.")||e.startsWith("10."))return!1;if(e.startsWith("172.")){let t=e.split(".");if(t.length>=2){let r=Number.parseInt(t[1]||"0",10);if(r>=16&&r<=31)return!1}}return!(e.startsWith("192.168.")||e.startsWith("169.254."))}function u(){return typeof process<"u"&&process.env?.IP_HEADER_ORDER?process.env.IP_HEADER_ORDER.split(",").map(e=>e.trim()):f}function o(e){let t=/^(\d{1,3}\.){3}\d{1,3}$/,r=/^([0-9a-fA-F]{0,4}:){2,7}[0-9a-fA-F]{0,4}$/;return l(e)&&(t.test(e)||r.test(e))}function d(e,t){let r=u();t&&(r=[t]);for(let n of r){let s=null;if(e instanceof Headers)s=e.get(n);else{let i=e[n];Array.isArray(i)?s=i[0]||null:s=i||null}if(s){if(n==="x-forwarded-for"){let i=s.split(",")[0]?.trim();if(i&&o(i))return{ip:i,header:n}}else if(n==="forwarded"){let a=s.match(/for=(?:"?\[?([^\]"]+)\]?"?)/i)?.[1];if(a&&o(a))return{ip:a,header:n}}else if(o(s))return{ip:s,header:n}}}return{ip:"",header:""}}var h=class{constructor(e){this.baseUrl=e.baseUrl,this.headers={"Content-Type":"application/json",...e.defaultHeaders},this.maxRetries=e.maxRetries??3,this.initialRetryDelay=e.initialRetryDelay??500}async resolveHeaders(){let e={};for(let[t,r]of Object.entries(this.headers)){let n=await r;n!==null&&(e[t]=n)}return e}addHeader(e,t){this.headers[e]=t}async post(e,t,r,n){try{let s=await fetch(e,{method:"POST",headers:await this.resolveHeaders(),body:t?JSON.stringify(t??{}):void 0,keepalive:!0,...r});if(s.status===401)return null;if(s.status!==200&&s.status!==202)throw new Error(`HTTP error! status: ${s.status}`);let i=await s.text();return i?JSON.parse(i):null}catch(s){if(n<this.maxRetries){let i=this.initialRetryDelay*2**n;return await new Promise(a=>setTimeout(a,i)),this.post(e,t,r,n+1)}return console.error("Max retries reached:",s),null}}async fetch(e,t,r={}){let n=`${this.baseUrl}${e}`;return this.post(n,t,r,0)}},p=class{constructor(e){this.options=e,this.queue=[];let t={"openpanel-client-id":e.clientId};e.clientSecret&&(t["openpanel-client-secret"]=e.clientSecret),t["openpanel-sdk-name"]=e.sdk||"node",t["openpanel-sdk-version"]=e.sdkVersion||"1.0.3",this.api=new h({baseUrl:e.apiUrl||"https://api.openpanel.dev",defaultHeaders:t})}init(){}ready(){this.options.waitForProfile=!1,this.flush()}async send(e){return this.options.disabled||this.options.filter&&!this.options.filter(e)?Promise.resolve():this.options.waitForProfile&&!this.profileId?(this.queue.push(e),Promise.resolve()):this.api.fetch("/track",e)}setGlobalProperties(e){this.global={...this.global,...e}}async track(e,t){return this.log("track event",e,t),this.send({type:"track",payload:{name:e,profileId:t?.profileId??this.profileId,properties:{...this.global??{},...t??{}}}})}async identify(e){if(this.log("identify user",e),e.profileId&&(this.profileId=e.profileId,this.flush()),Object.keys(e).length>1)return this.send({type:"identify",payload:{...e,properties:{...this.global,...e.properties}}})}async alias(e){}async increment(e){return this.send({type:"increment",payload:e})}async decrement(e){return this.send({type:"decrement",payload:e})}async revenue(e,t){let r=t?.deviceId;return delete t?.deviceId,this.track("revenue",{...t??{},...r?{__deviceId:r}:{},__revenue:e})}async fetchDeviceId(){return(await this.api.fetch("/track/device-id",void 0,{method:"GET",keepalive:!1}))?.deviceId??""}clear(){this.profileId=void 0}flush(){this.queue.forEach(e=>{this.send({...e,payload:{...e.payload,profileId:e.payload.profileId??this.profileId}})}),this.queue=[]}log(...e){this.options.debug&&console.log("[OpenPanel.dev]",...e)}};function y(e){return function(r,n,s){let i=new p(e),{ip:a}=d(r.headers);if(a&&i.api.addHeader("openpanel-client-ip",a),r.headers["user-agent"]&&i.api.addHeader("user-agent",r.headers["user-agent"]),e.trackRequest?.(r.url)){let c=e.getProfileId?e.getProfileId(r):void 0;i.track("request",{url:r.url,method:r.method,query:r.query,profileId:c})}return r.op=i,s()}}})(); | ||
| "use strict";(()=>{var f=["openpanel-client-ip","cf-connecting-ip","true-client-ip","x-client-ip","x-forwarded-for","x-real-ip","fastly-client-ip","x-cluster-client-ip","x-appengine-user-ip","do-connecting-ip","x-nf-client-connection-ip","x-forwarded","forwarded"];function l(e){if(e.startsWith("::ffff:")){let t=e.substring(7);return l(t)}if(e==="::1"||e.startsWith("fc00:")||e.startsWith("fd00:")||e.startsWith("fe80:")||e.startsWith("127.")||e.startsWith("10."))return!1;if(e.startsWith("172.")){let t=e.split(".");if(t.length>=2){let r=Number.parseInt(t[1]||"0",10);if(r>=16&&r<=31)return!1}}return!(e.startsWith("192.168.")||e.startsWith("169.254."))}function u(){return typeof process<"u"&&process.env?.IP_HEADER_ORDER?process.env.IP_HEADER_ORDER.split(",").map(e=>e.trim()):f}function o(e){let t=/^(\d{1,3}\.){3}\d{1,3}$/,r=/^([0-9a-fA-F]{0,4}:){2,7}[0-9a-fA-F]{0,4}$/;return l(e)&&(t.test(e)||r.test(e))}function d(e,t){let r=u();t&&(r=[t]);for(let n of r){let s=null;if(e instanceof Headers)s=e.get(n);else{let i=e[n];Array.isArray(i)?s=i[0]||null:s=i||null}if(s){if(n==="x-forwarded-for"){let i=s.split(",")[0]?.trim();if(i&&o(i))return{ip:i,header:n}}else if(n==="forwarded"){let a=s.match(/for=(?:"?\[?([^\]"]+)\]?"?)/i)?.[1];if(a&&o(a))return{ip:a,header:n}}else if(o(s))return{ip:s,header:n}}}return{ip:"",header:""}}var h=class{constructor(e){this.baseUrl=e.baseUrl,this.headers={"Content-Type":"application/json",...e.defaultHeaders},this.maxRetries=e.maxRetries??3,this.initialRetryDelay=e.initialRetryDelay??500}async resolveHeaders(){let e={};for(let[t,r]of Object.entries(this.headers)){let n=await r;n!==null&&(e[t]=n)}return e}addHeader(e,t){this.headers[e]=t}async post(e,t,r,n){try{let s=await fetch(e,{method:"POST",headers:await this.resolveHeaders(),body:t?JSON.stringify(t??{}):void 0,keepalive:!0,...r});if(s.status===401)return null;if(s.status!==200&&s.status!==202)throw new Error(`HTTP error! status: ${s.status}`);let i=await s.text();return i?JSON.parse(i):null}catch(s){if(n<this.maxRetries){let i=this.initialRetryDelay*2**n;return await new Promise(a=>setTimeout(a,i)),this.post(e,t,r,n+1)}return console.error("Max retries reached:",s),null}}async fetch(e,t,r={}){let n=`${this.baseUrl}${e}`;return this.post(n,t,r,0)}},p=class{constructor(e){this.options=e,this.queue=[];let t={"openpanel-client-id":e.clientId};e.clientSecret&&(t["openpanel-client-secret"]=e.clientSecret),t["openpanel-sdk-name"]=e.sdk||"node",t["openpanel-sdk-version"]=e.sdkVersion||"1.0.4",this.api=new h({baseUrl:e.apiUrl||"https://api.openpanel.dev",defaultHeaders:t})}init(){}ready(){this.options.waitForProfile=!1,this.flush()}async send(e){return this.options.disabled||this.options.filter&&!this.options.filter(e)?Promise.resolve():this.options.waitForProfile&&!this.profileId?(this.queue.push(e),Promise.resolve()):this.api.fetch("/track",e)}setGlobalProperties(e){this.global={...this.global,...e}}async track(e,t){return this.log("track event",e,t),this.send({type:"track",payload:{name:e,profileId:t?.profileId??this.profileId,properties:{...this.global??{},...t??{}}}})}async identify(e){if(this.log("identify user",e),e.profileId&&(this.profileId=e.profileId,this.flush()),Object.keys(e).length>1)return this.send({type:"identify",payload:{...e,properties:{...this.global,...e.properties}}})}async alias(e){}async increment(e){return this.send({type:"increment",payload:e})}async decrement(e){return this.send({type:"decrement",payload:e})}async revenue(e,t){let r=t?.deviceId;return delete t?.deviceId,this.track("revenue",{...t??{},...r?{__deviceId:r}:{},__revenue:e})}async fetchDeviceId(){return(await this.api.fetch("/track/device-id",void 0,{method:"GET",keepalive:!1}))?.deviceId??""}clear(){this.profileId=void 0}flush(){this.queue.forEach(e=>{this.send({...e,payload:{...e.payload,profileId:e.payload.profileId??this.profileId}})}),this.queue=[]}log(...e){this.options.debug&&console.log("[OpenPanel.dev]",...e)}};function y(e){return function(r,n,s){let i=new p(e),{ip:a}=d(r.headers);if(a&&i.api.addHeader("openpanel-client-ip",a),r.headers["user-agent"]&&i.api.addHeader("user-agent",r.headers["user-agent"]),e.trackRequest?.(r.url)){let c=e.getProfileId?e.getProfileId(r):void 0;i.track("request",{url:r.url,method:r.method,query:r.query,profileId:c})}return r.op=i,s()}}})(); |
+6
-3
| { | ||
| "name": "@openpanel/express", | ||
| "version": "1.0.4", | ||
| "version": "1.0.5", | ||
| "module": "./dist/index.js", | ||
| "config": { | ||
| "docPath": "apps/public/content/docs/(tracking)/sdks/express.mdx" | ||
| }, | ||
| "scripts": { | ||
@@ -10,3 +13,3 @@ "build": "rm -rf dist && tsup", | ||
| "dependencies": { | ||
| "@openpanel/sdk": "1.0.3", | ||
| "@openpanel/sdk": "1.0.4", | ||
| "@openpanel/common": "*" | ||
@@ -28,3 +31,3 @@ }, | ||
| "types": "./dist/index.d.ts", | ||
| "files": ["dist"], | ||
| "files": ["dist", "README.md"], | ||
| "exports": { | ||
@@ -31,0 +34,0 @@ ".": { |
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 2 instances in 1 package
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
No README
QualityPackage does not have a README. This may indicate a failed publish or a low quality package.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 2 instances in 1 package
12609
21.49%7
16.67%2
-33.33%85
Infinity%+ Added
- Removed
Updated