Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,4 @@ This library is one of Segment’s most popular Flagship libraries. It is active

- Contribution guidelines: [CONTRIBUTING.md](CONTRIBUTING.md)
- Development instructions: [DEVELOPMENT.md](DEVELOPMENT.md)
- Releasing (to npm): [RELEASING.md](RELEASING.md)
51 changes: 0 additions & 51 deletions packages/browser/ARCHITECTURE.md

This file was deleted.

55 changes: 3 additions & 52 deletions packages/browser/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ Analytics Next (aka Analytics 2.0) is the latest version of Segment’s JavaScri
- [🏎️ Quickstart](#-quickstart)
- [💡 Using with Segment](#-using-with-segment)
- [💻 Using as an `npm` package](#-using-as-an-npm-package)
- [🔌 Plugins](#-plugins)
- [🔌 Architecture](#-architecture--plugins)
- [🐒 Development](#-development)

---
Expand Down Expand Up @@ -187,6 +187,8 @@ declare global {
}
```

## 🔌 Architecture & Plugins
- See [ARCHITECTURE.md](architecture/ARCHITECTURE.md)

## 🐒 Development

Expand All @@ -207,56 +209,5 @@ Then, make your changes and test them out in the test app!

<img src="https://user-images.githubusercontent.com/2866515/135407053-7561d522-b969-484d-8d3a-6f1c4d9c025b.gif" alt="Example of the development app" width="500px">

# 🔌 Plugins

When developing against Analytics Next you will likely be writing plugins, which can augment functionality and enrich data. Plugins are isolated chunks which you can build, test, version, and deploy independently of the rest of the codebase. Plugins are bounded by Analytics Next which handles things such as observability, retries, and error management.

Plugins can be of two different priorities:

1. **Critical**: Analytics Next should expect this plugin to be loaded before starting event delivery
2. **Non-critical**: Analytics Next can start event delivery before this plugin has finished loading

and can be of five different types:

1. **Before**: Plugins that need to be run before any other plugins are run. An example of this would be validating events before passing them along to other plugins.
2. **After**: Plugins that need to run after all other plugins have run. An example of this is the segment.io integration, which will wait for destinations to succeed or fail so that it can send its observability metrics.
3. **Destination**: Destinations to send the event to (ie. legacy destinations). Does not modify the event and failure does not halt execution.
4. **Enrichment**: Modifies an event, failure here could halt the event pipeline.
5. **Utility**: Plugins that change Analytics Next functionality and don't fall into the other categories.

Here is an example of a simple plugin that would convert all track events event names to lowercase before the event gets sent through the rest of the pipeline:

```ts
import type { Plugin } from '@segment/analytics-next'

export const lowercase: Plugin = {
name: 'Lowercase Event Name',
type: 'before',
version: '1.0.0',

isLoaded: () => true,
load: () => Promise.resolve(),

track: (ctx) => {
ctx.event.event = ctx.event.event.toLowerCase()
return ctx
}
}

analytics.register(lowercase)
```

For further examples check out our [existing plugins](/packages/browser/src/plugins).

## 🧪 QA
Feature work and bug fixes should include tests. Run all [Jest](https://jestjs.io) tests:
```
$ yarn test
```
Lint all with [ESLint](https://github.com/typescript-eslint/typescript-eslint/):
```
$ yarn lint
```



151 changes: 151 additions & 0 deletions packages/browser/architecture/ARCHITECTURE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
## Analytics.js Plugin Architecture

> [!IMPORTANT]
> This doc may get out-of-date. Please prefer to use and link to Segment documentation for the most up-to-date information. It would be advisable to move this doc to https://segment.com/docs/connections/sources/catalog/libraries/website/javascript, so there is a single source of truth.

### Event Flow Diagram
More details on plugin architecture can be found here:
https://segment.com/docs/connections/sources/catalog/libraries/website/javascript/#plugins-and-source-middleware

### You can use the [vscode mermaid extension](https://marketplace.visualstudio.com/items?itemName=bierner.markdown-mermaid) to preview the following diagram code block inside of vscode, or copy and paste into the [mermaid live editor](https://mermaid.live/).

```mermaid
graph TD
subgraph Event Creation
A[analytics.track/page/identify] --> B[Event Factory]
B --> C[Event Queue]
end

subgraph Plugin Pipeline
C --> D[Before Plugins e.g add page context]
D --> E[Enrichment Plugins]
E --> F[Destination Plugins e.g Segment.io]
F --> G[After Plugins]
end

subgraph Plugin Types Details
I[Before Plugins<br/>Priority: Critical<br/>Example: Event Validation] --- D
J[Enrichment Plugins<br/>Priority: Critical<br/>Parallel Execution<br/>Can Modify Events<br/>Example: Add User Agent] --- E
K[Destination Plugins<br/>Parallel Execution<br/>Cannot Modify Events<br/>Example: Google Analytics] --- F
L[After Plugins<br/>Example: Metrics Collection] --- G
end

subgraph Notes
M[Plugin Priorities]
N[Critical - Must load before<br/>event delivery starts]
O[Non-Critical - Can load<br/>after event delivery starts]
M --> N
M --> O
end
```

### Plugin Types and Middleware
[This information is also available in the Segment documentation](https://segment.com/docs/connections/sources/catalog/libraries/website/javascript/#plugins-and-source-middleware)

- **Source Middleware** (see [Example](#example-source-middleware-implementation))
- **Source Middleware is just a light API wrapper around a "Before" type plugin Plugin**
- Source Middleware is the legacy API (pre-analytics next). It's less verbose than the full plugin API, but a bit less powerful. It is functionally equivalent to a "Before" type plugin.

- **Before Plugins** (see [Example](#example-plugin-implementation))
- Run before any other plugins
- Critical priority - block event pipeline until `.load()` resolves
- Use cases: Event validation, data transformation
- Example: Event validation before passing to other plugins)


- **Enrichment Plugins**
- Functionally Identitical to "before" plugins, but run after them. Before plugins are typically used internally (e.g adding page info), but there's no hard and fast rule.

- **Destination Plugins**
- Run after enrichment
- Cannot modify the event
- Execute in parallel
- Failures do not halt pipeline
- Example: Segment.io, Google Analytics, Mixpanel

- **After Plugins (uncommon)**
- Run after all other plugins complete
- Use cases: Metrics, logging
- Example: segment.io plugin for observability metrics

- **Utility Plugins**
- Executes only once during the analytics.js bootstrap. Gives you access to the analytics instance using the plugin's load() method. This doesn't allow you to modify events.
- Do not directly process events
- Example: some plugin that registers a bunch of analytics event listeners (e.g. analytics.on('track', ...) and reports them to an external system)

- **Destination Middleware** (See [Example](#example-destination-middleware-implementation))
- A special type of plugin that allows you to add a plugin that only affects a specific (device mode) destination plugin.


### Example: Plugin Implementation
```ts
analytics.register({
name: 'My Plugin',
type: 'before',
isLoaded: () => true,
load: () => Promise.resolve(),
// lowercase all track event names
track: (ctx) => {
ctx.event.event = ctx.event.event.toLowerCase()
return ctx
},
// drop page events with a specific title
page: (ctx) => {
if (ctx.properties.title === 'some title') {
return null
}
}
})
```
### Example: Source Middleware Implementation
```ts
analytics.addSourceMiddleware(({ payload, next }) => {
const { event } = payload.obj.context
if (event.type === 'track') {
// change the event name to lowercase
event.event = event.event.toLowerCase()
} else if (event.type === 'page') {
// drop any page events with a specific title
if (event.properties.title === 'some title') {
return null
}
}
next(payload)
})
```

### Example: Destination Middleware Implementation
> [!NOTE]
> It is not currently possible to add a destination middleware to the Segment.io destination.
```ts
analytics.addDestinationMiddleware('amplitude', ({ next, payload }) => {
payload.obj.properties!.hello = 'from the other side'
next(payload)
})
```
or, to apply to all destinations
```ts
analytics.addDestinationMiddleware('*', (ctx) => {
// This does not apply to the segment.io destination plugin, only device mode destinations.
ctx.event.properties!.hello = 'from the other side'
return ctx
})
```

### Event Flow Example

When `analytics.track()` is called:

1. Event is created via Event Factory
2. Event enters the queue
3. Before plugins run, in order of registration
3. Enrichment plugins run, in order of registration
4. Destination plugins receive the event in parallel (including Segment.io plugin)
5. Any after plugins handle post-processing (e.g. metrics collection)

### Plugin Priorities

- **Critical Plugins**: Must be loaded before event delivery starts
- Example: Before plugins, Validation plugins
- **Non-Critical Plugins**: Can load after event delivery begins
- Example: Destination plugins
Binary file added packages/browser/architecture/architecture.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.