Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@dialectlabs/monitor

Package Overview
Dependencies
Maintainers
6
Versions
52
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@dialectlabs/monitor - npm Package Compare versions

Comparing version 2.0.3-beta to 2.0.4-beta

examples/011-sms-notification-monitor.ts

2

examples/000.1-real-monoring-service-client.ts

@@ -11,6 +11,6 @@ import * as web3 from '@solana/web3.js';

Member,
programs,
sleep,
Wallet_,
} from '@dialectlabs/web3';
import { programs } from '@dialectlabs/web3';

@@ -17,0 +17,0 @@ const SOLANA_ENDPOINT = process.env.RPC_URL || 'http://localhost:8899';

@@ -40,2 +40,3 @@ import { Connection, Keypair, PublicKey } from '@solana/web3.js';

healthRatio: number;
resourceId: ResourceId;
};

@@ -54,4 +55,5 @@

healthRatio: Math.random(),
resourceId,
},
resourceId,
groupingKey: resourceId.toString(),
}),

@@ -77,8 +79,13 @@ );

.notify()
.dialectThread(({ value }) => ({
message: `Your cratio = ${value} below warning threshold`,
}))
.dialectThread(
({ value }) => ({
message: `Your cratio = ${value} below warning threshold`,
}),
{
dispatch: 'unicast',
to: ({ origin: { resourceId } }) => resourceId,
},
)
.and()
.dispatch('unicast')
.build();
dataSourceMonitor.start();

@@ -16,2 +16,3 @@ import {

healthRatio: number;
subscribers: ResourceId[];
};

@@ -34,4 +35,5 @@

healthRatio: Math.random() * 10,
subscribers,
},
resourceId,
groupingKey: resourceId.toBase58(),
}),

@@ -56,4 +58,5 @@ );

consoleNotificationSink,
{ dispatch: 'broadcast' },
)
.and()
.also()
.transform<number, number>({

@@ -74,7 +77,6 @@ keys: ['cratio'],

consoleNotificationSink,
() => Math.random() > 0.5,
{ dispatch: 'broadcast' },
)
.and()
.dispatch('broadcast')
.build();
monitor.start();

@@ -24,13 +24,21 @@ import {

.custom<DialectNotification>(
({ context }) => ({
message: `Hey ${context.resourceId}, welcome!`,
({
context: {
origin: { resourceId },
},
}) => ({
message: `Hey ${resourceId}, welcome!`,
}),
consoleNotificationSink,
{ dispatch: 'unicast', to: ({ origin: { resourceId } }) => resourceId },
)
.and()
.dispatch('unicast')
.build();
monitor.start().then(() => {
dummySubscriberRepository.addNewSubscriber(new Keypair().publicKey);
});
monitor.start();
const pk = new Keypair().publicKey;
setTimeout(() => {
dummySubscriberRepository.addNewSubscriber(pk);
}, 100);

@@ -5,2 +5,4 @@ import {

SubscriberRepository,
Web2Subscriber,
Web2SubscriberRepository,
} from '../src';

@@ -43,1 +45,11 @@ import { Keypair } from '@solana/web3.js';

}
export class DummyWeb2SubscriberRepository implements Web2SubscriberRepository {
async findAll(): Promise<Web2Subscriber[]> {
return Promise.resolve([]);
}
async findBy(resourceIds: ResourceId[]): Promise<Web2Subscriber[]> {
return Promise.resolve([]);
}
}

@@ -1,2 +0,2 @@

import { NotificationSink, ResourceId, Notification } from '..';
import { Notification, NotificationSink, ResourceId } from '../src';

@@ -3,0 +3,0 @@ export class ConsoleNotificationSink<N extends Notification>

@@ -15,2 +15,3 @@ import {

import { Duration } from 'luxon';
import { PublicKey } from '@solana/web3.js';

@@ -20,2 +21,3 @@ type DataType = {

healthRatio: number;
resourceId: PublicKey;
};

@@ -37,4 +39,5 @@

healthRatio: Math.random() * 10,
resourceId,
},
resourceId,
groupingKey: resourceId.toBase58(),
}),

@@ -82,6 +85,6 @@ );

consoleNotificationSink,
{ dispatch: 'unicast', to: ({ origin: { resourceId } }) => resourceId },
)
.and()
.dispatch('unicast')
.build();
monitor.start();

@@ -16,2 +16,3 @@ import {

share: number;
resourceId: ResourceId;
};

@@ -33,2 +34,3 @@

.poll((subscribers: ResourceId[]) => {
// subscribers are only those users who created a dialect thread!
const sourceData: SourceData<DataPool>[] = subscribers.map(

@@ -38,4 +40,5 @@ (resourceId) => ({

share: counter * counter,
resourceId,
},
resourceId,
groupingKey: resourceId.toBase58(),
}),

@@ -61,6 +64,6 @@ );

consoleDataSink,
{ dispatch: 'broadcast' },
)
.and()
.dispatch('broadcast')
.build();
monitor.start();

@@ -6,2 +6,3 @@ import {

Pipelines,
ResourceId,
SourceData,

@@ -11,3 +12,3 @@ } from '../src';

import { ConsoleNotificationSink } from './004-custom-notification-sink';
import { Observable } from 'rxjs';
import { Subject } from 'rxjs';
import { Keypair } from '@solana/web3.js';

@@ -18,2 +19,3 @@

healthRatio: number;
resourceId: ResourceId;
};

@@ -26,2 +28,4 @@

