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 CHANGED
@@ -1,8 +1,10 @@
1
1
  # notiformer
2
2
 
3
- Real-time alerts, notifications and feature gates for your backend code.
3
+ Real-time alerts and feature gates for your backend code.
4
4
 
5
- Send events from any point in your Node.js code and get notified instantly via push, email, Slack and more.
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: 'ntf_live_...', // from app.notiformer.com/projects
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: `$49.00 — john@example.com`,
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
- ## Events
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
- Use `n.event()` to log an event and optionally trigger notifications.
49
+ ---
50
+
51
+ ## Events
42
52
 
43
53
  ```ts
44
54
  await n.event({
45
- channel: 'auth', // channel name (auto-created)
46
- event: 'user_signup', // machine-readable event name
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
- // Optional metadata
51
- tags: { method: 'google', plan: 'free' },
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
- // 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',
63
+ notify: true, // true → triggers push/email notifications
64
+ // false → silent log for analytics only
60
65
  });
61
66
  ```
62
67
 
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.
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
- Use `n.gate()` to check a boolean toggle you control remotely from the dashboard.
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 checkout
80
+ // run new feature
78
81
  } else {
79
- // run legacy checkout
82
+ // run old feature
80
83
  }
81
84
  ```
82
85
 
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
- ```
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_...', // Required
94
+ apiKey: 'ntf_live_...', // required — from app.notiformer.com/projects
119
95
 
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'
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 business milestones
133
+ ### Track a payment
162
134
 
163
135
  ```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
- }
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 a new endpoint
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
- if (useNewFlow) {
184
- return newCheckoutHandler(req, res);
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
- description: `/dashboard visited`,
198
- tags: { userId: req.user.id },
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 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
- ```
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 Node.js SDK
2
+ * Notiformer — Real-time alerts and feature gates for your code.
3
3
  *
4
- * Real-time alerts, notifications and feature gates for your backend code.
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({ apiKey: 'ntf_live_...' });
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 './types';
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 to a channel. Triggers notifications based on your
38
- * dashboard settings.
37
+ * Send an event and optionally trigger a notification.
39
38
  *
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
- * ```
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. 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
44
+ * Check whether a feature gate is enabled.
45
+ * Cached locally for 30 seconds by default.
63
46
  *
64
- * @example
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 fetchWithTimeout;
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
@@ -1 +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"}
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 Node.js SDK
3
+ * Notiformer — Real-time alerts and feature gates for your code.
4
4
  *
5
- * Real-time alerts, notifications and feature gates for your backend code.
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({ apiKey: 'ntf_live_...' });
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
- const DEFAULT_BASE_URL = 'https://api.notiformer.com';
32
- const DEFAULT_TIMEOUT = 5000;
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, _d;
35
+ var _a, _b, _c;
37
36
  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.');
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.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');
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.logger.debug('Notiformer initialized', { baseUrl: this.baseUrl });
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() — Send a notification event
53
- // ─────────────────────────────────────────────
64
+ // ─────────────────────────────────────────────────────────────
65
+ // event()
66
+ // ─────────────────────────────────────────────────────────────
54
67
  /**
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
68
+ * Send an event and optionally trigger a notification.
60
69
  *
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
- * ```
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('Silent mode: event suppressed', payload);
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 body = {
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 err = await res.json().catch(() => ({ error: 'Unknown', code: String(res.status) }));
101
- this.logger.error(`Event failed: ${err.error} (${err.code})`);
102
- return null;
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
- const data = await res.json();
105
- this.logger.debug('Event sent', data);
106
- return data;
105
+ return (await res.json());
107
106
  }
108
107
  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;
108
+ return this.fail(this.friendlyNetworkError(err));
113
109
  }
114
110
  }
