@segment/analytics-node
Advanced tools
Comparing version 0.0.1-beta.9 to 0.0.1-beta.10
@@ -31,3 +31,3 @@ "use strict"; | ||
flushInterval, | ||
}); | ||
}, this); | ||
this._publisher = publisher; | ||
@@ -34,0 +34,0 @@ this.ready = this.register(plugin).then(() => undefined); |
@@ -5,3 +5,3 @@ "use strict"; | ||
// This file is generated. | ||
exports.version = '0.0.1-beta.9'; | ||
exports.version = '0.0.1-beta.10'; | ||
//# sourceMappingURL=version.js.map |
@@ -36,4 +36,4 @@ "use strict"; | ||
exports.createNodePlugin = createNodePlugin; | ||
const createConfiguredNodePlugin = (props) => { | ||
const publisher = new publisher_1.Publisher(props); | ||
const createConfiguredNodePlugin = (props, emitter) => { | ||
const publisher = new publisher_1.Publisher(props, emitter); | ||
return { | ||
@@ -40,0 +40,0 @@ publisher: publisher, |
@@ -18,3 +18,4 @@ "use strict"; | ||
class Publisher { | ||
constructor({ host, path, maxRetries, maxEventsInBatch, flushInterval, writeKey, httpRequestTimeout, }) { | ||
constructor({ host, path, maxRetries, maxEventsInBatch, flushInterval, writeKey, httpRequestTimeout, }, emitter) { | ||
this._emitter = emitter; | ||
this._maxRetries = maxRetries; | ||
@@ -132,3 +133,3 @@ this._maxEventsInBatch = Math.max(maxEventsInBatch, 1); | ||
try { | ||
const response = await (0, fetch_1.fetch)(this._url, { | ||
const requestInit = { | ||
signal: signal, | ||
@@ -142,3 +143,10 @@ method: 'POST', | ||
body: payload, | ||
}; | ||
this._emitter.emit('http_request', { | ||
url: this._url, | ||
method: requestInit.method, | ||
headers: requestInit.headers, | ||
body: requestInit.body, | ||
}); | ||
const response = await (0, fetch_1.fetch)(this._url, requestInit); | ||
clearTimeout(timeoutId); | ||
@@ -145,0 +153,0 @@ if (response.ok) { |
@@ -28,3 +28,3 @@ import { bindAll, pTimeout } from '@segment/analytics-core'; | ||
flushInterval, | ||
}); | ||
}, this); | ||
this._publisher = publisher; | ||
@@ -31,0 +31,0 @@ this.ready = this.register(plugin).then(() => undefined); |
// This file is generated. | ||
export const version = '0.0.1-beta.9'; | ||
export const version = '0.0.1-beta.10'; | ||
//# sourceMappingURL=version.js.map |
@@ -32,4 +32,4 @@ import { Publisher } from './publisher'; | ||
} | ||
export const createConfiguredNodePlugin = (props) => { | ||
const publisher = new Publisher(props); | ||
export const createConfiguredNodePlugin = (props, emitter) => { | ||
const publisher = new Publisher(props, emitter); | ||
return { | ||
@@ -36,0 +36,0 @@ publisher: publisher, |
@@ -15,3 +15,4 @@ import { backoff } from '@segment/analytics-core'; | ||
export class Publisher { | ||
constructor({ host, path, maxRetries, maxEventsInBatch, flushInterval, writeKey, httpRequestTimeout, }) { | ||
constructor({ host, path, maxRetries, maxEventsInBatch, flushInterval, writeKey, httpRequestTimeout, }, emitter) { | ||
this._emitter = emitter; | ||
this._maxRetries = maxRetries; | ||
@@ -129,3 +130,3 @@ this._maxEventsInBatch = Math.max(maxEventsInBatch, 1); | ||
try { | ||
const response = await fetch(this._url, { | ||
const requestInit = { | ||
signal: signal, | ||
@@ -139,3 +140,10 @@ method: 'POST', | ||
body: payload, | ||
}; | ||
this._emitter.emit('http_request', { | ||
url: this._url, | ||
method: requestInit.method, | ||
headers: requestInit.headers, | ||
body: requestInit.body, | ||
}); | ||
const response = await fetch(this._url, requestInit); | ||
clearTimeout(timeoutId); | ||
@@ -142,0 +150,0 @@ if (response.ok) { |
@@ -8,5 +8,13 @@ import { CoreEmitterContract, Emitter } from '@segment/analytics-core'; | ||
*/ | ||
declare type NodeEmitterEvents = CoreEmitterContract<Context> & { | ||
export declare type NodeEmitterEvents = CoreEmitterContract<Context> & { | ||
initialize: [AnalyticsSettings]; | ||
call_after_close: [SegmentEvent]; | ||
http_request: [ | ||
{ | ||
url: string; | ||
method: string; | ||
headers: Record<string, string>; | ||
body: string; | ||
} | ||
]; | ||
drained: []; | ||
@@ -16,3 +24,2 @@ }; | ||
} | ||
export {}; | ||
//# sourceMappingURL=emitter.d.ts.map |
@@ -1,2 +0,2 @@ | ||
export declare const version = "0.0.1-beta.9"; | ||
export declare const version = "0.0.1-beta.10"; | ||
//# sourceMappingURL=version.d.ts.map |
import { Publisher, PublisherProps } from './publisher'; | ||
import { Plugin } from '../../app/types'; | ||
import { NodeEmitter } from '../../app/emitter'; | ||
declare type DefinedPluginFields = 'name' | 'type' | 'version' | 'isLoaded' | 'load' | 'alias' | 'group' | 'identify' | 'page' | 'screen' | 'track'; | ||
@@ -7,3 +8,3 @@ declare type SegmentNodePlugin = Plugin & Required<Pick<Plugin, DefinedPluginFields>>; | ||
export declare function createNodePlugin(publisher: Publisher): SegmentNodePlugin; | ||
export declare const createConfiguredNodePlugin: (props: ConfigureNodePluginProps) => { | ||
export declare const createConfiguredNodePlugin: (props: ConfigureNodePluginProps, emitter: NodeEmitter) => { | ||
publisher: Publisher; | ||
@@ -10,0 +11,0 @@ plugin: SegmentNodePlugin; |
import type { Context } from '../../app/context'; | ||
import { NodeEmitter } from '../../app/emitter'; | ||
export interface PublisherProps { | ||
@@ -24,3 +25,4 @@ host?: string; | ||
private _httpRequestTimeout; | ||
constructor({ host, path, maxRetries, maxEventsInBatch, flushInterval, writeKey, httpRequestTimeout, }: PublisherProps); | ||
private _emitter; | ||
constructor({ host, path, maxRetries, maxEventsInBatch, flushInterval, writeKey, httpRequestTimeout, }: PublisherProps, emitter: NodeEmitter); | ||
private createBatch; | ||
@@ -27,0 +29,0 @@ private clearBatch; |
{ | ||
"name": "@segment/analytics-node", | ||
"version": "0.0.1-beta.9", | ||
"version": "0.0.1-beta.10", | ||
"main": "./dist/cjs/index.js", | ||
@@ -5,0 +5,0 @@ "module": "./dist/esm/index.js", |
129
README.md
# @segment/analytics-node | ||
> ### Warning: Until 1.x release, use this library at your own risk! | ||
> ### Warning: This library is in [public beta](https://segment.com/legal/first-access-beta-preview) ⚠️ | ||
@@ -18,3 +18,3 @@ ## Requirements | ||
### Usage | ||
### Usage | ||
Assuming some express-like web framework. | ||
@@ -46,15 +46,37 @@ ```ts | ||
``` | ||
## Regional configuration | ||
For Business plans with access to Regional Segment, you can use the host configuration parameter to send data to the desired region: | ||
Oregon (Default) — api.segment.io/v1 | ||
Dublin — events.eu1.segmentapis.com | ||
An example of setting the host to the EU endpoint using the Node library would be: | ||
## Graceful Exit | ||
Avoid losing events on exit! | ||
* Call `.closeAndFlush()` to stop collecting new events and flush all existing events. | ||
* If a callback on an event call is included, this also waits for all callbacks to be called, and any of their subsequent promises to be resolved. | ||
```ts | ||
await analytics.closeAndFlush() | ||
// or | ||
await analytics.closeAndFlush({ timeout: 5000 }) // force resolve after 5000ms | ||
``` | ||
#### Advanced Example | ||
```ts | ||
const app = express() | ||
const server = app.listen(3000) | ||
const onExit = async () => { | ||
await analytics.closeAndFlush() | ||
server.close(() => { | ||
console.log("Gracefully closing server...") | ||
process.exit() | ||
}) | ||
} | ||
['SIGINT', 'SIGTERM'].forEach((code) => process.on(code, onExit)) | ||
``` | ||
#### Collecting unflushed events | ||
If you absolutely need to preserve all possible events in the event of a forced timeout, even ones that came in after `analytics.closeAndFlush()` was called, you can collect those events. | ||
```ts | ||
const analytics = new Analytics({ | ||
... | ||
host: "https://events.eu1.segmentapis.com" | ||
}); | ||
const unflushedEvents = [] | ||
analytics.on('call_after_close', (event) => unflushedEvents.push(events)) | ||
await analytics.closeAndFlush() | ||
console.log(unflushedEvents) // all events that came in after closeAndFlush was called | ||
``` | ||
@@ -75,3 +97,17 @@ | ||
``` | ||
## Regional configuration | ||
For Business plans with access to Regional Segment, you can use the host configuration parameter to send data to the desired region: | ||
Oregon (Default) — api.segment.io/v1 | ||
Dublin — events.eu1.segmentapis.com | ||
An example of setting the host to the EU endpoint using the Node library would be: | ||
```ts | ||
const analytics = new Analytics({ | ||
... | ||
host: "https://events.eu1.segmentapis.com" | ||
}); | ||
``` | ||
## Batching | ||
@@ -84,3 +120,2 @@ Our libraries are built to support high performance environments. That means it is safe to use our Node library on a web server that’s serving thousands of requests per second. | ||
- The very first time it gets a message. | ||
- Every 15 messages (controlled by `settings.maxEventsInBatch`). | ||
@@ -119,38 +154,5 @@ - If 10 seconds has passed since the last flush (controlled by `settings.flushInterval`) | ||
## Graceful Exit | ||
Avoid losing events on exit! | ||
* Call `.closeAndFlush()` to stop collecting new events and flush all existing events. | ||
* If a callback on an event call is included, this also waits for all callbacks to be called, and any of their subsequent promises to be resolved. | ||
```ts | ||
await analytics.closeAndFlush() | ||
// or | ||
await analytics.closeAndFlush({ timeout: 5000 }) // force resolve after 5000ms | ||
``` | ||
#### Advanced Example | ||
```ts | ||
const app = express() | ||
const server = app.listen(3000) | ||
const onExit = async () => { | ||
await analytics.closeAndFlush() | ||
server.close(() => { | ||
console.log("Gracefully closing server...") | ||
process.exit() | ||
}) | ||
} | ||
['SIGINT', 'SIGTERM'].forEach((code) => process.on(code, onExit)) | ||
``` | ||
#### Collecting unflushed events | ||
If you absolutely need to preserve all possible events in the event of a forced timeout, even ones that came in after `analytics.closeAndFlush()` was called, you can collect those events. | ||
```ts | ||
const unflushedEvents = [] | ||
analytics.on('call_after_close', (event) => unflushedEvents.push(events)) | ||
await analytics.closeAndFlush() | ||
console.log(unflushedEvents) // all events that came in after closeAndFlush was called | ||
``` | ||
## Event Emitter Interface | ||
You can see the complete list of emitted events in [emitter.ts](src/app/emitter.ts) | ||
```ts | ||
@@ -162,4 +164,22 @@ analytics.on('error', (err) => console.error(err)) | ||
analytics.on('track', (ctx) => console.log(ctx)) | ||
``` | ||
#### You can use the emitter to log all HTTP Requests | ||
```ts | ||
analytics.on('http_request', (event) => console.log(event)) | ||
// when triggered, emits an event of the shape: | ||
{ | ||
url: 'https://api.segment.io/v1/batch', | ||
method: 'POST', | ||
headers: { | ||
'Content-Type': 'application/json', | ||
... | ||
}, | ||
body: '...', | ||
} | ||
``` | ||
## Multiple Clients | ||
@@ -244,3 +264,20 @@ Different parts of your application may require different types of batching, or even sending to multiple Segment sources. In that case, you can initialize multiple instances of Analytics with different settings: | ||
``` | ||
- Undocumented behavior around `track` properties have been removed. | ||
```ts | ||
// old, undocumented behavior | ||
analytics.track({ | ||
... | ||
event: 'Ultimate Played', | ||
myProp: 'abc' | ||
}) | ||
// new | ||
analytics.track({ | ||
... | ||
event: 'Ultimate Played', | ||
properties: { | ||
myProp: 'abc' | ||
} | ||
}) | ||
``` | ||
@@ -247,0 +284,0 @@ ## Plugin Architecture |
@@ -44,11 +44,14 @@ import { CoreAnalytics, bindAll, pTimeout } from '@segment/analytics-core' | ||
const { plugin, publisher } = createConfiguredNodePlugin({ | ||
writeKey: settings.writeKey, | ||
host: settings.host, | ||
path: settings.path, | ||
maxRetries: settings.maxRetries ?? 3, | ||
maxEventsInBatch: settings.maxEventsInBatch ?? 15, | ||
httpRequestTimeout: settings.httpRequestTimeout, | ||
flushInterval, | ||
}) | ||
const { plugin, publisher } = createConfiguredNodePlugin( | ||
{ | ||
writeKey: settings.writeKey, | ||
host: settings.host, | ||
path: settings.path, | ||
maxRetries: settings.maxRetries ?? 3, | ||
maxEventsInBatch: settings.maxEventsInBatch ?? 15, | ||
httpRequestTimeout: settings.httpRequestTimeout, | ||
flushInterval, | ||
}, | ||
this as NodeEmitter | ||
) | ||
this._publisher = publisher | ||
@@ -55,0 +58,0 @@ this.ready = this.register(plugin).then(() => undefined) |
@@ -9,5 +9,13 @@ import { CoreEmitterContract, Emitter } from '@segment/analytics-core' | ||
*/ | ||
type NodeEmitterEvents = CoreEmitterContract<Context> & { | ||
export type NodeEmitterEvents = CoreEmitterContract<Context> & { | ||
initialize: [AnalyticsSettings] | ||
call_after_close: [SegmentEvent] // any event that did not get dispatched due to close | ||
http_request: [ | ||
{ | ||
url: string | ||
method: string | ||
headers: Record<string, string> | ||
body: string | ||
} | ||
] | ||
drained: [] | ||
@@ -14,0 +22,0 @@ } |
// This file is generated. | ||
export const version = '0.0.1-beta.9' | ||
export const version = '0.0.1-beta.10' |
@@ -6,2 +6,3 @@ import { Publisher, PublisherProps } from './publisher' | ||
import { Context } from '../../app/context' | ||
import { NodeEmitter } from '../../app/emitter' | ||
@@ -56,4 +57,7 @@ function normalizeEvent(ctx: Context) { | ||
export const createConfiguredNodePlugin = (props: ConfigureNodePluginProps) => { | ||
const publisher = new Publisher(props) | ||
export const createConfiguredNodePlugin = ( | ||
props: ConfigureNodePluginProps, | ||
emitter: NodeEmitter | ||
) => { | ||
const publisher = new Publisher(props, emitter) | ||
return { | ||
@@ -60,0 +64,0 @@ publisher: publisher, |
@@ -8,2 +8,3 @@ import { backoff } from '@segment/analytics-core' | ||
import { ContextBatch } from './context-batch' | ||
import { NodeEmitter } from '../../app/emitter' | ||
@@ -45,12 +46,16 @@ function sleep(timeoutInMs: number): Promise<void> { | ||
private _httpRequestTimeout: number | ||
constructor({ | ||
host, | ||
path, | ||
maxRetries, | ||
maxEventsInBatch, | ||
flushInterval, | ||
writeKey, | ||
httpRequestTimeout, | ||
}: PublisherProps) { | ||
private _emitter: NodeEmitter | ||
constructor( | ||
{ | ||
host, | ||
path, | ||
maxRetries, | ||
maxEventsInBatch, | ||
flushInterval, | ||
writeKey, | ||
httpRequestTimeout, | ||
}: PublisherProps, | ||
emitter: NodeEmitter | ||
) { | ||
this._emitter = emitter | ||
this._maxRetries = maxRetries | ||
@@ -190,3 +195,3 @@ this._maxEventsInBatch = Math.max(maxEventsInBatch, 1) | ||
try { | ||
const response = await fetch(this._url, { | ||
const requestInit = { | ||
signal: signal, | ||
@@ -200,3 +205,13 @@ method: 'POST', | ||
body: payload, | ||
} | ||
this._emitter.emit('http_request', { | ||
url: this._url, | ||
method: requestInit.method, | ||
headers: requestInit.headers, | ||
body: requestInit.body, | ||
}) | ||
const response = await fetch(this._url, requestInit) | ||
clearTimeout(timeoutId) | ||
@@ -203,0 +218,0 @@ |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
166159
2651
352
1