notiformer 1.0.0
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/README.md +245 -0
- package/dist/cache.d.ts +13 -0
- package/dist/cache.d.ts.map +1 -0
- package/dist/cache.js +41 -0
- package/dist/cache.js.map +1 -0
- package/dist/index.d.ts +86 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +243 -0
- package/dist/index.js.map +1 -0
- package/dist/logger.d.ts +10 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +33 -0
- package/dist/logger.js.map +1 -0
- package/dist/types.d.ts +99 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +6 -0
- package/dist/types.js.map +1 -0
- package/package.json +38 -0
package/README.md
ADDED
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
# notiformer
|
|
2
|
+
|
|
3
|
+
Real-time alerts, notifications and feature gates for your backend code.
|
|
4
|
+
|
|
5
|
+
Send events from any point in your Node.js code and get notified instantly via push, email, Slack and more.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Install
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm install notiformer
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Quick start
|
|
18
|
+
|
|
19
|
+
```ts
|
|
20
|
+
import { Notiformer } from 'notiformer';
|
|
21
|
+
|
|
22
|
+
const n = new Notiformer({
|
|
23
|
+
apiKey: 'ntf_live_...', // from app.notiformer.com/projects
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
// Send an event + notification
|
|
27
|
+
await n.event({
|
|
28
|
+
channel: 'payments',
|
|
29
|
+
event: 'payment_success',
|
|
30
|
+
description: `$49.00 — john@example.com`,
|
|
31
|
+
icon: '💳',
|
|
32
|
+
tags: { userId: 'usr_123', plan: 'pro' },
|
|
33
|
+
notify: true,
|
|
34
|
+
});
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## Events
|
|
40
|
+
|
|
41
|
+
Use `n.event()` to log an event and optionally trigger notifications.
|
|
42
|
+
|
|
43
|
+
```ts
|
|
44
|
+
await n.event({
|
|
45
|
+
channel: 'auth', // channel name (auto-created)
|
|
46
|
+
event: 'user_signup', // machine-readable event name
|
|
47
|
+
description: 'New signup via Google',
|
|
48
|
+
icon: '🎉',
|
|
49
|
+
|
|
50
|
+
// Optional metadata
|
|
51
|
+
tags: { method: 'google', plan: 'free' },
|
|
52
|
+
value: '42nd user', // shown in the dashboard feed
|
|
53
|
+
|
|
54
|
+
// notify: true → triggers push/email/Slack per your dashboard settings
|
|
55
|
+
// notify: false → silent log only (analytics mode)
|
|
56
|
+
notify: true,
|
|
57
|
+
|
|
58
|
+
// Rate limiting: events with the same groupId are deduplicated
|
|
59
|
+
groupId: 'signup-batch-today',
|
|
60
|
+
});
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### event() is fire-and-forget safe
|
|
64
|
+
|
|
65
|
+
`event()` **never throws**. If the network is down or the API returns an error, it logs a warning and returns `null`. Your app continues running.
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
## Feature Gates
|
|
70
|
+
|
|
71
|
+
Use `n.gate()` to check a boolean toggle you control remotely from the dashboard.
|
|
72
|
+
|
|
73
|
+
```ts
|
|
74
|
+
const isEnabled = await n.gate('new-checkout-flow');
|
|
75
|
+
|
|
76
|
+
if (isEnabled) {
|
|
77
|
+
// run new checkout
|
|
78
|
+
} else {
|
|
79
|
+
// run legacy checkout
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Gates are cached locally for 30 seconds by default to minimise API calls.
|
|
84
|
+
|
|
85
|
+
```ts
|
|
86
|
+
// Custom fallback and cache TTL
|
|
87
|
+
const isEnabled = await n.gate('my-feature', {
|
|
88
|
+
fallback: false, // returned if network fails
|
|
89
|
+
cacheTtl: 60, // cache for 60 seconds (0 = disable)
|
|
90
|
+
});
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### Full gate details
|
|
94
|
+
|
|
95
|
+
```ts
|
|
96
|
+
const result = await n.gateDetails('my-feature');
|
|
97
|
+
// {
|
|
98
|
+
// key: 'my-feature',
|
|
99
|
+
// enabled: true,
|
|
100
|
+
// cached: false,
|
|
101
|
+
// fetchedAt: '2025-04-20T10:00:00.000Z'
|
|
102
|
+
// }
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### Force a fresh fetch
|
|
106
|
+
|
|
107
|
+
```ts
|
|
108
|
+
n.clearGateCache('my-feature'); // clear one gate
|
|
109
|
+
n.clearGateCache(); // clear all
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
## Configuration
|
|
115
|
+
|
|
116
|
+
```ts
|
|
117
|
+
const n = new Notiformer({
|
|
118
|
+
apiKey: 'ntf_live_...', // Required
|
|
119
|
+
|
|
120
|
+
// Optional
|
|
121
|
+
baseUrl: 'https://api.notiformer.com', // default
|
|
122
|
+
timeout: 5000, // ms, default: 5000
|
|
123
|
+
silent: false, // true = all calls are no-ops (good for local dev)
|
|
124
|
+
logLevel: 'error', // 'debug' | 'info' | 'warn' | 'error' | 'none'
|
|
125
|
+
});
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### Silence in development
|
|
129
|
+
|
|
130
|
+
```ts
|
|
131
|
+
const n = new Notiformer({
|
|
132
|
+
apiKey: process.env.NOTIFORMER_API_KEY!,
|
|
133
|
+
silent: process.env.NODE_ENV !== 'production',
|
|
134
|
+
});
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
---
|
|
138
|
+
|
|
139
|
+
## Common patterns
|
|
140
|
+
|
|
141
|
+
### Alert on errors
|
|
142
|
+
|
|
143
|
+
```ts
|
|
144
|
+
app.use(async (err, req, res, next) => {
|
|
145
|
+
await n.event({
|
|
146
|
+
channel: 'errors',
|
|
147
|
+
event: 'unhandled_error',
|
|
148
|
+
description: err.message,
|
|
149
|
+
icon: '🔴',
|
|
150
|
+
tags: {
|
|
151
|
+
path: req.path,
|
|
152
|
+
method: req.method,
|
|
153
|
+
status: 500,
|
|
154
|
+
},
|
|
155
|
+
notify: true,
|
|
156
|
+
});
|
|
157
|
+
res.status(500).json({ error: 'Internal server error' });
|
|
158
|
+
});
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### Track business milestones
|
|
162
|
+
|
|
163
|
+
```ts
|
|
164
|
+
async function onPaymentSuccess(charge: Charge) {
|
|
165
|
+
await n.event({
|
|
166
|
+
channel: 'payments',
|
|
167
|
+
event: 'payment_success',
|
|
168
|
+
description: `${formatCurrency(charge.amount)} — ${charge.email}`,
|
|
169
|
+
icon: '💳',
|
|
170
|
+
value: formatCurrency(charge.amount),
|
|
171
|
+
tags: { chargeId: charge.id, plan: charge.plan },
|
|
172
|
+
notify: true,
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
### Feature gate a new endpoint
|
|
178
|
+
|
|
179
|
+
```ts
|
|
180
|
+
app.post('/checkout', async (req, res) => {
|
|
181
|
+
const useNewFlow = await n.gate('new-checkout-flow');
|
|
182
|
+
|
|
183
|
+
if (useNewFlow) {
|
|
184
|
+
return newCheckoutHandler(req, res);
|
|
185
|
+
}
|
|
186
|
+
return legacyCheckoutHandler(req, res);
|
|
187
|
+
});
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
### Silent analytics (no notification)
|
|
191
|
+
|
|
192
|
+
```ts
|
|
193
|
+
// Log for analytics without pushing a notification
|
|
194
|
+
await n.event({
|
|
195
|
+
channel: 'monitoring',
|
|
196
|
+
event: 'page_view',
|
|
197
|
+
description: `/dashboard visited`,
|
|
198
|
+
tags: { userId: req.user.id },
|
|
199
|
+
notify: false, // ← silent
|
|
200
|
+
});
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
---
|
|
204
|
+
|
|
205
|
+
## TypeScript
|
|
206
|
+
|
|
207
|
+
Full TypeScript support included. All types are exported:
|
|
208
|
+
|
|
209
|
+
```ts
|
|
210
|
+
import type {
|
|
211
|
+
NotiformerConfig,
|
|
212
|
+
EventPayload,
|
|
213
|
+
EventResponse,
|
|
214
|
+
GateOptions,
|
|
215
|
+
GateResult,
|
|
216
|
+
} from 'notiformer';
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
---
|
|
220
|
+
|
|
221
|
+
## Requirements
|
|
222
|
+
|
|
223
|
+
- Node.js 16+
|
|
224
|
+
- Fetch API (native in Node 18+; polyfill needed for Node 16)
|
|
225
|
+
|
|
226
|
+
### Node 16 fetch polyfill
|
|
227
|
+
|
|
228
|
+
```bash
|
|
229
|
+
npm install node-fetch
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
```ts
|
|
233
|
+
import fetch from 'node-fetch';
|
|
234
|
+
(global as any).fetch = fetch;
|
|
235
|
+
|
|
236
|
+
import { Notiformer } from 'notiformer';
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
---
|
|
240
|
+
|
|
241
|
+
## Links
|
|
242
|
+
|
|
243
|
+
- Dashboard: [app.notiformer.com](https://app.notiformer.com)
|
|
244
|
+
- Docs: [docs.notiformer.com](https://docs.notiformer.com)
|
|
245
|
+
- Support: support@notiformer.com
|
package/dist/cache.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Simple in-memory TTL cache for gate values.
|
|
3
|
+
* Reduces API calls when gate() is called frequently.
|
|
4
|
+
*/
|
|
5
|
+
export declare class GateCache {
|
|
6
|
+
private store;
|
|
7
|
+
get(key: string): boolean | null;
|
|
8
|
+
set(key: string, value: boolean, ttlSeconds: number): void;
|
|
9
|
+
delete(key: string): void;
|
|
10
|
+
clear(): void;
|
|
11
|
+
size(): number;
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=cache.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../src/cache.ts"],"names":[],"mappings":"AAKA;;;GAGG;AACH,qBAAa,SAAS;IACpB,OAAO,CAAC,KAAK,CAAiC;IAE9C,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,GAAG,IAAI;IAUhC,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI;IAQ1D,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAIzB,KAAK,IAAI,IAAI;IAIb,IAAI,IAAI,MAAM;CAGf"}
|
package/dist/cache.js
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.GateCache = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Simple in-memory TTL cache for gate values.
|
|
6
|
+
* Reduces API calls when gate() is called frequently.
|
|
7
|
+
*/
|
|
8
|
+
class GateCache {
|
|
9
|
+
constructor() {
|
|
10
|
+
this.store = new Map();
|
|
11
|
+
}
|
|
12
|
+
get(key) {
|
|
13
|
+
const entry = this.store.get(key);
|
|
14
|
+
if (!entry)
|
|
15
|
+
return null;
|
|
16
|
+
if (Date.now() > entry.expiresAt) {
|
|
17
|
+
this.store.delete(key);
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
return entry.value;
|
|
21
|
+
}
|
|
22
|
+
set(key, value, ttlSeconds) {
|
|
23
|
+
if (ttlSeconds <= 0)
|
|
24
|
+
return;
|
|
25
|
+
this.store.set(key, {
|
|
26
|
+
value,
|
|
27
|
+
expiresAt: Date.now() + ttlSeconds * 1000,
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
delete(key) {
|
|
31
|
+
this.store.delete(key);
|
|
32
|
+
}
|
|
33
|
+
clear() {
|
|
34
|
+
this.store.clear();
|
|
35
|
+
}
|
|
36
|
+
size() {
|
|
37
|
+
return this.store.size;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
exports.GateCache = GateCache;
|
|
41
|
+
//# sourceMappingURL=cache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache.js","sourceRoot":"","sources":["../src/cache.ts"],"names":[],"mappings":";;;AAKA;;;GAGG;AACH,MAAa,SAAS;IAAtB;QACU,UAAK,GAAG,IAAI,GAAG,EAAsB,CAAC;IA+BhD,CAAC;IA7BC,GAAG,CAAC,GAAW;QACb,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAClC,IAAI,CAAC,KAAK;YAAE,OAAO,IAAI,CAAC;QACxB,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC,SAAS,EAAE,CAAC;YACjC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YACvB,OAAO,IAAI,CAAC;QACd,CAAC;QACD,OAAO,KAAK,CAAC,KAAK,CAAC;IACrB,CAAC;IAED,GAAG,CAAC,GAAW,EAAE,KAAc,EAAE,UAAkB;QACjD,IAAI,UAAU,IAAI,CAAC;YAAE,OAAO;QAC5B,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE;YAClB,KAAK;YACL,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,UAAU,GAAG,IAAI;SAC1C,CAAC,CAAC;IACL,CAAC;IAED,MAAM,CAAC,GAAW;QAChB,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IACzB,CAAC;IAED,KAAK;QACH,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;IACrB,CAAC;IAED,IAAI;QACF,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC;IACzB,CAAC;CACF;AAhCD,8BAgCC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Notiformer Node.js SDK
|
|
3
|
+
*
|
|
4
|
+
* Real-time alerts, notifications and feature gates for your backend code.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```ts
|
|
8
|
+
* import { Notiformer } from 'notiformer';
|
|
9
|
+
*
|
|
10
|
+
* const n = new Notiformer({ apiKey: 'ntf_live_...' });
|
|
11
|
+
*
|
|
12
|
+
* // Send a notification event
|
|
13
|
+
* await n.event({
|
|
14
|
+
* channel: 'payments',
|
|
15
|
+
* event: 'payment_success',
|
|
16
|
+
* description: '$49.00 — john@example.com',
|
|
17
|
+
* icon: '💳',
|
|
18
|
+
* notify: true,
|
|
19
|
+
* });
|
|
20
|
+
*
|
|
21
|
+
* // Check a feature gate
|
|
22
|
+
* const isEnabled = await n.gate('new-checkout-flow');
|
|
23
|
+
* if (isEnabled) { ... }
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
import type { NotiformerConfig, EventPayload, EventResponse, GateOptions, GateResult } from './types';
|
|
27
|
+
export type { NotiformerConfig, EventPayload, EventResponse, GateOptions, GateResult, };
|
|
28
|
+
export declare class Notiformer {
|
|
29
|
+
private readonly apiKey;
|
|
30
|
+
private readonly baseUrl;
|
|
31
|
+
private readonly timeout;
|
|
32
|
+
private readonly silent;
|
|
33
|
+
private readonly logger;
|
|
34
|
+
private readonly gateCache;
|
|
35
|
+
constructor(config: NotiformerConfig);
|
|
36
|
+
/**
|
|
37
|
+
* Send an event to a channel. Triggers notifications based on your
|
|
38
|
+
* dashboard settings.
|
|
39
|
+
*
|
|
40
|
+
* @param payload - The event data
|
|
41
|
+
* @returns The created event ID and timestamp, or null if silent mode
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* ```ts
|
|
45
|
+
* await n.event({
|
|
46
|
+
* channel: 'payments',
|
|
47
|
+
* event: 'payment_success',
|
|
48
|
+
* description: `$49.00 — ${user.email}`,
|
|
49
|
+
* icon: '💳',
|
|
50
|
+
* tags: { userId: user.id, plan: 'pro' },
|
|
51
|
+
* notify: true,
|
|
52
|
+
* });
|
|
53
|
+
* ```
|
|
54
|
+
*/
|
|
55
|
+
event(payload: EventPayload): Promise<EventResponse | null>;
|
|
56
|
+
/**
|
|
57
|
+
* Check whether a feature gate is enabled. Results are cached locally
|
|
58
|
+
* for `cacheTtl` seconds (default: 30) to minimize API calls.
|
|
59
|
+
*
|
|
60
|
+
* @param key - The gate key as defined in your dashboard
|
|
61
|
+
* @param options - Cache and fallback options
|
|
62
|
+
* @returns `true` if the gate is enabled, `false` otherwise
|
|
63
|
+
*
|
|
64
|
+
* @example
|
|
65
|
+
* ```ts
|
|
66
|
+
* const isEnabled = await n.gate('new-checkout-flow');
|
|
67
|
+
* if (isEnabled) {
|
|
68
|
+
* // use new checkout
|
|
69
|
+
* }
|
|
70
|
+
* ```
|
|
71
|
+
*/
|
|
72
|
+
gate(key: string, options?: GateOptions): Promise<boolean>;
|
|
73
|
+
/**
|
|
74
|
+
* Like `gate()` but returns the full GateResult object
|
|
75
|
+
* including whether the value was cached.
|
|
76
|
+
*/
|
|
77
|
+
gateDetails(key: string, options?: GateOptions): Promise<GateResult>;
|
|
78
|
+
/**
|
|
79
|
+
* Clears the in-memory gate cache.
|
|
80
|
+
* Useful if you need to force a fresh fetch.
|
|
81
|
+
*/
|
|
82
|
+
clearGateCache(key?: string): void;
|
|
83
|
+
private fetchWithTimeout;
|
|
84
|
+
}
|
|
85
|
+
export default Notiformer;
|
|
86
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,KAAK,EACV,gBAAgB,EAChB,YAAY,EACZ,aAAa,EACb,WAAW,EACX,UAAU,EAEX,MAAM,SAAS,CAAC;AAIjB,YAAY,EACV,gBAAgB,EAChB,YAAY,EACZ,aAAa,EACb,WAAW,EACX,UAAU,GACX,CAAC;AAMF,qBAAa,UAAU;IACrB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAU;IACjC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAY;gBAE1B,MAAM,EAAE,gBAAgB;IAsBpC;;;;;;;;;;;;;;;;;;OAkBG;IACG,KAAK,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC;IAiDjE;;;;;;;;;;;;;;;OAeG;IACG,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,GAAE,WAAgB,GAAG,OAAO,CAAC,OAAO,CAAC;IAyCpE;;;OAGG;IACG,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,GAAE,WAAgB,GAAG,OAAO,CAAC,UAAU,CAAC;IA+B9E;;;OAGG;IACH,cAAc,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI;YAYpB,gBAAgB;CAoB/B;AAMD,eAAe,UAAU,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Notiformer Node.js SDK
|
|
4
|
+
*
|
|
5
|
+
* Real-time alerts, notifications and feature gates for your backend code.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```ts
|
|
9
|
+
* import { Notiformer } from 'notiformer';
|
|
10
|
+
*
|
|
11
|
+
* const n = new Notiformer({ apiKey: 'ntf_live_...' });
|
|
12
|
+
*
|
|
13
|
+
* // Send a notification event
|
|
14
|
+
* await n.event({
|
|
15
|
+
* channel: 'payments',
|
|
16
|
+
* event: 'payment_success',
|
|
17
|
+
* description: '$49.00 — john@example.com',
|
|
18
|
+
* icon: '💳',
|
|
19
|
+
* notify: true,
|
|
20
|
+
* });
|
|
21
|
+
*
|
|
22
|
+
* // Check a feature gate
|
|
23
|
+
* const isEnabled = await n.gate('new-checkout-flow');
|
|
24
|
+
* if (isEnabled) { ... }
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
28
|
+
exports.Notiformer = void 0;
|
|
29
|
+
const logger_1 = require("./logger");
|
|
30
|
+
const cache_1 = require("./cache");
|
|
31
|
+
const DEFAULT_BASE_URL = 'https://api.notiformer.com';
|
|
32
|
+
const DEFAULT_TIMEOUT = 5000;
|
|
33
|
+
const DEFAULT_GATE_TTL = 30;
|
|
34
|
+
class Notiformer {
|
|
35
|
+
constructor(config) {
|
|
36
|
+
var _a, _b, _c, _d;
|
|
37
|
+
if (!config.apiKey) {
|
|
38
|
+
throw new Error('[notiformer] apiKey is required.');
|
|
39
|
+
}
|
|
40
|
+
if (!config.apiKey.startsWith('ntf_')) {
|
|
41
|
+
console.warn('[notiformer] API key should start with "ntf_". Make sure you are using a valid key from your dashboard.');
|
|
42
|
+
}
|
|
43
|
+
this.apiKey = config.apiKey;
|
|
44
|
+
this.baseUrl = ((_a = config.baseUrl) !== null && _a !== void 0 ? _a : DEFAULT_BASE_URL).replace(/\/$/, '');
|
|
45
|
+
this.timeout = (_b = config.timeout) !== null && _b !== void 0 ? _b : DEFAULT_TIMEOUT;
|
|
46
|
+
this.silent = (_c = config.silent) !== null && _c !== void 0 ? _c : false;
|
|
47
|
+
this.logger = new logger_1.Logger((_d = config.logLevel) !== null && _d !== void 0 ? _d : 'error');
|
|
48
|
+
this.gateCache = new cache_1.GateCache();
|
|
49
|
+
this.logger.debug('Notiformer initialized', { baseUrl: this.baseUrl });
|
|
50
|
+
}
|
|
51
|
+
// ─────────────────────────────────────────────
|
|
52
|
+
// event() — Send a notification event
|
|
53
|
+
// ─────────────────────────────────────────────
|
|
54
|
+
/**
|
|
55
|
+
* Send an event to a channel. Triggers notifications based on your
|
|
56
|
+
* dashboard settings.
|
|
57
|
+
*
|
|
58
|
+
* @param payload - The event data
|
|
59
|
+
* @returns The created event ID and timestamp, or null if silent mode
|
|
60
|
+
*
|
|
61
|
+
* @example
|
|
62
|
+
* ```ts
|
|
63
|
+
* await n.event({
|
|
64
|
+
* channel: 'payments',
|
|
65
|
+
* event: 'payment_success',
|
|
66
|
+
* description: `$49.00 — ${user.email}`,
|
|
67
|
+
* icon: '💳',
|
|
68
|
+
* tags: { userId: user.id, plan: 'pro' },
|
|
69
|
+
* notify: true,
|
|
70
|
+
* });
|
|
71
|
+
* ```
|
|
72
|
+
*/
|
|
73
|
+
async event(payload) {
|
|
74
|
+
var _a;
|
|
75
|
+
if (this.silent) {
|
|
76
|
+
this.logger.info('Silent mode: event suppressed', payload);
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
if (!payload.channel)
|
|
80
|
+
throw new Error('[notiformer] event.channel is required.');
|
|
81
|
+
if (!payload.event)
|
|
82
|
+
throw new Error('[notiformer] event.event is required.');
|
|
83
|
+
this.logger.debug('Sending event', payload);
|
|
84
|
+
try {
|
|
85
|
+
const body = {
|
|
86
|
+
channel: payload.channel,
|
|
87
|
+
event: payload.event,
|
|
88
|
+
description: payload.description,
|
|
89
|
+
icon: payload.icon,
|
|
90
|
+
tags: payload.tags,
|
|
91
|
+
value: payload.value,
|
|
92
|
+
notify: (_a = payload.notify) !== null && _a !== void 0 ? _a : true,
|
|
93
|
+
groupId: payload.groupId,
|
|
94
|
+
};
|
|
95
|
+
const res = await this.fetchWithTimeout('/v1/events', {
|
|
96
|
+
method: 'POST',
|
|
97
|
+
body: JSON.stringify(body),
|
|
98
|
+
});
|
|
99
|
+
if (!res.ok) {
|
|
100
|
+
const err = await res.json().catch(() => ({ error: 'Unknown', code: String(res.status) }));
|
|
101
|
+
this.logger.error(`Event failed: ${err.error} (${err.code})`);
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
const data = await res.json();
|
|
105
|
+
this.logger.debug('Event sent', data);
|
|
106
|
+
return data;
|
|
107
|
+
}
|
|
108
|
+
catch (err) {
|
|
109
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
110
|
+
this.logger.error('Event request failed', message);
|
|
111
|
+
// Never throw — a failed notification should not crash your app
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
// ─────────────────────────────────────────────
|
|
116
|
+
// gate() — Check a feature gate
|
|
117
|
+
// ─────────────────────────────────────────────
|
|
118
|
+
/**
|
|
119
|
+
* Check whether a feature gate is enabled. Results are cached locally
|
|
120
|
+
* for `cacheTtl` seconds (default: 30) to minimize API calls.
|
|
121
|
+
*
|
|
122
|
+
* @param key - The gate key as defined in your dashboard
|
|
123
|
+
* @param options - Cache and fallback options
|
|
124
|
+
* @returns `true` if the gate is enabled, `false` otherwise
|
|
125
|
+
*
|
|
126
|
+
* @example
|
|
127
|
+
* ```ts
|
|
128
|
+
* const isEnabled = await n.gate('new-checkout-flow');
|
|
129
|
+
* if (isEnabled) {
|
|
130
|
+
* // use new checkout
|
|
131
|
+
* }
|
|
132
|
+
* ```
|
|
133
|
+
*/
|
|
134
|
+
async gate(key, options = {}) {
|
|
135
|
+
var _a, _b;
|
|
136
|
+
if (!key)
|
|
137
|
+
throw new Error('[notiformer] gate key is required.');
|
|
138
|
+
const fallback = (_a = options.fallback) !== null && _a !== void 0 ? _a : false;
|
|
139
|
+
const ttl = (_b = options.cacheTtl) !== null && _b !== void 0 ? _b : DEFAULT_GATE_TTL;
|
|
140
|
+
if (this.silent) {
|
|
141
|
+
this.logger.info(`Silent mode: gate "${key}" returning fallback (${String(fallback)})`);
|
|
142
|
+
return fallback;
|
|
143
|
+
}
|
|
144
|
+
// Check cache first
|
|
145
|
+
const cached = this.gateCache.get(key);
|
|
146
|
+
if (cached !== null) {
|
|
147
|
+
this.logger.debug(`Gate "${key}" from cache: ${String(cached)}`);
|
|
148
|
+
return cached;
|
|
149
|
+
}
|
|
150
|
+
try {
|
|
151
|
+
const res = await this.fetchWithTimeout(`/v1/gates/${encodeURIComponent(key)}`);
|
|
152
|
+
if (!res.ok) {
|
|
153
|
+
this.logger.warn(`Gate "${key}" fetch failed (${res.status}), using fallback: ${String(fallback)}`);
|
|
154
|
+
return fallback;
|
|
155
|
+
}
|
|
156
|
+
const data = await res.json();
|
|
157
|
+
this.gateCache.set(key, data.enabled, ttl);
|
|
158
|
+
this.logger.debug(`Gate "${key}": ${String(data.enabled)}`);
|
|
159
|
+
return data.enabled;
|
|
160
|
+
}
|
|
161
|
+
catch (err) {
|
|
162
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
163
|
+
this.logger.warn(`Gate "${key}" request failed (${message}), using fallback: ${String(fallback)}`);
|
|
164
|
+
return fallback;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
// ─────────────────────────────────────────────
|
|
168
|
+
// gateDetails() — Get full gate result with metadata
|
|
169
|
+
// ─────────────────────────────────────────────
|
|
170
|
+
/**
|
|
171
|
+
* Like `gate()` but returns the full GateResult object
|
|
172
|
+
* including whether the value was cached.
|
|
173
|
+
*/
|
|
174
|
+
async gateDetails(key, options = {}) {
|
|
175
|
+
var _a, _b;
|
|
176
|
+
const fallback = (_a = options.fallback) !== null && _a !== void 0 ? _a : false;
|
|
177
|
+
const ttl = (_b = options.cacheTtl) !== null && _b !== void 0 ? _b : DEFAULT_GATE_TTL;
|
|
178
|
+
const now = new Date().toISOString();
|
|
179
|
+
if (this.silent) {
|
|
180
|
+
return { key, enabled: fallback, cached: false, fetchedAt: now };
|
|
181
|
+
}
|
|
182
|
+
const cachedVal = this.gateCache.get(key);
|
|
183
|
+
if (cachedVal !== null) {
|
|
184
|
+
return { key, enabled: cachedVal, cached: true, fetchedAt: now };
|
|
185
|
+
}
|
|
186
|
+
try {
|
|
187
|
+
const res = await this.fetchWithTimeout(`/v1/gates/${encodeURIComponent(key)}`);
|
|
188
|
+
if (!res.ok) {
|
|
189
|
+
return { key, enabled: fallback, cached: false, fetchedAt: now };
|
|
190
|
+
}
|
|
191
|
+
const data = await res.json();
|
|
192
|
+
this.gateCache.set(key, data.enabled, ttl);
|
|
193
|
+
return data;
|
|
194
|
+
}
|
|
195
|
+
catch (_c) {
|
|
196
|
+
return { key, enabled: fallback, cached: false, fetchedAt: now };
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
// ─────────────────────────────────────────────
|
|
200
|
+
// clearGateCache() — Force fresh gate fetch
|
|
201
|
+
// ─────────────────────────────────────────────
|
|
202
|
+
/**
|
|
203
|
+
* Clears the in-memory gate cache.
|
|
204
|
+
* Useful if you need to force a fresh fetch.
|
|
205
|
+
*/
|
|
206
|
+
clearGateCache(key) {
|
|
207
|
+
if (key) {
|
|
208
|
+
this.gateCache.delete(key);
|
|
209
|
+
}
|
|
210
|
+
else {
|
|
211
|
+
this.gateCache.clear();
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
// ─────────────────────────────────────────────
|
|
215
|
+
// Private helpers
|
|
216
|
+
// ─────────────────────────────────────────────
|
|
217
|
+
async fetchWithTimeout(path, init = {}) {
|
|
218
|
+
const controller = new AbortController();
|
|
219
|
+
const timer = setTimeout(() => controller.abort(), this.timeout);
|
|
220
|
+
try {
|
|
221
|
+
return await fetch(`${this.baseUrl}${path}`, {
|
|
222
|
+
...init,
|
|
223
|
+
signal: controller.signal,
|
|
224
|
+
headers: {
|
|
225
|
+
'Content-Type': 'application/json',
|
|
226
|
+
'Authorization': `Bearer ${this.apiKey}`,
|
|
227
|
+
'X-SDK-Version': '1.0.0',
|
|
228
|
+
'X-SDK-Language': 'node',
|
|
229
|
+
...init.headers,
|
|
230
|
+
},
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
finally {
|
|
234
|
+
clearTimeout(timer);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
exports.Notiformer = Notiformer;
|
|
239
|
+
// ─────────────────────────────────────────────
|
|
240
|
+
// Named export for convenience
|
|
241
|
+
// ─────────────────────────────────────────────
|
|
242
|
+
exports.default = Notiformer;
|
|
243
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/logger.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { LogLevel } from './types';
|
|
2
|
+
export declare class Logger {
|
|
3
|
+
private level;
|
|
4
|
+
constructor(level?: LogLevel);
|
|
5
|
+
debug(msg: string, ...args: unknown[]): void;
|
|
6
|
+
info(msg: string, ...args: unknown[]): void;
|
|
7
|
+
warn(msg: string, ...args: unknown[]): void;
|
|
8
|
+
error(msg: string, ...args: unknown[]): void;
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=logger.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAUxC,qBAAa,MAAM;IACjB,OAAO,CAAC,KAAK,CAAS;gBAEV,KAAK,GAAE,QAAkB;IAIrC,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI;IAI5C,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI;IAI3C,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI;IAI3C,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI;CAG7C"}
|
package/dist/logger.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Logger = void 0;
|
|
4
|
+
const LEVELS = {
|
|
5
|
+
debug: 0,
|
|
6
|
+
info: 1,
|
|
7
|
+
warn: 2,
|
|
8
|
+
error: 3,
|
|
9
|
+
none: 4,
|
|
10
|
+
};
|
|
11
|
+
class Logger {
|
|
12
|
+
constructor(level = 'error') {
|
|
13
|
+
this.level = LEVELS[level];
|
|
14
|
+
}
|
|
15
|
+
debug(msg, ...args) {
|
|
16
|
+
if (this.level <= LEVELS.debug)
|
|
17
|
+
console.debug(`[notiformer] ${msg}`, ...args);
|
|
18
|
+
}
|
|
19
|
+
info(msg, ...args) {
|
|
20
|
+
if (this.level <= LEVELS.info)
|
|
21
|
+
console.info(`[notiformer] ${msg}`, ...args);
|
|
22
|
+
}
|
|
23
|
+
warn(msg, ...args) {
|
|
24
|
+
if (this.level <= LEVELS.warn)
|
|
25
|
+
console.warn(`[notiformer] ${msg}`, ...args);
|
|
26
|
+
}
|
|
27
|
+
error(msg, ...args) {
|
|
28
|
+
if (this.level <= LEVELS.error)
|
|
29
|
+
console.error(`[notiformer] ${msg}`, ...args);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
exports.Logger = Logger;
|
|
33
|
+
//# sourceMappingURL=logger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.js","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":";;;AAEA,MAAM,MAAM,GAA6B;IACvC,KAAK,EAAE,CAAC;IACR,IAAI,EAAE,CAAC;IACP,IAAI,EAAE,CAAC;IACP,KAAK,EAAE,CAAC;IACR,IAAI,EAAE,CAAC;CACR,CAAC;AAEF,MAAa,MAAM;IAGjB,YAAY,QAAkB,OAAO;QACnC,IAAI,CAAC,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;IAC7B,CAAC;IAED,KAAK,CAAC,GAAW,EAAE,GAAG,IAAe;QACnC,IAAI,IAAI,CAAC,KAAK,IAAI,MAAM,CAAC,KAAK;YAAE,OAAO,CAAC,KAAK,CAAC,gBAAgB,GAAG,EAAE,EAAE,GAAG,IAAI,CAAC,CAAC;IAChF,CAAC;IAED,IAAI,CAAC,GAAW,EAAE,GAAG,IAAe;QAClC,IAAI,IAAI,CAAC,KAAK,IAAI,MAAM,CAAC,IAAI;YAAE,OAAO,CAAC,IAAI,CAAC,gBAAgB,GAAG,EAAE,EAAE,GAAG,IAAI,CAAC,CAAC;IAC9E,CAAC;IAED,IAAI,CAAC,GAAW,EAAE,GAAG,IAAe;QAClC,IAAI,IAAI,CAAC,KAAK,IAAI,MAAM,CAAC,IAAI;YAAE,OAAO,CAAC,IAAI,CAAC,gBAAgB,GAAG,EAAE,EAAE,GAAG,IAAI,CAAC,CAAC;IAC9E,CAAC;IAED,KAAK,CAAC,GAAW,EAAE,GAAG,IAAe;QACnC,IAAI,IAAI,CAAC,KAAK,IAAI,MAAM,CAAC,KAAK;YAAE,OAAO,CAAC,KAAK,CAAC,gBAAgB,GAAG,EAAE,EAAE,GAAG,IAAI,CAAC,CAAC;IAChF,CAAC;CACF;AAtBD,wBAsBC"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
export interface NotiformerConfig {
|
|
2
|
+
/**
|
|
3
|
+
* Your project API key from the Notiformer dashboard.
|
|
4
|
+
* Get it at: https://app.notiformer.com/projects
|
|
5
|
+
*/
|
|
6
|
+
apiKey: string;
|
|
7
|
+
/**
|
|
8
|
+
* Base URL of the Notiformer API.
|
|
9
|
+
* @default 'https://api.notiformer.com'
|
|
10
|
+
*/
|
|
11
|
+
baseUrl?: string;
|
|
12
|
+
/**
|
|
13
|
+
* Request timeout in milliseconds.
|
|
14
|
+
* @default 5000
|
|
15
|
+
*/
|
|
16
|
+
timeout?: number;
|
|
17
|
+
/**
|
|
18
|
+
* If true, SDK calls are no-ops. Useful for local development.
|
|
19
|
+
* @default false
|
|
20
|
+
*/
|
|
21
|
+
silent?: boolean;
|
|
22
|
+
/**
|
|
23
|
+
* Log level for SDK internals.
|
|
24
|
+
* @default 'error'
|
|
25
|
+
*/
|
|
26
|
+
logLevel?: 'debug' | 'info' | 'warn' | 'error' | 'none';
|
|
27
|
+
}
|
|
28
|
+
export interface EventPayload {
|
|
29
|
+
/**
|
|
30
|
+
* The channel to send this event to.
|
|
31
|
+
* Channels group related events (e.g. 'payments', 'auth', 'errors').
|
|
32
|
+
* Created automatically on first use.
|
|
33
|
+
*/
|
|
34
|
+
channel: string;
|
|
35
|
+
/**
|
|
36
|
+
* Machine-readable event name (e.g. 'payment_success', 'user_signup').
|
|
37
|
+
*/
|
|
38
|
+
event: string;
|
|
39
|
+
/**
|
|
40
|
+
* Human-readable description shown in the dashboard feed.
|
|
41
|
+
*/
|
|
42
|
+
description?: string;
|
|
43
|
+
/**
|
|
44
|
+
* An emoji icon shown next to the event in the feed.
|
|
45
|
+
* @example '💳' | '🚨' | '✅'
|
|
46
|
+
*/
|
|
47
|
+
icon?: string;
|
|
48
|
+
/**
|
|
49
|
+
* Optional key-value tags for filtering and search.
|
|
50
|
+
* @example { userId: 'usr_123', plan: 'pro', amount: 49 }
|
|
51
|
+
*/
|
|
52
|
+
tags?: Record<string, string | number | boolean>;
|
|
53
|
+
/**
|
|
54
|
+
* A scalar value to display alongside the event.
|
|
55
|
+
* @example '$49.00' | 200 | '98ms'
|
|
56
|
+
*/
|
|
57
|
+
value?: string | number;
|
|
58
|
+
/**
|
|
59
|
+
* If true, triggers configured notification channels (push, email, etc).
|
|
60
|
+
* If false, the event is logged silently (analytics only).
|
|
61
|
+
* @default true
|
|
62
|
+
*/
|
|
63
|
+
notify?: boolean;
|
|
64
|
+
/**
|
|
65
|
+
* Used for deduplication. Events with the same groupId within
|
|
66
|
+
* a short window are rate-limited.
|
|
67
|
+
*/
|
|
68
|
+
groupId?: string;
|
|
69
|
+
}
|
|
70
|
+
export interface EventResponse {
|
|
71
|
+
id: string;
|
|
72
|
+
createdAt: string;
|
|
73
|
+
}
|
|
74
|
+
export interface GateOptions {
|
|
75
|
+
/**
|
|
76
|
+
* Fallback value returned if the network request fails
|
|
77
|
+
* or times out.
|
|
78
|
+
* @default false
|
|
79
|
+
*/
|
|
80
|
+
fallback?: boolean;
|
|
81
|
+
/**
|
|
82
|
+
* Cache TTL in seconds. Set to 0 to disable caching.
|
|
83
|
+
* Recommended: 30 (default) to reduce API calls.
|
|
84
|
+
* @default 30
|
|
85
|
+
*/
|
|
86
|
+
cacheTtl?: number;
|
|
87
|
+
}
|
|
88
|
+
export interface GateResult {
|
|
89
|
+
key: string;
|
|
90
|
+
enabled: boolean;
|
|
91
|
+
cached: boolean;
|
|
92
|
+
fetchedAt: string;
|
|
93
|
+
}
|
|
94
|
+
export interface ApiErrorResponse {
|
|
95
|
+
error: string;
|
|
96
|
+
code: string;
|
|
97
|
+
}
|
|
98
|
+
export type LogLevel = 'debug' | 'info' | 'warn' | 'error' | 'none';
|
|
99
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,gBAAgB;IAC/B;;;OAGG;IACH,MAAM,EAAE,MAAM,CAAC;IAEf;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB;;;OAGG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IAEjB;;;OAGG;IACH,QAAQ,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,CAAC;CACzD;AAMD,MAAM,WAAW,YAAY;IAC3B;;;;OAIG;IACH,OAAO,EAAE,MAAM,CAAC;IAEhB;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;IAEd;;OAEG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,CAAC;IAEjD;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAExB;;;;OAIG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IAEjB;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;CACnB;AAMD,MAAM,WAAW,WAAW;IAC1B;;;;OAIG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IAEnB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,UAAU;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,OAAO,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;CACnB;AAMD,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,MAAM,QAAQ,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,CAAC"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":";AAAA,gDAAgD;AAChD,iCAAiC;AACjC,gDAAgD"}
|
package/package.json
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "notiformer",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Real-time alerts, notifications and feature gates for your backend code",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"dist"
|
|
9
|
+
],
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "tsc",
|
|
12
|
+
"dev": "tsc --watch",
|
|
13
|
+
"prepublishOnly": "npm run build"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"notifications",
|
|
17
|
+
"alerts",
|
|
18
|
+
"monitoring",
|
|
19
|
+
"feature-flags",
|
|
20
|
+
"feature-gates",
|
|
21
|
+
"events",
|
|
22
|
+
"saas",
|
|
23
|
+
"developer-tools"
|
|
24
|
+
],
|
|
25
|
+
"author": "Notiformer",
|
|
26
|
+
"license": "MIT",
|
|
27
|
+
"repository": {
|
|
28
|
+
"type": "git",
|
|
29
|
+
"url": "https://github.com/notiformer/notiformer-node"
|
|
30
|
+
},
|
|
31
|
+
"homepage": "https://notiformer.com",
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"typescript": "^5.3.3"
|
|
34
|
+
},
|
|
35
|
+
"engines": {
|
|
36
|
+
"node": ">=16.0.0"
|
|
37
|
+
}
|
|
38
|
+
}
|