const subject = new Subject<SourceData<DataType>>();
const monitor: Monitor<DataType> = Monitors.builder({

@@ -31,17 +35,3 @@ subscriberRepository: new DummySubscriberRepository(1),

.defineDataSource<DataType>()
.push(
new Observable((subscriber) => {
const publicKey = Keypair.generate().publicKey;
const d1: SourceData<DataType> = {
data: { cratio: 0, healthRatio: 2 },
resourceId: publicKey,
};
const d2: SourceData<DataType> = {
data: { cratio: 1, healthRatio: 0 },
resourceId: publicKey,
};
subscriber.next(d1);
subscriber.next(d2);
}),
)
.push(subject)
.transform<number, number>({

@@ -62,6 +52,29 @@ keys: ['cratio'],

consoleNotificationSink,
{
dispatch: 'multicast',
to: ({ origin: { resourceId } }) => [
resourceId,
Keypair.generate().publicKey,
],
},
)
.and()
.dispatch('unicast')
.build();
monitor.start();
const publicKey = Keypair.generate().publicKey;
const d1: SourceData<DataType> = {
data: { cratio: 0, healthRatio: 2, resourceId: publicKey },
groupingKey: publicKey.toBase58(),
};
setTimeout(() => {
subject.next(d1);
}, 100);
const d2: SourceData<DataType> = {
data: { cratio: 1, healthRatio: 0, resourceId: publicKey },
groupingKey: publicKey.toBase58(),
};
setTimeout(() => {
subject.next(d2);
}, 200);

@@ -7,2 +7,3 @@ import {

Pipelines,
ResourceId,
SourceData,

@@ -12,3 +13,3 @@ } from '../src';

import { ConsoleNotificationSink } from './004-custom-notification-sink';
import { AsyncSubject, Observable, Subject } from 'rxjs';
import { Subject } from 'rxjs';
import { Keypair, PublicKey } from '@solana/web3.js';

@@ -18,2 +19,3 @@

attribute: NestedObject[];
resourceId: ResourceId;
};

@@ -47,4 +49,5 @@

consoleNotificationSink,
{ dispatch: 'unicast', to: ({ origin: { resourceId } }) => resourceId },
)
.and()
.also()
.transform<NestedObject[], NestedObject[]>({

@@ -60,5 +63,5 @@ keys: ['attribute'],

consoleNotificationSink,
{ dispatch: 'unicast', to: ({ origin: { resourceId } }) => resourceId },
)
.and()
.dispatch('unicast')
.build();

@@ -74,8 +77,14 @@ monitor.start();

const d1: SourceData<DataType> = {
data: { attribute: [{ publicKey: pk1 }, { publicKey: pk2 }] },
resourceId: publicKey,
data: {
attribute: [{ publicKey: pk1 }, { publicKey: pk2 }],
resourceId: publicKey,
},
groupingKey: publicKey.toBase58(),
};
const d2: SourceData<DataType> = {
data: { attribute: [{ publicKey: pk3 }] },
resourceId: publicKey,
data: {
attribute: [{ publicKey: pk3 }],
resourceId: publicKey,
},
groupingKey: publicKey.toBase58(),
};

@@ -82,0 +91,0 @@ setTimeout(() => {

@@ -6,8 +6,9 @@ import {

Pipelines,
ResourceEmail,
ResourceEmailRepository,
ResourceId,
SourceData,
} from '../src';
import { DummySubscriberRepository } from './003-custom-subscriber-repository';
import {
DummySubscriberRepository,
DummyWeb2SubscriberRepository,
} from './003-custom-subscriber-repository';
import { ConsoleNotificationSink } from './004-custom-notification-sink';

@@ -20,2 +21,3 @@ import { Observable } from 'rxjs';

healthRatio: number;
resourceId: ResourceId;
};

@@ -28,14 +30,5 @@

class DummyResourceEmailRepository implements ResourceEmailRepository {
findBy(resourceIds: ResourceId[]): Promise<ResourceEmail[]> {
return Promise.resolve([
{
resourceId: resourceIds[0],
email: process.env.RECEIVER_EMAIL!,
},
]);
}
}
const monitor: Monitor<DataType> = Monitors.builder({
subscriberRepository: new DummySubscriberRepository(1),
web2SubscriberRepository: new DummyWeb2SubscriberRepository(),
sinks: {

@@ -45,3 +38,2 @@ email: {

apiToken: process.env.EMAIL_SINK_TOKEN!,
resourceEmailRepository: new DummyResourceEmailRepository(),
},

@@ -55,8 +47,8 @@ },

const d1: SourceData<DataType> = {
data: { cratio: 0, healthRatio: 2 },
resourceId: publicKey,
data: { cratio: 0, healthRatio: 2, resourceId: publicKey },
groupingKey: publicKey.toBase58(),
};
const d2: SourceData<DataType> = {
data: { cratio: 1, healthRatio: 0 },
resourceId: publicKey,
data: { cratio: 1, healthRatio: 0, resourceId: publicKey },
groupingKey: publicKey.toBase58(),
};

@@ -77,9 +69,15 @@ subscriber.next(d1);

.notify()
.email(({ value }) => ({
subject: '[WARNING] Cratio above warning threshold',
text: `Your cratio = ${value} above warning threshold`,
}))
.dialectThread(({ value }) => ({
message: `Your cratio = ${value} above warning threshold`,
}))
.email(
({ value }) => ({
subject: '[WARNING] Cratio above warning threshold',
text: `Your cratio = ${value} above warning threshold`,
}),
{ dispatch: 'unicast', to: ({ origin }) => origin.resourceId },
)
.dialectThread(
({ value }) => ({
message: `Your cratio = ${value} above warning threshold`,
}),
{ dispatch: 'unicast', to: ({ origin }) => origin.resourceId },
)
.custom<DialectNotification>(

@@ -90,6 +88,6 @@ ({ value }) => ({

consoleNotificationSink,
{ dispatch: 'unicast', to: ({ origin }) => origin.resourceId },
)
.and()
.dispatch('unicast')
.build();
monitor.start();

@@ -12,4 +12,3 @@ import { PublicKey } from '@solana/web3.js';

data: T;
resourceId: ResourceId;
groupingKey?: string;
groupingKey: string;
}

@@ -28,5 +27,6 @@ /**

export interface Context<T extends object> {
resourceId: ResourceId;
origin: T;
groupingKey: string;
trace: Trace[];
subscribers: ResourceId[];
}

@@ -51,4 +51,5 @@ /**

export interface SubscriberEvent {
resourceId: ResourceId;
state: SubscriberState;
}
export declare type SubscriberState = 'added' | 'removed';

@@ -6,3 +6,2 @@ "use strict";

const web3_1 = require("@dialectlabs/web3");
const ts_retry_1 = require("ts-retry");
class DialectNotificationSink {

@@ -18,6 +17,9 @@ constructor(dialectProgram, monitorKeypair, subscriberRepository) {

const recipientsFiltered = recipients.filter((it) => !!subscriberPkToSubscriber[it.toBase58()]);
const results = await Promise.allSettled(recipientsFiltered.map(async (it) => (0, ts_retry_1.retryAsync)(async () => {
const dialectAccount = await (0, dialect_extensions_1.getDialectAccount)(this.dialectProgram, [this.monitorKeypair.publicKey, it]);
const results = await Promise.allSettled(recipientsFiltered.map(async (it) => async () => {
const dialectAccount = await (0, dialect_extensions_1.getDialectAccount)(this.dialectProgram, [
this.monitorKeypair.publicKey,
it,
]);
return (0, web3_1.sendMessage)(this.dialectProgram, dialectAccount, this.monitorKeypair, message);
}, { delay: 100, maxTry: 5 })));
}, { delay: 100, maxTry: 5 }));
const failedSends = results

@@ -24,0 +26,0 @@ .filter((it) => it.status === 'rejected')

@@ -9,2 +9,5 @@ export * from './data-model';

export * from './sengrid-email-notification-sink';
export * from './telegram-notification-sink';
export * from './dialect-notification-sink';
export * from './twilio-sms-notification-sink';
export * from './web-subscriber.repository';

@@ -21,2 +21,5 @@ "use strict";

__exportStar(require("./sengrid-email-notification-sink"), exports);
__exportStar(require("./telegram-notification-sink"), exports);
__exportStar(require("./dialect-notification-sink"), exports);
__exportStar(require("./twilio-sms-notification-sink"), exports);
__exportStar(require("./web-subscriber.repository"), exports);
import { Duration } from 'luxon';
import { MonitorFactory } from '../monitor-factory';
import { DataSource, DataSourceTransformationPipeline, PollableDataSource } from '../ports';
import { DataSource, DataSourceTransformationPipeline } from '../ports';
import { Monitor, MonitorProps } from '../monitor-api';

@@ -8,10 +8,9 @@ import { SubscriberEvent } from '../data-model';

private readonly subscriberRepository;
private readonly web2SubscriberRepository;
private readonly shutdownHooks;
constructor({ dialectProgram, monitorKeypair, subscriberRepository, }: MonitorProps);
constructor({ dialectProgram, monitorKeypair, subscriberRepository, web2SubscriberRepository, }: MonitorProps);
shutdown(): Promise<any[]>;
createUnicastMonitor<T extends object>(dataSource: DataSource<T>, datasourceTransformationPipelines: DataSourceTransformationPipeline<T, any>[], pollInterval?: Duration): Monitor<T>;
private decorateWithPushyDataSource;
createBroadcastMonitor<T extends object>(dataSource: PollableDataSource<T>, datasourceTransformationPipelines: DataSourceTransformationPipeline<T, any>[], pollInterval?: Duration): Monitor<T>;
createDefaultMonitor<T extends object>(dataSource: DataSource<T>, datasourceTransformationPipelines: DataSourceTransformationPipeline<T, any>[], pollInterval?: Duration): Monitor<T>;
createSubscriberEventMonitor(dataSourceTransformationPipelines: DataSourceTransformationPipeline<SubscriberEvent, any>[]): Monitor<SubscriberEvent>;
private toPushyDataSource;
}

@@ -7,8 +7,9 @@ "use strict";

const luxon_1 = require("luxon");
const unicast_monitor_1 = require("./unicast-monitor");
const rxjs_1 = require("rxjs");
const broadcast_monitor_1 = require("./broadcast-monitor");
const operators_1 = require("rxjs/operators");
const web_subscriber_repository_1 = require("../web-subscriber.repository");
const subsbscriber_repository_utilts_1 = require("./subsbscriber-repository-utilts");
const default_monitor_1 = require("./default-monitor");
class DefaultMonitorFactory {
constructor({ dialectProgram, monitorKeypair, subscriberRepository, }) {
constructor({ dialectProgram, monitorKeypair, subscriberRepository, web2SubscriberRepository, }) {
this.shutdownHooks = [];

@@ -20,2 +21,4 @@ if (dialectProgram && monitorKeypair) {

}
this.web2SubscriberRepository =
web2SubscriberRepository !== null && web2SubscriberRepository !== void 0 ? web2SubscriberRepository : new web_subscriber_repository_1.NoopWeb2SubscriberRepository();
if (subscriberRepository) {

@@ -32,38 +35,30 @@ this.subscriberRepository = subscriberRepository;

}
createUnicastMonitor(dataSource, datasourceTransformationPipelines, pollInterval = luxon_1.Duration.fromObject({ seconds: 10 })) {
const pushyDataSource = this.decorateWithPushyDataSource(dataSource, pollInterval);
const unicastMonitor = new unicast_monitor_1.UnicastMonitor(pushyDataSource, datasourceTransformationPipelines);
this.shutdownHooks.push(() => unicastMonitor.stop());
return unicastMonitor;
createDefaultMonitor(dataSource, datasourceTransformationPipelines, pollInterval = luxon_1.Duration.fromObject({ seconds: 10 })) {
const pushyDataSource = !('subscribe' in dataSource)
? this.toPushyDataSource(dataSource, pollInterval, this.subscriberRepository, this.web2SubscriberRepository)
: dataSource;
const monitor = new default_monitor_1.DefaultMonitor(pushyDataSource, datasourceTransformationPipelines, this.subscriberRepository, this.web2SubscriberRepository);
this.shutdownHooks.push(() => monitor.stop());
return monitor;
}
decorateWithPushyDataSource(dataSource, pollInterval) {
if ('subscribe' in dataSource) {
return dataSource;
}
return this.toPushyDataSource(dataSource, pollInterval, this.subscriberRepository);
}
createBroadcastMonitor(dataSource, datasourceTransformationPipelines, pollInterval = luxon_1.Duration.fromObject({ seconds: 10 })) {
const pushyDataSource = this.toPushyDataSource(dataSource, pollInterval, this.subscriberRepository);
const broadcastMonitor = new broadcast_monitor_1.BroadcastMonitor(pushyDataSource, datasourceTransformationPipelines, this.subscriberRepository);
this.shutdownHooks.push(() => broadcastMonitor.stop());
return broadcastMonitor;
}
createSubscriberEventMonitor(dataSourceTransformationPipelines) {
const dataSource = new rxjs_1.Observable((subscriber) => this.subscriberRepository.subscribe((resourceId) => subscriber.next({
resourceId,
groupingKey: resourceId.toBase58(),
data: {
resourceId,
state: 'added',
},
}), (resourceId) => subscriber.next({
resourceId,
groupingKey: resourceId.toBase58(),
data: {
resourceId,
state: 'removed',
},
})));
const unicastMonitor = new unicast_monitor_1.UnicastMonitor(dataSource, dataSourceTransformationPipelines);
this.shutdownHooks.push(() => unicastMonitor.stop());
return unicastMonitor;
const monitor = new default_monitor_1.DefaultMonitor(dataSource, dataSourceTransformationPipelines, this.subscriberRepository, this.web2SubscriberRepository);
this.shutdownHooks.push(() => monitor.stop());
return monitor;
}
toPushyDataSource(dataSource, pollInterval, subscriberRepository, pollTimeout = luxon_1.Duration.fromObject({ minutes: 5 })) {
return (0, rxjs_1.timer)(0, pollInterval.toMillis()).pipe((0, rxjs_1.exhaustMap)(() => subscriberRepository.findAll()), (0, rxjs_1.exhaustMap)((resources) => (0, rxjs_1.from)(dataSource(resources))), (0, operators_1.timeout)(pollTimeout.toMillis()), (0, rxjs_1.catchError)((error) => {
toPushyDataSource(dataSource, pollInterval, subscriberRepository, web2SubscriberRepository, pollTimeout = luxon_1.Duration.fromObject({ minutes: 5 })) {
return (0, rxjs_1.timer)(0, pollInterval.toMillis()).pipe((0, rxjs_1.exhaustMap)(() => (0, rxjs_1.from)((0, subsbscriber_repository_utilts_1.findAllDistinct)(subscriberRepository, web2SubscriberRepository))), (0, rxjs_1.exhaustMap)((resources) => (0, rxjs_1.from)(dataSource(resources))), (0, operators_1.timeout)(pollTimeout.toMillis()), (0, rxjs_1.catchError)((error) => {
if (error instanceof rxjs_1.TimeoutError) {

@@ -73,5 +68,5 @@ return (0, rxjs_1.throwError)(new Error(`Poll timeout of ${pollTimeout.toISO()} reached. ` + error));

return (0, rxjs_1.throwError)(error);
}), (0, rxjs_1.concatMap)((it) => it));
}), (0, rxjs_1.mergeMap)((it) => it));
}
}
exports.DefaultMonitorFactory = DefaultMonitorFactory;

@@ -8,3 +8,2 @@ import { ResourceId } from '../data-model';

static decorate(other: SubscriberRepository): InMemorySubscriberRepository;
static decorateBlocking(other: SubscriberRepository): Promise<InMemorySubscriberRepository>;
findAll(): Promise<ResourceId[]>;

@@ -11,0 +10,0 @@ findByResourceId(resourceId: ResourceId): Promise<ResourceId | null>;

@@ -14,7 +14,2 @@ "use strict";

}
static async decorateBlocking(other) {
const repository = new InMemorySubscriberRepository(other);
await repository.initialize();
return repository;
}
async findAll() {

@@ -21,0 +16,0 @@ return Array(...this.subscribers.values());

@@ -1,2 +0,2 @@

import { AddSinksStep, AddTransformationsStep, BuildStep, ChooseDataSourceStep, DefineDataSourceStep, DispatchStrategy, NotifyStep, Transformation } from '../monitor-builder';
import { AddSinksStep, AddTransformationsStep, ChooseDataSourceStep, DefineDataSourceStep, NotifyStep, Transformation } from '../monitor-builder';
import { SubscriberEvent } from '../data-model';

@@ -8,2 +8,4 @@ import { DataSourceTransformationPipeline, PollableDataSource, PushyDataSource } from '../ports';

import { SengridEmailNotificationSink } from '../sengrid-email-notification-sink';
import { TwilioSmsNotificationSink } from '../twilio-sms-notification-sink';
import { TelegramNotificationSink } from '../telegram-notification-sink';
/**

@@ -19,2 +21,4 @@ * A set of factory methods to create monitors

emailNotificationSink?: SengridEmailNotificationSink;
smsNotificationSink?: TwilioSmsNotificationSink;
telegramNotificationSink?: TelegramNotificationSink;
constructor(monitorProps: MonitorProps);

@@ -42,10 +46,8 @@ }

declare class AddTransformationsStepImpl<T extends object> implements AddTransformationsStep<T> {
private readonly monitorBuilderState;
readonly monitorBuilderState: MonitorsBuilderState<T>;
dataSourceTransformationPipelines: DataSourceTransformationPipeline<T, any>[];
dispatchStrategy?: DispatchStrategy;
constructor(monitorBuilderState: MonitorsBuilderState<T>);
notify(): AddSinksStep<T, T>;
dispatch(strategy: DispatchStrategy): BuildStep<T>;
transform<V, R>(transformation: Transformation<T, V, R>): NotifyStep<T, R>;
}
export {};

@@ -10,4 +10,8 @@ "use strict";

const sengrid_email_notification_sink_1 = require("../sengrid-email-notification-sink");
const twilio_sms_notification_sink_1 = require("../twilio-sms-notification-sink");
const telegram_notification_sink_1 = require("../telegram-notification-sink");
const on_chain_subscriber_repository_1 = require("./on-chain-subscriber.repository");
const in_memory_subscriber_repository_1 = require("./in-memory-subscriber.repository");
const rest_web2_subscriber_repository_1 = require("./rest-web2-subscriber.repository");
const web_subscriber_repository_1 = require("../web-subscriber.repository");
/**

@@ -18,3 +22,11 @@ * A set of factory methods to create monitors

constructor(monitorProps) {
var _a, _b, _c;
this.monitorProps = monitorProps;
if (monitorProps.web2SubscriberRepositoryUrl &&
!monitorProps.web2SubscriberRepository) {
const postgresWeb2ResourceRepository = new rest_web2_subscriber_repository_1.RestWeb2SubscriberRepository(monitorProps.web2SubscriberRepositoryUrl, (_a = monitorProps.monitorKeypair) === null || _a === void 0 ? void 0 : _a.publicKey);
monitorProps.web2SubscriberRepository =
new rest_web2_subscriber_repository_1.InMemoryWeb2SubscriberRepository((_b = monitorProps.monitorKeypair) === null || _b === void 0 ? void 0 : _b.publicKey, postgresWeb2ResourceRepository);
}
const web2SubscriberRepository = (_c = monitorProps.web2SubscriberRepository) !== null && _c !== void 0 ? _c : new web_subscriber_repository_1.NoopWeb2SubscriberRepository();
if (monitorProps.dialectProgram && monitorProps.monitorKeypair) {

@@ -26,2 +38,3 @@ if (!monitorProps.subscriberRepository) {

}
// TODO inspect
this.dialectNotificationSink = new dialect_notification_sink_1.DialectNotificationSink(monitorProps.dialectProgram, monitorProps.monitorKeypair, monitorProps.subscriberRepository);

@@ -31,4 +44,13 @@ }

if (sinks === null || sinks === void 0 ? void 0 : sinks.email) {
this.emailNotificationSink = new sengrid_email_notification_sink_1.SengridEmailNotificationSink(sinks.email.apiToken, sinks.email.senderEmail, sinks.email.resourceEmailRepository);
this.emailNotificationSink = new sengrid_email_notification_sink_1.SengridEmailNotificationSink(sinks.email.apiToken, sinks.email.senderEmail, web2SubscriberRepository);
}
if (sinks === null || sinks === void 0 ? void 0 : sinks.sms) {
this.smsNotificationSink = new twilio_sms_notification_sink_1.TwilioSmsNotificationSink({
username: sinks.sms.twilioUsername,
password: sinks.sms.twilioPassword,
}, sinks.sms.senderSmsNumber, web2SubscriberRepository);
}
if (sinks === null || sinks === void 0 ? void 0 : sinks.telegram) {
this.telegramNotificationSink = new telegram_notification_sink_1.TelegramNotificationSink(sinks.telegram.telegramBotToken, web2SubscriberRepository);
}
}

@@ -80,17 +102,7 @@ }

notify() {
const identityTransformation = (dataSource) => dataSource.pipe((0, operators_1.map)(({ data: value, resourceId }) => ({
context: {
origin: value,
resourceId,
trace: [],
},
value,
})));
const identityTransformation = (dataSource) => dataSource;
// > = (dataSource) => dataSource.pipe(tap(console.log(t)));
this.dataSourceTransformationPipelines.push(identityTransformation);
return new AddSinksStepImpl(this, this.dataSourceTransformationPipelines, this.monitorBuilderState.dialectNotificationSink, this.monitorBuilderState.emailNotificationSink);
}
dispatch(strategy) {
this.dispatchStrategy = strategy;
return new BuildStepImpl(this.monitorBuilderState);
}
transform(transformation) {

@@ -100,9 +112,5 @@ const dataSourceTransformationPipelines = [];

const adaptedToDataSourceTypePipelines = keys.flatMap((key) => pipelines.map((pipeline) => {
const adaptedToDataSourceType = (dataSource) => pipeline(dataSource.pipe((0, operators_1.map)(({ data: origin, resourceId }) => ({
context: {
origin,
resourceId,
trace: [],
},
value: origin[key],
const adaptedToDataSourceType = (dataSource) => pipeline(dataSource.pipe((0, operators_1.map)((it) => ({
...it,
value: it.value[key],
}))));

@@ -122,7 +130,7 @@ return adaptedToDataSourceType;

notify() {
return new AddSinksStepImpl(this.addTransformationsStep, this.dataSourceTransformationPipelines, this.monitorBuilderState.dialectNotificationSink, this.monitorBuilderState.emailNotificationSink);
return new AddSinksStepImpl(this.addTransformationsStep, this.dataSourceTransformationPipelines, this.monitorBuilderState.dialectNotificationSink, this.monitorBuilderState.emailNotificationSink, this.monitorBuilderState.smsNotificationSink, this.monitorBuilderState.telegramNotificationSink);
}
}
class AddSinksStepImpl {
constructor(addTransformationsStep, dataSourceTransformationPipelines, dialectNotificationSink, emailNotificationSink) {
constructor(addTransformationsStep, dataSourceTransformationPipelines, dialectNotificationSink, emailNotificationSink, smsNotificationSink, telegramNotificationSink) {
this.addTransformationsStep = addTransformationsStep;

@@ -132,21 +140,20 @@ this.dataSourceTransformationPipelines = dataSourceTransformationPipelines;

this.emailNotificationSink = emailNotificationSink;
this.smsNotificationSink = smsNotificationSink;
this.telegramNotificationSink = telegramNotificationSink;
this.sinkWriters = [];
}
and() {
const transformAndLoadPipelines = this.dataSourceTransformationPipelines.map((dataSourceTransformationPipeline) => {
const transformAndLoadPipeline = (dataSource, targets) => dataSourceTransformationPipeline(dataSource, targets).pipe((0, rxjs_1.exhaustMap)((event) => (0, rxjs_1.from)(Promise.all(this.sinkWriters.map((it) => it(event, targets))))));
return transformAndLoadPipeline;
});
this.addTransformationsStep.dataSourceTransformationPipelines.push(...transformAndLoadPipelines);
also() {
this.populateDataSourceTransformationPipelines();
return this.addTransformationsStep;
}
dialectThread(adapter, recipientsSelector, recipientPredicate) {
dialectThread(adapter, dispatchStrategy) {
if (!this.dialectNotificationSink) {
throw new Error('Dialect notification sink must be initialized before using');
}
const sinkWriter = (data, resources) => {
const toBeNotified = recipientsSelector
? recipientsSelector(data)
: resources;
return this.dialectNotificationSink.push(adapter(data), toBeNotified.filter((it) => recipientPredicate ? recipientPredicate(data, it) : true));
return this.custom(adapter, this.dialectNotificationSink, dispatchStrategy);
}
custom(adapter, sink, dispatchStrategy) {
const sinkWriter = (data) => {
const toBeNotified = this.selectResources(dispatchStrategy, data.context.subscribers, data);
return sink.push(adapter(data), toBeNotified);
};

@@ -156,15 +163,44 @@ this.sinkWriters.push(sinkWriter);

}
custom(adapter, sink, recipientPredicate) {
const sinkWriter = (data, resources) => sink.push(adapter(data), resources.filter((it) => recipientPredicate ? recipientPredicate(data, it) : true));
this.sinkWriters.push(sinkWriter);
return this;
}
email(adapter, recipientPredicate) {
email(adapter, dispatchStrategy) {
if (!this.emailNotificationSink) {
throw new Error('Email notification sink must be initialized before using');
}
const sinkWriter = (data, resources) => this.emailNotificationSink.push(adapter(data), resources.filter((it) => recipientPredicate ? recipientPredicate(data, it) : true));
this.sinkWriters.push(sinkWriter);
return this;
return this.custom(adapter, this.emailNotificationSink, dispatchStrategy);
}
sms(adapter, dispatchStrategy) {
if (!this.smsNotificationSink) {
throw new Error('SMS notification sink must be initialized before using');
}
return this.custom(adapter, this.smsNotificationSink, dispatchStrategy);
}
telegram(adapter, dispatchStrategy) {
if (!this.telegramNotificationSink) {
throw new Error('Telegram notification sink must be initialized before using');
}
return this.custom(adapter, this.telegramNotificationSink, dispatchStrategy);
}
and() {
this.populateDataSourceTransformationPipelines();
return new BuildStepImpl(this.addTransformationsStep.monitorBuilderState);
}
populateDataSourceTransformationPipelines() {
const transformAndLoadPipelines = this.dataSourceTransformationPipelines.map((dataSourceTransformationPipeline) => {
const transformAndLoadPipeline = (dataSource) => dataSourceTransformationPipeline(dataSource).pipe((0, rxjs_1.exhaustMap)((event) => (0, rxjs_1.from)(Promise.all(this.sinkWriters.map((it) => it(event))))));
return transformAndLoadPipeline;
});
this.addTransformationsStep.dataSourceTransformationPipelines.push(...transformAndLoadPipelines);
}
selectResources(dispatchStrategy, resources, { context }) {
switch (dispatchStrategy.dispatch) {
case 'broadcast': {
return resources;
}
case 'unicast': {
return [dispatchStrategy.to(context)];
}
case 'multicast': {
return dispatchStrategy.to(context);
}
}
}
}

@@ -196,4 +232,4 @@ class BuildStepImpl {

buildSubscriberEventMonitor(addTransformationsStep, monitorProps) {
const { dataSourceTransformationPipelines, dispatchStrategy } = addTransformationsStep;
if (!dataSourceTransformationPipelines || !dispatchStrategy) {
const { dataSourceTransformationPipelines } = addTransformationsStep;
if (!dataSourceTransformationPipelines) {
throw new Error('Expected [dataSourceTransformationPipelines, dispatchStrategy] to be defined');

@@ -216,29 +252,18 @@ }

const { pollableDataSource, pollInterval } = defineDataSourceStep;
const { dataSourceTransformationPipelines, dispatchStrategy } = addTransformationsStep;
const { dataSourceTransformationPipelines } = addTransformationsStep;
if (!pollableDataSource ||
!pollInterval ||
!dataSourceTransformationPipelines ||
!dispatchStrategy) {
throw new Error('Expected [pollableDataSource, pollInterval, dataSourceTransformationPipelines, dispatchStrategy] to be defined');
!dataSourceTransformationPipelines) {
throw new Error('Expected [pollableDataSource, pollInterval, dataSourceTransformationPipelines] to be defined');
}
switch (addTransformationsStep.dispatchStrategy) {
case 'broadcast':
return monitor_api_1.Monitors.factory(monitorProps).createBroadcastMonitor(pollableDataSource, dataSourceTransformationPipelines, pollInterval);
case 'unicast':
return monitor_api_1.Monitors.factory(monitorProps).createUnicastMonitor(pollableDataSource, dataSourceTransformationPipelines, pollInterval);
default:
throw new Error('Unknown dispatchStrategy: ' +
addTransformationsStep.dispatchStrategy);
}
return monitor_api_1.Monitors.factory(monitorProps).createDefaultMonitor(pollableDataSource, dataSourceTransformationPipelines, pollInterval);
}
createForPushy(defineDataSourceStep, addTransformationsStep, monitorProps) {
const { pushyDataSource } = defineDataSourceStep;
const { dataSourceTransformationPipelines, dispatchStrategy } = addTransformationsStep;
if (!pushyDataSource ||
!dataSourceTransformationPipelines ||
!dispatchStrategy) {
throw new Error('Expected [pushyDataSource, dataSourceTransformationPipelines, dispatchStrategy] to be defined');
const { dataSourceTransformationPipelines } = addTransformationsStep;
if (!pushyDataSource || !dataSourceTransformationPipelines) {
throw new Error('Expected [pushyDataSource, dataSourceTransformationPipelines] to be defined');
}
return monitor_api_1.Monitors.factory(monitorProps).createUnicastMonitor(pushyDataSource, dataSourceTransformationPipelines, luxon_1.Duration.fromObject({ seconds: 1 }));
return monitor_api_1.Monitors.factory(monitorProps).createDefaultMonitor(pushyDataSource, dataSourceTransformationPipelines, luxon_1.Duration.fromObject({ seconds: 1 }));
}
}

@@ -6,3 +6,3 @@ import { ChooseDataSourceStep } from './monitor-builder';

import { SubscriberRepository } from './ports';
import { ResourceEmailRepository } from './sengrid-email-notification-sink';
import { Web2SubscriberRepository } from './web-subscriber.repository';
/**

@@ -27,2 +27,10 @@ * Please specify either

/**
* Allows to set custom web2 subscriber repository
*/
web2SubscriberRepositoryUrl?: string;
/**
* Allows to set custom web2 subscriber repository
*/
web2SubscriberRepository?: Web2SubscriberRepository;
/**
* Allows to set sinks configuration to send notifications

@@ -34,2 +42,4 @@ */

email?: EmailSinkConfiguration;
sms?: SmsSinkConfiguration;
telegram?: TelegramSinkConfiguration;
}

@@ -39,4 +49,11 @@ export interface EmailSinkConfiguration {

senderEmail: string;
resourceEmailRepository: ResourceEmailRepository;
}
export interface SmsSinkConfiguration {
twilioUsername: string;
twilioPassword: string;
senderSmsNumber: string;
}
export interface TelegramSinkConfiguration {
telegramBotToken: string;
}
/**

@@ -43,0 +60,0 @@ * A monitor is an entity that is responsible for execution of unbounded streaming ETL (Extract, Transform, Load)

import { Duration } from 'luxon';
import { NotificationSink, PollableDataSource, PushyDataSource, TransformationPipeline } from './ports';
import { Monitor } from './monitor-api';
import { Data, ResourceId, SubscriberEvent } from './data-model';
import { Context, Data, ResourceId, SubscriberEvent } from './data-model';
import { DialectNotification } from './dialect-notification-sink';
import { EmailNotification } from './sengrid-email-notification-sink';
import { SmsNotification } from './twilio-sms-notification-sink';
import { TelegramNotification } from './telegram-notification-sink';
export interface ChooseDataSourceStep {

@@ -54,6 +56,16 @@ /**

*/
export declare type DispatchStrategy = 'unicast' | 'broadcast';
export declare type DispatchStrategy<T extends object> = BroadcastDispatchStrategy | UnicastDispatchStrategy<T> | MulticastDispatchStrategy<T>;
export declare type BroadcastDispatchStrategy = {
dispatch: 'broadcast';
};
export declare type UnicastDispatchStrategy<T extends object> = {
dispatch: 'unicast';
to: (ctx: Context<T>) => ResourceId;
};
export declare type MulticastDispatchStrategy<T extends object> = {
dispatch: 'multicast';
to: (ctx: Context<T>) => ResourceId[];
};
export interface AddTransformationsStep<T extends object> {
transform<V, R>(transformation: Transformation<T, V, R>): NotifyStep<T, R>;
dispatch(strategy: DispatchStrategy): BuildStep<T>;
notify(): AddSinksStep<T, T>;

@@ -68,6 +80,9 @@ }

export interface AddSinksStep<T extends object, R> {
dialectThread(adapter: (data: Data<R, T>) => DialectNotification, recipientsSelector?: (data: Data<R, T>) => ResourceId[], recipientPredicate?: (data: Data<R, T>, recipient: ResourceId) => boolean): AddSinksStep<T, R>;
email(adapter: (data: Data<R, T>) => EmailNotification, recipientPredicate?: (data: Data<R, T>, recipient: ResourceId) => boolean): AddSinksStep<T, R>;
custom<N>(adapter: (data: Data<R, T>) => N, sink: NotificationSink<N>, recipientPredicate?: (data: Data<R, T>, recipient: ResourceId) => boolean): AddSinksStep<T, R>;
and(): AddTransformationsStep<T>;
dialectThread(adapter: (data: Data<R, T>) => DialectNotification, dispatchStrategy: DispatchStrategy<T>): AddSinksStep<T, R>;
email(adapter: (data: Data<R, T>) => EmailNotification, dispatchStrategy: DispatchStrategy<T>): AddSinksStep<T, R>;
sms(adapter: (data: Data<R, T>) => SmsNotification, dispatchStrategy: DispatchStrategy<T>): AddSinksStep<T, R>;
telegram(adapter: (data: Data<R, T>) => TelegramNotification, dispatchStrategy: DispatchStrategy<T>): AddSinksStep<T, R>;
custom<N>(adapter: (data: Data<R, T>) => N, sink: NotificationSink<N>, dispatchStrategy: DispatchStrategy<T>): AddSinksStep<T, R>;
also(): AddTransformationsStep<T>;
and(): BuildStep<T>;
}

@@ -74,0 +89,0 @@ export interface BuildStep<T extends object> {

@@ -6,5 +6,4 @@ import { Duration } from 'luxon';

export interface MonitorFactory {
createUnicastMonitor<T extends object>(dataSource: DataSource<T>, transformationPipelines: DataSourceTransformationPipeline<T, any>[], pollInterval: Duration): Monitor<T>;
createBroadcastMonitor<T extends object>(dataSource: DataSource<T>, transformationPipelines: DataSourceTransformationPipeline<T, any>[], pollInterval: Duration): Monitor<T>;
createDefaultMonitor<T extends object>(dataSource: DataSource<T>, transformationPipelines: DataSourceTransformationPipeline<T, any>[], pollInterval: Duration): Monitor<T>;
createSubscriberEventMonitor(eventDetectionPipelines: DataSourceTransformationPipeline<SubscriberEvent, any>[]): Monitor<SubscriberEvent>;
}
import { Observable } from 'rxjs';
import { Data, ResourceId, SourceData, Notification } from './data-model';
import { Data, Notification, ResourceId, SourceData } from './data-model';
/**

@@ -17,2 +17,3 @@ * An abstraction that represents a source of data, bound to specific type

export declare type PushyDataSource<T extends object> = Observable<SourceData<T>>;
export declare type ContextEnrichedPushyDataSource<T extends object> = Observable<Data<T, T>>;
/**

@@ -22,3 +23,3 @@ * A set of transformations that are executed on-top of unbound pushy data source

*/
export declare type DataSourceTransformationPipeline<T extends Object, R> = (dataSource: PushyDataSource<T>, targets: ResourceId[]) => Observable<R>;
export declare type DataSourceTransformationPipeline<T extends object, R> = (dataSource: ContextEnrichedPushyDataSource<T>) => Observable<R>;
/**

@@ -25,0 +26,0 @@ * A set of transformations that are executed on-top of a specific key from unbound pushy data source

import { Notification, ResourceId } from './data-model';
import { NotificationSink } from './ports';
import { Web2SubscriberRepository } from './web-subscriber.repository';
/**

@@ -10,16 +11,8 @@ * Email notification

}
export declare type Email = string;
export declare type ResourceEmail = {
resourceId: ResourceId;
email: Email;
};
export interface ResourceEmailRepository {
findBy(resourceIds: ResourceId[]): Promise<ResourceEmail[]>;
}
export declare class SengridEmailNotificationSink implements NotificationSink<EmailNotification> {
private readonly sengridApiKey;
private readonly senderEmail;
private readonly resourceIdToReceiverEmailMapper;
constructor(sengridApiKey: string, senderEmail: string, resourceIdToReceiverEmailMapper: ResourceEmailRepository);
private readonly web2SubscriberRepository;
constructor(sengridApiKey: string, senderEmail: string, web2SubscriberRepository: Web2SubscriberRepository);
push(notification: EmailNotification, recipients: ResourceId[]): Promise<void>;
}

@@ -9,11 +9,15 @@ "use strict";

class SengridEmailNotificationSink {
constructor(sengridApiKey, senderEmail, resourceIdToReceiverEmailMapper) {
constructor(sengridApiKey, senderEmail, web2SubscriberRepository) {
this.sengridApiKey = sengridApiKey;
this.senderEmail = senderEmail;
this.resourceIdToReceiverEmailMapper = resourceIdToReceiverEmailMapper;
this.web2SubscriberRepository = web2SubscriberRepository;
mail_1.default.setApiKey(sengridApiKey);
}
async push(notification, recipients) {
const recipientEmails = await this.resourceIdToReceiverEmailMapper.findBy(recipients);
const emails = recipientEmails.map(({ email }) => ({
const recipientEmails = await this.web2SubscriberRepository.findBy(recipients);
console.log('sendgrid-notif-sink, recipients:\n');
console.log(recipientEmails);
const emails = recipientEmails
.filter(({ email }) => email)
.map(({ email }) => ({
...notification,

@@ -23,5 +27,14 @@ from: this.senderEmail,

}));
return mail_1.default.send(emails).then(() => { });
const results = await Promise.allSettled(await mail_1.default.send(emails));
const failedSends = results
.filter((it) => it.status === 'rejected')
.map((it) => it);
if (failedSends.length > 0) {
console.log(`Failed to send dialect email notification to ${failedSends.length} recipient(s), reasons:
${failedSends.map((it) => it.reason)}
`);
}
return;
}
}
exports.SengridEmailNotificationSink = SengridEmailNotificationSink;

@@ -70,3 +70,3 @@ "use strict";

Operators.Window.fixedSizeSliding(2),
(0, rxjs_1.filter)((it) => it.length == 2 &&
(0, rxjs_1.filter)((it) => it.length === 2 &&
it[0].value <= threshold &&

@@ -80,3 +80,3 @@ threshold < it[1].value),

Operators.Window.fixedSizeSliding(2),
(0, rxjs_1.filter)((data) => data.length == 2 &&
(0, rxjs_1.filter)((data) => data.length === 2 &&
data[0].value >= threshold &&

@@ -90,3 +90,3 @@ threshold > data[1].value),

Operators.Window.fixedSizeSliding(2),
(0, rxjs_1.filter)((data) => data.length == 2 && data[1].value - data[0].value >= threshold),
(0, rxjs_1.filter)((data) => data.length === 2 && data[1].value - data[0].value >= threshold),
(0, operators_1.map)(([fst, snd]) => ({

@@ -111,3 +111,3 @@ ...snd,

Operators.Window.fixedSizeSliding(2),
(0, rxjs_1.filter)((data) => data.length == 2 && data[0].value - data[1].value >= threshold),
(0, rxjs_1.filter)((data) => data.length === 2 && data[0].value - data[1].value >= threshold),
(0, operators_1.map)(([_, snd]) => snd),

@@ -114,0 +114,0 @@ ];

@@ -25,3 +25,3 @@ "use strict";

.pipe(transformation_pipeline_operators_1.Operators.Window.fixedSizeSliding(2))
.pipe(transformation_pipeline_operators_1.Operators.Transform.filter((it) => it.length == 2))
.pipe(transformation_pipeline_operators_1.Operators.Transform.filter((it) => it.length === 2))
.pipe(transformation_pipeline_operators_1.Operators.Transform.map(([d1, d2]) => {

@@ -43,3 +43,3 @@ const added = d2.value.filter((e2) => !d1.value.find((e1) => compareBy(e1, e2)));

.pipe(transformation_pipeline_operators_1.Operators.Window.fixedSizeSliding(2))
.pipe(transformation_pipeline_operators_1.Operators.Transform.filter((it) => it.length == 2))
.pipe(transformation_pipeline_operators_1.Operators.Transform.filter((it) => it.length === 2))
.pipe(transformation_pipeline_operators_1.Operators.Transform.map(([d1, d2]) => {

@@ -61,3 +61,3 @@ const removed = d1.value.filter((e1) => !d2.value.find((e2) => compareBy(e1, e2)));

.pipe(transformation_pipeline_operators_1.Operators.Window.fixedSizeSliding(2))
.pipe(transformation_pipeline_operators_1.Operators.Transform.filter((it) => it.length == 2))
.pipe(transformation_pipeline_operators_1.Operators.Transform.filter((it) => it.length === 2))
.pipe(transformation_pipeline_operators_1.Operators.Transform.map(([d1, d2]) => {

@@ -64,0 +64,0 @@ const added = d2.value.filter((e2) => !d1.value.find((e1) => compareBy(e1, e2)));

{
"name": "@dialectlabs/monitor",
"version": "2.0.3-beta",
"version": "2.0.4-beta",
"repository": "git@github.com:dialectlabs/monitor.git",

@@ -18,5 +18,7 @@ "author": "dialectlabs",

"@solana/web3.js": "^1.27.0",
"axios": "^0.27.2",
"luxon": "^2.3.0",
"rxjs": "^7.5.2",
"ts-retry": "^2.4.1"
"telegraf": "^4.7.0",
"twilio": "^3.76.1"
},

@@ -23,0 +25,0 @@ "devDependencies": {

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc