@emit-vision/sdk-js 0.4.0 → 0.4.2

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.ts CHANGED
@@ -38,6 +38,7 @@ type CaptureOptions = {
38
38
  release?: string;
39
39
  sessionId?: string;
40
40
  user?: EmitVisionUser;
41
+ groupId?: string;
41
42
  context?: Record<string, unknown>;
42
43
  deployment?: DeploymentContext;
43
44
  featureFlags?: FeatureFlagsContext;
@@ -60,6 +61,10 @@ type EmitVisionOptions = {
60
61
  flushIntervalMs?: number;
61
62
  batchSize?: number;
62
63
  fetchImpl?: typeof fetch;
64
+ /** Project ID — passed through to the DevTools debug channel so the extension can build dashboard deep links. Not required for event capture. */
65
+ projectId?: string;
66
+ /** Initial group ID to associate with all events. Can be overridden per-event or updated via `group()`. */
67
+ groupId?: string;
63
68
  /** TTL in milliseconds for in-memory flag evaluation cache. Default: 60 000 (1 minute). */
64
69
  flagEvalTtlMs?: number;
65
70
  /** Initial delay (ms) before retrying after a flush failure. Default: 1000. Doubles each consecutive failure up to {@link retryBackoffMaxMs}. Set to 0 to retry immediately on every tick. */
@@ -68,17 +73,72 @@ type EmitVisionOptions = {
68
73
  retryBackoffMaxMs?: number;
69
74
  /** Disable the SDK after this many consecutive flush failures: the queue is dropped, future captures become no-ops, and a single warning is logged. Default: 20. Set to 0 to disable the cap and retry forever. */
70
75
  maxConsecutiveFlushFailures?: number;
76
+ /** When true, events are queued in memory but not sent until `consent()` is called. Default: false. */
77
+ consentRequired?: boolean;
78
+ /** Set to true when `consentRequired` is true but consent is already known at init time (e.g. read from a cookie server-side). Skips the consent gate. Default: false. */
79
+ consentGiven?: boolean;
71
80
  };
72
81
  type RequiredEmitVisionOptions = Required<Pick<EmitVisionOptions, "apiKey" | "endpoint" | "flushIntervalMs" | "batchSize" | "fetchImpl" | "flushOnCapture" | "flagEvalTtlMs" | "retryBackoffInitialMs" | "retryBackoffMaxMs" | "maxConsecutiveFlushFailures">> & Pick<EmitVisionOptions, "environment" | "release" | "sessionId" | "label" | "deployment" | "featureFlags" | "debug"> & {
73
82
  autoCapture: Required<AutoCaptureOptions> & {
74
83
  flagExposures: boolean;
75
84
  pageViews: boolean;
76
85
  };
86
+ projectId?: string;
87
+ groupId?: string;
88
+ consentRequired?: boolean;
89
+ consentGiven?: boolean;
90
+ };
91
+
92
+ type DevToolsMessage = {
93
+ type: "init";
94
+ payload: {
95
+ sdkVersion: string;
96
+ endpoint: string;
97
+ environment?: string;
98
+ release?: string;
99
+ sessionId: string;
100
+ autoCapture: AutoCaptureOptions;
101
+ projectId?: string;
102
+ consentRequired?: boolean;
103
+ };
104
+ } | {
105
+ type: "event";
106
+ payload: {
107
+ name: string;
108
+ properties?: Record<string, unknown>;
109
+ eventId?: string;
110
+ timestamp?: string;
111
+ environment?: string;
112
+ sessionId?: string;
113
+ };
114
+ } | {
115
+ type: "identify";
116
+ payload: {
117
+ userId?: string;
118
+ traits?: Record<string, unknown>;
119
+ };
120
+ } | {
121
+ type: "flush";
122
+ payload: {
123
+ count: number;
124
+ };
125
+ } | {
126
+ type: "error";
127
+ payload: {
128
+ message: string;
129
+ };
130
+ } | {
131
+ type: "group";
132
+ payload: {
133
+ groupId: string;
134
+ traits?: Record<string, unknown>;
135
+ };
77
136
  };
78
137
 
79
138
  declare class EmitVisionClient {
80
139
  private readonly options;
81
140
  private user;
141
+ private groupId;
82
142
  private context;
83
143
  private deployment;
84
144
  private featureFlags;
@@ -86,10 +146,13 @@ declare class EmitVisionClient {
86
146
  private readonly flagEval;
87
147
  private readonly autoCapture;
88
148
  private readonly flusher;
149
+ private readonly persistOnHide;
150
+ private readonly persistOnHidden;
89
151
  constructor(options: RequiredEmitVisionOptions);
90
152
  captureEvent(name: string, properties?: Record<string, unknown>, options?: CaptureOptions): void;
91
153
  captureError(error: unknown, options?: CaptureOptions): void;
92
154
  identify(userIdOrUser: string | EmitVisionUser, traits?: EmitVisionTraits): void;
155
+ group(groupId: string, traits?: Record<string, unknown>): void;
93
156
  setContext(context: Record<string, unknown>): void;
94
157
  setDeploymentContext(context: DeploymentContext): void;
95
158
  setFeatureFlags(featureFlags: FeatureFlagsContext): void;
@@ -97,8 +160,14 @@ declare class EmitVisionClient {
97
160
  getFlag<T extends string | number | boolean | null>(flagKey: string, fallback: T, opts: FlagEvaluationOptions): Promise<T>;
98
161
  refreshFlags(opts: FlagEvaluationOptions): Promise<FeatureFlagsContext>;
99
162
  capturePageView(path?: string): void;
163
+ captureFeedback(feedback: {
164
+ message: string;
165
+ rating?: number;
166
+ url?: string;
167
+ }, options?: CaptureOptions): void;
100
168
  captureExposure(flagKey: string, variantKey: string, variantValue?: string | number | boolean | null, reason?: string, environment?: string): void;
101
169
  setTags(tags: Record<string, string>): void;
170
+ consent(): Promise<void>;
102
171
  flush(): Promise<void>;
103
172
  close(): void;
104
173
  private withDefaults;
@@ -108,6 +177,11 @@ declare function init(options: EmitVisionOptions): EmitVisionClient;
108
177
  declare function captureEvent(name: string, properties?: Record<string, unknown>, options?: CaptureOptions): void;
109
178
  declare function captureError(error: unknown, options?: CaptureOptions): void;
110
179
  declare function capturePageView(path?: string): void;
180
+ declare function captureFeedback(feedback: {
181
+ message: string;
182
+ rating?: number;
183
+ url?: string;
184
+ }, options?: CaptureOptions): void;
111
185
  declare function identify(user: EmitVisionUser): void;
112
186
  declare function identify(userId: string, traits?: EmitVisionTraits): void;
113
187
  declare function setContext(context: Record<string, unknown>): void;
@@ -115,9 +189,11 @@ declare function setDeploymentContext(context: DeploymentContext): void;
115
189
  declare function setFeatureFlags(featureFlags: FeatureFlagsContext): void;
116
190
  declare function setTags(tags: Record<string, string>): void;
117
191
  declare function flush(): Promise<void>;
192
+ declare function consent(): Promise<void>;
193
+ declare function group(groupId: string, traits?: Record<string, unknown>): void;
118
194
  declare function evaluateFlags(options: FlagEvaluationOptions): Promise<FeatureFlagsContext>;
119
195
  declare function getFlag<T extends string | number | boolean | null>(flagKey: string, fallback: T, options: FlagEvaluationOptions): Promise<T>;
120
196
  declare function refreshFlags(options: FlagEvaluationOptions): Promise<FeatureFlagsContext>;
121
197
  declare function captureExposure(flagKey: string, variantKey: string, variantValue?: string | number | boolean | null, reason?: string, environment?: string): void;
122
198
 
123
- export { type AutoCaptureOptions, type CaptureOptions, type DeploymentContext, type EmitVisionOptions, type EmitVisionTraits, type EmitVisionUser, type FeatureFlagsContext, type FlagEvaluationOptions, captureError, captureEvent, captureExposure, capturePageView, evaluateFlags, flush, getFlag, identify, init, refreshFlags, setContext, setDeploymentContext, setFeatureFlags, setTags };
199
+ export { type AutoCaptureOptions, type CaptureOptions, type DeploymentContext, type DevToolsMessage, type EmitVisionOptions, type EmitVisionTraits, type EmitVisionUser, type FeatureFlagsContext, type FlagEvaluationOptions, captureError, captureEvent, captureExposure, captureFeedback, capturePageView, consent, evaluateFlags, flush, getFlag, group, identify, init, refreshFlags, setContext, setDeploymentContext, setFeatureFlags, setTags };
package/dist/index.js CHANGED
@@ -276,6 +276,21 @@ function mergeFeatureFlagsFn(clientFlags, overrideFlags) {
276
276
  function buildFlushDisabledMessage(label, consecutiveFailures, dropped) {
277
277
  return `[emit-vision${label ? `:${label}` : ""}] disabled after ${consecutiveFailures} consecutive flush failures; dropping ${dropped} queued events. Re-init the SDK to resume.`;
278
278
  }
279
+ function buildAuthDisabledMessage(label, status, dropped) {
280
+ return `[emit-vision${label ? `:${label}` : ""}] SDK disabled: received ${AUTH_FAILURE_CAP} consecutive ${status} responses from the ingest API. This usually means your DSN or API key is invalid \u2014 check the dsn or apiKey option passed to init(). Dropping ${dropped} queued events.`;
281
+ }
282
+ var AUTH_FAILURE_CAP = 3;
283
+ var FlushHttpError = class extends Error {
284
+ constructor(status, retryAfterSeconds, serverMessage) {
285
+ super(`emit-vision flush failed with ${status}`);
286
+ this.status = status;
287
+ this.retryAfterSeconds = retryAfterSeconds;
288
+ this.serverMessage = serverMessage;
289
+ }
290
+ status;
291
+ retryAfterSeconds;
292
+ serverMessage;
293
+ };
279
294
  async function executeHttpSend(chunk, endpoint, apiKey, fetchImpl) {
280
295
  const response = await fetchImpl(`${endpoint}/v1/batch`, {
281
296
  method: "POST",
@@ -284,7 +299,18 @@ async function executeHttpSend(chunk, endpoint, apiKey, fetchImpl) {
284
299
  keepalive: true
285
300
  });
286
301
  if (!response.ok) {
287
- throw new Error(`emit-vision flush failed with ${response.status}`);
302
+ const retryAfter = Number(response.headers?.get?.("retry-after"));
303
+ let serverMessage;
304
+ try {
305
+ const body = await response.json();
306
+ if (body && typeof body.message === "string") serverMessage = body.message;
307
+ } catch {
308
+ }
309
+ throw new FlushHttpError(
310
+ response.status,
311
+ Number.isFinite(retryAfter) && retryAfter > 0 ? retryAfter : void 0,
312
+ serverMessage
313
+ );
288
314
  }
289
315
  }
290
316
 
@@ -307,10 +333,20 @@ var FlushRetryController = class {
307
333
  if (this.disabled) return Infinity;
308
334
  return Math.max(0, this.backoffUntil - now);
309
335
  }
336
+ forceDisable() {
337
+ this.disabled = true;
338
+ this.backoffUntil = 0;
339
+ }
310
340
  reset() {
311
341
  this.consecutiveFailures = 0;
312
342
  this.backoffUntil = 0;
313
343
  }
344
+ /** Push the next retry out to honor a server-sent Retry-After hint. Capped at 5 minutes; never shortens an existing backoff. */
345
+ applyRetryAfter(seconds, now = Date.now()) {
346
+ if (this.disabled || !Number.isFinite(seconds) || seconds <= 0) return;
347
+ const cappedMs = Math.min(seconds, 300) * 1e3;
348
+ this.backoffUntil = Math.max(this.backoffUntil, now + cappedMs);
349
+ }
314
350
  recordFailure(now = Date.now()) {
315
351
  this.consecutiveFailures += 1;
316
352
  if (this.opts.failureCap > 0 && this.consecutiveFailures >= this.opts.failureCap) {
@@ -346,11 +382,77 @@ var FlushRetryController = class {
346
382
  }
347
383
  };
348
384
 
385
+ // src/persistence.ts
386
+ var MAX_PERSISTED_EVENTS = 200;
387
+ var MAX_PERSISTED_AGE_MS = 24 * 60 * 60 * 1e3;
388
+ function persistenceKey(apiKey, projectId) {
389
+ return `emit-vision:queue:${projectId ?? "-"}:${apiKey.slice(0, 16)}`;
390
+ }
391
+ function getStorage() {
392
+ try {
393
+ if (typeof localStorage === "undefined") return null;
394
+ return localStorage;
395
+ } catch {
396
+ return null;
397
+ }
398
+ }
399
+ function persistQueue(key, queue) {
400
+ const storage = getStorage();
401
+ if (!storage || queue.length === 0) return;
402
+ const record = {
403
+ savedAt: Date.now(),
404
+ events: queue.slice(0, MAX_PERSISTED_EVENTS)
405
+ };
406
+ try {
407
+ storage.setItem(key, JSON.stringify(record));
408
+ } catch {
409
+ }
410
+ }
411
+ function restoreQueue(key, now = Date.now()) {
412
+ const storage = getStorage();
413
+ if (!storage) return [];
414
+ try {
415
+ const raw = storage.getItem(key);
416
+ storage.removeItem(key);
417
+ if (!raw) return [];
418
+ const parsed = JSON.parse(raw);
419
+ if (typeof parsed.savedAt !== "number" || !Array.isArray(parsed.events)) {
420
+ return [];
421
+ }
422
+ if (now - parsed.savedAt > MAX_PERSISTED_AGE_MS) return [];
423
+ return parsed.events.slice(0, MAX_PERSISTED_EVENTS);
424
+ } catch {
425
+ return [];
426
+ }
427
+ }
428
+ function clearPersistedQueue(key) {
429
+ const storage = getStorage();
430
+ if (!storage) return;
431
+ try {
432
+ storage.removeItem(key);
433
+ } catch {
434
+ }
435
+ }
436
+
437
+ // src/devtools-channel.ts
438
+ function dispatchDevToolsEvent(type, payload) {
439
+ if (typeof window === "undefined" || typeof window.dispatchEvent !== "function") {
440
+ return;
441
+ }
442
+ window.dispatchEvent(
443
+ new CustomEvent("__emitVisionDevTools", {
444
+ detail: { type, payload, timestamp: Date.now() }
445
+ })
446
+ );
447
+ }
448
+
349
449
  // src/client-flush.ts
350
450
  var FlushManager = class {
351
451
  constructor(options, fetchImpl) {
352
452
  this.options = options;
353
453
  this.fetchImpl = fetchImpl;
454
+ this.consentGiven = !options.consentRequired || (options.consentGiven ?? false);
455
+ this.storageKey = persistenceKey(options.apiKey, options.projectId);
354
456
  this.retry = new FlushRetryController({
355
457
  initialMs: options.retryBackoffInitialMs,
356
458
  maxMs: options.retryBackoffMaxMs,
@@ -369,6 +471,9 @@ var FlushManager = class {
369
471
  timer;
370
472
  flushScheduled = false;
371
473
  retry;
474
+ storageKey;
475
+ consentGiven;
476
+ authFailureCount = 0;
372
477
  enqueue(payload) {
373
478
  if (this.retry.isDisabled()) return;
374
479
  const {
@@ -407,8 +512,27 @@ var FlushManager = class {
407
512
  );
408
513
  }
409
514
  }
515
+ giveConsent() {
516
+ this.consentGiven = true;
517
+ return this.flush();
518
+ }
519
+ /** Restore any queue persisted by a previous page load, prepending it so older events flush first. */
520
+ restorePersisted() {
521
+ const restored = restoreQueue(this.storageKey);
522
+ if (restored.length === 0) return;
523
+ this.queue.unshift(...restored);
524
+ debugLog("restored persisted queue", this.options, {
525
+ count: restored.length
526
+ });
527
+ }
528
+ /** Spill the current queue to localStorage (pagehide / tab hidden). No-op without consent. */
529
+ persistNow() {
530
+ if (!this.consentGiven || this.retry.isDisabled()) return;
531
+ persistQueue(this.storageKey, this.queue);
532
+ }
410
533
  async flush() {
411
534
  if (this.retry.isDisabled()) return;
535
+ if (!this.consentGiven) return;
412
536
  const batch = this.queue.splice(0, this.options.batchSize);
413
537
  if (batch.length === 0) return;
414
538
  debugLog("flushing batch", this.options, { count: batch.length });
@@ -446,19 +570,50 @@ var FlushManager = class {
446
570
  this.fetchImpl
447
571
  );
448
572
  } catch (err) {
449
- const m = err instanceof Error ? /flush failed with (\d+)/.exec(err.message) : null;
450
573
  this.handleFlushFailure(
451
574
  chunk,
452
575
  tailCount,
453
- m ? { reason: "status", status: Number(m[1]) } : { reason: "network", error: err }
576
+ err instanceof FlushHttpError ? {
577
+ reason: "status",
578
+ status: err.status,
579
+ retryAfterSeconds: err.retryAfterSeconds,
580
+ serverMessage: err.serverMessage
581
+ } : { reason: "network", error: err }
454
582
  );
455
583
  throw err;
456
584
  }
457
585
  this.retry.reset();
586
+ this.authFailureCount = 0;
587
+ clearPersistedQueue(this.storageKey);
458
588
  debugLog("flush complete", this.options, { count: chunk.length });
589
+ dispatchDevToolsEvent("flush", { count: chunk.length });
459
590
  }
460
591
  handleFlushFailure(batch, tailCount, detail) {
592
+ if (detail.reason === "status" && (detail.status === 401 || detail.status === 403)) {
593
+ this.authFailureCount += 1;
594
+ if (this.authFailureCount >= AUTH_FAILURE_CAP) {
595
+ const dropped = this.queue.length + batch.length + tailCount;
596
+ this.queue = [];
597
+ this.close();
598
+ this.retry.forceDisable();
599
+ const msg = buildAuthDisabledMessage(
600
+ this.options.label,
601
+ detail.status,
602
+ dropped
603
+ );
604
+ if (typeof console.warn === "function") console.warn(msg);
605
+ debugLog("disabled (auth)", this.options, {
606
+ status: detail.status,
607
+ authFailures: this.authFailureCount,
608
+ dropped
609
+ });
610
+ return;
611
+ }
612
+ }
461
613
  const result = this.retry.recordFailure();
614
+ if (detail.reason === "status" && (detail.status === 429 || detail.status === 503) && detail.retryAfterSeconds !== void 0) {
615
+ this.retry.applyRetryAfter(detail.retryAfterSeconds);
616
+ }
462
617
  if (result.justDisabled) {
463
618
  const dropped = this.queue.length + batch.length + tailCount;
464
619
  this.queue = [];
@@ -476,6 +631,7 @@ var FlushManager = class {
476
631
  return;
477
632
  }
478
633
  this.queue.unshift(...batch);
634
+ this.persistNow();
479
635
  const base = {
480
636
  restoredCount: batch.length,
481
637
  consecutiveFailures: result.consecutiveFailures,
@@ -489,6 +645,7 @@ var FlushManager = class {
489
645
  } else {
490
646
  debugLog("flush failed", this.options, {
491
647
  status: detail.status,
648
+ serverMessage: detail.serverMessage,
492
649
  ...base
493
650
  });
494
651
  }
@@ -608,9 +765,17 @@ var BrowserAutoCapture = class {
608
765
  var EmitVisionClient = class {
609
766
  constructor(options) {
610
767
  this.options = options;
768
+ this.groupId = options.groupId;
611
769
  this.deployment = options.deployment ? { ...options.deployment } : void 0;
612
770
  this.featureFlags = options.featureFlags ? { ...options.featureFlags } : {};
613
771
  this.flusher = new FlushManager(options, options.fetchImpl);
772
+ this.flusher.restorePersisted();
773
+ if (typeof window !== "undefined") {
774
+ window.addEventListener("pagehide", this.persistOnHide);
775
+ }
776
+ if (typeof document !== "undefined") {
777
+ document.addEventListener("visibilitychange", this.persistOnHidden);
778
+ }
614
779
  this.flagEval = new FlagEvaluator(
615
780
  {
616
781
  endpoint: options.endpoint,
@@ -641,9 +806,20 @@ var EmitVisionClient = class {
641
806
  flushIntervalMs: options.flushIntervalMs,
642
807
  flushOnCapture: options.flushOnCapture
643
808
  });
809
+ dispatchDevToolsEvent("init", {
810
+ sdkVersion: SDK_VERSION,
811
+ endpoint: options.endpoint,
812
+ environment: options.environment,
813
+ release: options.release,
814
+ sessionId: options.sessionId ?? "",
815
+ autoCapture: options.autoCapture,
816
+ projectId: options.projectId,
817
+ consentRequired: options.consentRequired
818
+ });
644
819
  }
645
820
  options;
646
821
  user;
822
+ groupId;
647
823
  context = {};
648
824
  deployment;
649
825
  featureFlags = {};
@@ -651,13 +827,28 @@ var EmitVisionClient = class {
651
827
  flagEval;
652
828
  autoCapture;
653
829
  flusher;
830
+ persistOnHide = () => this.flusher.persistNow();
831
+ persistOnHidden = () => {
832
+ if (typeof document !== "undefined" && document.visibilityState === "hidden") {
833
+ this.flusher.persistNow();
834
+ }
835
+ };
654
836
  captureEvent(name, properties, options = {}) {
837
+ const eventId = options.eventId ?? (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function" ? crypto.randomUUID() : void 0);
655
838
  debugLog("queue event", this.options, { name, properties, options });
656
839
  this.flusher.enqueue({
657
840
  type: "event",
658
841
  name,
659
842
  properties,
660
- ...this.withDefaults(options)
843
+ ...this.withDefaults({ ...options, eventId })
844
+ });
845
+ dispatchDevToolsEvent("event", {
846
+ name,
847
+ properties,
848
+ eventId,
849
+ timestamp: options.timestamp,
850
+ environment: options.environment,
851
+ sessionId: options.sessionId
661
852
  });
662
853
  }
663
854
  captureError(error, options = {}) {
@@ -676,6 +867,13 @@ var EmitVisionClient = class {
676
867
  identify(userIdOrUser, traits = {}) {
677
868
  this.user = typeof userIdOrUser === "string" ? { id: userIdOrUser, ...traits } : { ...userIdOrUser };
678
869
  debugLog("identified user", this.options, this.user);
870
+ const { id, ...rest } = this.user;
871
+ dispatchDevToolsEvent("identify", { userId: id, traits: rest });
872
+ }
873
+ group(groupId, traits) {
874
+ this.groupId = groupId;
875
+ debugLog("set group", this.options, { groupId, traits });
876
+ dispatchDevToolsEvent("group", { groupId, traits });
679
877
  }
680
878
  setContext(context) {
681
879
  this.context = { ...this.context, ...context };
@@ -712,6 +910,14 @@ var EmitVisionClient = class {
712
910
  if (!page) return;
713
911
  this.captureEvent("$page_view", { page });
714
912
  }
913
+ captureFeedback(feedback, options = {}) {
914
+ const url = feedback.url ?? (typeof window !== "undefined" ? window.location.href : void 0);
915
+ this.captureEvent(
916
+ "$feedback",
917
+ { message: feedback.message, ...feedback.rating !== void 0 ? { rating: feedback.rating } : {}, ...url ? { url } : {} },
918
+ options
919
+ );
920
+ }
715
921
  captureExposure(flagKey, variantKey, variantValue, reason, environment) {
716
922
  this.captureEvent("$flag_exposure", {
717
923
  flagKey,
@@ -725,10 +931,19 @@ var EmitVisionClient = class {
725
931
  this.tags = { ...this.tags, ...tags };
726
932
  debugLog("updated tags", this.options, this.tags);
727
933
  }
934
+ consent() {
935
+ return this.flusher.giveConsent();
936
+ }
728
937
  flush() {
729
938
  return this.flusher.flush();
730
939
  }
731
940
  close() {
941
+ if (typeof window !== "undefined") {
942
+ window.removeEventListener("pagehide", this.persistOnHide);
943
+ }
944
+ if (typeof document !== "undefined") {
945
+ document.removeEventListener("visibilitychange", this.persistOnHidden);
946
+ }
732
947
  this.flusher.close();
733
948
  this.autoCapture.detach();
734
949
  debugLog("closed client", this.options);
@@ -741,7 +956,7 @@ var EmitVisionClient = class {
741
956
  this.featureFlags,
742
957
  this.tags,
743
958
  this.options,
744
- opts
959
+ { groupId: this.groupId, ...opts }
745
960
  );
746
961
  }
747
962
  };
@@ -780,6 +995,9 @@ function captureError(error, options) {
780
995
  function capturePageView(path) {
781
996
  requireClient().capturePageView(path);
782
997
  }
998
+ function captureFeedback(feedback, options) {
999
+ requireClient().captureFeedback(feedback, options);
1000
+ }
783
1001
  function identify(userIdOrUser, traits) {
784
1002
  requireClient().identify(userIdOrUser, traits);
785
1003
  }
@@ -798,6 +1016,12 @@ function setTags(tags) {
798
1016
  async function flush() {
799
1017
  await requireClient().flush();
800
1018
  }
1019
+ async function consent() {
1020
+ await requireClient().consent();
1021
+ }
1022
+ function group(groupId, traits) {
1023
+ requireClient().group(groupId, traits);
1024
+ }
801
1025
  async function evaluateFlags(options) {
802
1026
  return requireClient().evaluateFlags(options);
803
1027
  }
@@ -826,10 +1050,13 @@ export {
826
1050
  captureError,
827
1051
  captureEvent,
828
1052
  captureExposure,
1053
+ captureFeedback,
829
1054
  capturePageView,
1055
+ consent,
830
1056
  evaluateFlags,
831
1057
  flush,
832
1058
  getFlag,
1059
+ group,
833
1060
  identify,
834
1061
  init,
835
1062
  refreshFlags,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@emit-vision/sdk-js",
3
- "version": "0.4.0",
3
+ "version": "0.4.2",
4
4
  "description": "Browser SDK for self-hosted emit-vision analytics and error tracking.",
5
5
  "private": false,
6
6
  "license": "MIT",
@@ -33,8 +33,19 @@
33
33
  "default": "./dist/index.js"
34
34
  }
35
35
  },
36
+ "size-limit": [
37
+ {
38
+ "name": "@emit-vision/sdk-js (gzip)",
39
+ "path": "dist/index.js",
40
+ "limit": "9 kB"
41
+ }
42
+ ],
43
+ "devDependencies": {
44
+ "@size-limit/file": "^11.1.6",
45
+ "size-limit": "^11.1.6"
46
+ },
36
47
  "scripts": {
37
48
  "build": "tsup --config tsup.config.ts",
38
- "prepack": "pnpm run build"
49
+ "size": "size-limit"
39
50
  }
40
- }
51
+ }