-
Notifications
You must be signed in to change notification settings - Fork 128
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
8 changed files
with
130 additions
and
35 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,10 @@ | ||
export class SignalBuffer {} | ||
import { Emitter } from '@segment/analytics-generic-utils' | ||
import { Signal } from '../../types' | ||
|
||
export class SignalBuffer extends Emitter<{ add: [Signal] }> { | ||
signals: Signal[] = [] | ||
add(signal: Signal) { | ||
this.emit('add', signal) | ||
this.signals.push(signal) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
import { Analytics } from '@segment/analytics-next' | ||
import { SignalType } from '../../types' | ||
|
||
const MAGIC_EVENT_NAME = 'Segment Signal Generated' | ||
|
||
/** | ||
* This currently just uses the Segment analytics-next library to send signals. | ||
* This persists the signals in a queue until the client is initialized. | ||
*/ | ||
export class SignalsClient { | ||
private queue: { | ||
type: SignalType | ||
data: Record<string, any> | ||
}[] | ||
private analytics: Analytics | undefined | ||
constructor() { | ||
this.queue = [] | ||
this.analytics = undefined | ||
} | ||
|
||
initAndFlush(writeKey: string) { | ||
this.analytics = new Analytics({ writeKey }) | ||
this.flush() | ||
} | ||
|
||
send(type: SignalType, data: Record<string, any>) { | ||
if (!this.analytics) { | ||
this.queue.push({ type, data }) | ||
} else { | ||
void this.analytics!.track(MAGIC_EVENT_NAME, { | ||
type, | ||
data, | ||
}) | ||
} | ||
} | ||
|
||
flush() { | ||
if (!this.analytics) { | ||
throw new Error('Please initialize before calling this method.') | ||
} | ||
this.queue.forEach(({ type, data }) => { | ||
void this.analytics!.track(MAGIC_EVENT_NAME, { | ||
type, | ||
data, | ||
}) | ||
}) | ||
this.queue = [] | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
11 changes: 8 additions & 3 deletions
11
packages/signals/browser-signals/src/core/signal-generators/types.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,12 +1,17 @@ | ||
import type { SignalsEmitter } from '../emitter' | ||
import type { SignalEmitter } from '../emitter' | ||
|
||
export interface SignalGenerator { | ||
/** | ||
* To support unregistering by name/label | ||
* e.g "form-submit" | ||
*/ | ||
name: string | ||
register(emitter: SignalsEmitter): () => void | ||
/** | ||
* Register a custom function that emits signals | ||
*/ | ||
register(emitter: SignalEmitter): () => void | ||
} | ||
|
||
export type SignalGeneratorClass = new () => SignalGenerator | ||
export interface SignalGeneratorClass { | ||
new (): SignalGenerator | ||
} |
77 changes: 50 additions & 27 deletions
77
packages/signals/browser-signals/src/core/signals/index.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,48 +1,71 @@ | ||
import { SignalsClient } from '../client' | ||
import { SignalBuffer } from '../buffer' | ||
import { SignalsEmitter } from '../emitter' | ||
import { SignalEmitter } from '../emitter' | ||
import { | ||
ClickSignalsGenerator, | ||
FormSubmitGenerator, | ||
} from '../signal-generators/dom-generators' | ||
import { SignalGeneratorClass } from '../signal-generators/types' | ||
import { | ||
SignalGenerator, | ||
SignalGeneratorClass, | ||
} from '../signal-generators/types' | ||
import { AnyAnalytics } from '../../types' | ||
import { isClass } from '../../utils/is-class' | ||
|
||
export class Signals { | ||
private buffer: SignalBuffer | ||
private signalsEmitter: SignalEmitter | ||
private cleanup: (() => void)[] = [] | ||
private signalsApi: SignalsClient | ||
|
||
constructor() { | ||
this.buffer = new SignalBuffer() | ||
this.signalsApi = new SignalsClient() | ||
this.signalsEmitter = new SignalEmitter() | ||
this.signalsEmitter.subscribe(this.buffer.add) | ||
this.signalsEmitter.subscribe((signal) => { | ||
this.signalsApi.send(signal.type, signal.data) | ||
}) | ||
this.cleanup.push(registerLocalGenerators(this.signalsEmitter)) | ||
} | ||
|
||
start(analytics: AnyAnalytics) { | ||
this.signalsApi.initAndFlush(analytics.settings.writeKey) | ||
} | ||
|
||
stop() { | ||
this.cleanup.forEach((fn) => fn()) | ||
} | ||
} | ||
|
||
type CleanupGenerators = () => void | ||
|
||
const registerGenerators = ( | ||
emitter: SignalsEmitter, | ||
signalGenerators: SignalGeneratorClass[] | ||
emitter: SignalEmitter, | ||
signalGenerators: (SignalGeneratorClass | SignalGenerator)[] | ||
): CleanupGenerators => { | ||
const cleanupFns = signalGenerators.map((Gen) => new Gen().register(emitter)) | ||
const cleanupFns = signalGenerators.map((gen) => { | ||
if (isClass(gen)) { | ||
// Check if Gen is a function and has a constructor | ||
return new gen().register(emitter) | ||
} else { | ||
return gen.register(emitter) | ||
} | ||
}) | ||
// Return a cleanup function that calls all the cleanup functions (e.g unsubscribes from event listeners) | ||
return () => cleanupFns.forEach((fn) => fn()) | ||
} | ||
|
||
const registerLocalGenerators = ( | ||
emitter: SignalsEmitter | ||
): CleanupGenerators => { | ||
const registerLocalGenerators = (emitter: SignalEmitter): CleanupGenerators => { | ||
return registerGenerators(emitter, [ | ||
ClickSignalsGenerator, | ||
FormSubmitGenerator, | ||
]) | ||
} | ||
|
||
export class Signals { | ||
private buffer = new SignalBuffer() | ||
private signalsEmitter: SignalsEmitter | ||
private cleanupGenerators: CleanupGenerators | ||
|
||
constructor() { | ||
this.signalsEmitter = new SignalsEmitter() | ||
this.cleanupGenerators = registerLocalGenerators(this.signalsEmitter) | ||
} | ||
|
||
start(analytics: any) { | ||
this.signalsEmitter.subscribe((signal) => { | ||
this.buffer.add(signal) | ||
}) | ||
} | ||
|
||
stop() { | ||
this.cleanupGenerators() | ||
} | ||
const registerSegmentEventGenerators = ( | ||
analytics: AnyAnalytics, | ||
emitter: SignalEmitter | ||
): CleanupGenerators => { | ||
return registerGenerators(emitter, []) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
export const isClass = (value: any): value is NewableFunction => { | ||
return ( | ||
typeof value === 'function' && value.prototype.constructor !== undefined | ||
) | ||
} |