@anomira/node-sdk 0.1.5 → 0.1.7

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/dist/index.d.cts CHANGED
@@ -1,10 +1,10 @@
1
1
  import { Request, Response, NextFunction } from 'express';
2
2
  import { FastifyPluginAsync } from 'fastify';
3
3
 
4
- interface SentinelConfig {
5
- /** SDK API key — get this from the SentinelAPI dashboard */
4
+ interface AnomiraConfig {
5
+ /** SDK API key — get this from the Anomira dashboard */
6
6
  apiKey: string;
7
- /** Your app ID from the SentinelAPI dashboard */
7
+ /** Your app ID from the Anomira dashboard */
8
8
  appId: string;
9
9
  /**
10
10
  * Ingest endpoint URL.
@@ -13,7 +13,7 @@ interface SentinelConfig {
13
13
  ingestUrl?: string;
14
14
  /**
15
15
  * Geo-lookup endpoint URL for client-side geo-velocity checks.
16
- * Should point to your SentinelAPI ingest service geo endpoint.
16
+ * Should point to your Anomira ingest service geo endpoint.
17
17
  * If not provided, client-side geo-velocity is skipped (server-side still runs).
18
18
  * @example "https://ingest.anomira.io/v1/geo"
19
19
  */
@@ -40,7 +40,7 @@ interface SentinelConfig {
40
40
  debug?: boolean;
41
41
  /**
42
42
  * If true, automatically intercept console.log/info/warn/error/debug
43
- * and forward them to the SentinelAPI Logs dashboard.
43
+ * and forward them to the Anomira Logs dashboard.
44
44
  * Existing console output is preserved — logs still print to your terminal.
45
45
  * @default false
46
46
  */
@@ -116,6 +116,14 @@ type EventNameValue = typeof EventName[keyof typeof EventName];
116
116
  type FirewallField = "url" | "body" | "header" | "user_agent" | "ip";
117
117
  type FirewallOperator = "contains" | "equals" | "starts_with" | "ends_with" | "regex";
118
118
  type FirewallAction = "block" | "flag";
119
+ interface EndpointDeclaration {
120
+ /** HTTP method, e.g. "GET". Use "*" to match any method. */
121
+ method: string;
122
+ /** Express-style path, e.g. "/api/users/:id" — colons are normalized automatically. */
123
+ path: string;
124
+ /** Whether this endpoint requires authentication. Defaults to true. */
125
+ auth?: boolean;
126
+ }
119
127
  interface FirewallRule {
120
128
  id: string;
121
129
  field: FirewallField;
@@ -127,25 +135,25 @@ interface FirewallRule {
127
135
  }
128
136
 
129
137
  /**
130
- * SentinelClient — the core SDK object.
138
+ * AnomiraClient — the core SDK object.
131
139
  *
132
140
  * Instantiate once and reuse across your application:
133
141
  *
134
142
  * ```ts
135
- * import { SentinelAPI } from "@anomira/node-sdk";
143
+ * import { Anomira } from "@anomira/node-sdk";
136
144
  *
137
- * export const sentinel = new SentinelAPI({
145
+ * export const sentinel = new Anomira({
138
146
  * apiKey: process.env.SENTINEL_API_KEY!,
139
147
  * appId: process.env.SENTINEL_APP_ID!,
140
148
  * });
141
149
  * ```
142
150
  */
143
- declare class SentinelClient {
151
+ declare class AnomiraClient {
144
152
  #private;
145
- readonly config: Required<Omit<SentinelConfig, "getUserId" | "getIp" | "detect">> & {
146
- getUserId: NonNullable<SentinelConfig["getUserId"]>;
147
- getIp: NonNullable<SentinelConfig["getIp"]>;
148
- detect: Required<NonNullable<SentinelConfig["detect"]>>;
153
+ readonly config: Required<Omit<AnomiraConfig, "getUserId" | "getIp" | "detect">> & {
154
+ getUserId: NonNullable<AnomiraConfig["getUserId"]>;
155
+ getIp: NonNullable<AnomiraConfig["getIp"]>;
156
+ detect: Required<NonNullable<AnomiraConfig["detect"]>>;
149
157
  captureConsole: boolean;
150
158
  service: string;
151
159
  };
@@ -163,7 +171,7 @@ declare class SentinelClient {
163
171
  private readonly _origLog;
164
172
  private readonly _origWarn;
165
173
  private readonly _origError;
166
- constructor(config: SentinelConfig);
174
+ constructor(config: AnomiraConfig);
167
175
  /**
168
176
  * Track a custom security event.
169
177
  *
@@ -230,7 +238,7 @@ declare class SentinelClient {
230
238
  meta?: Record<string, unknown>;
231
239
  }): void;
232
240
  /**
233
- * Send a structured log entry to the SentinelAPI Logs dashboard.
241
+ * Send a structured log entry to the Anomira Logs dashboard.
234
242
  *
235
243
  * ```ts
236
244
  * sentinel.log("info", "User registered", { userId: user.id });
@@ -241,6 +249,21 @@ declare class SentinelClient {
241
249
  log(level: "debug" | "info" | "warn" | "error" | "fatal", message: string, meta?: Record<string, unknown> & {
242
250
  service?: string;
243
251
  }): void;
252
+ /**
253
+ * Declare your API's known endpoints so Anomira can flag undiscovered
254
+ * traffic as shadow endpoints.
255
+ *
256
+ * Call this once on startup after your routes are registered:
257
+ *
258
+ * ```ts
259
+ * await sentinel.declareEndpoints([
260
+ * { method: "GET", path: "/api/users/:id", auth: true },
261
+ * { method: "POST", path: "/api/orders", auth: true },
262
+ * { method: "GET", path: "/api/health", auth: false },
263
+ * ]);
264
+ * ```
265
+ */
266
+ declareEndpoints(endpoints: EndpointDeclaration[]): Promise<void>;
244
267
  /**
245
268
  * Flush all pending events immediately.
246
269
  * Useful before a graceful shutdown outside of the process lifecycle hooks.
@@ -278,10 +301,10 @@ declare class SentinelClient {
278
301
  *
279
302
  * ```ts
280
303
  * import express from "express";
281
- * import { SentinelAPI } from "@sentinelapi/node-sdk";
304
+ * import { Anomira } from "@sentinelapi/node-sdk";
282
305
  *
283
306
  * const app = express();
284
- * const sentinel = new SentinelAPI({ apiKey: "...", appId: "..." });
307
+ * const sentinel = new Anomira({ apiKey: "...", appId: "..." });
285
308
  *
286
309
  * app.use(sentinel.express()); // auto-instrument all routes
287
310
  *
@@ -290,7 +313,7 @@ declare class SentinelClient {
290
313
  * app.use(createExpressMiddleware(sentinel));
291
314
  * ```
292
315
  */
293
- declare function createExpressMiddleware(client: SentinelClient): (req: Request, res: Response, next: NextFunction) => void;
316
+ declare function createExpressMiddleware(client: AnomiraClient): (req: Request, res: Response, next: NextFunction) => void;
294
317
 
295
318
  /**
296
319
  * Typed Fastify plugin — exported separately for projects that want
@@ -304,14 +327,14 @@ declare function createExpressMiddleware(client: SentinelClient): (req: Request,
304
327
  *
305
328
  * ```ts
306
329
  * import Fastify from "fastify";
307
- * import { SentinelAPI } from "@sentinelapi/node-sdk";
330
+ * import { Anomira } from "@sentinelapi/node-sdk";
308
331
  *
309
332
  * const app = Fastify();
310
- * const sentinel = new SentinelAPI({ apiKey: "...", appId: "..." });
333
+ * const sentinel = new Anomira({ apiKey: "...", appId: "..." });
311
334
  *
312
335
  * await app.register(sentinel.fastify());
313
336
  * ```
314
337
  */
315
- declare function createFastifyPlugin(client: SentinelClient): FastifyPluginAsync;
338
+ declare function createFastifyPlugin(client: AnomiraClient): FastifyPluginAsync;
316
339
 
317
- export { EventName, type EventNameValue, type IngestPayload, type SdkEvent, SentinelClient as SentinelAPI, type SentinelConfig, createExpressMiddleware, createFastifyPlugin, SentinelClient as default };
340
+ export { AnomiraClient as Anomira, type AnomiraConfig, type EndpointDeclaration, EventName, type EventNameValue, type IngestPayload, type SdkEvent, AnomiraClient as SentinelAPI, type AnomiraConfig as SentinelConfig, createExpressMiddleware, createFastifyPlugin, AnomiraClient as default };
package/dist/index.d.ts CHANGED
@@ -1,10 +1,10 @@
1
1
  import { Request, Response, NextFunction } from 'express';
2
2
  import { FastifyPluginAsync } from 'fastify';
3
3
 
4
- interface SentinelConfig {
5
- /** SDK API key — get this from the SentinelAPI dashboard */
4
+ interface AnomiraConfig {
5
+ /** SDK API key — get this from the Anomira dashboard */
6
6
  apiKey: string;
7
- /** Your app ID from the SentinelAPI dashboard */
7
+ /** Your app ID from the Anomira dashboard */
8
8
  appId: string;
9
9
  /**
10
10
  * Ingest endpoint URL.
@@ -13,7 +13,7 @@ interface SentinelConfig {
13
13
  ingestUrl?: string;
14
14
  /**
15
15
  * Geo-lookup endpoint URL for client-side geo-velocity checks.
16
- * Should point to your SentinelAPI ingest service geo endpoint.
16
+ * Should point to your Anomira ingest service geo endpoint.
17
17
  * If not provided, client-side geo-velocity is skipped (server-side still runs).
18
18
  * @example "https://ingest.anomira.io/v1/geo"
19
19
  */
@@ -40,7 +40,7 @@ interface SentinelConfig {
40
40
  debug?: boolean;
41
41
  /**
42
42
  * If true, automatically intercept console.log/info/warn/error/debug
43
- * and forward them to the SentinelAPI Logs dashboard.
43
+ * and forward them to the Anomira Logs dashboard.
44
44
  * Existing console output is preserved — logs still print to your terminal.
45
45
  * @default false
46
46
  */
@@ -116,6 +116,14 @@ type EventNameValue = typeof EventName[keyof typeof EventName];
116
116
  type FirewallField = "url" | "body" | "header" | "user_agent" | "ip";
117
117
  type FirewallOperator = "contains" | "equals" | "starts_with" | "ends_with" | "regex";
118
118
  type FirewallAction = "block" | "flag";
119
+ interface EndpointDeclaration {
120
+ /** HTTP method, e.g. "GET". Use "*" to match any method. */
121
+ method: string;
122
+ /** Express-style path, e.g. "/api/users/:id" — colons are normalized automatically. */
123
+ path: string;
124
+ /** Whether this endpoint requires authentication. Defaults to true. */
125
+ auth?: boolean;
126
+ }
119
127
  interface FirewallRule {
120
128
  id: string;
121
129
  field: FirewallField;
@@ -127,25 +135,25 @@ interface FirewallRule {
127
135
  }
128
136
 
129
137
  /**
130
- * SentinelClient — the core SDK object.
138
+ * AnomiraClient — the core SDK object.
131
139
  *
132
140
  * Instantiate once and reuse across your application:
133
141
  *
134
142
  * ```ts
135
- * import { SentinelAPI } from "@anomira/node-sdk";
143
+ * import { Anomira } from "@anomira/node-sdk";
136
144
  *
137
- * export const sentinel = new SentinelAPI({
145
+ * export const sentinel = new Anomira({
138
146
  * apiKey: process.env.SENTINEL_API_KEY!,
139
147
  * appId: process.env.SENTINEL_APP_ID!,
140
148
  * });
141
149
  * ```
142
150
  */
143
- declare class SentinelClient {
151
+ declare class AnomiraClient {
144
152
  #private;
145
- readonly config: Required<Omit<SentinelConfig, "getUserId" | "getIp" | "detect">> & {
146
- getUserId: NonNullable<SentinelConfig["getUserId"]>;
147
- getIp: NonNullable<SentinelConfig["getIp"]>;
148
- detect: Required<NonNullable<SentinelConfig["detect"]>>;
153
+ readonly config: Required<Omit<AnomiraConfig, "getUserId" | "getIp" | "detect">> & {
154
+ getUserId: NonNullable<AnomiraConfig["getUserId"]>;
155
+ getIp: NonNullable<AnomiraConfig["getIp"]>;
156
+ detect: Required<NonNullable<AnomiraConfig["detect"]>>;
149
157
  captureConsole: boolean;
150
158
  service: string;
151
159
  };
@@ -163,7 +171,7 @@ declare class SentinelClient {
163
171
  private readonly _origLog;
164
172
  private readonly _origWarn;
165
173
  private readonly _origError;
166
- constructor(config: SentinelConfig);
174
+ constructor(config: AnomiraConfig);
167
175
  /**
168
176
  * Track a custom security event.
169
177
  *
@@ -230,7 +238,7 @@ declare class SentinelClient {
230
238
  meta?: Record<string, unknown>;
231
239
  }): void;
232
240
  /**
233
- * Send a structured log entry to the SentinelAPI Logs dashboard.
241
+ * Send a structured log entry to the Anomira Logs dashboard.
234
242
  *
235
243
  * ```ts
236
244
  * sentinel.log("info", "User registered", { userId: user.id });
@@ -241,6 +249,21 @@ declare class SentinelClient {
241
249
  log(level: "debug" | "info" | "warn" | "error" | "fatal", message: string, meta?: Record<string, unknown> & {
242
250
  service?: string;
243
251
  }): void;
252
+ /**
253
+ * Declare your API's known endpoints so Anomira can flag undiscovered
254
+ * traffic as shadow endpoints.
255
+ *
256
+ * Call this once on startup after your routes are registered:
257
+ *
258
+ * ```ts
259
+ * await sentinel.declareEndpoints([
260
+ * { method: "GET", path: "/api/users/:id", auth: true },
261
+ * { method: "POST", path: "/api/orders", auth: true },
262
+ * { method: "GET", path: "/api/health", auth: false },
263
+ * ]);
264
+ * ```
265
+ */
266
+ declareEndpoints(endpoints: EndpointDeclaration[]): Promise<void>;
244
267
  /**
245
268
  * Flush all pending events immediately.
246
269
  * Useful before a graceful shutdown outside of the process lifecycle hooks.
@@ -278,10 +301,10 @@ declare class SentinelClient {
278
301
  *
279
302
  * ```ts
280
303
  * import express from "express";
281
- * import { SentinelAPI } from "@sentinelapi/node-sdk";
304
+ * import { Anomira } from "@sentinelapi/node-sdk";
282
305
  *
283
306
  * const app = express();
284
- * const sentinel = new SentinelAPI({ apiKey: "...", appId: "..." });
307
+ * const sentinel = new Anomira({ apiKey: "...", appId: "..." });
285
308
  *
286
309
  * app.use(sentinel.express()); // auto-instrument all routes
287
310
  *
@@ -290,7 +313,7 @@ declare class SentinelClient {
290
313
  * app.use(createExpressMiddleware(sentinel));
291
314
  * ```
292
315
  */
293
- declare function createExpressMiddleware(client: SentinelClient): (req: Request, res: Response, next: NextFunction) => void;
316
+ declare function createExpressMiddleware(client: AnomiraClient): (req: Request, res: Response, next: NextFunction) => void;
294
317
 
295
318
  /**
296
319
  * Typed Fastify plugin — exported separately for projects that want
@@ -304,14 +327,14 @@ declare function createExpressMiddleware(client: SentinelClient): (req: Request,
304
327
  *
305
328
  * ```ts
306
329
  * import Fastify from "fastify";
307
- * import { SentinelAPI } from "@sentinelapi/node-sdk";
330
+ * import { Anomira } from "@sentinelapi/node-sdk";
308
331
  *
309
332
  * const app = Fastify();
310
- * const sentinel = new SentinelAPI({ apiKey: "...", appId: "..." });
333
+ * const sentinel = new Anomira({ apiKey: "...", appId: "..." });
311
334
  *
312
335
  * await app.register(sentinel.fastify());
313
336
  * ```
314
337
  */
315
- declare function createFastifyPlugin(client: SentinelClient): FastifyPluginAsync;
338
+ declare function createFastifyPlugin(client: AnomiraClient): FastifyPluginAsync;
316
339
 
317
- export { EventName, type EventNameValue, type IngestPayload, type SdkEvent, SentinelClient as SentinelAPI, type SentinelConfig, createExpressMiddleware, createFastifyPlugin, SentinelClient as default };
340
+ export { AnomiraClient as Anomira, type AnomiraConfig, type EndpointDeclaration, EventName, type EventNameValue, type IngestPayload, type SdkEvent, AnomiraClient as SentinelAPI, type AnomiraConfig as SentinelConfig, createExpressMiddleware, createFastifyPlugin, AnomiraClient as default };
package/dist/index.js CHANGED
@@ -105,10 +105,10 @@ var EventBuffer = class {
105
105
  }
106
106
  }
107
107
  log(...args) {
108
- if (this.opts.debug) console.log("[SentinelAPI]", ...args);
108
+ if (this.opts.debug) console.log("[Anomira]", ...args);
109
109
  }
110
110
  warn(...args) {
111
- console.warn("[SentinelAPI]", ...args);
111
+ console.warn("[Anomira]", ...args);
112
112
  }
113
113
  };
114
114
  function sleep(ms) {
@@ -195,17 +195,15 @@ async function checkGeoVelocity(userId, ip, tsMs, lookupUrl) {
195
195
 
196
196
  // src/sensitive.ts
197
197
  var PATTERNS = [
198
- // ── Cryptographic keys and certificates ───────────────────────────────────
198
+ // ── Cryptographic private keys ────────────────────────────────────────────
199
+ // NOTE: -----BEGIN CERTIFICATE----- is intentionally excluded — public
200
+ // certificates are designed to be public and committing them is correct.
201
+ // Only PRIVATE keys are dangerous.
199
202
  {
200
203
  type: "private_key",
201
204
  label: "Private Key",
202
205
  regex: /-----BEGIN (?:RSA |EC |DSA |OPENSSH )?PRIVATE KEY-----/
203
206
  },
204
- {
205
- type: "certificate",
206
- label: "Certificate / Public Key",
207
- regex: /-----BEGIN CERTIFICATE-----/
208
- },
209
207
  // ── Cloud provider credentials ────────────────────────────────────────────
210
208
  {
211
209
  // AWS access key ID — highly specific, almost no false positives
@@ -320,26 +318,30 @@ var PATTERNS = [
320
318
  regex: /eyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}/
321
319
  },
322
320
  {
323
- // Generic API key / token assigned to a keyword (must have 20+ char value)
321
+ // Generic API key / token only flag QUOTED literals, not variable/env references.
322
+ // Skips: api_key = process.env.KEY, token = myVar
323
+ // Matches: api_key = "sk-abc123...", bearer: "eyJhb..."
324
324
  type: "api_key",
325
325
  label: "API Key / Token",
326
- regex: /\b(?:api[_-]?key|access[_-]?token|auth[_-]?token|bearer|client[_-]?secret)\s*[:=]\s*["']?[A-Za-z0-9_.\/+\-]{20,}["']?/i
326
+ regex: /\b(?:api[_-]?key|access[_-]?token|auth[_-]?token|bearer|client[_-]?secret)\s*[:=]\s*["'][A-Za-z0-9_.\/+\-]{20,}["']/i
327
327
  },
328
328
  // ── Password fields ───────────────────────────────────────────────────────
329
329
  {
330
- // Password/secret keyword immediately followed by a non-placeholder value
331
- // Avoids false positives on "password: ${PASSWORD}" or "password: xxxx"
330
+ // Only flag QUOTED string literals after a password/secret keyword.
331
+ // Skips: process.env.*, variable references, undefined/null, template literals.
332
+ // Matches: password: "hunter2", secret: 'abc123def', pass="hardcoded!"
332
333
  type: "password",
333
- label: "Password / Secret",
334
- regex: /\b(?:password|passwd|pwd|pass|secret|credentials?)\s*[:=]\s*["']?(?!\$\{)[^\s"'$]{6,}["']?/i
334
+ label: "Hardcoded Password",
335
+ regex: /\b(?:password|passwd|pwd|pass|secret|credentials?)\s*[:=]\s*["'][^"'$\s]{6,}["']/i
335
336
  },
336
337
  // ── Nigeria-specific PII ──────────────────────────────────────────────────
337
338
  {
338
- // BVN / NIN: 11 digits, first digit 1-9 (not a phone starting with 0)
339
- // Require a non-digit boundary on both sides to reduce false positives
339
+ // BVN / NIN: 11 digits, first digit 1-9 (not a phone number starting with 0)
340
+ // Exclude: inside URLs (preceded by / : @ - %), hex strings (followed by a-f),
341
+ // and UUIDs/hashes (surrounded by alphanumeric chars)
340
342
  type: "bvn",
341
343
  label: "BVN / NIN",
342
- regex: /(?<!\d)[1-9]\d{10}(?!\d)/
344
+ regex: /(?<![/\-:@%=a-fA-F\w])[1-9]\d{10}(?![a-fA-F\d])/
343
345
  },
344
346
  {
345
347
  // Nigerian phone numbers: 080x, 081x, 070x, 090x, 091x — or with +234 prefix
@@ -406,7 +408,7 @@ var DEFAULT_INGEST_URL = "https://ingest.anomira.io/v1/events";
406
408
  var DEFAULT_BATCH_SIZE = 100;
407
409
  var DEFAULT_FLUSH_MS = 5e3;
408
410
  var DEFAULT_MAX_RETRIES = 3;
409
- var SentinelClient = class {
411
+ var AnomiraClient = class {
410
412
  constructor(config) {
411
413
  this.logBuffer = [];
412
414
  this.logFlushTimer = null;
@@ -505,7 +507,7 @@ var SentinelClient = class {
505
507
  console[method] = (...args) => {
506
508
  original(...args);
507
509
  const first = args[0];
508
- if (typeof first === "string" && first.startsWith("[SentinelAPI]")) return;
510
+ if (typeof first === "string" && first.startsWith("[Anomira]")) return;
509
511
  const message = args.map((a) => typeof a === "string" ? a : JSON.stringify(a)).join(" ");
510
512
  const ctx = requestContext.getStore();
511
513
  this.log(level, message, {
@@ -529,7 +531,7 @@ var SentinelClient = class {
529
531
  const data = await res.json();
530
532
  this.blockedIpCache = new Set(data.ips ?? []);
531
533
  if (this.config.debug && this.blockedIpCache.size > 0) {
532
- this._origLog(`[SentinelAPI] blocklist refreshed \u2014 ${this.blockedIpCache.size} blocked IPs`);
534
+ this._origLog(`[Anomira] blocklist refreshed \u2014 ${this.blockedIpCache.size} blocked IPs`);
533
535
  }
534
536
  } catch {
535
537
  }
@@ -557,7 +559,7 @@ var SentinelClient = class {
557
559
  })() : void 0
558
560
  }));
559
561
  if (this.config.debug && this.compiledRules.length > 0) {
560
- this._origLog(`[SentinelAPI] firewall rules refreshed \u2014 ${this.compiledRules.length} active rules`);
562
+ this._origLog(`[Anomira] firewall rules refreshed \u2014 ${this.compiledRules.length} active rules`);
561
563
  }
562
564
  } catch {
563
565
  }
@@ -608,7 +610,7 @@ var SentinelClient = class {
608
610
  signal: AbortSignal.timeout(8e3)
609
611
  });
610
612
  if (this.config.debug) {
611
- this._origLog(`[SentinelAPI] [logs] \u2705 sent ${batch.length} log entries (${res.status})`);
613
+ this._origLog(`[Anomira] [logs] \u2705 sent ${batch.length} log entries (${res.status})`);
612
614
  }
613
615
  } catch {
614
616
  this.logBuffer.unshift(...batch);
@@ -627,24 +629,24 @@ var SentinelClient = class {
627
629
  signal: AbortSignal.timeout(5e3)
628
630
  });
629
631
  if (res.status >= 300 && res.status < 400) {
630
- this._origWarn(`[SentinelAPI] \u274C Wrong ingest URL \u2014 got redirect to ${res.headers.get("location")}. Check SENTINEL_INGEST_URL.`);
632
+ this._origWarn(`[Anomira] \u274C Wrong ingest URL \u2014 got redirect to ${res.headers.get("location")}. Check SENTINEL_INGEST_URL.`);
631
633
  return;
632
634
  }
633
635
  if (res.ok) {
634
- this._origLog(`[SentinelAPI] \u2705 Connected (appId: ${this.config.appId.slice(0, 8)}\u2026)`);
636
+ this._origLog(`[Anomira] \u2705 Connected (appId: ${this.config.appId.slice(0, 8)}\u2026)`);
635
637
  return;
636
638
  }
637
639
  if (res.status === 401) {
638
- this._origWarn("[SentinelAPI] \u274C Invalid API key \u2014 check your SENTINEL_API_KEY");
640
+ this._origWarn("[Anomira] \u274C Invalid API key \u2014 check your SENTINEL_API_KEY");
639
641
  return;
640
642
  }
641
643
  if (res.status === 403) {
642
- this._origWarn("[SentinelAPI] \u274C App not found or appId mismatch \u2014 check your SENTINEL_APP_ID");
644
+ this._origWarn("[Anomira] \u274C App not found or appId mismatch \u2014 check your SENTINEL_APP_ID");
643
645
  return;
644
646
  }
645
- this._origWarn(`[SentinelAPI] \u26A0\uFE0F Ingest returned HTTP ${res.status} \u2014 check your configuration`);
647
+ this._origWarn(`[Anomira] \u26A0\uFE0F Ingest returned HTTP ${res.status} \u2014 check your configuration`);
646
648
  } catch {
647
- this._origWarn("[SentinelAPI] \u26A0\uFE0F Could not reach ingest endpoint \u2014 check SENTINEL_INGEST_URL (current: " + this.config.ingestUrl + ")");
649
+ this._origWarn("[Anomira] \u26A0\uFE0F Could not reach ingest endpoint \u2014 check SENTINEL_INGEST_URL (current: " + this.config.ingestUrl + ")");
648
650
  }
649
651
  }
650
652
  // ─── Public API ────────────────────────────────────────────────────────────
@@ -741,7 +743,7 @@ var SentinelClient = class {
741
743
  });
742
744
  }
743
745
  /**
744
- * Send a structured log entry to the SentinelAPI Logs dashboard.
746
+ * Send a structured log entry to the Anomira Logs dashboard.
745
747
  *
746
748
  * ```ts
747
749
  * sentinel.log("info", "User registered", { userId: user.id });
@@ -757,16 +759,47 @@ var SentinelClient = class {
757
759
  rest["sensitiveLeaks"] = leaks.map((l) => l.type);
758
760
  if (this.config.debug) {
759
761
  this._origWarn(
760
- `[SentinelAPI] \u26A0\uFE0F Sensitive data in log (${leaks.map((l) => l.label).join(", ")}): "${message.slice(0, 60)}\u2026"`
762
+ `[Anomira] \u26A0\uFE0F Sensitive data in log (${leaks.map((l) => l.label).join(", ")}): "${message.slice(0, 60)}\u2026"`
761
763
  );
762
764
  }
763
765
  }
764
766
  this.logBuffer.push({ level, service: service ?? this.config.service, message, meta: rest, ts: Date.now() });
765
767
  if (this.config.debug && leaks.length === 0) {
766
- this._origLog(`[SentinelAPI] log:${level} ${message}`);
768
+ this._origLog(`[Anomira] log:${level} ${message}`);
767
769
  }
768
770
  if (this.logBuffer.length >= 50) void this.#flushLogs();
769
771
  }
772
+ /**
773
+ * Declare your API's known endpoints so Anomira can flag undiscovered
774
+ * traffic as shadow endpoints.
775
+ *
776
+ * Call this once on startup after your routes are registered:
777
+ *
778
+ * ```ts
779
+ * await sentinel.declareEndpoints([
780
+ * { method: "GET", path: "/api/users/:id", auth: true },
781
+ * { method: "POST", path: "/api/orders", auth: true },
782
+ * { method: "GET", path: "/api/health", auth: false },
783
+ * ]);
784
+ * ```
785
+ */
786
+ async declareEndpoints(endpoints) {
787
+ if (this.disabled || endpoints.length === 0) return;
788
+ const url = this.config.ingestUrl.replace(/\/v1\/events$/, "/v1/declare-endpoints");
789
+ try {
790
+ await fetch(url, {
791
+ method: "POST",
792
+ headers: {
793
+ Authorization: `Bearer ${this.config.apiKey}`,
794
+ "Content-Type": "application/json",
795
+ "User-Agent": `@anomira/node-sdk/0.1.0`
796
+ },
797
+ body: JSON.stringify({ appId: this.config.appId, endpoints }),
798
+ signal: AbortSignal.timeout(1e4)
799
+ });
800
+ } catch {
801
+ }
802
+ }
770
803
  /**
771
804
  * Flush all pending events immediately.
772
805
  * Useful before a graceful shutdown outside of the process lifecycle hooks.
@@ -1107,6 +1140,6 @@ function createFastifyPlugin2(client) {
1107
1140
  };
1108
1141
  }
1109
1142
 
1110
- export { EventName, SentinelClient as SentinelAPI, createExpressMiddleware2 as createExpressMiddleware, createFastifyPlugin2 as createFastifyPlugin, SentinelClient as default };
1143
+ export { AnomiraClient as Anomira, EventName, AnomiraClient as SentinelAPI, createExpressMiddleware2 as createExpressMiddleware, createFastifyPlugin2 as createFastifyPlugin, AnomiraClient as default };
1111
1144
  //# sourceMappingURL=index.js.map
1112
1145
  //# sourceMappingURL=index.js.map