@shipeasy/sdk 2.1.12 → 2.1.13

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.
@@ -48,6 +48,7 @@ declare class FlagsClientBrowser {
48
48
  private guardrailsInstalled;
49
49
  private listeners;
50
50
  private overrideListenerInstalled;
51
+ private identifySeq;
51
52
  private onOverrideChange;
52
53
  constructor(opts: FlagsClientBrowserOptions);
53
54
  identify(user: User): Promise<void>;
@@ -117,11 +118,22 @@ interface ShipeasyClientConfig {
117
118
  baseUrl?: string;
118
119
  /** Override the admin URL for the devtools overlay (dev use). */
119
120
  adminUrl?: string;
121
+ /**
122
+ * Skip the lazy auto-identify({}) at boot. Defaults to true (auto-identify on).
123
+ * Turn off when the host has its own identify orchestration and wants to
124
+ * avoid the initial anon /sdk/evaluate round-trip.
125
+ */
126
+ autoIdentify?: boolean;
120
127
  }
121
128
  /**
122
129
  * Initialise the ShipEasy client SDK and wire up lazy devtools.
123
130
  * Call this once at app startup (e.g. in a useEffect in your root layout).
124
131
  * Returns a cleanup function — call it on unmount to remove event listeners.
132
+ *
133
+ * Lazy-identifies the visitor under the hood with a stable anonId + auto-collected
134
+ * browser attrs (locale, timezone, path, screen, referrer, user_agent), so flags
135
+ * and experiments are warm without callers having to wire identify() manually.
136
+ * A later flags.identify({ user_id }) overrides this in place; anonId stays stable.
125
137
  */
126
138
  declare function shipeasy(opts: ShipeasyClientConfig): () => void;
127
139
  declare function configureShipeasy(opts: FlagsClientBrowserOptions): FlagsClientBrowser;
@@ -48,6 +48,7 @@ declare class FlagsClientBrowser {
48
48
  private guardrailsInstalled;
49
49
  private listeners;
50
50
  private overrideListenerInstalled;
51
+ private identifySeq;
51
52
  private onOverrideChange;
52
53
  constructor(opts: FlagsClientBrowserOptions);
53
54
  identify(user: User): Promise<void>;
@@ -117,11 +118,22 @@ interface ShipeasyClientConfig {
117
118
  baseUrl?: string;
118
119
  /** Override the admin URL for the devtools overlay (dev use). */
119
120
  adminUrl?: string;
121
+ /**
122
+ * Skip the lazy auto-identify({}) at boot. Defaults to true (auto-identify on).
123
+ * Turn off when the host has its own identify orchestration and wants to
124
+ * avoid the initial anon /sdk/evaluate round-trip.
125
+ */
126
+ autoIdentify?: boolean;
120
127
  }
121
128
  /**
122
129
  * Initialise the ShipEasy client SDK and wire up lazy devtools.
123
130
  * Call this once at app startup (e.g. in a useEffect in your root layout).
124
131
  * Returns a cleanup function — call it on unmount to remove event listeners.
132
+ *
133
+ * Lazy-identifies the visitor under the hood with a stable anonId + auto-collected
134
+ * browser attrs (locale, timezone, path, screen, referrer, user_agent), so flags
135
+ * and experiments are warm without callers having to wire identify() manually.
136
+ * A later flags.identify({ user_id }) overrides this in place; anonId stays stable.
125
137
  */
126
138
  declare function shipeasy(opts: ShipeasyClientConfig): () => void;
127
139
  declare function configureShipeasy(opts: FlagsClientBrowserOptions): FlagsClientBrowser;
@@ -399,6 +399,9 @@ var FlagsClientBrowser = class {
399
399
  guardrailsInstalled = false;
400
400
  listeners = /* @__PURE__ */ new Set();
401
401
  overrideListenerInstalled = false;
402
+ // Monotonic counter so a later identify() always wins even if its /sdk/evaluate
403
+ // response races and lands before an earlier in-flight call's response.
404
+ identifySeq = 0;
402
405
  onOverrideChange = () => {
403
406
  this.installBridge();
404
407
  this.notify();
@@ -413,8 +416,9 @@ var FlagsClientBrowser = class {
413
416
  void this.buffer.flushPendingAlias();
414
417
  }
415
418
  async identify(user) {
419
+ const seq = ++this.identifySeq;
416
420
  const prevUserId = this.userId;
417
- this.userId = user.user_id ?? "";
421
+ if (user.user_id !== void 0) this.userId = user.user_id;
418
422
  if (this.anonId && this.userId && this.userId !== prevUserId) {
419
423
  await this.buffer.alias(this.anonId, this.userId);
420
424
  }
@@ -432,7 +436,9 @@ var FlagsClientBrowser = class {
432
436
  })
433
437
  });
434
438
  if (!res.ok) throw new Error(`/sdk/evaluate returned ${res.status}`);
435
- this.evalResult = await res.json();
439
+ const data = await res.json();
440
+ if (seq !== this.identifySeq) return;
441
+ this.evalResult = data;
436
442
  if (this.autoGuardrails && !this.guardrailsInstalled) {
437
443
  this.guardrailsInstalled = true;
438
444
  installAutoGuardrails(this.buffer, this.userId, this.anonId);
@@ -656,6 +662,11 @@ function shipeasy(opts) {
656
662
  baseUrl: opts.baseUrl ?? "https://cdn.shipeasy.ai"
657
663
  });
658
664
  flags.notifyMounted();
665
+ if (opts.autoIdentify !== false) {
666
+ void client.identify({}).catch((err) => {
667
+ console.warn("[shipeasy] auto-identify failed:", String(err));
668
+ });
669
+ }
659
670
  return attachDevtools(client, { adminUrl: opts.adminUrl });
660
671
  }
661
672
  function configureShipeasy(opts) {
@@ -356,6 +356,9 @@ var FlagsClientBrowser = class {
356
356
  guardrailsInstalled = false;
357
357
  listeners = /* @__PURE__ */ new Set();
358
358
  overrideListenerInstalled = false;
359
+ // Monotonic counter so a later identify() always wins even if its /sdk/evaluate
360
+ // response races and lands before an earlier in-flight call's response.
361
+ identifySeq = 0;
359
362
  onOverrideChange = () => {
360
363
  this.installBridge();
361
364
  this.notify();
@@ -370,8 +373,9 @@ var FlagsClientBrowser = class {
370
373
  void this.buffer.flushPendingAlias();
371
374
  }
372
375
  async identify(user) {
376
+ const seq = ++this.identifySeq;
373
377
  const prevUserId = this.userId;
374
- this.userId = user.user_id ?? "";
378
+ if (user.user_id !== void 0) this.userId = user.user_id;
375
379
  if (this.anonId && this.userId && this.userId !== prevUserId) {
376
380
  await this.buffer.alias(this.anonId, this.userId);
377
381
  }
@@ -389,7 +393,9 @@ var FlagsClientBrowser = class {
389
393
  })
390
394
  });
391
395
  if (!res.ok) throw new Error(`/sdk/evaluate returned ${res.status}`);
392
- this.evalResult = await res.json();
396
+ const data = await res.json();
397
+ if (seq !== this.identifySeq) return;
398
+ this.evalResult = data;
393
399
  if (this.autoGuardrails && !this.guardrailsInstalled) {
394
400
  this.guardrailsInstalled = true;
395
401
  installAutoGuardrails(this.buffer, this.userId, this.anonId);
@@ -613,6 +619,11 @@ function shipeasy(opts) {
613
619
  baseUrl: opts.baseUrl ?? "https://cdn.shipeasy.ai"
614
620
  });
615
621
  flags.notifyMounted();
622
+ if (opts.autoIdentify !== false) {
623
+ void client.identify({}).catch((err) => {
624
+ console.warn("[shipeasy] auto-identify failed:", String(err));
625
+ });
626
+ }
616
627
  return attachDevtools(client, { adminUrl: opts.adminUrl });
617
628
  }
618
629
  function configureShipeasy(opts) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shipeasy/sdk",
3
- "version": "2.1.12",
3
+ "version": "2.1.13",
4
4
  "description": "Shipeasy SDK — feature gates, runtime configs, experiments, and metrics for the Shipeasy hosted service.",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "homepage": "https://shipeasy.ai",