@simplr-ai/node 1.1.1 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -1,9 +1,33 @@
1
+ type NetworkSource = "frontend" | "backend";
2
+ interface NetworkLogEntry {
3
+ id: string;
4
+ source: NetworkSource;
5
+ timestamp: string;
6
+ sdk?: string;
7
+ applicationId?: string;
8
+ environment?: string;
9
+ method: string;
10
+ url: string;
11
+ requestHeaders?: Record<string, string>;
12
+ requestBody?: unknown;
13
+ status?: number;
14
+ statusText?: string;
15
+ responseHeaders?: Record<string, string>;
16
+ responseBody?: unknown;
17
+ durationMs?: number;
18
+ ok?: boolean;
19
+ error?: string;
20
+ }
21
+ type NetworkLogger = (entry: NetworkLogEntry) => void;
22
+
1
23
  interface HttpConfig {
2
- /** Auth headers to send (e.g. { "X-API-Key": "sk_…" } or { Authorization: "Bearer …" }). */
3
24
  authHeaders: Record<string, string>;
4
25
  baseUrl: string;
5
26
  timeoutMs: number;
6
27
  fetchImpl: typeof fetch;
28
+ onNetworkLog?: NetworkLogger;
29
+ logBodies?: boolean;
30
+ redactFields?: string[];
7
31
  }
8
32
 
9
33
  type RiskLevel = "low" | "medium" | "high" | "critical";
@@ -25,6 +49,11 @@ interface SimplrOptions {
25
49
  timeoutMs?: number;
26
50
  /** Override the fetch implementation (defaults to global fetch). */
27
51
  fetch?: typeof fetch;
52
+ onNetworkLog?: NetworkLogger;
53
+ logBodies?: boolean;
54
+ redactFields?: string[];
55
+ shipNetworkLogs?: boolean;
56
+ applicationId?: string;
28
57
  }
