@simplr-ai/node 1.1.1 → 1.2.1

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,12 @@ 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;
57
+ logSelfCalls?: boolean;
28
58
  }
29
59
  interface CheckInput {
30
60
  email?: string;
@@ -274,6 +304,9 @@ interface FlagsOptions {
274
304
  refreshIntervalMs?: number;
275
305
  timeoutMs?: number;
276
306
  fetch?: typeof fetch;
307
+ onNetworkLog?: NetworkLogger;
308
+ logBodies?: boolean;
309
+ redactFields?: string[];
277
310
  }
278
311
  interface EvalContext {
279
312
  userId?: string;
@@ -473,6 +506,27 @@ declare class WebhookVerificationError extends Error {
473
506
  constructor(message: string);
474
507
  }
475
508
 
509
+ interface ShipperConfig {
510
+ baseUrl: string;
511
+ apiKey: string;
512
+ fetchImpl: typeof fetch;
513
+ sdk: string;
514
+ applicationId?: string;
515
+ environment?: string;
516
+ batchSize?: number;
517
+ flushIntervalMs?: number;
518
+ }
519
+ declare class NetworkLogShipper {
520
+ private readonly cfg;
521
+ private queue;
522
+ private timer;
523
+ constructor(cfg: ShipperConfig);
524
+ start(): void;
525
+ add(entry: NetworkLogEntry): void;
526
+ flush(): Promise<void>;
527
+ stop(): void;
528
+ }
529
+
476
530
  interface SimplrAdminOptions {
477
531
  /** Portal token (JWT) for dashboard/admin operations. */
478
532
  token: string;
@@ -564,6 +618,7 @@ declare class Simplr {
564
618
  /** Webhook signature helpers (no network). */
565
619
  readonly webhooks: typeof webhooks$1;
566
620
  private readonly _flags?;
621
+ private readonly shipper?;
567
622
  constructor(options: SimplrOptions);
568
623
  /**
569
624
  * Server-side feature flags. Requires a `publicKey` in the constructor options
@@ -574,6 +629,8 @@ declare class Simplr {
574
629
  check(input: CheckInput): Promise<CheckResult>;
575
630
  /** Run up to 100 checks at once. */
576
631
  checkBulk(items: CheckInput[]): Promise<BulkResult<CheckResult>>;
632
+ flushNetworkLogs(): Promise<void>;
633
+ close(): void;
577
634
  }
578
635
 
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 };
636
+ 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,12 @@ 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;
57
+ logSelfCalls?: boolean;
28
58
  }
29
59
  interface CheckInput {
30
60
  email?: string;
@@ -274,6 +304,9 @@ interface FlagsOptions {
274
304
  refreshIntervalMs?: number;
275
305
  timeoutMs?: number;
276
306
  fetch?: typeof fetch;
307
+ onNetworkLog?: NetworkLogger;
308
+ logBodies?: boolean;
309
+ redactFields?: string[];
277
310
  }
278
311
  interface EvalContext {
279
312
  userId?: string;
@@ -473,6 +506,27 @@ declare class WebhookVerificationError extends Error {
473
506
  constructor(message: string);
474
507
  }
475
508
 
509
+ interface ShipperConfig {
510
+ baseUrl: string;
511
+ apiKey: string;
512
+ fetchImpl: typeof fetch;
513
+ sdk: string;
514
+ applicationId?: string;
515
+ environment?: string;
516
+ batchSize?: number;
517
+ flushIntervalMs?: number;
518
+ }
519
+ declare class NetworkLogShipper {
520
+ private readonly cfg;
521
+ private queue;
522
+ private timer;
523
+ constructor(cfg: ShipperConfig);
524
+ start(): void;
525
+ add(entry: NetworkLogEntry): void;
526
+ flush(): Promise<void>;
527
+ stop(): void;
528
+ }
529
+
476
530
  interface SimplrAdminOptions {
477
531
  /** Portal token (JWT) for dashboard/admin operations. */
478
532
  token: string;
@@ -564,6 +618,7 @@ declare class Simplr {
564
618
  /** Webhook signature helpers (no network). */
565
619
  readonly webhooks: typeof webhooks$1;
566
620
  private readonly _flags?;
621
+ private readonly shipper?;
567
622
  constructor(options: SimplrOptions);
568
623
  /**
569
624
  * Server-side feature flags. Requires a `publicKey` in the constructor options
@@ -574,6 +629,8 @@ declare class Simplr {
574
629
  check(input: CheckInput): Promise<CheckResult>;
575
630
  /** Run up to 100 checks at once. */
576
631
  checkBulk(items: CheckInput[]): Promise<BulkResult<CheckResult>>;
632
+ flushNetworkLogs(): Promise<void>;
633
+ close(): void;
577
634
  }
578
635
 
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 };
636
+ 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,35 @@ 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
+ const logSelf = !!options.logSelfCalls;
847
+ if (options.shipNetworkLogs && logSelf) {
848
+ this.shipper = new NetworkLogShipper({
849
+ baseUrl,
850
+ apiKey: options.apiKey,
851
+ fetchImpl,
852
+ sdk: "node",
853
+ applicationId: options.applicationId,
854
+ environment: options.environment
855
+ });
856
+ this.shipper.start();
857
+ }
858
+ const onNetworkLog = this.shipper || options.onNetworkLog ? (entry) => {
859
+ this.shipper?.add(entry);
860
+ options.onNetworkLog?.(entry);
861
+ } : void 0;
668
862
  this.cfg = {
669
863
  authHeaders: { "X-API-Key": options.apiKey },
670
- baseUrl: (options.baseUrl || DEFAULT_BASE_URL).replace(/\/+$/, ""),
864
+ baseUrl,
671
865
  timeoutMs: options.timeoutMs ?? 15e3,
672
- fetchImpl: options.fetch ?? globalThis.fetch
866
+ fetchImpl,
867
+ onNetworkLog,
868
+ logBodies: options.logBodies ?? (options.shipNetworkLogs && logSelf),
869
+ redactFields: options.redactFields
673
870
  };
674
871
  if (typeof this.cfg.fetchImpl !== "function") {
675
872
  throw new Error("Simplr: no global fetch available \u2014 use Node 18+ or pass `fetch` in options");
@@ -686,7 +883,10 @@ var Simplr = class {
686
883
  environment: options.environment,
687
884
  baseUrl: this.cfg.baseUrl,
688
885
  timeoutMs: this.cfg.timeoutMs,
689
- fetch: this.cfg.fetchImpl
886
+ fetch: this.cfg.fetchImpl,
887
+ onNetworkLog: this.cfg.onNetworkLog,
888
+ logBodies: this.cfg.logBodies,
889
+ redactFields: options.redactFields
690
890
  });
691
891
  }
692
892
  }
@@ -710,9 +910,17 @@ var Simplr = class {
710
910
  checkBulk(items) {
711
911
  return apiRequest(this.cfg, "POST", "/v1/check/bulk", { items });
712
912
  }
913
+ flushNetworkLogs() {
914
+ return this.shipper?.flush() ?? Promise.resolve();
915
+ }
916
+ close() {
917
+ this.shipper?.stop();
918
+ this._flags?.dispose();
919
+ }
713
920
  };
714
921
  var src_default = Simplr;
715
922
  export {
923
+ NetworkLogShipper,
716
924
  Simplr,
717
925
  SimplrAI,
718
926
  SimplrAdmin,