@simplr-ai/node 1.1.0 → 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/README.md +94 -2
- package/dist/index.cjs +523 -10
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +322 -4
- package/dist/index.d.ts +322 -4
- package/dist/index.js +519 -10
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -20,10 +20,14 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/index.ts
|
|
21
21
|
var src_exports = {};
|
|
22
22
|
__export(src_exports, {
|
|
23
|
+
NetworkLogShipper: () => NetworkLogShipper,
|
|
23
24
|
Simplr: () => Simplr,
|
|
25
|
+
SimplrAI: () => SimplrAI,
|
|
24
26
|
SimplrAdmin: () => SimplrAdmin,
|
|
25
27
|
SimplrError: () => SimplrError,
|
|
26
28
|
SimplrFlags: () => SimplrFlags,
|
|
29
|
+
SimplrProfiles: () => SimplrProfiles,
|
|
30
|
+
SimplrRUM: () => SimplrRUM,
|
|
27
31
|
WebhookVerificationError: () => WebhookVerificationError,
|
|
28
32
|
constructWebhookEvent: () => constructEvent,
|
|
29
33
|
default: () => src_default,
|
|
@@ -49,17 +53,128 @@ var WebhookVerificationError = class extends Error {
|
|
|
49
53
|
}
|
|
50
54
|
};
|
|
51
55
|
|
|
56
|
+
// src/network-log.ts
|
|
57
|
+
var SENSITIVE_HEADERS = /* @__PURE__ */ new Set([
|
|
58
|
+
"authorization",
|
|
59
|
+
"cookie",
|
|
60
|
+
"set-cookie",
|
|
61
|
+
"x-api-key",
|
|
62
|
+
"x-auth-token",
|
|
63
|
+
"x-csrf-token",
|
|
64
|
+
"x-xsrf-token"
|
|
65
|
+
]);
|
|
66
|
+
var SENSITIVE_KEY_PARTS = [
|
|
67
|
+
"password",
|
|
68
|
+
"passwd",
|
|
69
|
+
"secret",
|
|
70
|
+
"token",
|
|
71
|
+
"api_key",
|
|
72
|
+
"apikey",
|
|
73
|
+
"authorization",
|
|
74
|
+
"auth",
|
|
75
|
+
"credential",
|
|
76
|
+
"private_key",
|
|
77
|
+
"card",
|
|
78
|
+
"cardnumber",
|
|
79
|
+
"pan",
|
|
80
|
+
"cvv",
|
|
81
|
+
"cvc",
|
|
82
|
+
"ssn",
|
|
83
|
+
"pin",
|
|
84
|
+
"otp"
|
|
85
|
+
];
|
|
86
|
+
var MAX_REDACT_DEPTH = 8;
|
|
87
|
+
var MAX_BODY_CHARS = 1e4;
|
|
88
|
+
function isSensitiveKey(key, extraKeys) {
|
|
89
|
+
const lower = key.toLowerCase();
|
|
90
|
+
if (extraKeys.some((k) => lower === k.toLowerCase())) return true;
|
|
91
|
+
return SENSITIVE_KEY_PARTS.some((part) => lower.includes(part));
|
|
92
|
+
}
|
|
93
|
+
function redactDeep(value, extraKeys, depth = 0) {
|
|
94
|
+
if (depth >= MAX_REDACT_DEPTH) return "[truncated]";
|
|
95
|
+
if (Array.isArray(value)) return value.map((v) => redactDeep(v, extraKeys, depth + 1));
|
|
96
|
+
if (value && typeof value === "object") {
|
|
97
|
+
const out = {};
|
|
98
|
+
for (const [key, val] of Object.entries(value)) {
|
|
99
|
+
out[key] = isSensitiveKey(key, extraKeys) ? "[REDACTED]" : redactDeep(val, extraKeys, depth + 1);
|
|
100
|
+
}
|
|
101
|
+
return out;
|
|
102
|
+
}
|
|
103
|
+
return value;
|
|
104
|
+
}
|
|
105
|
+
function redactHeaders(headers) {
|
|
106
|
+
if (!headers) return void 0;
|
|
107
|
+
const out = {};
|
|
108
|
+
const set = (key, value) => {
|
|
109
|
+
out[key] = SENSITIVE_HEADERS.has(key.toLowerCase()) ? "[REDACTED]" : value;
|
|
110
|
+
};
|
|
111
|
+
if (typeof headers.forEach === "function" && !Array.isArray(headers)) {
|
|
112
|
+
headers.forEach((value, key) => set(key, value));
|
|
113
|
+
} else {
|
|
114
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
115
|
+
set(key, String(value));
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return out;
|
|
119
|
+
}
|
|
120
|
+
function previewBody(raw, redactFields = []) {
|
|
121
|
+
if (raw === void 0 || raw === null) return void 0;
|
|
122
|
+
let value = raw;
|
|
123
|
+
if (typeof raw === "string") {
|
|
124
|
+
try {
|
|
125
|
+
value = JSON.parse(raw);
|
|
126
|
+
} catch {
|
|
127
|
+
return raw.length > MAX_BODY_CHARS ? raw.slice(0, MAX_BODY_CHARS) + "\u2026[truncated]" : raw;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
const redacted = redactDeep(value, redactFields);
|
|
131
|
+
let text;
|
|
132
|
+
try {
|
|
133
|
+
text = JSON.stringify(redacted);
|
|
134
|
+
} catch {
|
|
135
|
+
return "[unserializable]";
|
|
136
|
+
}
|
|
137
|
+
if (text.length > MAX_BODY_CHARS) {
|
|
138
|
+
return text.slice(0, MAX_BODY_CHARS) + "\u2026[truncated]";
|
|
139
|
+
}
|
|
140
|
+
return redacted;
|
|
141
|
+
}
|
|
142
|
+
function newLogId() {
|
|
143
|
+
const uuid = globalThis?.crypto?.randomUUID;
|
|
144
|
+
if (typeof uuid === "function") return uuid.call(globalThis.crypto);
|
|
145
|
+
return `req_${Date.now().toString(36)}${Math.random().toString(36).slice(2, 10)}`;
|
|
146
|
+
}
|
|
147
|
+
|
|
52
148
|
// src/http.ts
|
|
53
149
|
async function apiRequest(cfg, method, path, body) {
|
|
54
150
|
const controller = new AbortController();
|
|
55
151
|
const timer = setTimeout(() => controller.abort(), cfg.timeoutMs);
|
|
152
|
+
const url = `${cfg.baseUrl}${path}`;
|
|
153
|
+
const requestHeaders = {
|
|
154
|
+
"Content-Type": "application/json",
|
|
155
|
+
...cfg.authHeaders
|
|
156
|
+
};
|
|
157
|
+
const startedAt = Date.now();
|
|
158
|
+
const log = cfg.onNetworkLog ? {
|
|
159
|
+
id: newLogId(),
|
|
160
|
+
source: "backend",
|
|
161
|
+
timestamp: new Date(startedAt).toISOString(),
|
|
162
|
+
method,
|
|
163
|
+
url,
|
|
164
|
+
requestHeaders: redactHeaders(requestHeaders),
|
|
165
|
+
requestBody: cfg.logBodies ? previewBody(body, cfg.redactFields) : void 0
|
|
166
|
+
} : null;
|
|
167
|
+
const emit = (extra) => {
|
|
168
|
+
if (!log || !cfg.onNetworkLog) return;
|
|
169
|
+
try {
|
|
170
|
+
cfg.onNetworkLog({ ...log, durationMs: Date.now() - startedAt, ...extra });
|
|
171
|
+
} catch {
|
|
172
|
+
}
|
|
173
|
+
};
|
|
56
174
|
try {
|
|
57
|
-
const res = await cfg.fetchImpl(
|
|
175
|
+
const res = await cfg.fetchImpl(url, {
|
|
58
176
|
method,
|
|
59
|
-
headers:
|
|
60
|
-
"Content-Type": "application/json",
|
|
61
|
-
...cfg.authHeaders
|
|
62
|
-
},
|
|
177
|
+
headers: requestHeaders,
|
|
63
178
|
body: body !== void 0 ? JSON.stringify(body) : void 0,
|
|
64
179
|
signal: controller.signal
|
|
65
180
|
});
|
|
@@ -70,6 +185,13 @@ async function apiRequest(cfg, method, path, body) {
|
|
|
70
185
|
} catch {
|
|
71
186
|
parsed = text;
|
|
72
187
|
}
|
|
188
|
+
emit({
|
|
189
|
+
status: res.status,
|
|
190
|
+
statusText: res.statusText,
|
|
191
|
+
ok: res.ok,
|
|
192
|
+
responseHeaders: redactHeaders(res.headers),
|
|
193
|
+
responseBody: cfg.logBodies ? previewBody(parsed ?? text, cfg.redactFields) : void 0
|
|
194
|
+
});
|
|
73
195
|
if (!res.ok) {
|
|
74
196
|
const message = parsed && (parsed.message || parsed.error) || `Simplr API error ${res.status}`;
|
|
75
197
|
throw new SimplrError(message, res.status, parsed);
|
|
@@ -78,14 +200,68 @@ async function apiRequest(cfg, method, path, body) {
|
|
|
78
200
|
} catch (err) {
|
|
79
201
|
if (err instanceof SimplrError) throw err;
|
|
80
202
|
if (err instanceof Error && err.name === "AbortError") {
|
|
203
|
+
emit({ ok: false, error: `timed out after ${cfg.timeoutMs}ms` });
|
|
81
204
|
throw new SimplrError(`Request to ${path} timed out after ${cfg.timeoutMs}ms`, 0, null);
|
|
82
205
|
}
|
|
206
|
+
emit({ ok: false, error: err instanceof Error ? err.message : "Network error" });
|
|
83
207
|
throw new SimplrError(err instanceof Error ? err.message : "Network error", 0, null);
|
|
84
208
|
} finally {
|
|
85
209
|
clearTimeout(timer);
|
|
86
210
|
}
|
|
87
211
|
}
|
|
88
212
|
|
|
213
|
+
// src/network-shipper.ts
|
|
214
|
+
var DEFAULT_BATCH = 25;
|
|
215
|
+
var DEFAULT_FLUSH_MS = 5e3;
|
|
216
|
+
var NetworkLogShipper = class {
|
|
217
|
+
cfg;
|
|
218
|
+
queue = [];
|
|
219
|
+
timer = null;
|
|
220
|
+
constructor(cfg) {
|
|
221
|
+
this.cfg = cfg;
|
|
222
|
+
}
|
|
223
|
+
start() {
|
|
224
|
+
if (this.timer) return;
|
|
225
|
+
const interval = this.cfg.flushIntervalMs ?? DEFAULT_FLUSH_MS;
|
|
226
|
+
this.timer = setInterval(() => {
|
|
227
|
+
void this.flush();
|
|
228
|
+
}, interval);
|
|
229
|
+
this.timer?.unref?.();
|
|
230
|
+
}
|
|
231
|
+
add(entry) {
|
|
232
|
+
this.queue.push({
|
|
233
|
+
...entry,
|
|
234
|
+
sdk: this.cfg.sdk,
|
|
235
|
+
applicationId: entry.applicationId ?? this.cfg.applicationId,
|
|
236
|
+
environment: entry.environment ?? this.cfg.environment
|
|
237
|
+
});
|
|
238
|
+
if (this.queue.length >= (this.cfg.batchSize ?? DEFAULT_BATCH)) {
|
|
239
|
+
void this.flush();
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
async flush() {
|
|
243
|
+
if (this.queue.length === 0) return;
|
|
244
|
+
const logs = this.queue;
|
|
245
|
+
this.queue = [];
|
|
246
|
+
try {
|
|
247
|
+
await this.cfg.fetchImpl(`${this.cfg.baseUrl}/v1/network-logs`, {
|
|
248
|
+
method: "POST",
|
|
249
|
+
headers: {
|
|
250
|
+
"Content-Type": "application/json",
|
|
251
|
+
"X-API-Key": this.cfg.apiKey
|
|
252
|
+
},
|
|
253
|
+
body: JSON.stringify({ logs })
|
|
254
|
+
});
|
|
255
|
+
} catch {
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
stop() {
|
|
259
|
+
if (this.timer) clearInterval(this.timer);
|
|
260
|
+
this.timer = null;
|
|
261
|
+
void this.flush();
|
|
262
|
+
}
|
|
263
|
+
};
|
|
264
|
+
|
|
89
265
|
// src/resources.ts
|
|
90
266
|
var OrdersResource = class {
|
|
91
267
|
constructor(cfg) {
|
|
@@ -188,7 +364,10 @@ var SimplrFlags = class {
|
|
|
188
364
|
authHeaders: { "X-API-Key": options.publicKey },
|
|
189
365
|
baseUrl: (options.baseUrl || "https://api.simplr.sh").replace(/\/+$/, ""),
|
|
190
366
|
timeoutMs: options.timeoutMs ?? 15e3,
|
|
191
|
-
fetchImpl: options.fetch ?? globalThis.fetch
|
|
367
|
+
fetchImpl: options.fetch ?? globalThis.fetch,
|
|
368
|
+
onNetworkLog: options.onNetworkLog,
|
|
369
|
+
logBodies: options.logBodies,
|
|
370
|
+
redactFields: options.redactFields
|
|
192
371
|
};
|
|
193
372
|
this.environment = options.environment;
|
|
194
373
|
this.refreshIntervalMs = options.refreshIntervalMs ?? 6e4;
|
|
@@ -210,7 +389,7 @@ var SimplrFlags = class {
|
|
|
210
389
|
}
|
|
211
390
|
/** Re-fetch the flag config (counts as one billable request). */
|
|
212
391
|
async refresh() {
|
|
213
|
-
const path = this.environment ? `/v1/flags?environment=${this.environment}` : "/v1/flags";
|
|
392
|
+
const path = this.environment ? `/v1/flags?environment=${encodeURIComponent(this.environment)}` : "/v1/flags";
|
|
214
393
|
try {
|
|
215
394
|
const content = await apiRequest(this.cfg, "GET", path);
|
|
216
395
|
const list = content?.flags || [];
|
|
@@ -246,6 +425,295 @@ var SimplrFlags = class {
|
|
|
246
425
|
}
|
|
247
426
|
};
|
|
248
427
|
|
|
428
|
+
// src/profiles.ts
|
|
429
|
+
var SimplrProfiles = class {
|
|
430
|
+
constructor(cfg) {
|
|
431
|
+
this.cfg = cfg;
|
|
432
|
+
}
|
|
433
|
+
cfg;
|
|
434
|
+
/**
|
|
435
|
+
* Identify a user — creates or updates an anonymous profile and (optionally)
|
|
436
|
+
* links a device fingerprint. POST /v1/profiles.
|
|
437
|
+
*/
|
|
438
|
+
identify(externalId, options) {
|
|
439
|
+
const { profileType, fingerprintHash, ...rest } = options ?? {};
|
|
440
|
+
const body = {
|
|
441
|
+
external_id: externalId,
|
|
442
|
+
profile_type: profileType || "customer",
|
|
443
|
+
...rest
|
|
444
|
+
};
|
|
445
|
+
if (fingerprintHash) body.fingerprint_hash = fingerprintHash;
|
|
446
|
+
return apiRequest(this.cfg, "POST", "/v1/profiles", body);
|
|
447
|
+
}
|
|
448
|
+
/** Submit an order for real-time fraud scoring. POST /v1/orders. */
|
|
449
|
+
submitOrder(order) {
|
|
450
|
+
return apiRequest(this.cfg, "POST", "/v1/orders", order);
|
|
451
|
+
}
|
|
452
|
+
/** Get the risk profile for a user. GET /v1/profiles/{externalId}. */
|
|
453
|
+
getProfileRisk(externalId) {
|
|
454
|
+
return apiRequest(
|
|
455
|
+
this.cfg,
|
|
456
|
+
"GET",
|
|
457
|
+
`/v1/profiles/${encodeURIComponent(externalId)}`
|
|
458
|
+
);
|
|
459
|
+
}
|
|
460
|
+
/** Report a profile as fraud or legitimate. POST /v1/profiles/{externalId}/outcome. */
|
|
461
|
+
async reportOutcome(externalId, outcome) {
|
|
462
|
+
await apiRequest(
|
|
463
|
+
this.cfg,
|
|
464
|
+
"POST",
|
|
465
|
+
`/v1/profiles/${encodeURIComponent(externalId)}/outcome`,
|
|
466
|
+
{ outcome }
|
|
467
|
+
);
|
|
468
|
+
}
|
|
469
|
+
};
|
|
470
|
+
|
|
471
|
+
// src/rum.ts
|
|
472
|
+
var DEFAULT_BATCH_SIZE = 30;
|
|
473
|
+
var DEFAULT_FLUSH_INTERVAL = 1e4;
|
|
474
|
+
var DEFAULT_ENDPOINT = "/v1/rum/events";
|
|
475
|
+
function genId() {
|
|
476
|
+
return Date.now().toString(36) + Math.random().toString(36).slice(2, 10);
|
|
477
|
+
}
|
|
478
|
+
var SimplrRUM = class {
|
|
479
|
+
constructor(cfg) {
|
|
480
|
+
this.cfg = cfg;
|
|
481
|
+
}
|
|
482
|
+
cfg;
|
|
483
|
+
config = null;
|
|
484
|
+
initialized = false;
|
|
485
|
+
queue = [];
|
|
486
|
+
timer = null;
|
|
487
|
+
flushing = false;
|
|
488
|
+
sessionId = null;
|
|
489
|
+
currentViewId = null;
|
|
490
|
+
userId;
|
|
491
|
+
userAttributes;
|
|
492
|
+
globalAttributes = {};
|
|
493
|
+
batchSize = DEFAULT_BATCH_SIZE;
|
|
494
|
+
endpoint = DEFAULT_ENDPOINT;
|
|
495
|
+
/** Initialize the SDK, start a session, and begin the flush timer. */
|
|
496
|
+
initialize(config) {
|
|
497
|
+
if (this.initialized) return;
|
|
498
|
+
this.config = config;
|
|
499
|
+
this.batchSize = config.batchSize ?? DEFAULT_BATCH_SIZE;
|
|
500
|
+
this.endpoint = config.endpoint ?? DEFAULT_ENDPOINT;
|
|
501
|
+
this.sessionId = genId();
|
|
502
|
+
this.initialized = true;
|
|
503
|
+
this.trackEvent("session_start");
|
|
504
|
+
const interval = config.flushInterval ?? DEFAULT_FLUSH_INTERVAL;
|
|
505
|
+
if (interval > 0) {
|
|
506
|
+
this.timer = setInterval(() => {
|
|
507
|
+
void this.flush();
|
|
508
|
+
}, interval);
|
|
509
|
+
this.timer?.unref?.();
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
isInitialized() {
|
|
513
|
+
return this.initialized;
|
|
514
|
+
}
|
|
515
|
+
/** Associate subsequent events with a user. */
|
|
516
|
+
setUser(userId, attributes) {
|
|
517
|
+
this.userId = userId;
|
|
518
|
+
this.userAttributes = attributes;
|
|
519
|
+
}
|
|
520
|
+
clearUser() {
|
|
521
|
+
this.userId = void 0;
|
|
522
|
+
this.userAttributes = void 0;
|
|
523
|
+
}
|
|
524
|
+
addAttribute(key, value) {
|
|
525
|
+
this.globalAttributes[key] = value;
|
|
526
|
+
}
|
|
527
|
+
removeAttribute(key) {
|
|
528
|
+
delete this.globalAttributes[key];
|
|
529
|
+
}
|
|
530
|
+
/** Track a screen/page view. */
|
|
531
|
+
trackView(name, attributes) {
|
|
532
|
+
if (!this.initialized) return;
|
|
533
|
+
this.currentViewId = genId();
|
|
534
|
+
this.trackEvent("view", {
|
|
535
|
+
view: { id: this.currentViewId, name },
|
|
536
|
+
attributes
|
|
537
|
+
});
|
|
538
|
+
}
|
|
539
|
+
/** Track a user action. */
|
|
540
|
+
trackAction(name, attributes) {
|
|
541
|
+
if (!this.initialized) return;
|
|
542
|
+
this.trackEvent("action", { action: { name, type: "custom" }, attributes });
|
|
543
|
+
}
|
|
544
|
+
/** Track an error. */
|
|
545
|
+
trackError(error, attributes) {
|
|
546
|
+
if (!this.initialized) return;
|
|
547
|
+
const data = error instanceof Error ? { message: error.message, stack: error.stack, type: error.constructor.name } : error;
|
|
548
|
+
this.trackEvent("error", { error: data, attributes });
|
|
549
|
+
}
|
|
550
|
+
/** Emit a log line. */
|
|
551
|
+
log(level, message, attributes) {
|
|
552
|
+
if (!this.initialized) return;
|
|
553
|
+
this.trackEvent("log", { log: { level, message }, attributes });
|
|
554
|
+
}
|
|
555
|
+
trackEvent(type, data) {
|
|
556
|
+
if (!this.initialized || !this.sessionId) return;
|
|
557
|
+
const event = {
|
|
558
|
+
type,
|
|
559
|
+
timestamp: Date.now(),
|
|
560
|
+
sessionId: this.sessionId,
|
|
561
|
+
viewId: this.currentViewId || void 0,
|
|
562
|
+
userId: this.userId,
|
|
563
|
+
applicationId: this.config.applicationId,
|
|
564
|
+
applicationVersion: this.config?.applicationVersion,
|
|
565
|
+
environment: this.config?.environment,
|
|
566
|
+
userAttributes: this.userAttributes,
|
|
567
|
+
globalAttributes: Object.keys(this.globalAttributes).length > 0 ? this.globalAttributes : void 0,
|
|
568
|
+
...data
|
|
569
|
+
};
|
|
570
|
+
this.queue.push(event);
|
|
571
|
+
if (this.queue.length >= this.batchSize) void this.flush();
|
|
572
|
+
}
|
|
573
|
+
/** Flush queued events to POST /v1/rum/events. */
|
|
574
|
+
async flush() {
|
|
575
|
+
if (this.flushing || this.queue.length === 0) return;
|
|
576
|
+
this.flushing = true;
|
|
577
|
+
const events = this.queue;
|
|
578
|
+
this.queue = [];
|
|
579
|
+
try {
|
|
580
|
+
await apiRequest(this.cfg, "POST", this.endpoint, {
|
|
581
|
+
events,
|
|
582
|
+
sentAt: Date.now()
|
|
583
|
+
});
|
|
584
|
+
} catch {
|
|
585
|
+
this.queue = [...events, ...this.queue];
|
|
586
|
+
} finally {
|
|
587
|
+
this.flushing = false;
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
/** End the session, flush remaining events, and stop the timer. */
|
|
591
|
+
async stopSession() {
|
|
592
|
+
if (!this.initialized) return;
|
|
593
|
+
this.trackEvent("session_end");
|
|
594
|
+
await this.flush();
|
|
595
|
+
if (this.timer) {
|
|
596
|
+
clearInterval(this.timer);
|
|
597
|
+
this.timer = null;
|
|
598
|
+
}
|
|
599
|
+
this.initialized = false;
|
|
600
|
+
}
|
|
601
|
+
getSessionId() {
|
|
602
|
+
return this.sessionId;
|
|
603
|
+
}
|
|
604
|
+
getViewId() {
|
|
605
|
+
return this.currentViewId;
|
|
606
|
+
}
|
|
607
|
+
};
|
|
608
|
+
|
|
609
|
+
// src/ai.ts
|
|
610
|
+
function mapDelegation(d) {
|
|
611
|
+
return {
|
|
612
|
+
delegationId: d.delegation_id,
|
|
613
|
+
endUserId: d.end_user_id,
|
|
614
|
+
bindingMode: d.binding_mode,
|
|
615
|
+
status: d.status,
|
|
616
|
+
expiresAt: d.expires_at,
|
|
617
|
+
useCount: d.use_count,
|
|
618
|
+
lastUsedAt: d.last_used_at,
|
|
619
|
+
createdAt: d.created_at
|
|
620
|
+
};
|
|
621
|
+
}
|
|
622
|
+
var SimplrAI = class {
|
|
623
|
+
constructor(cfg) {
|
|
624
|
+
this.cfg = cfg;
|
|
625
|
+
}
|
|
626
|
+
cfg;
|
|
627
|
+
/** Create a new AI delegation token for a user. POST /v1/ai/delegations. */
|
|
628
|
+
async createDelegation(options) {
|
|
629
|
+
const content = await apiRequest(this.cfg, "POST", "/v1/ai/delegations", {
|
|
630
|
+
end_user_id: options.userId,
|
|
631
|
+
end_user_email: options.email,
|
|
632
|
+
binding: options.binding || "any_location",
|
|
633
|
+
expires_in_days: options.expiresInDays || 7,
|
|
634
|
+
session_id: options.sessionId,
|
|
635
|
+
fingerprint_hash: options.fingerprintHash
|
|
636
|
+
});
|
|
637
|
+
const d = content.delegation;
|
|
638
|
+
return {
|
|
639
|
+
token: d.token,
|
|
640
|
+
delegationId: d.delegation_id,
|
|
641
|
+
expiresAt: d.expires_at,
|
|
642
|
+
bindingMode: d.binding_mode
|
|
643
|
+
};
|
|
644
|
+
}
|
|
645
|
+
/** Validate (introspect) an AI delegation token. POST /v1/ai/validate. */
|
|
646
|
+
async validate(token, options) {
|
|
647
|
+
try {
|
|
648
|
+
const content = await apiRequest(this.cfg, "POST", "/v1/ai/validate", {
|
|
649
|
+
token,
|
|
650
|
+
fingerprint_hash: options?.fingerprintHash,
|
|
651
|
+
ai_provider: options?.aiProvider,
|
|
652
|
+
action: options?.action
|
|
653
|
+
});
|
|
654
|
+
return {
|
|
655
|
+
valid: true,
|
|
656
|
+
sessionType: content.session_type,
|
|
657
|
+
endUserId: content.end_user_id,
|
|
658
|
+
delegation: content.delegation ? {
|
|
659
|
+
delegationId: content.delegation.delegation_id,
|
|
660
|
+
bindingMode: content.delegation.binding_mode,
|
|
661
|
+
expiresAt: content.delegation.expires_at,
|
|
662
|
+
useCount: content.delegation.use_count
|
|
663
|
+
} : void 0
|
|
664
|
+
};
|
|
665
|
+
} catch (err) {
|
|
666
|
+
return { valid: false, error: err instanceof Error ? err.message : "Validation failed" };
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
/** Revoke a delegation. POST /v1/ai/delegations/{id}/revoke. */
|
|
670
|
+
async revoke(delegationId, reason) {
|
|
671
|
+
await apiRequest(
|
|
672
|
+
this.cfg,
|
|
673
|
+
"POST",
|
|
674
|
+
`/v1/ai/delegations/${encodeURIComponent(delegationId)}/revoke`,
|
|
675
|
+
{ reason }
|
|
676
|
+
);
|
|
677
|
+
}
|
|
678
|
+
/** List delegations, optionally filtered by user. GET /v1/ai/delegations. */
|
|
679
|
+
async list(userId) {
|
|
680
|
+
const path = userId ? `/v1/ai/delegations?end_user_id=${encodeURIComponent(userId)}` : "/v1/ai/delegations";
|
|
681
|
+
const content = await apiRequest(this.cfg, "GET", path);
|
|
682
|
+
return (content.delegations || []).map(mapDelegation);
|
|
683
|
+
}
|
|
684
|
+
/** Get a single delegation. GET /v1/ai/delegations/{id}. */
|
|
685
|
+
async get(delegationId) {
|
|
686
|
+
const content = await apiRequest(
|
|
687
|
+
this.cfg,
|
|
688
|
+
"GET",
|
|
689
|
+
`/v1/ai/delegations/${encodeURIComponent(delegationId)}`
|
|
690
|
+
);
|
|
691
|
+
return mapDelegation(content.delegation);
|
|
692
|
+
}
|
|
693
|
+
/** Get delegation statistics. GET /v1/ai/stats. */
|
|
694
|
+
async stats() {
|
|
695
|
+
const content = await apiRequest(this.cfg, "GET", "/v1/ai/stats");
|
|
696
|
+
const s = content.stats;
|
|
697
|
+
return {
|
|
698
|
+
totalDelegations: s.total_delegations,
|
|
699
|
+
activeDelegations: s.active_delegations,
|
|
700
|
+
totalUses: s.total_uses,
|
|
701
|
+
delegationsByBinding: {
|
|
702
|
+
verifiedDevice: s.delegations_by_binding.verified_device,
|
|
703
|
+
anyLocation: s.delegations_by_binding.any_location
|
|
704
|
+
}
|
|
705
|
+
};
|
|
706
|
+
}
|
|
707
|
+
/** Revoke all delegations for a user (e.g. on logout). POST /v1/ai/revoke-all. */
|
|
708
|
+
async revokeAllForUser(userId, reason) {
|
|
709
|
+
const content = await apiRequest(this.cfg, "POST", "/v1/ai/revoke-all", {
|
|
710
|
+
end_user_id: userId,
|
|
711
|
+
reason
|
|
712
|
+
});
|
|
713
|
+
return content.revoked_count;
|
|
714
|
+
}
|
|
715
|
+
};
|
|
716
|
+
|
|
249
717
|
// src/webhooks.ts
|
|
250
718
|
var webhooks_exports = {};
|
|
251
719
|
__export(webhooks_exports, {
|
|
@@ -392,16 +860,43 @@ var Simplr = class {
|
|
|
392
860
|
orders;
|
|
393
861
|
phone;
|
|
394
862
|
edge;
|
|
863
|
+
/** Anonymous user profiles + order fraud monitoring. */
|
|
864
|
+
profiles;
|
|
865
|
+
/** Real User Monitoring — batched events to /v1/rum/events. */
|
|
866
|
+
rum;
|
|
867
|
+
/** AI delegation — OAuth-like AI authentication. */
|
|
868
|
+
ai;
|
|
395
869
|
/** Webhook signature helpers (no network). */
|
|
396
870
|
webhooks = webhooks_exports;
|
|
397
871
|
_flags;
|
|
872
|
+
shipper;
|
|
398
873
|
constructor(options) {
|
|
399
874
|
if (!options?.apiKey) throw new Error("Simplr: `apiKey` is required");
|
|
875
|
+
const baseUrl = (options.baseUrl || DEFAULT_BASE_URL).replace(/\/+$/, "");
|
|
876
|
+
const fetchImpl = options.fetch ?? globalThis.fetch;
|
|
877
|
+
if (options.shipNetworkLogs) {
|
|
878
|
+
this.shipper = new NetworkLogShipper({
|
|
879
|
+
baseUrl,
|
|
880
|
+
apiKey: options.apiKey,
|
|
881
|
+
fetchImpl,
|
|
882
|
+
sdk: "node",
|
|
883
|
+
applicationId: options.applicationId,
|
|
884
|
+
environment: options.environment
|
|
885
|
+
});
|
|
886
|
+
this.shipper.start();
|
|
887
|
+
}
|
|
888
|
+
const onNetworkLog = this.shipper || options.onNetworkLog ? (entry) => {
|
|
889
|
+
this.shipper?.add(entry);
|
|
890
|
+
options.onNetworkLog?.(entry);
|
|
891
|
+
} : void 0;
|
|
400
892
|
this.cfg = {
|
|
401
893
|
authHeaders: { "X-API-Key": options.apiKey },
|
|
402
|
-
baseUrl
|
|
894
|
+
baseUrl,
|
|
403
895
|
timeoutMs: options.timeoutMs ?? 15e3,
|
|
404
|
-
fetchImpl
|
|
896
|
+
fetchImpl,
|
|
897
|
+
onNetworkLog,
|
|
898
|
+
logBodies: options.logBodies ?? options.shipNetworkLogs,
|
|
899
|
+
redactFields: options.redactFields
|
|
405
900
|
};
|
|
406
901
|
if (typeof this.cfg.fetchImpl !== "function") {
|
|
407
902
|
throw new Error("Simplr: no global fetch available \u2014 use Node 18+ or pass `fetch` in options");
|
|
@@ -409,12 +904,19 @@ var Simplr = class {
|
|
|
409
904
|
this.orders = new OrdersResource(this.cfg);
|
|
410
905
|
this.phone = new PhoneResource(this.cfg);
|
|
411
906
|
this.edge = new EdgeResource(this.cfg);
|
|
907
|
+
this.profiles = new SimplrProfiles(this.cfg);
|
|
908
|
+
this.rum = new SimplrRUM(this.cfg);
|
|
909
|
+
this.ai = new SimplrAI(this.cfg);
|
|
412
910
|
if (options.publicKey) {
|
|
413
911
|
this._flags = new SimplrFlags({
|
|
414
912
|
publicKey: options.publicKey,
|
|
913
|
+
environment: options.environment,
|
|
415
914
|
baseUrl: this.cfg.baseUrl,
|
|
416
915
|
timeoutMs: this.cfg.timeoutMs,
|
|
417
|
-
fetch: this.cfg.fetchImpl
|
|
916
|
+
fetch: this.cfg.fetchImpl,
|
|
917
|
+
onNetworkLog: this.cfg.onNetworkLog,
|
|
918
|
+
logBodies: this.cfg.logBodies,
|
|
919
|
+
redactFields: options.redactFields
|
|
418
920
|
});
|
|
419
921
|
}
|
|
420
922
|
}
|
|
@@ -438,14 +940,25 @@ var Simplr = class {
|
|
|
438
940
|
checkBulk(items) {
|
|
439
941
|
return apiRequest(this.cfg, "POST", "/v1/check/bulk", { items });
|
|
440
942
|
}
|
|
943
|
+
flushNetworkLogs() {
|
|
944
|
+
return this.shipper?.flush() ?? Promise.resolve();
|
|
945
|
+
}
|
|
946
|
+
close() {
|
|
947
|
+
this.shipper?.stop();
|
|
948
|
+
this._flags?.dispose();
|
|
949
|
+
}
|
|
441
950
|
};
|
|
442
951
|
var src_default = Simplr;
|
|
443
952
|
// Annotate the CommonJS export names for ESM import in node:
|
|
444
953
|
0 && (module.exports = {
|
|
954
|
+
NetworkLogShipper,
|
|
445
955
|
Simplr,
|
|
956
|
+
SimplrAI,
|
|
446
957
|
SimplrAdmin,
|
|
447
958
|
SimplrError,
|
|
448
959
|
SimplrFlags,
|
|
960
|
+
SimplrProfiles,
|
|
961
|
+
SimplrRUM,
|
|
449
962
|
WebhookVerificationError,
|
|
450
963
|
constructWebhookEvent,
|
|
451
964
|
verifyWebhook
|