-
Notifications
You must be signed in to change notification settings - Fork 132
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
41 changed files
with
1,812 additions
and
140 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 |
---|---|---|
|
@@ -34,3 +34,4 @@ reports/* | |
playwright-report/ | ||
playwright/.cache/ | ||
tmp.tsconfig.json | ||
.env |
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
40 changes: 0 additions & 40 deletions
40
packages/signals/browser-signals-example/src/components/ComplexForm.tsx
This file was deleted.
Oops, something went wrong.
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,3 @@ | ||
/* eslint-disable no-undef */ | ||
require('fake-indexeddb/auto') | ||
globalThis.structuredClone = (v) => JSON.parse(JSON.stringify(v)) |
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
45 changes: 45 additions & 0 deletions
45
packages/signals/browser-signals/src/core/analytics-service/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 |
---|---|---|
@@ -0,0 +1,45 @@ | ||
import { AnyAnalytics } from '../../types' | ||
import { SignalGenerator } from '../signal-generators/types' | ||
|
||
/** | ||
* Helper / facade that wraps the analytics, and abstracts away the details of the analytics instance. | ||
*/ | ||
export class AnalyticsService { | ||
private instance: AnyAnalytics | ||
constructor(analyticsInstance: AnyAnalytics) { | ||
this.instance = analyticsInstance | ||
} | ||
get writeKey() { | ||
return this.instance.settings.writeKey | ||
} | ||
createSegmentInstrumentationEventGenerator(): SignalGenerator { | ||
let disable = false | ||
const generator: SignalGenerator = { | ||
id: 'segment-event-generator', | ||
register: async (signalEmitter) => { | ||
await this.instance.addSourceMiddleware(({ payload, next }) => { | ||
if (disable) { | ||
return | ||
} | ||
const event = payload.obj | ||
|
||
const isEventFromSignalEdgeFunction = | ||
event.context.__eventOrigin?.type === 'Signal' | ||
|
||
if (isEventFromSignalEdgeFunction) { | ||
return next(payload) | ||
} else { | ||
signalEmitter.emit({ | ||
type: 'instrumentation', | ||
data: event, | ||
}) | ||
} | ||
}) | ||
return () => { | ||
disable = true | ||
} | ||
}, | ||
} | ||
return generator | ||
} | ||
} |
22 changes: 16 additions & 6 deletions
22
packages/signals/browser-signals/src/core/buffer/__tests__/buffer.test.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,13 +1,23 @@ | ||
import { SignalBuffer } from '../index' | ||
import { createMockSignal } from '../../../test-helpers/fixtures/signal' | ||
import { getSignalBuffer, SignalBuffer } from '../index' | ||
|
||
describe(SignalBuffer, () => { | ||
describe(getSignalBuffer, () => { | ||
let buffer: SignalBuffer | ||
|
||
beforeEach(() => { | ||
buffer = new SignalBuffer() | ||
beforeEach(async () => { | ||
buffer = getSignalBuffer({ | ||
maxBufferSize: 10, | ||
}) | ||
await buffer.clear() | ||
}) | ||
|
||
it('should exits', () => { | ||
it('should instantiate without throwing an error', () => { | ||
expect(buffer).toBeTruthy() | ||
}) | ||
it('should add and clear', async () => { | ||
const mockSignal = createMockSignal() | ||
await buffer.add(mockSignal) | ||
await expect(buffer.getAll()).resolves.toEqual([mockSignal]) | ||
await buffer.clear() | ||
await expect(buffer.getAll()).resolves.toHaveLength(0) | ||
}) | ||
}) |
135 changes: 134 additions & 1 deletion
135
packages/signals/browser-signals/src/core/buffer/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 +1,134 @@ | ||
export class SignalBuffer {} | ||
import { Signal } from '../../types' | ||
import { openDB, DBSchema, IDBPDatabase } from 'idb' | ||
import { logger } from '../../lib/logger' | ||
|
||
interface SignalDatabase extends DBSchema { | ||
signals: { | ||
key: string | ||
value: Signal | ||
} | ||
} | ||
|
||
export interface PersistentStorage<T = any> { | ||
getAll(): Promise<T[]> | T[] | ||
add(value: T): Promise<void> | void | ||
clear(): void | ||
} | ||
|
||
export interface SignalPersistentStorage extends PersistentStorage<Signal> {} | ||
|
||
export class SignalStore implements SignalPersistentStorage { | ||
static readonly DB_NAME = 'Segment Signals Buffer' | ||
static readonly STORE_NAME = 'signals' | ||
private signalStore: Promise<IDBPDatabase<SignalDatabase>> | ||
private signalCount = 0 | ||
private maxBufferSize: number | ||
|
||
public length() { | ||
return this.signalCount | ||
} | ||
|
||
static deleteDatabase() { | ||
return indexedDB.deleteDatabase(SignalStore.DB_NAME) | ||
} | ||
|
||
constructor(settings: { maxBufferSize?: number } = {}) { | ||
this.maxBufferSize = settings.maxBufferSize ?? 25 // TODO: increase this number after development | ||
this.signalStore = this.createSignalStore() | ||
void this.initializeSignalCount() | ||
} | ||
|
||
private getStore() { | ||
return this.signalStore | ||
} | ||
|
||
private async createSignalStore() { | ||
const db = await openDB<SignalDatabase>(SignalStore.DB_NAME, 1, { | ||
upgrade(db) { | ||
db.createObjectStore(SignalStore.STORE_NAME, { autoIncrement: true }) | ||
}, | ||
}) | ||
logger.debug('Signals Buffer (indexDB) initialized') | ||
return db | ||
} | ||
|
||
private async initializeSignalCount() { | ||
const store = await this.signalStore | ||
this.signalCount = await store.count(SignalStore.STORE_NAME) | ||
logger.debug( | ||
`Signal count initialized with ${this.signalCount} signals (max: ${this.maxBufferSize})` | ||
) | ||
} | ||
|
||
async add(signal: Signal): Promise<void> { | ||
const store = await this.signalStore | ||
if (this.signalCount >= this.maxBufferSize) { | ||
// Get the key of the oldest signal and delete it | ||
const oldestKey = await store | ||
.transaction(SignalStore.STORE_NAME) | ||
.store.getKey(IDBKeyRange.lowerBound(0)) | ||
if (oldestKey !== undefined) { | ||
await store.delete(SignalStore.STORE_NAME, oldestKey) | ||
} else { | ||
this.signalCount-- | ||
} | ||
} | ||
await store.add(SignalStore.STORE_NAME, signal) | ||
this.signalCount++ | ||
} | ||
|
||
async getAll(): Promise<Signal[]> { | ||
const store = await this.getStore() | ||
return store.getAll(SignalStore.STORE_NAME) | ||
} | ||
|
||
async clear() { | ||
const store = await this.getStore() | ||
return store.clear(SignalStore.STORE_NAME) | ||
} | ||
} | ||
|
||
export class SignalBuffer< | ||
T extends SignalPersistentStorage = SignalPersistentStorage | ||
> { | ||
public store: T | ||
constructor(store: T) { | ||
this.store = store | ||
} | ||
async add(signal: Signal) { | ||
try { | ||
return await this.store.add(signal) | ||
} catch (e) { | ||
console.error(e) | ||
return undefined | ||
} | ||
} | ||
async getAll() { | ||
try { | ||
return await this.store.getAll() | ||
} catch (e) { | ||
console.error(e) | ||
return [] | ||
} | ||
} | ||
async clear() { | ||
try { | ||
return await this.store.clear() | ||
} catch (e) { | ||
console.error(e) | ||
return undefined | ||
} | ||
} | ||
} | ||
|
||
export const getSignalBuffer = < | ||
T extends SignalPersistentStorage = SignalPersistentStorage | ||
>( | ||
settings: { | ||
maxBufferSize?: number | ||
signalStorage?: T | ||
} = {} | ||
) => { | ||
const store = settings.signalStorage ?? new SignalStore(settings) | ||
return new SignalBuffer(store) | ||
} |
34 changes: 34 additions & 0 deletions
34
packages/signals/browser-signals/src/core/client/__tests__/client.test.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 |
---|---|---|
@@ -0,0 +1,34 @@ | ||
import { SignalsIngestClient } from '../index' | ||
import { createSuccess } from '@segment/analytics-next/src/test-helpers/factories' | ||
import unfetch from 'unfetch' | ||
jest.mock('unfetch') | ||
jest | ||
.mocked(unfetch) | ||
.mockImplementation(() => createSuccess({ integrations: {} })) | ||
|
||
describe(SignalsIngestClient, () => { | ||
let client: SignalsIngestClient | ||
|
||
beforeEach(async () => { | ||
client = new SignalsIngestClient() | ||
await client.init({ writeKey: 'test' }) | ||
}) | ||
|
||
it('makes a track call via the analytics api', async () => { | ||
expect(client).toBeTruthy() | ||
const ctx = await client.send({ | ||
type: 'instrumentation', | ||
data: { | ||
foo: 'bar', | ||
}, | ||
}) | ||
|
||
expect(ctx!.event.type).toEqual('track') | ||
expect(ctx!.event.properties).toEqual({ | ||
type: 'instrumentation', | ||
data: { | ||
foo: 'bar', | ||
}, | ||
}) | ||
}) | ||
}) |
Oops, something went wrong.