denotify-client 1.1.9 → 1.1.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env +9 -0
- package/.eslintignore +8 -0
- package/.eslintrc +18 -0
- package/.lintstagedrc +4 -0
- package/.prettierignore +8 -0
- package/.prettierrc +16 -0
- package/.stylelintignore +8 -0
- package/.stylelintrc +18 -0
- package/CHANGELOG.md +0 -0
- package/dts-bundle-generator.config.ts +18 -0
- package/favicon.svg +15 -0
- package/index.html +13 -0
- package/jest.config.ts +9 -0
- package/package.json +51 -62
- package/tsconfig.json +22 -0
- package/vite.config.ts +35 -0
- package/dist/alertbuilder.js +0 -67
- package/dist/denotifyclient.js +0 -125
- package/dist/examples/balance-monitor.js +0 -54
- package/dist/examples/delete-alert.js +0 -11
- package/dist/functionbuilder.js +0 -41
- package/dist/index.js +0 -569
- package/dist/index.min.js +0 -1
- package/dist/index.mjs +0 -539
- package/dist/notifications/index.js +0 -3
- package/dist/notifications/notification.js +0 -8
- package/dist/notifications/notify_discord_webhook.js +0 -21
- package/dist/triggers/handler_function_call.js +0 -26
- package/dist/triggers/handler_function_call_v2.js +0 -41
- package/dist/triggers/handler_onchain_event.js +0 -28
- package/dist/triggers/index.js +0 -5
- package/dist/triggers/trigger.js +0 -14
- package/dist/types/types.js +0 -1
- package/dist/util/filter.js +0 -160
- /package/{license → LICENSE.md} +0 -0
package/dist/index.js
DELETED
@@ -1,569 +0,0 @@
|
|
1
|
-
'use strict';
|
2
|
-
|
3
|
-
var supabaseJs = require('@supabase/supabase-js');
|
4
|
-
var yup = require('yup');
|
5
|
-
var ethers = require('ethers');
|
6
|
-
var fetch = require('node-fetch');
|
7
|
-
|
8
|
-
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
|
9
|
-
|
10
|
-
function _interopNamespace(e) {
|
11
|
-
if (e && e.__esModule) return e;
|
12
|
-
var n = Object.create(null);
|
13
|
-
if (e) {
|
14
|
-
Object.keys(e).forEach(function (k) {
|
15
|
-
if (k !== 'default') {
|
16
|
-
var d = Object.getOwnPropertyDescriptor(e, k);
|
17
|
-
Object.defineProperty(n, k, d.get ? d : {
|
18
|
-
enumerable: true,
|
19
|
-
get: function () { return e[k]; }
|
20
|
-
});
|
21
|
-
}
|
22
|
-
});
|
23
|
-
}
|
24
|
-
n["default"] = e;
|
25
|
-
return Object.freeze(n);
|
26
|
-
}
|
27
|
-
|
28
|
-
var yup__namespace = /*#__PURE__*/_interopNamespace(yup);
|
29
|
-
var fetch__default = /*#__PURE__*/_interopDefaultLegacy(fetch);
|
30
|
-
|
31
|
-
const NOTIFY_DISCORD_WEBHOOK_RAW_ID = 'notify_discord_webhook';
|
32
|
-
class NotifyDiscordWebhook {
|
33
|
-
static SimpleToRaw(config) {
|
34
|
-
return {
|
35
|
-
name: '',
|
36
|
-
notify_type: NOTIFY_DISCORD_WEBHOOK_RAW_ID,
|
37
|
-
notify: config
|
38
|
-
};
|
39
|
-
}
|
40
|
-
static validateCreate(options) {
|
41
|
-
const urlRegex = /^(https?|ftp):\/\/(-\.)?([^\s/?\.#]+\.?)+([^\s\.?#]+)?(\?\S*)?$/;
|
42
|
-
const schema = yup__namespace.object({
|
43
|
-
url: yup__namespace.string().matches(urlRegex, 'url is not a valid url').required(),
|
44
|
-
username: yup__namespace.string(),
|
45
|
-
avatar_url: yup__namespace.string().matches(urlRegex, 'url is not a valid url'),
|
46
|
-
message: yup__namespace.string().required()
|
47
|
-
});
|
48
|
-
return schema.validate(options);
|
49
|
-
}
|
50
|
-
}
|
51
|
-
|
52
|
-
class Notification {
|
53
|
-
static SimpleToRaw(id, config) {
|
54
|
-
switch (id) {
|
55
|
-
case 'Discord': return NotifyDiscordWebhook.SimpleToRaw(config);
|
56
|
-
}
|
57
|
-
}
|
58
|
-
}
|
59
|
-
|
60
|
-
const HANDLER_FUNCTION_CALL_V1_RAW_ID = 'handler_function_call';
|
61
|
-
class HandlerFunctionCall {
|
62
|
-
static SimpleToRaw(name, network, config) {
|
63
|
-
return {
|
64
|
-
alertType: 'event',
|
65
|
-
network,
|
66
|
-
nickname: name,
|
67
|
-
type: HANDLER_FUNCTION_CALL_V1_RAW_ID,
|
68
|
-
handler: config
|
69
|
-
};
|
70
|
-
}
|
71
|
-
static validateCreate(options) {
|
72
|
-
const requiredWhenConditional = ([condition], schema) => condition === 'true' ? schema.notRequired() : schema.required();
|
73
|
-
const schema = yup__namespace.object({
|
74
|
-
address: yup__namespace.string().required(),
|
75
|
-
abi: yup__namespace.array().required(),
|
76
|
-
nBlocks: yup__namespace.number().min(10),
|
77
|
-
condition: yup__namespace.string().oneOf(['>', '>=', '<', '<=', '=', 'true']),
|
78
|
-
constant: yup__namespace.number().min(0).when('condition', requiredWhenConditional),
|
79
|
-
responseArgIndex: yup__namespace.number().min(0).when('condition', requiredWhenConditional),
|
80
|
-
responseArgDecimals: yup__namespace.number().min(0).when('condition', requiredWhenConditional),
|
81
|
-
});
|
82
|
-
return schema.validate(options);
|
83
|
-
}
|
84
|
-
}
|
85
|
-
|
86
|
-
const HANDLER_ONCHAIN_EVENT_V1_RAW_ID = 'handler_onchain_event';
|
87
|
-
class HandlerOnchainEvent {
|
88
|
-
static SimpleToRaw(name, network, config) {
|
89
|
-
return {
|
90
|
-
alertType: 'event',
|
91
|
-
network,
|
92
|
-
nickname: name,
|
93
|
-
type: HANDLER_ONCHAIN_EVENT_V1_RAW_ID,
|
94
|
-
handler: config
|
95
|
-
};
|
96
|
-
}
|
97
|
-
static validateCreate(options) {
|
98
|
-
const requiredWhenConditional = ([condition], schema) => condition === 'true' ? schema.notRequired() : schema.required();
|
99
|
-
const onchainEventSchema = yup__namespace.object({
|
100
|
-
address: yup__namespace.string().required(),
|
101
|
-
event: yup__namespace.string().required(),
|
102
|
-
abi: yup__namespace.array().required(),
|
103
|
-
condition: yup__namespace.string().oneOf(['>', '>=', '<', '<=', '=', 'true']),
|
104
|
-
constant: yup__namespace.number().min(0).when('condition', requiredWhenConditional),
|
105
|
-
paramsIndex: yup__namespace.number().min(0).when('condition', requiredWhenConditional),
|
106
|
-
paramsDecimals: yup__namespace.number().min(0).when('condition', requiredWhenConditional),
|
107
|
-
});
|
108
|
-
return onchainEventSchema.validate(options);
|
109
|
-
}
|
110
|
-
static validateUpdate(options) {
|
111
|
-
}
|
112
|
-
}
|
113
|
-
|
114
|
-
class FunctionBuilder {
|
115
|
-
api;
|
116
|
-
data = [];
|
117
|
-
constructor(api) {
|
118
|
-
this.api = api;
|
119
|
-
}
|
120
|
-
static create(api) {
|
121
|
-
return new FunctionBuilder(api);
|
122
|
-
}
|
123
|
-
getAbiHash(abi) {
|
124
|
-
if (this.api === undefined)
|
125
|
-
throw new Error('FunctionBuilder must be initialised with api');
|
126
|
-
return this.api.getAbiHash(abi);
|
127
|
-
}
|
128
|
-
async addFunction(address, func, args, abi) {
|
129
|
-
const contract = new ethers.ethers.Contract(address, abi);
|
130
|
-
contract.interface.encodeFunctionData(func, args);
|
131
|
-
this.data.push({
|
132
|
-
address,
|
133
|
-
bytecode: contract.interface.encodeFunctionData(func, args),
|
134
|
-
abiHash: await this.getAbiHash(abi),
|
135
|
-
function: func,
|
136
|
-
});
|
137
|
-
return this;
|
138
|
-
}
|
139
|
-
get() {
|
140
|
-
return this.data;
|
141
|
-
}
|
142
|
-
static schema() {
|
143
|
-
return yup__namespace.array().of(yup__namespace.object({
|
144
|
-
address: yup__namespace.string().required(),
|
145
|
-
bytecode: yup__namespace.string().matches(/^(0x)?([0-9a-fA-F]{2})*$/).required(),
|
146
|
-
abiHash: yup__namespace.string().matches(/^[A-Fa-f0-9]{64}/).required(),
|
147
|
-
function: yup__namespace.string().required()
|
148
|
-
}));
|
149
|
-
}
|
150
|
-
}
|
151
|
-
|
152
|
-
class Filter {
|
153
|
-
static version() {
|
154
|
-
return 'v1';
|
155
|
-
}
|
156
|
-
static execute(record, groupsIn, version = 'v1') {
|
157
|
-
// Deep copy to so we can edit the object during recursion
|
158
|
-
const groups = JSON.parse(JSON.stringify(groupsIn));
|
159
|
-
let lhs = true;
|
160
|
-
for (const group of groups) {
|
161
|
-
const rhs = Filter.process(record, group.conditions);
|
162
|
-
lhs = Filter.execLogic(lhs, group.logic, rhs);
|
163
|
-
}
|
164
|
-
return lhs;
|
165
|
-
}
|
166
|
-
static process(record, conditions, lhs) {
|
167
|
-
const condition = conditions.shift();
|
168
|
-
if (!condition) {
|
169
|
-
if (lhs === undefined)
|
170
|
-
return true;
|
171
|
-
return lhs;
|
172
|
-
}
|
173
|
-
// lhs <logic> rhs
|
174
|
-
const rhs = Filter.execCondition(record, condition);
|
175
|
-
if (lhs === undefined || condition.logic === 'WHERE')
|
176
|
-
return Filter.process(record, conditions, rhs);
|
177
|
-
if (condition.logic === undefined)
|
178
|
-
throw new Error('Invalid filter. Condition must have logic value');
|
179
|
-
const next = Filter.execLogic(lhs, condition.logic, rhs);
|
180
|
-
return Filter.process(record, conditions, next);
|
181
|
-
}
|
182
|
-
static execLogic(lhs, logic, rhs) {
|
183
|
-
switch (logic) {
|
184
|
-
case 'AND': return lhs && rhs;
|
185
|
-
case 'OR': return lhs || rhs;
|
186
|
-
case 'XOR': return (lhs || rhs) && !(lhs && rhs);
|
187
|
-
case 'NAND': return !(lhs && rhs);
|
188
|
-
case 'NOR': return !(lhs && rhs);
|
189
|
-
case 'WHERE': return rhs;
|
190
|
-
default: throw new Error(`Invalid Filter. Bad logic value: ${logic}`);
|
191
|
-
}
|
192
|
-
}
|
193
|
-
static execCondition(record, condition) {
|
194
|
-
const data = record[condition.key];
|
195
|
-
if (condition.type === 'Number') {
|
196
|
-
if (typeof condition.constant !== 'number' || typeof data !== 'number')
|
197
|
-
throw new Error(`Invalid filter. Type miss-match. Type: ${condition.type}, Variable: ${condition.constant}, Data: ${data}`);
|
198
|
-
const n = data;
|
199
|
-
const constant = condition.constant;
|
200
|
-
switch (condition.operation) {
|
201
|
-
case 'eq': return n === constant;
|
202
|
-
case '!eq': return n !== constant;
|
203
|
-
case 'gt': return n > constant;
|
204
|
-
case 'gte': return n >= constant;
|
205
|
-
case 'lt': return n < constant;
|
206
|
-
case 'lte': return n <= constant;
|
207
|
-
default: throw new Error('Invalid Filter. Operation does not match type');
|
208
|
-
}
|
209
|
-
}
|
210
|
-
if (condition.type === 'String') {
|
211
|
-
if (typeof condition.constant !== 'string' || typeof data !== 'string')
|
212
|
-
throw new Error(`Invalid filter. Type miss-match. Type: ${condition.type}, Variable: ${condition.constant}, Data: ${data}`);
|
213
|
-
const str = data;
|
214
|
-
const constant = condition.constant;
|
215
|
-
switch (condition.operation) {
|
216
|
-
case 'contains': return str.includes(constant);
|
217
|
-
case '!contains': return !str.includes(constant);
|
218
|
-
case 'is': return str === constant;
|
219
|
-
case '!is': return str !== constant;
|
220
|
-
case 'isEmpty': return str === '';
|
221
|
-
case '!isEmpty': return str !== '';
|
222
|
-
default: throw new Error('Invalid Filter. Operation does not match type');
|
223
|
-
}
|
224
|
-
}
|
225
|
-
if (condition.type === 'Address') {
|
226
|
-
if (typeof condition.constant !== 'string' || typeof data !== 'string')
|
227
|
-
throw new Error(`Invalid filter. Type miss-match. Type: ${condition.type}, Variable: ${condition.constant}, Data: ${data}`);
|
228
|
-
const str = data;
|
229
|
-
const constant = condition.constant;
|
230
|
-
switch (condition.operation) {
|
231
|
-
case 'contains': return str.toLowerCase().includes(constant.toLowerCase());
|
232
|
-
case '!contains': return !str.toLowerCase().includes(constant.toLowerCase());
|
233
|
-
case 'is': return str.toLowerCase() === constant.toLowerCase();
|
234
|
-
case '!is': return str.toLowerCase() !== constant.toLowerCase();
|
235
|
-
case 'isEmpty': return str === '';
|
236
|
-
case '!isEmpty': return str !== '';
|
237
|
-
default: throw new Error('Invalid Filter. Operation does not match type');
|
238
|
-
}
|
239
|
-
}
|
240
|
-
throw new Error('Invalid Filter. Unknown Type');
|
241
|
-
}
|
242
|
-
}
|
243
|
-
class FilterBuilder {
|
244
|
-
groups = [];
|
245
|
-
constructor() {
|
246
|
-
this.newGroup('WHERE');
|
247
|
-
}
|
248
|
-
static version() {
|
249
|
-
return Filter.version();
|
250
|
-
}
|
251
|
-
static new() {
|
252
|
-
return new FilterBuilder();
|
253
|
-
}
|
254
|
-
newGroup(logic) {
|
255
|
-
if (this.groups.length !== 0 && logic === 'WHERE')
|
256
|
-
throw new Error('Only the first groups can start with "WHERE"');
|
257
|
-
this.groups.push({ logic: 'WHERE', conditions: [] });
|
258
|
-
return this;
|
259
|
-
}
|
260
|
-
addCondition(logic, key, type, operation, constant) {
|
261
|
-
const group = this.groups[this.groups.length - 1];
|
262
|
-
if (group.conditions.length === 0 && logic !== 'WHERE')
|
263
|
-
throw new Error('Logic for the first condition of a group must be "WHERE"');
|
264
|
-
if (group.conditions.length > 0 && logic === 'WHERE')
|
265
|
-
throw new Error('Only the first condition of a group can be "WHERE"');
|
266
|
-
group.conditions.push({
|
267
|
-
logic,
|
268
|
-
key,
|
269
|
-
type,
|
270
|
-
operation,
|
271
|
-
constant
|
272
|
-
});
|
273
|
-
return this;
|
274
|
-
}
|
275
|
-
finalise() {
|
276
|
-
for (const group of this.groups) {
|
277
|
-
if (group.conditions.length === 0)
|
278
|
-
throw new Error('Bad Filter. All Groups must have atleast one condition');
|
279
|
-
}
|
280
|
-
return this.groups;
|
281
|
-
}
|
282
|
-
static schema() {
|
283
|
-
const logic = yup__namespace.string().oneOf(['AND', 'OR', 'XOR', 'NAND', 'NOR', 'WHERE']).required();
|
284
|
-
const condition = yup__namespace.object({
|
285
|
-
logic,
|
286
|
-
key: yup__namespace.string().required(),
|
287
|
-
type: yup__namespace.string().oneOf(['String', 'Address', 'Number']).required(),
|
288
|
-
operation: yup__namespace.string().when('type', ([type], schema) => {
|
289
|
-
switch (type) {
|
290
|
-
case 'String': return schema.oneOf(['contains', '!contains', 'is', '!is', 'isEmpty', '!isEmpty']);
|
291
|
-
case 'Address': return schema.oneOf(['is', '!is', 'isEmpty', '!isEmpty']);
|
292
|
-
case 'Number': return schema.oneOf(['String', 'Address', 'Number']);
|
293
|
-
default: throw new Error('Invalid Filter Data Type');
|
294
|
-
}
|
295
|
-
}),
|
296
|
-
constant: yup__namespace.mixed().when('type', ([type]) => {
|
297
|
-
switch (type) {
|
298
|
-
case 'String': return yup__namespace.string();
|
299
|
-
case 'Address': return yup__namespace.string();
|
300
|
-
case 'Number': return yup__namespace.number();
|
301
|
-
default: throw new Error('Invalid Filter Data Type');
|
302
|
-
}
|
303
|
-
}),
|
304
|
-
});
|
305
|
-
return yup__namespace.array().of(yup__namespace.object({
|
306
|
-
logic,
|
307
|
-
conditions: yup__namespace.array().of(condition)
|
308
|
-
}));
|
309
|
-
}
|
310
|
-
}
|
311
|
-
|
312
|
-
const HANDLER_FUNCTION_CALL_V2_RAW_ID = 'handler_function_call_v2';
|
313
|
-
class HandlerFunctionCallV2 {
|
314
|
-
static SimpleToRaw(name, network, config) {
|
315
|
-
return {
|
316
|
-
alertType: 'event',
|
317
|
-
network,
|
318
|
-
nickname: name,
|
319
|
-
type: HANDLER_FUNCTION_CALL_V2_RAW_ID,
|
320
|
-
handler: config
|
321
|
-
};
|
322
|
-
}
|
323
|
-
static validateCreate(options) {
|
324
|
-
const timePeriodRegex = /^(\d+)([SMHD])$/i;
|
325
|
-
const onchainEventSchema = yup__namespace.object({
|
326
|
-
timeBase: yup__namespace.string().oneOf(['blocks', 'time']).required(),
|
327
|
-
// Blocks config
|
328
|
-
nBlocks: yup__namespace.number()
|
329
|
-
.min(10)
|
330
|
-
.when('timeBase', ([timeBase], schema) => timeBase === 'blocks' ? schema.required() : schema),
|
331
|
-
// Or Time config
|
332
|
-
timePeriod: yup__namespace.string()
|
333
|
-
.matches(timePeriodRegex, 'timePeriod must be of the format n[s|m|h|d], eg 10s for 10 seconds')
|
334
|
-
.when('timeBase', ([timeBase], schema) => timeBase === 'time' ? schema.required() : schema),
|
335
|
-
startTime: yup__namespace.number().default(0),
|
336
|
-
// Debouncing. Default is 0
|
337
|
-
debounceCount: yup__namespace.number(),
|
338
|
-
// Functions
|
339
|
-
functions: FunctionBuilder.schema(),
|
340
|
-
// Trigger
|
341
|
-
triggerOn: yup__namespace.string().oneOf(['always', 'filter']).default('always'),
|
342
|
-
latch: yup__namespace.boolean().default(false),
|
343
|
-
// Filter
|
344
|
-
filterVersion: yup__namespace.string().when('triggerOn', ([triggerOn], schema) => triggerOn === 'filter' ? schema.required() : schema),
|
345
|
-
filter: FilterBuilder.schema().when('triggerOn', ([triggerOn], schema) => triggerOn === 'filter' ? schema.required() : schema),
|
346
|
-
});
|
347
|
-
return onchainEventSchema.validate(options);
|
348
|
-
}
|
349
|
-
}
|
350
|
-
|
351
|
-
class Trigger {
|
352
|
-
static SimpleToRaw(name, id, network, config) {
|
353
|
-
switch (id) {
|
354
|
-
case 'PollFunctionV2': return HandlerFunctionCallV2.SimpleToRaw(name, network, config);
|
355
|
-
case 'PollFunctionV1': return HandlerFunctionCall.SimpleToRaw(name, network, config);
|
356
|
-
case 'OnchainEventV1': return HandlerOnchainEvent.SimpleToRaw(name, network, config);
|
357
|
-
default:
|
358
|
-
throw new Error('Invalid Trigger ID');
|
359
|
-
}
|
360
|
-
}
|
361
|
-
}
|
362
|
-
|
363
|
-
const toFunctionsUrl = (id) => {
|
364
|
-
return `https://${id}.functions.supabase.co/`;
|
365
|
-
};
|
366
|
-
const toApiUrl = (id) => {
|
367
|
-
return `https://${id}.supabase.co/`;
|
368
|
-
};
|
369
|
-
const PROD_PROJECT_ID = 'fdgtrxmmrtlokhgkvcjz';
|
370
|
-
// const API_URL = ''
|
371
|
-
const ANON_KEY = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImZkZ3RyeG1tcnRsb2toZ2t2Y2p6Iiwicm9sZSI6ImFub24iLCJpYXQiOjE2NzMwODcwNzYsImV4cCI6MTk4ODY2MzA3Nn0.sAMxjlcJSSozBGr-LNcsudyxzUEM9e-UspMHHQLqLr4';
|
372
|
-
class DeNotifyClient {
|
373
|
-
url;
|
374
|
-
token;
|
375
|
-
headers = {};
|
376
|
-
constructor(url, token) {
|
377
|
-
this.url = url;
|
378
|
-
this.token = token;
|
379
|
-
this.headers = {
|
380
|
-
'Authorization': `Bearer ${token}`,
|
381
|
-
'Content-Type': 'application/json'
|
382
|
-
};
|
383
|
-
}
|
384
|
-
static async create(options) {
|
385
|
-
if (options.key) {
|
386
|
-
// TODO
|
387
|
-
throw new Error('Auth by key not yet supported');
|
388
|
-
}
|
389
|
-
else if (options.email && options.password) {
|
390
|
-
const url = options.projectId ? toApiUrl(options.projectId) : toApiUrl(PROD_PROJECT_ID);
|
391
|
-
const functionsUrl = options.projectId ? toFunctionsUrl(options.projectId) : toFunctionsUrl(PROD_PROJECT_ID);
|
392
|
-
const anonKey = options.anonKey ? options.anonKey : ANON_KEY;
|
393
|
-
const supabase = supabaseJs.createClient(url, anonKey);
|
394
|
-
const { data: login, error } = await supabase.auth.signInWithPassword({
|
395
|
-
email: options.email,
|
396
|
-
password: options.password,
|
397
|
-
});
|
398
|
-
if (error)
|
399
|
-
throw error;
|
400
|
-
if (login.session?.access_token === undefined)
|
401
|
-
throw new Error('Access token not found');
|
402
|
-
return new DeNotifyClient(functionsUrl, login.session?.access_token);
|
403
|
-
}
|
404
|
-
else {
|
405
|
-
throw new Error('Authentication Required. DeNotify supports either username/password or API key authentication');
|
406
|
-
}
|
407
|
-
}
|
408
|
-
async alertHistory(id, pagination) {
|
409
|
-
const url = `alert-history`;
|
410
|
-
const params = pagination ? pagination : {};
|
411
|
-
if (id)
|
412
|
-
params.id = id;
|
413
|
-
if (Object.keys(params).length > 0) {
|
414
|
-
return await this.request('get', url, { params: pagination });
|
415
|
-
}
|
416
|
-
else {
|
417
|
-
return await this.request('get', url);
|
418
|
-
}
|
419
|
-
}
|
420
|
-
// TODO - Beutify the reponse
|
421
|
-
async getAlert(id) {
|
422
|
-
const alerts = await this.request('get', `alerts/${id}`);
|
423
|
-
return alerts[0];
|
424
|
-
}
|
425
|
-
async getAlerts() {
|
426
|
-
const alerts = await this.request('get', 'alerts');
|
427
|
-
return alerts;
|
428
|
-
}
|
429
|
-
async createAlert(config) {
|
430
|
-
const trigger = Trigger.SimpleToRaw(config.name, config.triggerId, config.network, config.trigger);
|
431
|
-
const notification = Notification.SimpleToRaw(config.notificationId, config.notification);
|
432
|
-
const alert = await this.request('post', `alerts`, { body: { trigger, notification } });
|
433
|
-
return alert;
|
434
|
-
}
|
435
|
-
async deleteAlert(id) {
|
436
|
-
const alerts = await this.request('delete', `alerts/${id}`);
|
437
|
-
return alerts;
|
438
|
-
}
|
439
|
-
async request(method, path, options = {}) {
|
440
|
-
const url = new URL(`${this.url}${path}`);
|
441
|
-
// append params
|
442
|
-
if (options.params) {
|
443
|
-
for (const param of Object.keys(options.params)) {
|
444
|
-
url.searchParams.append(param, options.params[param]);
|
445
|
-
}
|
446
|
-
}
|
447
|
-
const payload = {
|
448
|
-
method,
|
449
|
-
headers: this.headers
|
450
|
-
};
|
451
|
-
if (options.body)
|
452
|
-
payload.body = JSON.stringify(options.body);
|
453
|
-
const res = await fetch__default["default"](url.toString(), payload);
|
454
|
-
if (!res.ok)
|
455
|
-
throw new Error(`unexpected response ${res.statusText}`);
|
456
|
-
return await res.json();
|
457
|
-
}
|
458
|
-
async getAbi(network, address) {
|
459
|
-
const ret = await this.request('get', `abi/${network}/${address}`);
|
460
|
-
return ret;
|
461
|
-
}
|
462
|
-
async getAbiHash(abi) {
|
463
|
-
const ret = await this.request('post', 'abi', { body: abi });
|
464
|
-
return ret.hash;
|
465
|
-
}
|
466
|
-
async setAlertName(triggerId, name) {
|
467
|
-
throw new Error('Not yet supported - Sorry!');
|
468
|
-
}
|
469
|
-
async enableAlert(triggerId) {
|
470
|
-
throw new Error('Not yet supported - Sorry!');
|
471
|
-
}
|
472
|
-
async disableAlert(triggerId) {
|
473
|
-
throw new Error('Not yet supported - Sorry!');
|
474
|
-
}
|
475
|
-
async updateNotification(triggerId) {
|
476
|
-
throw new Error('Not yet supported - Sorry!');
|
477
|
-
}
|
478
|
-
async updateTrigger(triggerId, update) {
|
479
|
-
// TODO - Input validation
|
480
|
-
const ret = await this.request('patch', `alerts/trigger-handler/${triggerId}`, { body: update });
|
481
|
-
return ret;
|
482
|
-
}
|
483
|
-
}
|
484
|
-
|
485
|
-
class AlertBuilder {
|
486
|
-
name;
|
487
|
-
network;
|
488
|
-
triggerId;
|
489
|
-
trigger;
|
490
|
-
notificationId;
|
491
|
-
notification;
|
492
|
-
constructor(name) {
|
493
|
-
this.name = name;
|
494
|
-
}
|
495
|
-
static create(name) {
|
496
|
-
return new AlertBuilder(name);
|
497
|
-
}
|
498
|
-
onNetwork(network) {
|
499
|
-
this.network = network;
|
500
|
-
return this;
|
501
|
-
}
|
502
|
-
/**
|
503
|
-
* Call withTrigger with one of the TriggerConfig types:
|
504
|
-
* PollFunctionV2 | PollFunctionV1 | OnchainEventV1
|
505
|
-
* @param id Simple ID
|
506
|
-
* @param options Desired trigger configuration
|
507
|
-
* @returns self for piping
|
508
|
-
*/
|
509
|
-
withTrigger(id, options) {
|
510
|
-
this.triggerId = id;
|
511
|
-
this.trigger = options;
|
512
|
-
return this;
|
513
|
-
}
|
514
|
-
withNotification(id, options) {
|
515
|
-
this.notificationId = id;
|
516
|
-
this.notification = options;
|
517
|
-
return this;
|
518
|
-
}
|
519
|
-
async validate() {
|
520
|
-
// Validate trigger
|
521
|
-
switch (this.triggerId) {
|
522
|
-
case 'OnchainEventV1': return HandlerOnchainEvent.validateCreate(this.trigger);
|
523
|
-
case 'PollFunctionV1': return HandlerFunctionCall.validateCreate(this.trigger);
|
524
|
-
case 'PollFunctionV2': return HandlerFunctionCallV2.validateCreate(this.trigger);
|
525
|
-
}
|
526
|
-
switch (this.notificationId) {
|
527
|
-
case 'Discord': return NotifyDiscordWebhook.validateCreate(this.notification);
|
528
|
-
}
|
529
|
-
}
|
530
|
-
async config() {
|
531
|
-
if (this.trigger === undefined || this.triggerId === undefined)
|
532
|
-
throw new Error('Trigger not configured');
|
533
|
-
if (this.notification === undefined || this.notificationId === undefined)
|
534
|
-
throw new Error('Notification not configured');
|
535
|
-
if (this.network === undefined)
|
536
|
-
throw new Error('Network not configured');
|
537
|
-
await this.validate();
|
538
|
-
return {
|
539
|
-
name: this.name,
|
540
|
-
network: this.network,
|
541
|
-
triggerId: this.triggerId,
|
542
|
-
trigger: this.trigger,
|
543
|
-
notificationId: this.notificationId,
|
544
|
-
notification: this.notification,
|
545
|
-
};
|
546
|
-
}
|
547
|
-
}
|
548
|
-
|
549
|
-
var index$1 = /*#__PURE__*/Object.freeze({
|
550
|
-
__proto__: null,
|
551
|
-
Trigger: Trigger,
|
552
|
-
HandlerFunctionCallV2: HandlerFunctionCallV2,
|
553
|
-
HandlerFunctionCall: HandlerFunctionCall,
|
554
|
-
HandlerOnchainEvent: HandlerOnchainEvent
|
555
|
-
});
|
556
|
-
|
557
|
-
var index = /*#__PURE__*/Object.freeze({
|
558
|
-
__proto__: null,
|
559
|
-
NOTIFY_DISCORD_WEBHOOK_RAW_ID: NOTIFY_DISCORD_WEBHOOK_RAW_ID,
|
560
|
-
NotifyDiscordWebhook: NotifyDiscordWebhook,
|
561
|
-
Notification: Notification
|
562
|
-
});
|
563
|
-
|
564
|
-
exports.AlertBuilder = AlertBuilder;
|
565
|
-
exports.DeNotifyClient = DeNotifyClient;
|
566
|
-
exports.FilterBuilder = FilterBuilder;
|
567
|
-
exports.FunctionBuilder = FunctionBuilder;
|
568
|
-
exports.Notification = index;
|
569
|
-
exports.Trigger = index$1;
|
package/dist/index.min.js
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports,require("@supabase/supabase-js"),require("yup"),require("ethers"),require("node-fetch")):"function"==typeof define&&define.amd?define(["exports","@supabase/supabase-js","yup","ethers","node-fetch"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self)["denotify-client"]={},t.supabaseJs,t.yup,t.ethers,t.fetch)}(this,(function(t,e,r,i,n){"use strict";function s(t){return t&&"object"==typeof t&&"default"in t?t:{default:t}}function o(t){if(t&&t.__esModule)return t;var e=Object.create(null);return t&&Object.keys(t).forEach((function(r){if("default"!==r){var i=Object.getOwnPropertyDescriptor(t,r);Object.defineProperty(e,r,i.get?i:{enumerable:!0,get:function(){return t[r]}})}})),e.default=t,Object.freeze(e)}var a=o(r),c=s(n);const u="notify_discord_webhook";class d{static SimpleToRaw(t){return{name:"",notify_type:u,notify:t}}static validateCreate(t){const e=/^(https?|ftp):\/\/(-\.)?([^\s/?\.#]+\.?)+([^\s\.?#]+)?(\?\S*)?$/;return a.object({url:a.string().matches(e,"url is not a valid url").required(),username:a.string(),avatar_url:a.string().matches(e,"url is not a valid url"),message:a.string().required()}).validate(t)}}class l{static SimpleToRaw(t,e){if("Discord"===t)return d.SimpleToRaw(e)}}class h{static SimpleToRaw(t,e,r){return{alertType:"event",network:e,nickname:t,type:"handler_function_call",handler:r}}static validateCreate(t){const e=([t],e)=>"true"===t?e.notRequired():e.required();return a.object({address:a.string().required(),abi:a.array().required(),nBlocks:a.number().min(10),condition:a.string().oneOf([">",">=","<","<=","=","true"]),constant:a.number().min(0).when("condition",e),responseArgIndex:a.number().min(0).when("condition",e),responseArgDecimals:a.number().min(0).when("condition",e)}).validate(t)}}class p{static SimpleToRaw(t,e,r){return{alertType:"event",network:e,nickname:t,type:"handler_onchain_event",handler:r}}static validateCreate(t){const e=([t],e)=>"true"===t?e.notRequired():e.required();return a.object({address:a.string().required(),event:a.string().required(),abi:a.array().required(),condition:a.string().oneOf([">",">=","<","<=","=","true"]),constant:a.number().min(0).when("condition",e),paramsIndex:a.number().min(0).when("condition",e),paramsDecimals:a.number().min(0).when("condition",e)}).validate(t)}static validateUpdate(t){}}class f{api;data=[];constructor(t){this.api=t}static create(t){return new f(t)}getAbiHash(t){if(void 0===this.api)throw new Error("FunctionBuilder must be initialised with api");return this.api.getAbiHash(t)}async addFunction(t,e,r,n){const s=new i.ethers.Contract(t,n);return s.interface.encodeFunctionData(e,r),this.data.push({address:t,bytecode:s.interface.encodeFunctionData(e,r),abiHash:await this.getAbiHash(n),function:e}),this}get(){return this.data}static schema(){return a.array().of(a.object({address:a.string().required(),bytecode:a.string().matches(/^(0x)?([0-9a-fA-F]{2})*$/).required(),abiHash:a.string().matches(/^[A-Fa-f0-9]{64}/).required(),function:a.string().required()}))}}class g{static version(){return"v1"}static execute(t,e,r="v1"){const i=JSON.parse(JSON.stringify(e));let n=!0;for(const e of i){const r=g.process(t,e.conditions);n=g.execLogic(n,e.logic,r)}return n}static process(t,e,r){const i=e.shift();if(!i)return void 0===r||r;const n=g.execCondition(t,i);if(void 0===r||"WHERE"===i.logic)return g.process(t,e,n);if(void 0===i.logic)throw new Error("Invalid filter. Condition must have logic value");const s=g.execLogic(r,i.logic,n);return g.process(t,e,s)}static execLogic(t,e,r){switch(e){case"AND":return t&&r;case"OR":return t||r;case"XOR":return(t||r)&&!(t&&r);case"NAND":case"NOR":return!(t&&r);case"WHERE":return r;default:throw new Error(`Invalid Filter. Bad logic value: ${e}`)}}static execCondition(t,e){const r=t[e.key];if("Number"===e.type){if("number"!=typeof e.constant||"number"!=typeof r)throw new Error(`Invalid filter. Type miss-match. Type: ${e.type}, Variable: ${e.constant}, Data: ${r}`);const t=r,i=e.constant;switch(e.operation){case"eq":return t===i;case"!eq":return t!==i;case"gt":return t>i;case"gte":return t>=i;case"lt":return t<i;case"lte":return t<=i;default:throw new Error("Invalid Filter. Operation does not match type")}}if("String"===e.type){if("string"!=typeof e.constant||"string"!=typeof r)throw new Error(`Invalid filter. Type miss-match. Type: ${e.type}, Variable: ${e.constant}, Data: ${r}`);const t=r,i=e.constant;switch(e.operation){case"contains":return t.includes(i);case"!contains":return!t.includes(i);case"is":return t===i;case"!is":return t!==i;case"isEmpty":return""===t;case"!isEmpty":return""!==t;default:throw new Error("Invalid Filter. Operation does not match type")}}if("Address"===e.type){if("string"!=typeof e.constant||"string"!=typeof r)throw new Error(`Invalid filter. Type miss-match. Type: ${e.type}, Variable: ${e.constant}, Data: ${r}`);const t=r,i=e.constant;switch(e.operation){case"contains":return t.toLowerCase().includes(i.toLowerCase());case"!contains":return!t.toLowerCase().includes(i.toLowerCase());case"is":return t.toLowerCase()===i.toLowerCase();case"!is":return t.toLowerCase()!==i.toLowerCase();case"isEmpty":return""===t;case"!isEmpty":return""!==t;default:throw new Error("Invalid Filter. Operation does not match type")}}throw new Error("Invalid Filter. Unknown Type")}}class w{groups=[];constructor(){this.newGroup("WHERE")}static version(){return g.version()}static new(){return new w}newGroup(t){if(0!==this.groups.length&&"WHERE"===t)throw new Error('Only the first groups can start with "WHERE"');return this.groups.push({logic:"WHERE",conditions:[]}),this}addCondition(t,e,r,i,n){const s=this.groups[this.groups.length-1];if(0===s.conditions.length&&"WHERE"!==t)throw new Error('Logic for the first condition of a group must be "WHERE"');if(s.conditions.length>0&&"WHERE"===t)throw new Error('Only the first condition of a group can be "WHERE"');return s.conditions.push({logic:t,key:e,type:r,operation:i,constant:n}),this}finalise(){for(const t of this.groups)if(0===t.conditions.length)throw new Error("Bad Filter. All Groups must have atleast one condition");return this.groups}static schema(){const t=a.string().oneOf(["AND","OR","XOR","NAND","NOR","WHERE"]).required(),e=a.object({logic:t,key:a.string().required(),type:a.string().oneOf(["String","Address","Number"]).required(),operation:a.string().when("type",(([t],e)=>{switch(t){case"String":return e.oneOf(["contains","!contains","is","!is","isEmpty","!isEmpty"]);case"Address":return e.oneOf(["is","!is","isEmpty","!isEmpty"]);case"Number":return e.oneOf(["String","Address","Number"]);default:throw new Error("Invalid Filter Data Type")}})),constant:a.mixed().when("type",(([t])=>{switch(t){case"String":case"Address":return a.string();case"Number":return a.number();default:throw new Error("Invalid Filter Data Type")}}))});return a.array().of(a.object({logic:t,conditions:a.array().of(e)}))}}class y{static SimpleToRaw(t,e,r){return{alertType:"event",network:e,nickname:t,type:"handler_function_call_v2",handler:r}}static validateCreate(t){return a.object({timeBase:a.string().oneOf(["blocks","time"]).required(),nBlocks:a.number().min(10).when("timeBase",(([t],e)=>"blocks"===t?e.required():e)),timePeriod:a.string().matches(/^(\d+)([SMHD])$/i,"timePeriod must be of the format n[s|m|h|d], eg 10s for 10 seconds").when("timeBase",(([t],e)=>"time"===t?e.required():e)),startTime:a.number().default(0),debounceCount:a.number(),functions:f.schema(),triggerOn:a.string().oneOf(["always","filter"]).default("always"),latch:a.boolean().default(!1),filterVersion:a.string().when("triggerOn",(([t],e)=>"filter"===t?e.required():e)),filter:w.schema().when("triggerOn",(([t],e)=>"filter"===t?e.required():e))}).validate(t)}}class m{static SimpleToRaw(t,e,r,i){switch(e){case"PollFunctionV2":return y.SimpleToRaw(t,r,i);case"PollFunctionV1":return h.SimpleToRaw(t,r,i);case"OnchainEventV1":return p.SimpleToRaw(t,r,i);default:throw new Error("Invalid Trigger ID")}}}const b=t=>`https://${t}.functions.supabase.co/`,E=t=>`https://${t}.supabase.co/`,v="fdgtrxmmrtlokhgkvcjz";class I{url;token;headers={};constructor(t,e){this.url=t,this.token=e,this.headers={Authorization:`Bearer ${e}`,"Content-Type":"application/json"}}static async create(t){if(t.key)throw new Error("Auth by key not yet supported");if(t.email&&t.password){const r=t.projectId?E(t.projectId):E(v),i=t.projectId?b(t.projectId):b(v),n=t.anonKey?t.anonKey:"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImZkZ3RyeG1tcnRsb2toZ2t2Y2p6Iiwicm9sZSI6ImFub24iLCJpYXQiOjE2NzMwODcwNzYsImV4cCI6MTk4ODY2MzA3Nn0.sAMxjlcJSSozBGr-LNcsudyxzUEM9e-UspMHHQLqLr4",s=e.createClient(r,n),{data:o,error:a}=await s.auth.signInWithPassword({email:t.email,password:t.password});if(a)throw a;if(void 0===o.session?.access_token)throw new Error("Access token not found");return new I(i,o.session?.access_token)}throw new Error("Authentication Required. DeNotify supports either username/password or API key authentication")}async alertHistory(t,e){const r="alert-history",i=e||{};return t&&(i.id=t),Object.keys(i).length>0?await this.request("get",r,{params:e}):await this.request("get",r)}async getAlert(t){return(await this.request("get",`alerts/${t}`))[0]}async getAlerts(){return await this.request("get","alerts")}async createAlert(t){const e=m.SimpleToRaw(t.name,t.triggerId,t.network,t.trigger),r=l.SimpleToRaw(t.notificationId,t.notification);return await this.request("post","alerts",{body:{trigger:e,notification:r}})}async deleteAlert(t){return await this.request("delete",`alerts/${t}`)}async request(t,e,r={}){const i=new URL(`${this.url}${e}`);if(r.params)for(const t of Object.keys(r.params))i.searchParams.append(t,r.params[t]);const n={method:t,headers:this.headers};r.body&&(n.body=JSON.stringify(r.body));const s=await c.default(i.toString(),n);if(!s.ok)throw new Error(`unexpected response ${s.statusText}`);return await s.json()}async getAbi(t,e){return await this.request("get",`abi/${t}/${e}`)}async getAbiHash(t){return(await this.request("post","abi",{body:t})).hash}async setAlertName(t,e){throw new Error("Not yet supported - Sorry!")}async enableAlert(t){throw new Error("Not yet supported - Sorry!")}async disableAlert(t){throw new Error("Not yet supported - Sorry!")}async updateNotification(t){throw new Error("Not yet supported - Sorry!")}async updateTrigger(t,e){return await this.request("patch",`alerts/trigger-handler/${t}`,{body:e})}}class O{name;network;triggerId;trigger;notificationId;notification;constructor(t){this.name=t}static create(t){return new O(t)}onNetwork(t){return this.network=t,this}withTrigger(t,e){return this.triggerId=t,this.trigger=e,this}withNotification(t,e){return this.notificationId=t,this.notification=e,this}async validate(){switch(this.triggerId){case"OnchainEventV1":return p.validateCreate(this.trigger);case"PollFunctionV1":return h.validateCreate(this.trigger);case"PollFunctionV2":return y.validateCreate(this.trigger)}if("Discord"===this.notificationId)return d.validateCreate(this.notification)}async config(){if(void 0===this.trigger||void 0===this.triggerId)throw new Error("Trigger not configured");if(void 0===this.notification||void 0===this.notificationId)throw new Error("Notification not configured");if(void 0===this.network)throw new Error("Network not configured");return await this.validate(),{name:this.name,network:this.network,triggerId:this.triggerId,trigger:this.trigger,notificationId:this.notificationId,notification:this.notification}}}var k=Object.freeze({__proto__:null,Trigger:m,HandlerFunctionCallV2:y,HandlerFunctionCall:h,HandlerOnchainEvent:p}),q=Object.freeze({__proto__:null,NOTIFY_DISCORD_WEBHOOK_RAW_ID:u,NotifyDiscordWebhook:d,Notification:l});t.AlertBuilder=O,t.DeNotifyClient=I,t.FilterBuilder=w,t.FunctionBuilder=f,t.Notification=q,t.Trigger=k}));
|