115
- // ─────────────────────────────────────────────
116
- // gate() — Check a feature gate
117
- // ─────────────────────────────────────────────
111
+ // ─────────────────────────────────────────────────────────────
112
+ // gate()
113
+ // ─────────────────────────────────────────────────────────────
118
114
  /**
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
115
+ * Check whether a feature gate is enabled.
116
+ * Cached locally for 30 seconds by default.
125
117
  *
126
- * @example
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('[notiformer] gate key is required.');
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.fetchWithTimeout(`/v1/gates/${encodeURIComponent(key)}`);
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 (err) {
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
- 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
- }
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
- if (key) {
208
- this.gateCache.delete(key);
209
- }
210
- else {
211
- this.gateCache.clear();
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
- // Private helpers
216
- // ─────────────────────────────────────────────
217
- async fetchWithTimeout(path, init = {}) {
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
- 'Content-Type': 'application/json',
226
- 'Authorization': `Bearer ${this.apiKey}`,
227
- 'X-SDK-Version': '1.0.0',
228
- 'X-SDK-Language': 'node',
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":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;;;AASH,qCAAkC;AAClC,mCAAoC;AAUpC,wEAAwE;AACxE,MAAM,eAAe,GAAG,eAAe,CAAC;AACxC,MAAM,OAAO,GAAG,4BAA4B,CAAC;AAC7C,MAAM,eAAe,GAAG,IAAK,CAAC;AAC9B,MAAM,gBAAgB,GAAG,EAAE,CAAC;AAE5B,MAAa,UAAU;IAWrB,YAAY,MAAwB;;QAClC,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;YACnB,MAAM,IAAI,KAAK,CACb,oCAAoC;gBAClC,oDAAoD,CACvD,CAAC;QACJ,CAAC;QAED,IAAI,CAAC,aAAa,GAAG,MAAM,CAAC,MAAM,KAAK,eAAe,CAAC;QACvD,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC;QAC5B,IAAI,CAAC,OAAO,GAAG,CAAC,MAAA,MAAM,CAAC,QAAQ,mCAAI,OAAO,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QAC/D,IAAI,CAAC,OAAO,GAAG,eAAe,CAAC;QAC/B,IAAI,CAAC,MAAM,GAAG,MAAA,MAAM,CAAC,MAAM,mCAAI,KAAK,CAAC;QACrC,IAAI,CAAC,YAAY,GAAG,MAAA,MAAM,CAAC,YAAY,mCAAI,KAAK,CAAC;QACjD,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC;QAC9B,IAAI,CAAC,MAAM,GAAG,IAAI,eAAM,CAAC,MAAM,CAAC,CAAC;QACjC,IAAI,CAAC,SAAS,GAAG,IAAI,iBAAS,EAAE,CAAC;QAEjC,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;YACvB,OAAO,CAAC,IAAI,CACV,IAAI;gBACF,2DAA2D;gBAC3D,2DAA2D;gBAC3D,2DAA2D;gBAC3D,2DAA2D;gBAC3D,2DAA2D;gBAC3D,2DAA2D;gBAC3D,2DAA2D;gBAC3D,2DAA2D;gBAC3D,2DAA2D;gBAC3D,2DAA2D;gBAC3D,2DAA2D,CAC9D,CAAC;QACJ,CAAC;IACH,CAAC;IAED,gEAAgE;IAChE,UAAU;IACV,gEAAgE;IAEhE;;;;;OAKG;IACH,KAAK,CAAC,KAAK,CAAC,OAAqB;;QAC/B,IAAI,CAAC,OAAO,CAAC,OAAO;YAClB,MAAM,IAAI,KAAK,CAAC,yCAAyC,CAAC,CAAC;QAC7D,IAAI,CAAC,OAAO,CAAC,KAAK;YAChB,MAAM,IAAI,KAAK,CAAC,uCAAuC,CAAC,CAAC;QAE3D,iEAAiE;QACjE,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;YACvB,OAAO,CAAC,IAAI,CACV,gFAAgF;gBAC9E,8EAA8E,CACjF,CAAC;YACF,OAAO,IAAI,CAAC;QACd,CAAC;QAED,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YAChB,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,gCAAgC,CAAC,CAAC;YACnD,OAAO,IAAI,CAAC;QACd,CAAC;QAED,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE;gBACxC,OAAO,EAAE,OAAO,CAAC,OAAO;gBACxB,KAAK,EAAE,OAAO,CAAC,KAAK;gBACpB,WAAW,EAAE,OAAO,CAAC,WAAW;gBAChC,IAAI,EAAE,OAAO,CAAC,IAAI;gBAClB,IAAI,EAAE,OAAO,CAAC,IAAI;gBAClB,KAAK,EAAE,OAAO,CAAC,KAAK;gBACpB,MAAM,EAAE,MAAA,OAAO,CAAC,MAAM,mCAAI,IAAI;aAC/B,CAAC,CAAC;YAEH,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;gBACZ,MAAM,IAAI,GAAG,MAAM,GAAG;qBACnB,IAAI,EAAE;qBACN,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,QAAQ,GAAG,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,CAAC;gBAClD,OAAO,IAAI,CAAC,IAAI,CACd,IAAI,KAAK,CAAC,gBAAgB,MAAA,IAAI,CAAC,KAAK,mCAAI,GAAG,CAAC,UAAU,EAAE,CAAC,CAC1D,CAAC;YACJ,CAAC;YAED,OAAO,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAkB,CAAC;QAC7C,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,oBAAoB,CAAC,GAAG,CAAC,CAAC,CAAC;QACnD,CAAC;IACH,CAAC;IAED,gEAAgE;IAChE,SAAS;IACT,gEAAgE;IAEhE;;;;;OAKG;IACH,KAAK,CAAC,IAAI,CAAC,GAAW,EAAE,UAAuB,EAAE;;QAC/C,IAAI,CAAC,GAAG;YAAE,MAAM,IAAI,KAAK,CAAC,oCAAoC,CAAC,CAAC;QAEhE,MAAM,QAAQ,GAAG,MAAA,OAAO,CAAC,QAAQ,mCAAI,KAAK,CAAC;QAC3C,MAAM,GAAG,GAAG,MAAA,OAAO,CAAC,QAAQ,mCAAI,gBAAgB,CAAC;QAEjD,IAAI,IAAI,CAAC,aAAa,IAAI,IAAI,CAAC,MAAM;YAAE,OAAO,QAAQ,CAAC;QAEvD,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACvC,IAAI,MAAM,KAAK,IAAI;YAAE,OAAO,MAAM,CAAC;QAEnC,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,aAAa,kBAAkB,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YACnE,IAAI,CAAC,GAAG,CAAC,EAAE;gBAAE,OAAO,QAAQ,CAAC;YAC7B,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAe,CAAC;YAC9C,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;YAC3C,OAAO,IAAI,CAAC,OAAO,CAAC;QACtB,CAAC;QAAC,WAAM,CAAC;YACP,OAAO,QAAQ,CAAC;QAClB,CAAC;IACH,CAAC;IAED,qCAAqC;IACrC,KAAK,CAAC,WAAW,CACf,GAAW,EACX,UAAuB,EAAE;QAEzB,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;QAC9C,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,CAAC;IAC9E,CAAC;IAED,+BAA+B;IAC/B,cAAc,CAAC,GAAY;QACzB,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,CAAC;IAC5D,CAAC;IAED,gEAAgE;IAChE,UAAU;IACV,gEAAgE;IAExD,KAAK,CAAC,IAAI,CAAC,IAAY,EAAE,IAAa;QAC5C,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE;YACxB,MAAM,EAAE,MAAM;YACd,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC;SAC3B,CAAC,CAAC;IACL,CAAC;IAEO,KAAK,CAAC,GAAG,CAAC,IAAY;QAC5B,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC;IAC/C,CAAC;IAEO,KAAK,CAAC,OAAO,CAAC,IAAY,EAAE,IAAiB;;QACnD,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;QACzC,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;QACjE,IAAI,CAAC;YACH,OAAO,MAAM,KAAK,CAAC,GAAG,IAAI,CAAC,OAAO,GAAG,IAAI,EAAE,EAAE;gBAC3C,GAAG,IAAI;gBACP,MAAM,EAAE,UAAU,CAAC,MAAM;gBACzB,OAAO,EAAE;oBACP,cAAc,EAAE,kBAAkB;oBAClC,aAAa,EAAE,UAAU,IAAI,CAAC,MAAM,EAAE;oBACtC,eAAe,EAAE,OAAO;oBACxB,GAAG,CAAC,MAAA,IAAI,CAAC,OAAO,mCAAI,EAAE,CAAC;iBACxB;aACF,CAAC,CAAC;QACL,CAAC;gBAAS,CAAC;YACT,YAAY,CAAC,KAAK,CAAC,CAAC;QACtB,CAAC;IACH,CAAC;IAEO,IAAI,CAAC,KAAY;;QACvB,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QACjC,MAAA,IAAI,CAAC,OAAO,qDAAG,KAAK,CAAC,CAAC;QACtB,IAAI,IAAI,CAAC,YAAY;YAAE,MAAM,KAAK,CAAC;QACnC,OAAO,IAAI,CAAC;IACd,CAAC;IAEO,oBAAoB,CAAC,GAAY;QACvC,MAAM,GAAG,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAElE,IAAI,GAAG,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,GAAG,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;YACvD,OAAO,IAAI,KAAK,CACd,wCAAwC,IAAI,CAAC,OAAO,GAAG,IAAI,MAAM;gBAC/D,oDAAoD,CACvD,CAAC;QACJ,CAAC;QAED,IACE,GAAG,CAAC,QAAQ,CAAC,iBAAiB,CAAC;YAC/B,GAAG,CAAC,QAAQ,CAAC,WAAW,CAAC;YACzB,GAAG,CAAC,QAAQ,CAAC,SAAS,CAAC,EACvB,CAAC;YACD,OAAO,IAAI,KAAK,CACd,iDAAiD;gBAC/C,8FAA8F,CACjG,CAAC;QACJ,CAAC;QAED,OAAO,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;IAC7D,CAAC;CACF;AArND,gCAqNC;AAED,kBAAe,UAAU,CAAC"}
package/dist/types.d.ts CHANGED
@@ -1,88 +1,67 @@
1
1
  export interface NotiformerConfig {
2
2
  /**
3
- * Your project API key from the Notiformer dashboard.
4
- * Get it at: https://app.notiformer.com/projects
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
- * Base URL of the Notiformer API.
9
- * @default 'https://api.notiformer.com'
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
- baseUrl?: string;
12
- /**
13
- * Request timeout in milliseconds.
14
- * @default 5000
15
- */
16
- timeout?: number;
20
+ silent?: boolean;
17
21
  /**
18
- * If true, SDK calls are no-ops. Useful for local development.
19
- * @default false
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
- silent?: boolean;
28
+ onError?: (error: Error) => void;
22
29
  /**
23
- * Log level for SDK internals.
24
- * @default 'error'
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
- logLevel?: 'debug' | 'info' | 'warn' | 'error' | 'none';
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 notification channels (push, email, etc).
60
- * If false, the event is logged silently (analytics only).
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
@@ -1 +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"}
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 Node.js SDK — Types
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,iCAAiC;AACjC,gDAAgD"}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":";AAAA,gDAAgD;AAChD,gCAAgC;AAChC,gDAAgD"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "notiformer",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "Real-time alerts, notifications and feature gates for your backend code",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",