@simplr-ai/vue 1.0.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.cjs ADDED
@@ -0,0 +1,617 @@
1
+ 'use strict';
2
+
3
+ var js = require('@simplr-ai/js');
4
+ var vue = require('vue');
5
+
6
+ // src/plugin.ts
7
+
8
+ // src/keys.ts
9
+ var SimplrInjectionKey = /* @__PURE__ */ Symbol("simplr");
10
+ function installContext(app, ctx) {
11
+ app.provide(SimplrInjectionKey, ctx);
12
+ app.config.globalProperties.$simplr = ctx;
13
+ }
14
+ var stateMap = /* @__PURE__ */ new WeakMap();
15
+ var vSimplrProtect = {
16
+ mounted(el, binding) {
17
+ attach(el, binding);
18
+ },
19
+ updated(el, binding) {
20
+ detach(el);
21
+ attach(el, binding);
22
+ },
23
+ beforeUnmount(el) {
24
+ detach(el);
25
+ }
26
+ };
27
+ function attach(el, binding) {
28
+ const value = binding.value;
29
+ const field = typeof value === "string" ? value : value?.field ?? "field";
30
+ const factory = typeof value === "object" && value?.trackInput ? value.trackInput : null;
31
+ el.setAttribute("data-simplr-field", field);
32
+ if (!factory) {
33
+ const state2 = {
34
+ field,
35
+ onKeyDown: () => {
36
+ },
37
+ onKeyUp: () => {
38
+ },
39
+ onPaste: () => {
40
+ if (js.simplrRUM.isInitialized()) {
41
+ js.simplrRUM.trackAction(`Paste into ${field}`, "custom", {
42
+ target: { tag: "input" }
43
+ });
44
+ }
45
+ }
46
+ };
47
+ el.addEventListener("paste", state2.onPaste);
48
+ stateMap.set(el, state2);
49
+ return;
50
+ }
51
+ const handlers = factory(field);
52
+ const state = {
53
+ field,
54
+ onKeyDown: handlers.onKeyDown,
55
+ onKeyUp: handlers.onKeyUp,
56
+ onPaste: handlers.onPaste
57
+ };
58
+ el.addEventListener("keydown", state.onKeyDown);
59
+ el.addEventListener("keyup", state.onKeyUp);
60
+ el.addEventListener("paste", state.onPaste);
61
+ stateMap.set(el, state);
62
+ }
63
+ function detach(el) {
64
+ const state = stateMap.get(el);
65
+ if (!state) return;
66
+ el.removeEventListener("keydown", state.onKeyDown);
67
+ el.removeEventListener("keyup", state.onKeyUp);
68
+ el.removeEventListener("paste", state.onPaste);
69
+ stateMap.delete(el);
70
+ }
71
+
72
+ // src/plugin.ts
73
+ function resolveBlock(block, fallback) {
74
+ if (block === false || block === void 0) return void 0;
75
+ if (block === true) return fallback;
76
+ return { ...fallback, ...block };
77
+ }
78
+ function createSimplr(config = {}) {
79
+ const apiKey = config.apiKey;
80
+ const fraud = new js.SimplrFraud({ apiKey, ...config.fraud });
81
+ const flagsConfig = resolveBlock(config.flags, {
82
+ apiKey: apiKey ?? "",
83
+ baseUrl: config.baseUrl
84
+ });
85
+ let flags;
86
+ if (flagsConfig) {
87
+ flags = new js.SimplrFlags();
88
+ void flags.initialize(flagsConfig);
89
+ }
90
+ const profilesConfig = resolveBlock(config.profiles, {
91
+ apiKey: apiKey ?? "",
92
+ baseUrl: config.baseUrl
93
+ });
94
+ let profiles;
95
+ if (profilesConfig) {
96
+ profiles = new js.SimplrProfiles(profilesConfig);
97
+ profiles.setDeviceSignalCollector(() => fraud.collectDeviceSignals());
98
+ }
99
+ let rum = void 0;
100
+ if (config.rum) {
101
+ if (!js.simplrRUM.isInitialized()) {
102
+ js.simplrRUM.initialize(config.rum);
103
+ }
104
+ rum = js.simplrRUM;
105
+ }
106
+ const aiConfig = resolveBlock(config.ai, {
107
+ apiKey: apiKey ?? ""
108
+ });
109
+ let ai;
110
+ if (aiConfig && aiConfig.apiKey) {
111
+ ai = new js.SimplrAI(aiConfig);
112
+ }
113
+ const context = { fraud, flags, profiles, rum, ai };
114
+ const plugin = {
115
+ context,
116
+ install(app) {
117
+ installContext(app, context);
118
+ app.directive("simplr-protect", vSimplrProtect);
119
+ }
120
+ };
121
+ return plugin;
122
+ }
123
+ function useSimplrContext() {
124
+ const ctx = vue.inject(SimplrInjectionKey, null);
125
+ if (!ctx) {
126
+ throw new Error(
127
+ "[@simplr-ai/vue] No Simplr context found. Did you call app.use(createSimplr(...))?"
128
+ );
129
+ }
130
+ return ctx;
131
+ }
132
+ function useSimplr() {
133
+ const { fraud } = useSimplrContext();
134
+ return {
135
+ simplr: fraud,
136
+ isReady: vue.computed(() => fraud.isReady),
137
+ collect: () => fraud.collect(),
138
+ collectDeviceSignals: () => fraud.collectDeviceSignals(),
139
+ collectBehaviorSignals: () => fraud.collectBehaviorSignals(),
140
+ reset: () => fraud.reset(),
141
+ trackInput: (fieldName) => fraud.trackInput(fieldName),
142
+ startTracking: () => fraud.startTracking(),
143
+ stopTracking: () => fraud.stopTracking()
144
+ };
145
+ }
146
+ function useDeviceSignals(options = {}) {
147
+ const { fraud } = useSimplrContext();
148
+ const signals = vue.ref(null);
149
+ const loading = vue.ref(false);
150
+ const error = vue.ref(null);
151
+ const refresh = async () => {
152
+ loading.value = true;
153
+ error.value = null;
154
+ try {
155
+ signals.value = await fraud.collectDeviceSignals();
156
+ } catch (e) {
157
+ error.value = e instanceof Error ? e : new Error("Failed to collect device signals");
158
+ } finally {
159
+ loading.value = false;
160
+ }
161
+ };
162
+ if (options.immediate !== false) {
163
+ vue.onMounted(refresh);
164
+ }
165
+ return { signals, loading, error, refresh };
166
+ }
167
+ function getFlags() {
168
+ const { flags } = useSimplrContext();
169
+ if (!flags) {
170
+ throw new Error(
171
+ "[@simplr-ai/vue] Feature flags are not configured. Pass `flags: true` (or a config) to createSimplr()."
172
+ );
173
+ }
174
+ return flags;
175
+ }
176
+ function useFlagsTick(flags) {
177
+ const tick = vue.ref(0);
178
+ const ready = vue.ref(flags.isReady());
179
+ let last = JSON.stringify(flags.getAll());
180
+ let timer = null;
181
+ const check = () => {
182
+ const wasReady = ready.value;
183
+ ready.value = flags.isReady();
184
+ const snapshot = JSON.stringify(flags.getAll());
185
+ if (snapshot !== last || ready.value !== wasReady) {
186
+ last = snapshot;
187
+ tick.value++;
188
+ }
189
+ };
190
+ vue.onMounted(() => {
191
+ check();
192
+ timer = setInterval(check, 500);
193
+ });
194
+ vue.onUnmounted(() => {
195
+ if (timer) clearInterval(timer);
196
+ });
197
+ return { tick, ready };
198
+ }
199
+ function useFeatureFlag(key, ctx) {
200
+ const flags = getFlags();
201
+ const { tick, ready } = useFlagsTick(flags);
202
+ const enabled = vue.computed(() => {
203
+ void tick.value;
204
+ return flags.isEnabled(vue.toValue(key), vue.toValue(ctx));
205
+ });
206
+ return { enabled, isReady: ready };
207
+ }
208
+ function useFeatureFlags(ctx) {
209
+ const client = getFlags();
210
+ const { tick, ready } = useFlagsTick(client);
211
+ const definitions = vue.computed(() => {
212
+ void tick.value;
213
+ return client.getAll();
214
+ });
215
+ const flags = vue.computed(() => {
216
+ void tick.value;
217
+ const evalCtx = vue.toValue(ctx);
218
+ const out = {};
219
+ for (const key of Object.keys(client.getAll())) {
220
+ out[key] = client.isEnabled(key, evalCtx);
221
+ }
222
+ return out;
223
+ });
224
+ return {
225
+ flags,
226
+ definitions,
227
+ isEnabled: (key, c) => client.isEnabled(key, c ?? vue.toValue(ctx)),
228
+ isReady: ready,
229
+ refresh: () => client.refresh(),
230
+ setUser: (userId) => client.setUser(userId)
231
+ };
232
+ }
233
+ function useProfiles() {
234
+ const { profiles } = useSimplrContext();
235
+ if (!profiles) {
236
+ throw new Error(
237
+ "[@simplr-ai/vue] Profiles are not configured. Pass `profiles: true` (or a config) to createSimplr()."
238
+ );
239
+ }
240
+ const loading = vue.ref(false);
241
+ const error = vue.ref(null);
242
+ async function run(fn) {
243
+ loading.value = true;
244
+ error.value = null;
245
+ try {
246
+ return await fn();
247
+ } catch (e) {
248
+ error.value = e instanceof Error ? e : new Error(String(e));
249
+ throw error.value;
250
+ } finally {
251
+ loading.value = false;
252
+ }
253
+ }
254
+ return {
255
+ profiles,
256
+ loading,
257
+ error,
258
+ identify: (externalId, options) => run(() => profiles.identify(externalId, options)),
259
+ submitOrder: (order) => run(() => profiles.submitOrder(order)),
260
+ getProfileRisk: (externalId) => run(() => profiles.getProfileRisk(externalId)),
261
+ reportOutcome: (externalId, outcome) => run(() => profiles.reportOutcome(externalId, outcome))
262
+ };
263
+ }
264
+ function useRum() {
265
+ const ctx = vue.inject(SimplrInjectionKey, null);
266
+ return ctx?.rum ?? js.simplrRUM;
267
+ }
268
+ function useTrackView(name) {
269
+ const rum = useRum();
270
+ vue.onMounted(() => {
271
+ if (rum.isInitialized()) rum.trackView(vue.toValue(name));
272
+ });
273
+ }
274
+ function useTrackAction() {
275
+ const rum = useRum();
276
+ return (name, type, attributes) => {
277
+ if (rum.isInitialized()) rum.trackAction(name, type, { attributes });
278
+ };
279
+ }
280
+ function useTrackError() {
281
+ const rum = useRum();
282
+ return (error, context) => {
283
+ if (rum.isInitialized()) rum.trackError(error, context);
284
+ };
285
+ }
286
+ function useRumLogger() {
287
+ const rum = useRum();
288
+ const make = (level) => (message, context) => {
289
+ if (rum.isInitialized()) rum.log(level, message, context);
290
+ };
291
+ return {
292
+ debug: make("debug"),
293
+ info: make("info"),
294
+ warn: make("warn"),
295
+ error: make("error")
296
+ };
297
+ }
298
+ function useRumUser() {
299
+ const rum = useRum();
300
+ return {
301
+ setUser: (userId, attributes) => {
302
+ if (rum.isInitialized()) rum.setUser(userId, attributes);
303
+ },
304
+ clearUser: () => {
305
+ if (rum.isInitialized()) rum.clearUser();
306
+ }
307
+ };
308
+ }
309
+ function useRumAttributes() {
310
+ const rum = useRum();
311
+ return {
312
+ addAttribute: (key, value) => rum.addAttribute(key, value),
313
+ removeAttribute: (key) => rum.removeAttribute(key)
314
+ };
315
+ }
316
+ function useTrackAsync(operationName) {
317
+ const rum = useRum();
318
+ return async (asyncFn) => {
319
+ const start = Date.now();
320
+ try {
321
+ const result = await asyncFn();
322
+ rum.trackAction(`${operationName} completed`, "custom", {
323
+ attributes: { duration: Date.now() - start, success: true }
324
+ });
325
+ return result;
326
+ } catch (error) {
327
+ rum.trackError(
328
+ error instanceof Error ? error : new Error(String(error)),
329
+ { operationName, duration: Date.now() - start }
330
+ );
331
+ throw error;
332
+ }
333
+ };
334
+ }
335
+ function flushRumOnUnmount() {
336
+ const rum = useRum();
337
+ vue.onUnmounted(() => {
338
+ void rum.flush();
339
+ });
340
+ }
341
+ function resolveClient(config) {
342
+ const ctx = vue.inject(SimplrInjectionKey, null);
343
+ if (ctx?.ai) return ctx.ai;
344
+ if (config) return new js.SimplrAI(config);
345
+ throw new Error(
346
+ "[@simplr-ai/vue] AI delegation is not configured. Pass `ai: true` to createSimplr() or supply a config to useAIDelegation()."
347
+ );
348
+ }
349
+ function useAIDelegation(config) {
350
+ const ai = resolveClient(config);
351
+ const loading = vue.ref(false);
352
+ const error = vue.ref(null);
353
+ async function run(fn) {
354
+ loading.value = true;
355
+ error.value = null;
356
+ try {
357
+ return await fn();
358
+ } catch (e) {
359
+ error.value = e instanceof Error ? e : new Error(String(e));
360
+ throw error.value;
361
+ } finally {
362
+ loading.value = false;
363
+ }
364
+ }
365
+ return {
366
+ ai,
367
+ loading,
368
+ error,
369
+ connect: (options) => run(() => ai.connect(options)),
370
+ createDelegation: (options) => run(() => ai.createDelegation(options)),
371
+ validate: (token, options) => run(() => ai.validate(token, options)),
372
+ revoke: (delegationId, reason) => run(() => ai.revoke(delegationId, reason)),
373
+ list: (userId) => run(() => ai.list(userId)),
374
+ get: (delegationId) => run(() => ai.get(delegationId)),
375
+ stats: () => run(() => ai.stats()),
376
+ revokeAllForUser: (userId, reason) => run(() => ai.revokeAllForUser(userId, reason))
377
+ };
378
+ }
379
+ function useDelegations(config, userId) {
380
+ const ai = resolveClient(config);
381
+ const delegations = vue.ref([]);
382
+ const loading = vue.ref(true);
383
+ const error = vue.ref(null);
384
+ const refresh = async () => {
385
+ loading.value = true;
386
+ error.value = null;
387
+ try {
388
+ delegations.value = await ai.list(userId);
389
+ } catch (e) {
390
+ error.value = e instanceof Error ? e : new Error(String(e));
391
+ } finally {
392
+ loading.value = false;
393
+ }
394
+ };
395
+ const revoke = async (delegationId, reason) => {
396
+ await ai.revoke(delegationId, reason);
397
+ await refresh();
398
+ };
399
+ vue.onMounted(refresh);
400
+ return { delegations, loading, error, refresh, revoke };
401
+ }
402
+ var _sfc_main = /* @__PURE__ */ vue.defineComponent({
403
+ __name: "SimplrProtectedForm",
404
+ emits: ["submit", "error"],
405
+ setup(__props, { expose: __expose, emit: __emit }) {
406
+ const emit = __emit;
407
+ const { collect } = useSimplr();
408
+ const submitting = vue.ref(false);
409
+ async function handleSubmit(event) {
410
+ event.preventDefault();
411
+ submitting.value = true;
412
+ try {
413
+ const signals = await collect();
414
+ emit("submit", signals, event);
415
+ } catch (e) {
416
+ emit("error", e instanceof Error ? e : new Error(String(e)));
417
+ } finally {
418
+ submitting.value = false;
419
+ }
420
+ }
421
+ __expose({ submitting });
422
+ return (_ctx, _cache) => {
423
+ return vue.openBlock(), vue.createElementBlock(
424
+ "form",
425
+ { onSubmit: handleSubmit },
426
+ [vue.renderSlot(_ctx.$slots, "default", { submitting: submitting.value })],
427
+ 32
428
+ /* NEED_HYDRATION */
429
+ );
430
+ };
431
+ }
432
+ });
433
+ var SimplrProtectedForm_default = _sfc_main;
434
+ var _hoisted_1 = ["value", "data-simplr-field"];
435
+ var _sfc_main2 = /* @__PURE__ */ vue.defineComponent({
436
+ __name: "SimplrProtectedInput",
437
+ props: {
438
+ field: {},
439
+ modelValue: {}
440
+ },
441
+ emits: ["update:modelValue"],
442
+ setup(__props, { emit: __emit }) {
443
+ const props = __props;
444
+ const emit = __emit;
445
+ const { trackInput } = useSimplr();
446
+ const tracking = vue.computed(() => trackInput(props.field));
447
+ vue.watchEffect(() => {
448
+ void tracking.value;
449
+ });
450
+ function onInput(event) {
451
+ emit("update:modelValue", event.target.value);
452
+ }
453
+ function onKeyDown(e) {
454
+ tracking.value.onKeyDown(e);
455
+ }
456
+ function onKeyUp(e) {
457
+ tracking.value.onKeyUp(e);
458
+ }
459
+ function onPaste() {
460
+ tracking.value.onPaste();
461
+ }
462
+ return (_ctx, _cache) => {
463
+ return vue.openBlock(), vue.createElementBlock("input", {
464
+ value: __props.modelValue,
465
+ "data-simplr-field": __props.field,
466
+ onInput,
467
+ onKeydown: onKeyDown,
468
+ onKeyup: onKeyUp,
469
+ onPaste
470
+ }, null, 40, _hoisted_1);
471
+ };
472
+ }
473
+ });
474
+ var SimplrProtectedInput_default = _sfc_main2;
475
+ var _sfc_main3 = /* @__PURE__ */ vue.defineComponent({
476
+ __name: "RumView",
477
+ props: { name: {} },
478
+ setup(__props) {
479
+ const props = __props;
480
+ useTrackView(vue.toRef(props, "name"));
481
+ return (_ctx, _cache) => {
482
+ return vue.renderSlot(_ctx.$slots, "default");
483
+ };
484
+ }
485
+ });
486
+ var RumView_default = _sfc_main3;
487
+ var _sfc_main4 = /* @__PURE__ */ vue.defineComponent({
488
+ __name: "RumErrorBoundary",
489
+ emits: ["error"],
490
+ setup(__props, { emit: __emit }) {
491
+ const emit = __emit;
492
+ const rum = useRum();
493
+ const error = vue.ref(null);
494
+ vue.onErrorCaptured((err, _instance, info) => {
495
+ const normalized = err instanceof Error ? err : new Error(String(err));
496
+ error.value = normalized;
497
+ if (rum.isInitialized()) {
498
+ rum.trackError(normalized, {
499
+ vueErrorInfo: info,
500
+ isVueError: true
501
+ });
502
+ }
503
+ emit("error", normalized, info);
504
+ return false;
505
+ });
506
+ return (_ctx, _cache) => {
507
+ return error.value ? vue.renderSlot(_ctx.$slots, "fallback", {
508
+ key: 0,
509
+ error: error.value
510
+ }, () => [_cache[0] || (_cache[0] = vue.createElementVNode(
511
+ "div",
512
+ { style: {
513
+ "padding": "20px",
514
+ "text-align": "center"
515
+ } },
516
+ [vue.createElementVNode("h2", null, "Something went wrong"), vue.createElementVNode("p", null, "We've been notified and are working to fix the issue.")],
517
+ -1
518
+ /* CACHED */
519
+ ))]) : vue.renderSlot(_ctx.$slots, "default", { key: 1 });
520
+ };
521
+ }
522
+ });
523
+ var RumErrorBoundary_default = _sfc_main4;
524
+ function installRumErrorHandler(app, rum = js.simplrRUM) {
525
+ const previous = app.config.errorHandler;
526
+ app.config.errorHandler = (err, instance, info) => {
527
+ const normalized = err instanceof Error ? err : new Error(String(err));
528
+ if (rum.isInitialized()) {
529
+ rum.trackError(normalized, { vueErrorInfo: info, isVueError: true });
530
+ }
531
+ previous?.(err, instance, info);
532
+ };
533
+ }
534
+
535
+ Object.defineProperty(exports, "SimplrAI", {
536
+ enumerable: true,
537
+ get: function () { return js.SimplrAI; }
538
+ });
539
+ Object.defineProperty(exports, "SimplrFlags", {
540
+ enumerable: true,
541
+ get: function () { return js.SimplrFlags; }
542
+ });
543
+ Object.defineProperty(exports, "SimplrFraud", {
544
+ enumerable: true,
545
+ get: function () { return js.SimplrFraud; }
546
+ });
547
+ Object.defineProperty(exports, "SimplrProfiles", {
548
+ enumerable: true,
549
+ get: function () { return js.SimplrProfiles; }
550
+ });
551
+ Object.defineProperty(exports, "SimplrRUM", {
552
+ enumerable: true,
553
+ get: function () { return js.SimplrRUM; }
554
+ });
555
+ Object.defineProperty(exports, "clearDeviceId", {
556
+ enumerable: true,
557
+ get: function () { return js.clearDeviceId; }
558
+ });
559
+ Object.defineProperty(exports, "createFingerprintHash", {
560
+ enumerable: true,
561
+ get: function () { return js.createFingerprintHash; }
562
+ });
563
+ Object.defineProperty(exports, "createSimplrAI", {
564
+ enumerable: true,
565
+ get: function () { return js.createSimplrAI; }
566
+ });
567
+ Object.defineProperty(exports, "createSimplrProfiles", {
568
+ enumerable: true,
569
+ get: function () { return js.createSimplrProfiles; }
570
+ });
571
+ Object.defineProperty(exports, "getDeviceId", {
572
+ enumerable: true,
573
+ get: function () { return js.getDeviceId; }
574
+ });
575
+ Object.defineProperty(exports, "murmurHash3", {
576
+ enumerable: true,
577
+ get: function () { return js.murmurHash3; }
578
+ });
579
+ Object.defineProperty(exports, "sha256", {
580
+ enumerable: true,
581
+ get: function () { return js.sha256; }
582
+ });
583
+ Object.defineProperty(exports, "simplrFlags", {
584
+ enumerable: true,
585
+ get: function () { return js.simplrFlags; }
586
+ });
587
+ Object.defineProperty(exports, "simplrRUM", {
588
+ enumerable: true,
589
+ get: function () { return js.simplrRUM; }
590
+ });
591
+ exports.RumErrorBoundary = RumErrorBoundary_default;
592
+ exports.RumView = RumView_default;
593
+ exports.SimplrInjectionKey = SimplrInjectionKey;
594
+ exports.SimplrProtectedForm = SimplrProtectedForm_default;
595
+ exports.SimplrProtectedInput = SimplrProtectedInput_default;
596
+ exports.createSimplr = createSimplr;
597
+ exports.flushRumOnUnmount = flushRumOnUnmount;
598
+ exports.installRumErrorHandler = installRumErrorHandler;
599
+ exports.useAIDelegation = useAIDelegation;
600
+ exports.useDelegations = useDelegations;
601
+ exports.useDeviceSignals = useDeviceSignals;
602
+ exports.useFeatureFlag = useFeatureFlag;
603
+ exports.useFeatureFlags = useFeatureFlags;
604
+ exports.useProfiles = useProfiles;
605
+ exports.useRum = useRum;
606
+ exports.useRumAttributes = useRumAttributes;
607
+ exports.useRumLogger = useRumLogger;
608
+ exports.useRumUser = useRumUser;
609
+ exports.useSimplr = useSimplr;
610
+ exports.useSimplrContext = useSimplrContext;
611
+ exports.useTrackAction = useTrackAction;
612
+ exports.useTrackAsync = useTrackAsync;
613
+ exports.useTrackError = useTrackError;
614
+ exports.useTrackView = useTrackView;
615
+ exports.vSimplrProtect = vSimplrProtect;
616
+ //# sourceMappingURL=index.cjs.map
617
+ //# sourceMappingURL=index.cjs.map