notiformer 1.0.0 → 1.0.2
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 +55 -120
- package/dist/index.d.ts +24 -49
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +118 -152
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +35 -60
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
# notiformer
|
|
2
2
|
|
|
3
|
-
Real-time alerts
|
|
3
|
+
Real-time alerts and feature gates for your backend code.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Get notified instantly via push, email and more whenever something important happens in your app.
|
|
6
|
+
|
|
7
|
+
> ⚠️ **Server-side only.** Do not use in browser/frontend code — your API key would be exposed publicly. Use in Node.js, Express, Next.js API routes, serverless functions, etc.
|
|
6
8
|
|
|
7
9
|
---
|
|
8
10
|
|
|
@@ -19,95 +21,69 @@ npm install notiformer
|
|
|
19
21
|
```ts
|
|
20
22
|
import { Notiformer } from 'notiformer';
|
|
21
23
|
|
|
24
|
+
// Get your API key at https://app.notiformer.com/projects
|
|
22
25
|
const n = new Notiformer({
|
|
23
|
-
apiKey: '
|
|
26
|
+
apiKey: 'ntf_live_test', // ← replace this with your real key
|
|
24
27
|
});
|
|
25
28
|
|
|
26
|
-
// Send an event + notification
|
|
27
29
|
await n.event({
|
|
28
30
|
channel: 'payments',
|
|
29
31
|
event: 'payment_success',
|
|
30
|
-
description:
|
|
32
|
+
description: '$49.00 — john@example.com',
|
|
31
33
|
icon: '💳',
|
|
32
|
-
tags: { userId: 'usr_123', plan: 'pro' },
|
|
33
34
|
notify: true,
|
|
34
35
|
});
|
|
35
36
|
```
|
|
36
37
|
|
|
38
|
+
> Running with `apiKey: 'ntf_live_test'` will print a setup message in your console explaining how to get your real key. No events will be sent until you replace it.
|
|
39
|
+
|
|
37
40
|
---
|
|
38
41
|
|
|
39
|
-
##
|
|
42
|
+
## Get your API key
|
|
43
|
+
|
|
44
|
+
1. Go to [app.notiformer.com/projects](https://app.notiformer.com/projects)
|
|
45
|
+
2. Create or open a project
|
|
46
|
+
3. Copy the API key
|
|
47
|
+
4. Replace `'ntf_live_test'` in your code
|
|
40
48
|
|
|
41
|
-
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## Events
|
|
42
52
|
|
|
43
53
|
```ts
|
|
44
54
|
await n.event({
|
|
45
|
-
channel: 'auth',
|
|
46
|
-
event: 'user_signup',
|
|
55
|
+
channel: 'auth', // groups related events (auto-created on first use)
|
|
56
|
+
event: 'user_signup', // machine-readable name
|
|
47
57
|
description: 'New signup via Google',
|
|
48
58
|
icon: '🎉',
|
|
49
59
|
|
|
50
|
-
//
|
|
51
|
-
|
|
52
|
-
value: '42nd user', // shown in the dashboard feed
|
|
60
|
+
tags: { method: 'google', plan: 'free' }, // optional metadata
|
|
61
|
+
value: '42nd user', // optional value shown in the feed
|
|
53
62
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
notify: true,
|
|
57
|
-
|
|
58
|
-
// Rate limiting: events with the same groupId are deduplicated
|
|
59
|
-
groupId: 'signup-batch-today',
|
|
63
|
+
notify: true, // true → triggers push/email notifications
|
|
64
|
+
// false → silent log for analytics only
|
|
60
65
|
});
|
|
61
66
|
```
|
|
62
67
|
|
|
63
|
-
|
|
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.
|
|
68
|
+
`event()` **never throws** by default. If something goes wrong it logs a warning and returns `null` — a failed notification should never crash your app.
|
|
66
69
|
|
|
67
70
|
---
|
|
68
71
|
|
|
69
72
|
## Feature Gates
|
|
70
73
|
|
|
71
|
-
|
|
74
|
+
Toggle features remotely from your Notiformer dashboard without redeploying.
|
|
72
75
|
|
|
73
76
|
```ts
|
|
74
77
|
const isEnabled = await n.gate('new-checkout-flow');
|
|
75
78
|
|
|
76
79
|
if (isEnabled) {
|
|
77
|
-
// run new
|
|
80
|
+
// run new feature
|
|
78
81
|
} else {
|
|
79
|
-
// run
|
|
82
|
+
// run old feature
|
|
80
83
|
}
|
|
81
84
|
```
|
|
82
85
|
|
|
83
|
-
Gates are cached
|
|
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
|
-
```
|
|
86
|
+
Gates are cached for 30 seconds by default to minimise API calls.
|
|
111
87
|
|
|
112
88
|
---
|
|
113
89
|
|
|
@@ -115,17 +91,17 @@ n.clearGateCache(); // clear all
|
|
|
115
91
|
|
|
116
92
|
```ts
|
|
117
93
|
const n = new Notiformer({
|
|
118
|
-
apiKey: 'ntf_live_...',
|
|
94
|
+
apiKey: 'ntf_live_...', // required — from app.notiformer.com/projects
|
|
119
95
|
|
|
120
|
-
//
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
96
|
+
silent: false, // true = no calls made (useful for local dev)
|
|
97
|
+
throwOnError: false, // true = throws instead of returning null
|
|
98
|
+
onError: (err) => { // called on any failed call
|
|
99
|
+
console.error(err.message);
|
|
100
|
+
},
|
|
125
101
|
});
|
|
126
102
|
```
|
|
127
103
|
|
|
128
|
-
### Silence in development
|
|
104
|
+
### Silence in local development
|
|
129
105
|
|
|
130
106
|
```ts
|
|
131
107
|
const n = new Notiformer({
|
|
@@ -138,7 +114,7 @@ const n = new Notiformer({
|
|
|
138
114
|
|
|
139
115
|
## Common patterns
|
|
140
116
|
|
|
141
|
-
### Alert on errors
|
|
117
|
+
### Alert on unhandled errors
|
|
142
118
|
|
|
143
119
|
```ts
|
|
144
120
|
app.use(async (err, req, res, next) => {
|
|
@@ -147,94 +123,54 @@ app.use(async (err, req, res, next) => {
|
|
|
147
123
|
event: 'unhandled_error',
|
|
148
124
|
description: err.message,
|
|
149
125
|
icon: '🔴',
|
|
150
|
-
tags: {
|
|
151
|
-
path: req.path,
|
|
152
|
-
method: req.method,
|
|
153
|
-
status: 500,
|
|
154
|
-
},
|
|
126
|
+
tags: { path: req.path, status: 500 },
|
|
155
127
|
notify: true,
|
|
156
128
|
});
|
|
157
129
|
res.status(500).json({ error: 'Internal server error' });
|
|
158
130
|
});
|
|
159
131
|
```
|
|
160
132
|
|
|
161
|
-
### Track
|
|
133
|
+
### Track a payment
|
|
162
134
|
|
|
163
135
|
```ts
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
notify: true,
|
|
173
|
-
});
|
|
174
|
-
}
|
|
136
|
+
await n.event({
|
|
137
|
+
channel: 'payments',
|
|
138
|
+
event: 'payment_success',
|
|
139
|
+
description: `${amount} — ${user.email}`,
|
|
140
|
+
icon: '💳',
|
|
141
|
+
value: amount,
|
|
142
|
+
notify: true,
|
|
143
|
+
});
|
|
175
144
|
```
|
|
176
145
|
|
|
177
|
-
### Feature gate
|
|
146
|
+
### Feature gate
|
|
178
147
|
|
|
179
148
|
```ts
|
|
180
149
|
app.post('/checkout', async (req, res) => {
|
|
181
150
|
const useNewFlow = await n.gate('new-checkout-flow');
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
}
|
|
186
|
-
return legacyCheckoutHandler(req, res);
|
|
151
|
+
return useNewFlow
|
|
152
|
+
? newCheckoutHandler(req, res)
|
|
153
|
+
: legacyCheckoutHandler(req, res);
|
|
187
154
|
});
|
|
188
155
|
```
|
|
189
156
|
|
|
190
157
|
### Silent analytics (no notification)
|
|
191
158
|
|
|
192
159
|
```ts
|
|
193
|
-
// Log for analytics without pushing a notification
|
|
194
160
|
await n.event({
|
|
195
161
|
channel: 'monitoring',
|
|
196
162
|
event: 'page_view',
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
notify: false, // ← silent
|
|
163
|
+
tags: { path: req.path },
|
|
164
|
+
notify: false, // ← no notification, just logged
|
|
200
165
|
});
|
|
201
166
|
```
|
|
202
167
|
|
|
203
168
|
---
|
|
204
169
|
|
|
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
170
|
## Requirements
|
|
222
171
|
|
|
223
|
-
- Node.js
|
|
224
|
-
-
|
|
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
|
-
```
|
|
172
|
+
- Node.js 18+ (uses native `fetch`)
|
|
173
|
+
- For Node 16: install `node-fetch` and polyfill `global.fetch`
|
|
238
174
|
|
|
239
175
|
---
|
|
240
176
|
|
|
@@ -242,4 +178,3 @@ import { Notiformer } from 'notiformer';
|
|
|
242
178
|
|
|
243
179
|
- Dashboard: [app.notiformer.com](https://app.notiformer.com)
|
|
244
180
|
- Docs: [docs.notiformer.com](https://docs.notiformer.com)
|
|
245
|
-
- Support: support@notiformer.com
|
package/dist/index.d.ts
CHANGED
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Notiformer
|
|
2
|
+
* Notiformer — Real-time alerts and feature gates for your code.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* ⚠️ SERVER-SIDE ONLY. Do not use in browser/frontend code.
|
|
5
|
+
* Your API key would be exposed publicly.
|
|
6
|
+
* Use in: Node.js, Express, Next.js API routes, serverless functions.
|
|
5
7
|
*
|
|
6
8
|
* @example
|
|
7
|
-
* ```ts
|
|
8
9
|
* import { Notiformer } from 'notiformer';
|
|
9
10
|
*
|
|
10
|
-
* const n = new Notiformer({
|
|
11
|
+
* const n = new Notiformer({
|
|
12
|
+
* apiKey: 'ntf_live_xxxxxxxxxxxxxxxx', // from app.notiformer.com/projects
|
|
13
|
+
* });
|
|
11
14
|
*
|
|
12
|
-
* // Send a notification event
|
|
13
15
|
* await n.event({
|
|
14
16
|
* channel: 'payments',
|
|
15
17
|
* event: 'payment_success',
|
|
@@ -17,70 +19,43 @@
|
|
|
17
19
|
* icon: '💳',
|
|
18
20
|
* notify: true,
|
|
19
21
|
* });
|
|
20
|
-
*
|
|
21
|
-
* // Check a feature gate
|
|
22
|
-
* const isEnabled = await n.gate('new-checkout-flow');
|
|
23
|
-
* if (isEnabled) { ... }
|
|
24
|
-
* ```
|
|
25
22
|
*/
|
|
26
|
-
import type { NotiformerConfig, EventPayload, EventResponse, GateOptions, GateResult } from
|
|
23
|
+
import type { NotiformerConfig, EventPayload, EventResponse, GateOptions, GateResult } from "./types";
|
|
27
24
|
export type { NotiformerConfig, EventPayload, EventResponse, GateOptions, GateResult, };
|
|
28
25
|
export declare class Notiformer {
|
|
29
26
|
private readonly apiKey;
|
|
30
27
|
private readonly baseUrl;
|
|
31
28
|
private readonly timeout;
|
|
32
29
|
private readonly silent;
|
|
30
|
+
private readonly throwOnError;
|
|
31
|
+
private readonly onError?;
|
|
33
32
|
private readonly logger;
|
|
34
33
|
private readonly gateCache;
|
|
34
|
+
private readonly isPlaceholder;
|
|
35
35
|
constructor(config: NotiformerConfig);
|
|
36
36
|
/**
|
|
37
|
-
* Send an event
|
|
38
|
-
* dashboard settings.
|
|
37
|
+
* Send an event and optionally trigger a notification.
|
|
39
38
|
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
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
|
-
* ```
|
|
39
|
+
* Safe to use anywhere in your code — never throws by default.
|
|
40
|
+
* Returns null if the call fails (and calls onError if configured).
|
|
54
41
|
*/
|
|
55
42
|
event(payload: EventPayload): Promise<EventResponse | null>;
|
|
56
43
|
/**
|
|
57
|
-
* Check whether a feature gate is enabled.
|
|
58
|
-
*
|
|
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
|
|
44
|
+
* Check whether a feature gate is enabled.
|
|
45
|
+
* Cached locally for 30 seconds by default.
|
|
63
46
|
*
|
|
64
|
-
*
|
|
65
|
-
* ```ts
|
|
66
|
-
* const isEnabled = await n.gate('new-checkout-flow');
|
|
67
|
-
* if (isEnabled) {
|
|
68
|
-
* // use new checkout
|
|
69
|
-
* }
|
|
70
|
-
* ```
|
|
47
|
+
* Returns fallback (false) if the call fails.
|
|
71
48
|
*/
|
|
72
49
|
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
|
-
*/
|
|
50
|
+
/** Full gate result with metadata */
|
|
77
51
|
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
|
-
*/
|
|
52
|
+
/** Clear cached gate values */
|
|
82
53
|
clearGateCache(key?: string): void;
|
|
83
|
-
private
|
|
54
|
+
private post;
|
|
55
|
+
private get;
|
|
56
|
+
private request;
|
|
57
|
+
private fail;
|
|
58
|
+
private friendlyNetworkError;
|
|
84
59
|
}
|
|
85
60
|
export default Notiformer;
|
|
86
61
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,KAAK,EACV,gBAAgB,EAChB,YAAY,EACZ,aAAa,EACb,WAAW,EACX,UAAU,EACX,MAAM,SAAS,CAAC;AAIjB,YAAY,EACV,gBAAgB,EAChB,YAAY,EACZ,aAAa,EACb,WAAW,EACX,UAAU,GACX,CAAC;AAQF,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,YAAY,CAAU;IACvC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,CAA8B;IACvD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAY;IACtC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAU;gBAE5B,MAAM,EAAE,gBAAgB;IAwCpC;;;;;OAKG;IACG,KAAK,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC;IAkDjE;;;;;OAKG;IACG,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,GAAE,WAAgB,GAAG,OAAO,CAAC,OAAO,CAAC;IAsBpE,qCAAqC;IAC/B,WAAW,CACf,GAAG,EAAE,MAAM,EACX,OAAO,GAAE,WAAgB,GACxB,OAAO,CAAC,UAAU,CAAC;IAKtB,+BAA+B;IAC/B,cAAc,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI;YAQpB,IAAI;YAOJ,GAAG;YAIH,OAAO;IAmBrB,OAAO,CAAC,IAAI;IAOZ,OAAO,CAAC,oBAAoB;CAuB7B;AAED,eAAe,UAAU,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/**
|
|
3
|
-
* Notiformer
|
|
3
|
+
* Notiformer — Real-time alerts and feature gates for your code.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
5
|
+
* ⚠️ SERVER-SIDE ONLY. Do not use in browser/frontend code.
|
|
6
|
+
* Your API key would be exposed publicly.
|
|
7
|
+
* Use in: Node.js, Express, Next.js API routes, serverless functions.
|
|
6
8
|
*
|
|
7
9
|
* @example
|
|
8
|
-
* ```ts
|
|
9
10
|
* import { Notiformer } from 'notiformer';
|
|
10
11
|
*
|
|
11
|
-
* const n = new Notiformer({
|
|
12
|
+
* const n = new Notiformer({
|
|
13
|
+
* apiKey: 'ntf_live_xxxxxxxxxxxxxxxx', // from app.notiformer.com/projects
|
|
14
|
+
* });
|
|
12
15
|
*
|
|
13
|
-
* // Send a notification event
|
|
14
16
|
* await n.event({
|
|
15
17
|
* channel: 'payments',
|
|
16
18
|
* event: 'payment_success',
|
|
@@ -18,71 +20,74 @@
|
|
|
18
20
|
* icon: '💳',
|
|
19
21
|
* notify: true,
|
|
20
22
|
* });
|
|
21
|
-
*
|
|
22
|
-
* // Check a feature gate
|
|
23
|
-
* const isEnabled = await n.gate('new-checkout-flow');
|
|
24
|
-
* if (isEnabled) { ... }
|
|
25
|
-
* ```
|
|
26
23
|
*/
|
|
27
24
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
28
25
|
exports.Notiformer = void 0;
|
|
29
26
|
const logger_1 = require("./logger");
|
|
30
27
|
const cache_1 = require("./cache");
|
|
31
|
-
|
|
32
|
-
const
|
|
28
|
+
// The placeholder key shown in docs — triggers a friendly setup message
|
|
29
|
+
const PLACEHOLDER_KEY = "ntf_live_test";
|
|
30
|
+
const API_URL = "https://api.notiformer.com";
|
|
31
|
+
const DEFAULT_TIMEOUT = 8000;
|
|
33
32
|
const DEFAULT_GATE_TTL = 30;
|
|
34
33
|
class Notiformer {
|
|
35
34
|
constructor(config) {
|
|
36
|
-
var _a, _b, _c
|
|
35
|
+
var _a, _b, _c;
|
|
37
36
|
if (!config.apiKey) {
|
|
38
|
-
throw new Error(
|
|
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.');
|
|
37
|
+
throw new Error("[notiformer] apiKey is required.\n" +
|
|
38
|
+
"→ Get yours at https://app.notiformer.com/projects");
|
|
42
39
|
}
|
|
40
|
+
this.isPlaceholder = config.apiKey === PLACEHOLDER_KEY;
|
|
43
41
|
this.apiKey = config.apiKey;
|
|
44
|
-
this.baseUrl = ((_a = config.
|
|
45
|
-
this.timeout =
|
|
46
|
-
this.silent = (
|
|
47
|
-
this.
|
|
42
|
+
this.baseUrl = ((_a = config._baseUrl) !== null && _a !== void 0 ? _a : API_URL).replace(/\/$/, "");
|
|
43
|
+
this.timeout = DEFAULT_TIMEOUT;
|
|
44
|
+
this.silent = (_b = config.silent) !== null && _b !== void 0 ? _b : false;
|
|
45
|
+
this.throwOnError = (_c = config.throwOnError) !== null && _c !== void 0 ? _c : false;
|
|
46
|
+
this.onError = config.onError;
|
|
47
|
+
this.logger = new logger_1.Logger("warn");
|
|
48
48
|
this.gateCache = new cache_1.GateCache();
|
|
49
|
-
this.
|
|
49
|
+
if (this.isPlaceholder) {
|
|
50
|
+
console.warn("\n" +
|
|
51
|
+
"┌─────────────────────────────────────────────────────┐\n" +
|
|
52
|
+
"│ notiformer — setup required │\n" +
|
|
53
|
+
"├─────────────────────────────────────────────────────┤\n" +
|
|
54
|
+
'│ You are using the example API key "ntf_live_test". │\n' +
|
|
55
|
+
"│ Events will not be sent until you use a real key. │\n" +
|
|
56
|
+
"│ │\n" +
|
|
57
|
+
"│ 1. Go to https://app.notiformer.com/projects │\n" +
|
|
58
|
+
"│ 2. Create or open a project │\n" +
|
|
59
|
+
"│ 3. Copy your API key │\n" +
|
|
60
|
+
'│ 4. Replace "ntf_live_test" with your real key │\n' +
|
|
61
|
+
"└─────────────────────────────────────────────────────┘\n");
|
|
62
|
+
}
|
|
50
63
|
}
|
|
51
|
-
//
|
|
52
|
-
// event()
|
|
53
|
-
//
|
|
64
|
+
// ─────────────────────────────────────────────────────────────
|
|
65
|
+
// event()
|
|
66
|
+
// ─────────────────────────────────────────────────────────────
|
|
54
67
|
/**
|
|
55
|
-
* Send an event
|
|
56
|
-
* dashboard settings.
|
|
57
|
-
*
|
|
58
|
-
* @param payload - The event data
|
|
59
|
-
* @returns The created event ID and timestamp, or null if silent mode
|
|
68
|
+
* Send an event and optionally trigger a notification.
|
|
60
69
|
*
|
|
61
|
-
*
|
|
62
|
-
*
|
|
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
|
-
* ```
|
|
70
|
+
* Safe to use anywhere in your code — never throws by default.
|
|
71
|
+
* Returns null if the call fails (and calls onError if configured).
|
|
72
72
|
*/
|
|
73
73
|
async event(payload) {
|
|
74
|
-
var _a;
|
|
74
|
+
var _a, _b;
|
|
75
|
+
if (!payload.channel)
|
|
76
|
+
throw new Error("[notiformer] event.channel is required.");
|
|
77
|
+
if (!payload.event)
|
|
78
|
+
throw new Error("[notiformer] event.event is required.");
|
|
79
|
+
// Placeholder key — log a clear message, do not attempt the call
|
|
80
|
+
if (this.isPlaceholder) {
|
|
81
|
+
console.warn('[notiformer] Event not sent — you are using the example key "ntf_live_test".\n' +
|
|
82
|
+
"→ Replace it with your real API key from https://app.notiformer.com/projects");
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
75
85
|
if (this.silent) {
|
|
76
|
-
this.logger.info(
|
|
86
|
+
this.logger.info("Silent mode: event suppressed.");
|
|
77
87
|
return null;
|
|
78
88
|
}
|
|
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
89
|
try {
|
|
85
|
-
const
|
|
90
|
+
const res = await this.post("/v1/events", {
|
|
86
91
|
channel: payload.channel,
|
|
87
92
|
event: payload.event,
|
|
88
93
|
description: payload.description,
|
|
@@ -90,131 +95,74 @@ class Notiformer {
|
|
|
90
95
|
tags: payload.tags,
|
|
91
96
|
value: payload.value,
|
|
92
97
|
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
98
|
});
|
|
99
99
|
if (!res.ok) {
|
|
100
|
-
const
|
|
101
|
-
|
|
102
|
-
|
|
100
|
+
const body = await res
|
|
101
|
+
.json()
|
|
102
|
+
.catch(() => ({ error: `HTTP ${res.status}` }));
|
|
103
|
+
return this.fail(new Error(`[notiformer] ${(_b = body.error) !== null && _b !== void 0 ? _b : res.statusText}`));
|
|
103
104
|
}
|
|
104
|
-
|
|
105
|
-
this.logger.debug('Event sent', data);
|
|
106
|
-
return data;
|
|
105
|
+
return (await res.json());
|
|
107
106
|
}
|
|
108
107
|
catch (err) {
|
|
109
|
-
|
|
110
|
-
this.logger.error('Event request failed', message);
|
|
111
|
-
// Never throw — a failed notification should not crash your app
|
|
112
|
-
return null;
|
|
108
|
+
return this.fail(this.friendlyNetworkError(err));
|
|
113
109
|
}
|
|
114
110
|
}
|
|
115
|
-
//
|
|
116
|
-
// gate()
|
|
117
|
-
//
|
|
111
|
+
// ─────────────────────────────────────────────────────────────
|
|
112
|
+
// gate()
|
|
113
|
+
// ─────────────────────────────────────────────────────────────
|
|
118
114
|
/**
|
|
119
|
-
* Check whether a feature gate is enabled.
|
|
120
|
-
*
|
|
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
|
|
115
|
+
* Check whether a feature gate is enabled.
|
|
116
|
+
* Cached locally for 30 seconds by default.
|
|
125
117
|
*
|
|
126
|
-
*
|
|
127
|
-
* ```ts
|
|
128
|
-
* const isEnabled = await n.gate('new-checkout-flow');
|
|
129
|
-
* if (isEnabled) {
|
|
130
|
-
* // use new checkout
|
|
131
|
-
* }
|
|
132
|
-
* ```
|
|
118
|
+
* Returns fallback (false) if the call fails.
|
|
133
119
|
*/
|
|
134
120
|
async gate(key, options = {}) {
|
|
135
121
|
var _a, _b;
|
|
136
122
|
if (!key)
|
|
137
|
-
throw new Error(
|
|
123
|
+
throw new Error("[notiformer] gate key is required.");
|
|
138
124
|
const fallback = (_a = options.fallback) !== null && _a !== void 0 ? _a : false;
|
|
139
125
|
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)})`);
|
|
126
|
+
if (this.isPlaceholder || this.silent)
|
|
142
127
|
return fallback;
|
|
143
|
-
}
|
|
144
|
-
// Check cache first
|
|
145
128
|
const cached = this.gateCache.get(key);
|
|
146
|
-
if (cached !== null)
|
|
147
|
-
this.logger.debug(`Gate "${key}" from cache: ${String(cached)}`);
|
|
129
|
+
if (cached !== null)
|
|
148
130
|
return cached;
|
|
149
|
-
}
|
|
150
131
|
try {
|
|
151
|
-
const res = await this.
|
|
152
|
-
if (!res.ok)
|
|
153
|
-
this.logger.warn(`Gate "${key}" fetch failed (${res.status}), using fallback: ${String(fallback)}`);
|
|
132
|
+
const res = await this.get(`/v1/gates/${encodeURIComponent(key)}`);
|
|
133
|
+
if (!res.ok)
|
|
154
134
|
return fallback;
|
|
155
|
-
|
|
156
|
-
const data = await res.json();
|
|
135
|
+
const data = (await res.json());
|
|
157
136
|
this.gateCache.set(key, data.enabled, ttl);
|
|
158
|
-
this.logger.debug(`Gate "${key}": ${String(data.enabled)}`);
|
|
159
137
|
return data.enabled;
|
|
160
138
|
}
|
|
161
|
-
catch (
|
|
162
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
163
|
-
this.logger.warn(`Gate "${key}" request failed (${message}), using fallback: ${String(fallback)}`);
|
|
139
|
+
catch (_c) {
|
|
164
140
|
return fallback;
|
|
165
141
|
}
|
|
166
142
|
}
|
|
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
|
-
*/
|
|
143
|
+
/** Full gate result with metadata */
|
|
174
144
|
async gateDetails(key, options = {}) {
|
|
175
|
-
|
|
176
|
-
|
|
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
|
-
}
|
|
145
|
+
const enabled = await this.gate(key, options);
|
|
146
|
+
return { key, enabled, cached: false, fetchedAt: new Date().toISOString() };
|
|
198
147
|
}
|
|
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
|
-
*/
|
|
148
|
+
/** Clear cached gate values */
|
|
206
149
|
clearGateCache(key) {
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
150
|
+
key ? this.gateCache.delete(key) : this.gateCache.clear();
|
|
151
|
+
}
|
|
152
|
+
// ─────────────────────────────────────────────────────────────
|
|
153
|
+
// Private
|
|
154
|
+
// ─────────────────────────────────────────────────────────────
|
|
155
|
+
async post(path, body) {
|
|
156
|
+
return this.request(path, {
|
|
157
|
+
method: "POST",
|
|
158
|
+
body: JSON.stringify(body),
|
|
159
|
+
});
|
|
213
160
|
}
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
async
|
|
161
|
+
async get(path) {
|
|
162
|
+
return this.request(path, { method: "GET" });
|
|
163
|
+
}
|
|
164
|
+
async request(path, init) {
|
|
165
|
+
var _a;
|
|
218
166
|
const controller = new AbortController();
|
|
219
167
|
const timer = setTimeout(() => controller.abort(), this.timeout);
|
|
220
168
|
try {
|
|
@@ -222,11 +170,10 @@ class Notiformer {
|
|
|
222
170
|
...init,
|
|
223
171
|
signal: controller.signal,
|
|
224
172
|
headers: {
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
...init.headers,
|
|
173
|
+
"Content-Type": "application/json",
|
|
174
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
175
|
+
"X-SDK-Version": "1.0.2",
|
|
176
|
+
...((_a = init.headers) !== null && _a !== void 0 ? _a : {}),
|
|
230
177
|
},
|
|
231
178
|
});
|
|
232
179
|
}
|
|
@@ -234,10 +181,29 @@ class Notiformer {
|
|
|
234
181
|
clearTimeout(timer);
|
|
235
182
|
}
|
|
236
183
|
}
|
|
184
|
+
fail(error) {
|
|
185
|
+
var _a;
|
|
186
|
+
this.logger.error(error.message);
|
|
187
|
+
(_a = this.onError) === null || _a === void 0 ? void 0 : _a.call(this, error);
|
|
188
|
+
if (this.throwOnError)
|
|
189
|
+
throw error;
|
|
190
|
+
return null;
|
|
191
|
+
}
|
|
192
|
+
friendlyNetworkError(err) {
|
|
193
|
+
const msg = err instanceof Error ? err.message.toLowerCase() : "";
|
|
194
|
+
if (msg.includes("aborted") || msg.includes("timeout")) {
|
|
195
|
+
return new Error(`[notiformer] Request timed out after ${this.timeout / 1000}s.\n` +
|
|
196
|
+
"Check your internet connection or try again later.");
|
|
197
|
+
}
|
|
198
|
+
if (msg.includes("failed to fetch") ||
|
|
199
|
+
msg.includes("enotfound") ||
|
|
200
|
+
msg.includes("network")) {
|
|
201
|
+
return new Error("[notiformer] Cannot reach the Notiformer API.\n" +
|
|
202
|
+
"Check your internet connection. If the problem persists, visit https://status.notiformer.com");
|
|
203
|
+
}
|
|
204
|
+
return err instanceof Error ? err : new Error(String(err));
|
|
205
|
+
}
|
|
237
206
|
}
|
|
238
207
|
exports.Notiformer = Notiformer;
|
|
239
|
-
// ─────────────────────────────────────────────
|
|
240
|
-
// Named export for convenience
|
|
241
|
-
// ─────────────────────────────────────────────
|
|
242
208
|
exports.default = Notiformer;
|
|
243
209
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
CHANGED
|
@@ -1,88 +1,67 @@
|
|
|
1
1
|
export interface NotiformerConfig {
|
|
2
2
|
/**
|
|
3
|
-
* Your
|
|
4
|
-
*
|
|
3
|
+
* Your Notiformer API key.
|
|
4
|
+
* Find it at: https://app.notiformer.com/projects
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* const n = new Notiformer({ apiKey: 'ntf_live_xxxxxxxxxxxxxxxx' })
|
|
5
8
|
*/
|
|
6
9
|
apiKey: string;
|
|
7
10
|
/**
|
|
8
|
-
*
|
|
9
|
-
*
|
|
11
|
+
* Silence all SDK calls. Useful for local development.
|
|
12
|
+
* No events will be sent, no network calls made.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* const n = new Notiformer({
|
|
16
|
+
* apiKey: process.env.NOTIFORMER_API_KEY,
|
|
17
|
+
* silent: process.env.NODE_ENV !== 'production',
|
|
18
|
+
* })
|
|
10
19
|
*/
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Request timeout in milliseconds.
|
|
14
|
-
* @default 5000
|
|
15
|
-
*/
|
|
16
|
-
timeout?: number;
|
|
20
|
+
silent?: boolean;
|
|
17
21
|
/**
|
|
18
|
-
*
|
|
19
|
-
*
|
|
22
|
+
* Called when an event() or gate() call fails.
|
|
23
|
+
* Use this to forward errors to your own logging system.
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* onError: (err) => console.error('[notiformer]', err.message)
|
|
20
27
|
*/
|
|
21
|
-
|
|
28
|
+
onError?: (error: Error) => void;
|
|
22
29
|
/**
|
|
23
|
-
*
|
|
24
|
-
*
|
|
30
|
+
* If true, throws on failure instead of returning null silently.
|
|
31
|
+
* Default: false — a failed notification should never crash your app.
|
|
25
32
|
*/
|
|
26
|
-
|
|
33
|
+
throwOnError?: boolean;
|
|
34
|
+
/** @internal — do not use in production */
|
|
35
|
+
_baseUrl?: string;
|
|
27
36
|
}
|
|
28
37
|
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
|
-
*/
|
|
38
|
+
/** Channel name. Groups related events. Created automatically. e.g. 'payments', 'errors' */
|
|
34
39
|
channel: string;
|
|
35
|
-
/**
|
|
36
|
-
* Machine-readable event name (e.g. 'payment_success', 'user_signup').
|
|
37
|
-
*/
|
|
40
|
+
/** Event name. e.g. 'user_signed_up', 'payment_failed' */
|
|
38
41
|
event: string;
|
|
39
|
-
/**
|
|
40
|
-
* Human-readable description shown in the dashboard feed.
|
|
41
|
-
*/
|
|
42
|
+
/** Human-readable description shown in the dashboard */
|
|
42
43
|
description?: string;
|
|
43
|
-
/**
|
|
44
|
-
* An emoji icon shown next to the event in the feed.
|
|
45
|
-
* @example '💳' | '🚨' | '✅'
|
|
46
|
-
*/
|
|
44
|
+
/** Emoji icon shown in the feed */
|
|
47
45
|
icon?: string;
|
|
48
|
-
/**
|
|
49
|
-
* Optional key-value tags for filtering and search.
|
|
50
|
-
* @example { userId: 'usr_123', plan: 'pro', amount: 49 }
|
|
51
|
-
*/
|
|
46
|
+
/** Optional key/value metadata for filtering */
|
|
52
47
|
tags?: Record<string, string | number | boolean>;
|
|
53
|
-
/**
|
|
54
|
-
* A scalar value to display alongside the event.
|
|
55
|
-
* @example '$49.00' | 200 | '98ms'
|
|
56
|
-
*/
|
|
48
|
+
/** A value to display alongside the event. e.g. '$49.00' */
|
|
57
49
|
value?: string | number;
|
|
58
50
|
/**
|
|
59
|
-
* If true, triggers configured
|
|
60
|
-
* If false,
|
|
51
|
+
* If true, triggers your configured notifications (push, email…).
|
|
52
|
+
* If false, logs silently for analytics only.
|
|
61
53
|
* @default true
|
|
62
54
|
*/
|
|
63
55
|
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
56
|
}
|
|
70
57
|
export interface EventResponse {
|
|
71
58
|
id: string;
|
|
72
59
|
createdAt: string;
|
|
73
60
|
}
|
|
74
61
|
export interface GateOptions {
|
|
75
|
-
/**
|
|
76
|
-
* Fallback value returned if the network request fails
|
|
77
|
-
* or times out.
|
|
78
|
-
* @default false
|
|
79
|
-
*/
|
|
62
|
+
/** Returned if the request fails. @default false */
|
|
80
63
|
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
|
-
*/
|
|
64
|
+
/** Cache duration in seconds. 0 to disable. @default 30 */
|
|
86
65
|
cacheTtl?: number;
|
|
87
66
|
}
|
|
88
67
|
export interface GateResult {
|
|
@@ -91,9 +70,5 @@ export interface GateResult {
|
|
|
91
70
|
cached: boolean;
|
|
92
71
|
fetchedAt: string;
|
|
93
72
|
}
|
|
94
|
-
export interface ApiErrorResponse {
|
|
95
|
-
error: string;
|
|
96
|
-
code: string;
|
|
97
|
-
}
|
|
98
73
|
export type LogLevel = 'debug' | 'info' | 'warn' | 'error' | 'none';
|
|
99
74
|
//# sourceMappingURL=types.d.ts.map
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,gBAAgB;IAC/B
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,gBAAgB;IAC/B;;;;;;OAMG;IACH,MAAM,EAAE,MAAM,CAAC;IAEf;;;;;;;;;OASG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IAEjB;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IAEjC;;;OAGG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IAEvB,2CAA2C;IAC3C,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,YAAY;IAC3B,4FAA4F;IAC5F,OAAO,EAAE,MAAM,CAAC;IAChB,0DAA0D;IAC1D,KAAK,EAAE,MAAM,CAAC;IACd,wDAAwD;IACxD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,mCAAmC;IACnC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,gDAAgD;IAChD,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,CAAC;IACjD,4DAA4D;IAC5D,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACxB;;;;OAIG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,WAAW;IAC1B,oDAAoD;IACpD,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,2DAA2D;IAC3D,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;AAED,MAAM,MAAM,QAAQ,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,CAAC"}
|
package/dist/types.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
// ─────────────────────────────────────────────
|
|
3
|
-
// Notiformer
|
|
3
|
+
// Notiformer SDK — Public Types
|
|
4
4
|
// ─────────────────────────────────────────────
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
//# sourceMappingURL=types.js.map
|
package/dist/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":";AAAA,gDAAgD;AAChD,
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":";AAAA,gDAAgD;AAChD,gCAAgC;AAChC,gDAAgD"}
|