29
58
  interface CheckInput {
30
59
  email?: string;
@@ -274,6 +303,9 @@ interface FlagsOptions {
274
303
  refreshIntervalMs?: number;
275
304
  timeoutMs?: number;
276
305
  fetch?: typeof fetch;
306
+ onNetworkLog?: NetworkLogger;
307
+ logBodies?: boolean;
308
+ redactFields?: string[];
277
309
  }
278
310
  interface EvalContext {
279
311
  userId?: string;
@@ -473,6 +505,27 @@ declare class WebhookVerificationError extends Error {
473
505
  constructor(message: string);
474
506
  }
475
507
 
508
+ interface ShipperConfig {
509
+ baseUrl: string;
510
+ apiKey: string;
511
+ fetchImpl: typeof fetch;
512
+ sdk: string;
513
+ applicationId?: string;
514
+ environment?: string;
515
+ batchSize?: number;
516
+ flushIntervalMs?: number;
517
+ }
518
+ declare class NetworkLogShipper {
519
+ private readonly cfg;
520
+ private queue;
521
+ private timer;
522
+ constructor(cfg: ShipperConfig);
523
+ start(): void;
524
+ add(entry: NetworkLogEntry): void;
525
+ flush(): Promise<void>;
526
+ stop(): void;
527
+ }
528
+
476
529
  interface SimplrAdminOptions {
477
530
  /** Portal token (JWT) for dashboard/admin operations. */
478
531
  token: string;
@@ -564,6 +617,7 @@ declare class Simplr {
564
617
  /** Webhook signature helpers (no network). */
565
618
  readonly webhooks: typeof webhooks$1;
566
619
  private readonly _flags?;
620
+ private readonly shipper?;
567
621
  constructor(options: SimplrOptions);
568
622
  /**
569
623
  * Server-side feature flags. Requires a `publicKey` in the constructor options
@@ -574,6 +628,8 @@ declare class Simplr {
574
628
  check(input: CheckInput): Promise<CheckResult>;
575
629
  /** Run up to 100 checks at once. */
576
630
  checkBulk(items: CheckInput[]): Promise<BulkResult<CheckResult>>;
631
+ flushNetworkLogs(): Promise<void>;
632
+ close(): void;
577
633
  }
578
634
 
579
- export { type BindingMode, type BulkResult, type CheckInput, type CheckResult, type CreateDelegationOptions, type DelegationInfo, type DelegationResult, type DelegationStats, type EdgeLogEntry, type EvalContext, type FlagDefinition, type FlagRule, type FlagsOptions, type IdentifyOptions, type OrderInput, type OrderResult, type PhoneOutcome, type PhoneReportInput, type ProfileResult, type ProfileRiskResult, type RUMEvent, type RUMEventBatch, type RUMEventType, type RUMLogLevel, type RiskLevel, Simplr, SimplrAI, SimplrAdmin, type SimplrAdminOptions, SimplrError, SimplrFlags, type SimplrOptions, SimplrProfiles, SimplrRUM, type SimplrRUMConfig, type ValidationResult, WebhookVerificationError, constructEvent as constructWebhookEvent, Simplr as default, verify as verifyWebhook };
635
+ export { type BindingMode, type BulkResult, type CheckInput, type CheckResult, type CreateDelegationOptions, type DelegationInfo, type DelegationResult, type DelegationStats, type EdgeLogEntry, type EvalContext, type FlagDefinition, type FlagRule, type FlagsOptions, type IdentifyOptions, type NetworkLogEntry, NetworkLogShipper, type NetworkLogger, type NetworkSource, type OrderInput, type OrderResult, type PhoneOutcome, type PhoneReportInput, type ProfileResult, type ProfileRiskResult, type RUMEvent, type RUMEventBatch, type RUMEventType, type RUMLogLevel, type RiskLevel, type ShipperConfig, Simplr, SimplrAI, SimplrAdmin, type SimplrAdminOptions, SimplrError, SimplrFlags, type SimplrOptions, SimplrProfiles, SimplrRUM, type SimplrRUMConfig, type ValidationResult, WebhookVerificationError, constructEvent as constructWebhookEvent, Simplr as default, verify as verifyWebhook };
package/dist/index.d.ts CHANGED
@@ -1,9 +1,33 @@
1
+ type NetworkSource = "frontend" | "backend";
2
+ interface NetworkLogEntry {
3
+ id: string;
4
+ source: NetworkSource;
5
+ timestamp: string;
6
+ sdk?: string;
7
+ applicationId?: string;
8
+ environment?: string;
9
+ method: string;
10
+ url: string;
11
+ requestHeaders?: Record<string, string>;
12
+ requestBody?: unknown;
13
+ status?: number;
14
+ statusText?: string;
15
+ responseHeaders?: Record<string, string>;
16
+ responseBody?: unknown;
17
+ durationMs?: number;
18
+ ok?: boolean;
19
+ error?: string;
20
+ }
21
+ type NetworkLogger = (entry: NetworkLogEntry) => void;
22
+
1
23
  interface HttpConfig {
2
- /** Auth headers to send (e.g. { "X-API-Key": "sk_…" } or { Authorization: "Bearer …" }). */
3
24
  authHeaders: Record<string, string>;
4
25
  baseUrl: string;
5
26
  timeoutMs: number;
6
27
  fetchImpl: typeof fetch;
28
+ onNetworkLog?: NetworkLogger;
29
+ logBodies?: boolean;
30
+ redactFields?: string[];
7
31
  }
8
32
 
9
33
  type RiskLevel = "low" | "medium" | "high" | "critical";
@@ -25,6 +49,11 @@ interface SimplrOptions {
25
49
  timeoutMs?: number;
26
50
  /** Override the fetch implementation (defaults to global fetch). */
27
51
  fetch?: typeof fetch;
52
+ onNetworkLog?: NetworkLogger;
53
+ logBodies?: boolean;
54
+ redactFields?: string[];
55
+ shipNetworkLogs?: boolean;
56
+ applicationId?: string;
28
57
  }
29
58
  interface CheckInput {
30
59
  email?: string;
@@ -274,6 +303,9 @@ interface FlagsOptions {
274
303
  refreshIntervalMs?: number;
275
304
  timeoutMs?: number;
276
305
  fetch?: typeof fetch;
306
+ onNetworkLog?: NetworkLogger;
307
+ logBodies?: boolean;
308
+ redactFields?: string[];
277
309
  }
278
310
  interface EvalContext {
279
311
  userId?: string;
@@ -473,6 +505,27 @@ declare class WebhookVerificationError extends Error {
473
505
  constructor(message: string);
474
506
  }
475
507
 
508
+ interface ShipperConfig {
509
+ baseUrl: string;
510
+ apiKey: string;
511
+ fetchImpl: typeof fetch;
512
+ sdk: string;
513
+ applicationId?: string;
514
+ environment?: string;
515
+ batchSize?: number;
516
+ flushIntervalMs?: number;
517
+ }
518
+ declare class NetworkLogShipper {
519
+ private readonly cfg;
520
+ private queue;
521
+ private timer;
522
+ constructor(cfg: ShipperConfig);
523
+ start(): void;
524
+ add(entry: NetworkLogEntry): void;
525
+ flush(): Promise<void>;
526
+ stop(): void;
527
+ }
528
+
476
529
  interface SimplrAdminOptions {
477
530
  /** Portal token (JWT) for dashboard/admin operations. */
478
531
  token: string;
@@ -564,6 +617,7 @@ declare class Simplr {
564
617
  /** Webhook signature helpers (no network). */
565
618
  readonly webhooks: typeof webhooks$1;
566
619
  private readonly _flags?;
620
+ private readonly shipper?;
567
621
  constructor(options: SimplrOptions);
568
622
  /**
569
623
  * Server-side feature flags. Requires a `publicKey` in the constructor options
@@ -574,6 +628,8 @@ declare class Simplr {
574
628
  check(input: CheckInput): Promise<CheckResult>;
575
629
  /** Run up to 100 checks at once. */
576
630
  checkBulk(items: CheckInput[]): Promise<BulkResult<CheckResult>>;
631
+ flushNetworkLogs(): Promise<void>;
632
+ close(): void;
577
633
  }
578
634
 
579
- export { type BindingMode, type BulkResult, type CheckInput, type CheckResult, type CreateDelegationOptions, type DelegationInfo, type DelegationResult, type DelegationStats, type EdgeLogEntry, type EvalContext, type FlagDefinition, type FlagRule, type FlagsOptions, type IdentifyOptions, type OrderInput, type OrderResult, type PhoneOutcome, type PhoneReportInput, type ProfileResult, type ProfileRiskResult, type RUMEvent, type RUMEventBatch, type RUMEventType, type RUMLogLevel, type RiskLevel, Simplr, SimplrAI, SimplrAdmin, type SimplrAdminOptions, SimplrError, SimplrFlags, type SimplrOptions, SimplrProfiles, SimplrRUM, type SimplrRUMConfig, type ValidationResult, WebhookVerificationError, constructEvent as constructWebhookEvent, Simplr as default, verify as verifyWebhook };
635
+ export { type BindingMode, type BulkResult, type CheckInput, type CheckResult, type CreateDelegationOptions, type DelegationInfo, type DelegationResult, type DelegationStats, type EdgeLogEntry, type EvalContext, type FlagDefinition, type FlagRule, type FlagsOptions, type IdentifyOptions, type NetworkLogEntry, NetworkLogShipper, type NetworkLogger, type NetworkSource, type OrderInput, type OrderResult, type PhoneOutcome, type PhoneReportInput, type ProfileResult, type ProfileRiskResult, type RUMEvent, type RUMEventBatch, type RUMEventType, type RUMLogLevel, type RiskLevel, type ShipperConfig, Simplr, SimplrAI, SimplrAdmin, type SimplrAdminOptions, SimplrError, SimplrFlags, type SimplrOptions, SimplrProfiles, SimplrRUM, type SimplrRUMConfig, type ValidationResult, WebhookVerificationError, constructEvent as constructWebhookEvent, Simplr as default, verify as verifyWebhook };
package/dist/index.js CHANGED
@@ -22,17 +22,128 @@ var WebhookVerificationError = class extends Error {
22
22
  }
23
23
  };
24
24
 
25
+ // src/network-log.ts
26
+ var SENSITIVE_HEADERS = /* @__PURE__ */ new Set([
27
+ "authorization",
28
+ "cookie",
29
+ "set-cookie",
30
+ "x-api-key",
31
+ "x-auth-token",
32
+ "x-csrf-token",
33
+ "x-xsrf-token"
34
+ ]);
35
+ var SENSITIVE_KEY_PARTS = [
36
+ "password",
37
+ "passwd",
38
+ "secret",
39
+ "token",
40
+ "api_key",
41
+ "apikey",
42
+ "authorization",
43
+ "auth",
44
+ "credential",
45
+ "private_key",
46
+ "card",
47
+ "cardnumber",
48
+ "pan",
49
+ "cvv",
50
+ "cvc",
51
+ "ssn",
52
+ "pin",
53
+ "otp"
54
+ ];
55
+ var MAX_REDACT_DEPTH = 8;
56
+ var MAX_BODY_CHARS = 1e4;
57
+ function isSensitiveKey(key, extraKeys) {
58
+ const lower = key.toLowerCase();
59
+ if (extraKeys.some((k) => lower === k.toLowerCase())) return true;
60
+ return SENSITIVE_KEY_PARTS.some((part) => lower.includes(part));
61
+ }
62
+ function redactDeep(value, extraKeys, depth = 0) {
63
+ if (depth >= MAX_REDACT_DEPTH) return "[truncated]";
64
+ if (Array.isArray(value)) return value.map((v) => redactDeep(v, extraKeys, depth + 1));
65
+ if (value && typeof value === "object") {
66
+ const out = {};
67
+ for (const [key, val] of Object.entries(value)) {
68
+ out[key] = isSensitiveKey(key, extraKeys) ? "[REDACTED]" : redactDeep(val, extraKeys, depth + 1);
69
+ }
70
+ return out;
71
+ }
72
+ return value;
73
+ }
74
+ function redactHeaders(headers) {
75
+ if (!headers) return void 0;
76
+ const out = {};
77
+ const set = (key, value) => {
78
+ out[key] = SENSITIVE_HEADERS.has(key.toLowerCase()) ? "[REDACTED]" : value;
79
+ };
80
+ if (typeof headers.forEach === "function" && !Array.isArray(headers)) {
81
+ headers.forEach((value, key) => set(key, value));
82
+ } else {
83
+ for (const [key, value] of Object.entries(headers)) {
84
+ set(key, String(value));
85
+ }
86
+ }
87
+ return out;
88
+ }
89
+ function previewBody(raw, redactFields = []) {
90
+ if (raw === void 0 || raw === null) return void 0;
91
+ let value = raw;
92
+ if (typeof raw === "string") {
93
+ try {
94
+ value = JSON.parse(raw);
95
+ } catch {
96
+ return raw.length > MAX_BODY_CHARS ? raw.slice(0, MAX_BODY_CHARS) + "\u2026[truncated]" : raw;
97
+ }
98
+ }
99
+ const redacted = redactDeep(value, redactFields);
100
+ let text;
101
+ try {
102
+ text = JSON.stringify(redacted);
103
+ } catch {
104
+ return "[unserializable]";
105
+ }
106
+ if (text.length > MAX_BODY_CHARS) {
107
+ return text.slice(0, MAX_BODY_CHARS) + "\u2026[truncated]";
108
+ }
109
+ return redacted;
110
+ }
111
+ function newLogId() {
112
+ const uuid = globalThis?.crypto?.randomUUID;
113
+ if (typeof uuid === "function") return uuid.call(globalThis.crypto);
114
+ return `req_${Date.now().toString(36)}${Math.random().toString(36).slice(2, 10)}`;
115
+ }
116
+
25
117
  // src/http.ts
26
118
  async function apiRequest(cfg, method, path, body) {
27
119
  const controller = new AbortController();
28
120
  const timer = setTimeout(() => controller.abort(), cfg.timeoutMs);
121
+ const url = `${cfg.baseUrl}${path}`;
122
+ const requestHeaders = {
123
+ "Content-Type": "application/json",
124
+ ...cfg.authHeaders
125
+ };
126
+ const startedAt = Date.now();
127
+ const log = cfg.onNetworkLog ? {
128
+ id: newLogId(),
129
+ source: "backend",
130
+ timestamp: new Date(startedAt).toISOString(),
131
+ method,
132
+ url,
133
+ requestHeaders: redactHeaders(requestHeaders),
134
+ requestBody: cfg.logBodies ? previewBody(body, cfg.redactFields) : void 0
135
+ } : null;
136
+ const emit = (extra) => {
137
+ if (!log || !cfg.onNetworkLog) return;
138
+ try {
139
+ cfg.onNetworkLog({ ...log, durationMs: Date.now() - startedAt, ...extra });
140
+ } catch {
141
+ }
142
+ };
29
143
  try {
30
- const res = await cfg.fetchImpl(`${cfg.baseUrl}${path}`, {
144
+ const res = await cfg.fetchImpl(url, {
31
145
  method,
32
- headers: {
33
- "Content-Type": "application/json",
34
- ...cfg.authHeaders
35
- },
146
+ headers: requestHeaders,
36
147
  body: body !== void 0 ? JSON.stringify(body) : void 0,
37
148
  signal: controller.signal
38
149
  });
@@ -43,6 +154,13 @@ async function apiRequest(cfg, method, path, body) {
43
154
  } catch {
44
155
  parsed = text;
45
156
  }
157
+ emit({
158
+ status: res.status,
159
+ statusText: res.statusText,
160
+ ok: res.ok,
161
+ responseHeaders: redactHeaders(res.headers),
162
+ responseBody: cfg.logBodies ? previewBody(parsed ?? text, cfg.redactFields) : void 0
163
+ });
46
164
  if (!res.ok) {
47
165
  const message = parsed && (parsed.message || parsed.error) || `Simplr API error ${res.status}`;
48
166
  throw new SimplrError(message, res.status, parsed);
@@ -51,14 +169,68 @@ async function apiRequest(cfg, method, path, body) {
51
169
  } catch (err) {
52
170
  if (err instanceof SimplrError) throw err;
53
171
  if (err instanceof Error && err.name === "AbortError") {
172
+ emit({ ok: false, error: `timed out after ${cfg.timeoutMs}ms` });
54
173
  throw new SimplrError(`Request to ${path} timed out after ${cfg.timeoutMs}ms`, 0, null);
55
174
  }
175
+ emit({ ok: false, error: err instanceof Error ? err.message : "Network error" });
56
176
  throw new SimplrError(err instanceof Error ? err.message : "Network error", 0, null);
57
177
  } finally {
58
178
  clearTimeout(timer);
59
179
  }
60
180
  }
61
181
 
182
+ // src/network-shipper.ts
183
+ var DEFAULT_BATCH = 25;
184
+ var DEFAULT_FLUSH_MS = 5e3;
185
+ var NetworkLogShipper = class {
186
+ cfg;
187
+ queue = [];
188
+ timer = null;
189
+ constructor(cfg) {
190
+ this.cfg = cfg;
191
+ }
192
+ start() {
193
+ if (this.timer) return;
194
+ const interval = this.cfg.flushIntervalMs ?? DEFAULT_FLUSH_MS;
195
+ this.timer = setInterval(() => {
196
+ void this.flush();
197
+ }, interval);
198
+ this.timer?.unref?.();
199
+ }
200
+ add(entry) {
201
+ this.queue.push({
202
+ ...entry,
203
+ sdk: this.cfg.sdk,
204
+ applicationId: entry.applicationId ?? this.cfg.applicationId,
205
+ environment: entry.environment ?? this.cfg.environment
206
+ });
207
+ if (this.queue.length >= (this.cfg.batchSize ?? DEFAULT_BATCH)) {
208
+ void this.flush();
209
+ }
210
+ }
211
+ async flush() {
212
+ if (this.queue.length === 0) return;
213
+ const logs = this.queue;
214
+ this.queue = [];
215
+ try {
216
+ await this.cfg.fetchImpl(`${this.cfg.baseUrl}/v1/network-logs`, {
217
+ method: "POST",
218
+ headers: {
219
+ "Content-Type": "application/json",
220
+ "X-API-Key": this.cfg.apiKey
221
+ },
222
+ body: JSON.stringify({ logs })
223
+ });
224
+ } catch {
225
+ }
226
+ }
227
+ stop() {
228
+ if (this.timer) clearInterval(this.timer);
229
+ this.timer = null;
230
+ void this.flush();
231
+ }
232
+ };
233
+
62
234
  // src/resources.ts
63
235
  var OrdersResource = class {
64
236
  constructor(cfg) {
@@ -161,7 +333,10 @@ var SimplrFlags = class {
161
333
  authHeaders: { "X-API-Key": options.publicKey },
162
334
  baseUrl: (options.baseUrl || "https://api.simplr.sh").replace(/\/+$/, ""),
163
335
  timeoutMs: options.timeoutMs ?? 15e3,
164
- fetchImpl: options.fetch ?? globalThis.fetch
336
+ fetchImpl: options.fetch ?? globalThis.fetch,
337
+ onNetworkLog: options.onNetworkLog,
338
+ logBodies: options.logBodies,
339
+ redactFields: options.redactFields
165
340
  };
166
341
  this.environment = options.environment;
167
342
  this.refreshIntervalMs = options.refreshIntervalMs ?? 6e4;
@@ -663,13 +838,34 @@ var Simplr = class {
663
838
  /** Webhook signature helpers (no network). */
664
839
  webhooks = webhooks_exports;
665
840
  _flags;
841
+ shipper;
666
842
  constructor(options) {
667
843
  if (!options?.apiKey) throw new Error("Simplr: `apiKey` is required");
844
+ const baseUrl = (options.baseUrl || DEFAULT_BASE_URL).replace(/\/+$/, "");
845
+ const fetchImpl = options.fetch ?? globalThis.fetch;
846
+ if (options.shipNetworkLogs) {
847
+ this.shipper = new NetworkLogShipper({
848
+ baseUrl,
849
+ apiKey: options.apiKey,
850
+ fetchImpl,
851
+ sdk: "node",
852
+ applicationId: options.applicationId,
853
+ environment: options.environment
854
+ });
855
+ this.shipper.start();
856
+ }
857
+ const onNetworkLog = this.shipper || options.onNetworkLog ? (entry) => {
858
+ this.shipper?.add(entry);
859
+ options.onNetworkLog?.(entry);
860
+ } : void 0;
668
861
  this.cfg = {
669
862
  authHeaders: { "X-API-Key": options.apiKey },
670
- baseUrl: (options.baseUrl || DEFAULT_BASE_URL).replace(/\/+$/, ""),
863
+ baseUrl,
671
864
  timeoutMs: options.timeoutMs ?? 15e3,
672
- fetchImpl: options.fetch ?? globalThis.fetch
865
+ fetchImpl,
866
+ onNetworkLog,
867
+ logBodies: options.logBodies ?? options.shipNetworkLogs,
868
+ redactFields: options.redactFields
673
869
  };
674
870
  if (typeof this.cfg.fetchImpl !== "function") {
675
871
  throw new Error("Simplr: no global fetch available \u2014 use Node 18+ or pass `fetch` in options");
@@ -686,7 +882,10 @@ var Simplr = class {
686
882
  environment: options.environment,
687
883
  baseUrl: this.cfg.baseUrl,
688
884
  timeoutMs: this.cfg.timeoutMs,
689
- fetch: this.cfg.fetchImpl
885
+ fetch: this.cfg.fetchImpl,
886
+ onNetworkLog: this.cfg.onNetworkLog,
887
+ logBodies: this.cfg.logBodies,
888
+ redactFields: options.redactFields
690
889
  });
691
890
  }
692
891
  }
@@ -710,9 +909,17 @@ var Simplr = class {
710
909
  checkBulk(items) {
711
910
  return apiRequest(this.cfg, "POST", "/v1/check/bulk", { items });
712
911
  }
912
+ flushNetworkLogs() {
913
+ return this.shipper?.flush() ?? Promise.resolve();
914
+ }
915
+ close() {
916
+ this.shipper?.stop();
917
+ this._flags?.dispose();
918
+ }
713
919
  };
714
920
  var src_default = Simplr;
715
921
  export {
922
+ NetworkLogShipper,
716
923
  Simplr,
717
924
  SimplrAI,
718
925
  SimplrAdmin,