@shipeasy/sdk 1.2.0 → 2.0.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.
@@ -126,12 +126,21 @@ declare function _resetShipeasyForTests(): void;
126
126
  declare const flags: {
127
127
  configure(opts: FlagsClientBrowserOptions): void;
128
128
  identify(user: User): Promise<void>;
129
- /** Read a feature gate. Returns false until identify() resolves. */
129
+ /**
130
+ * Read a feature gate. Returns false until FlagsBoundary mounts (SSR-safe).
131
+ * After mount, URL overrides (?se_ks_*) apply even without a configured client.
132
+ */
130
133
  get(name: string): boolean;
131
134
  getConfig<T = unknown>(name: string, decode?: (raw: unknown) => T): T | undefined;
132
135
  getExperiment<P extends Record<string, unknown>>(name: string, defaultParams: P, decode?: (raw: unknown) => P, variants?: Record<string, Partial<P>>): ExperimentResult<P>;
133
136
  track(eventName: string, props?: Record<string, unknown>): void;
134
137
  flush(): Promise<void>;
138
+ /**
139
+ * Called by FlagsBoundary after React hydration to unlock flag reads.
140
+ * Dispatches se:override:change so subscribers (FlagsBoundary) re-render
141
+ * once with real values — URL overrides and server-evaluated flags.
142
+ */
143
+ notifyMounted(): void;
135
144
  /** Subscribe for change notifications (identify/override). Used by framework adapters. */
136
145
  subscribe(listener: () => void): () => void;
137
146
  /** True once identify() has completed and flags are available. */
@@ -168,7 +177,7 @@ declare const i18n: {
168
177
  * Falls back to a plain translated string if `createElement` was not
169
178
  * configured (e.g. server-side or in non-JSX contexts).
170
179
  */
171
- tEl(key: string, variables?: Record<string, string | number>, desc?: string): any;
180
+ tEl(key: string, fallback: string, variables?: Record<string, string | number>, desc?: string): any;
172
181
  /** Wire up the element creator once at app startup (call before any tEl use). */
173
182
  configure(opts: {
174
183
  createElement: (tag: string, props: object, children: string) => any;
@@ -126,12 +126,21 @@ declare function _resetShipeasyForTests(): void;
126
126
  declare const flags: {
127
127
  configure(opts: FlagsClientBrowserOptions): void;
128
128
  identify(user: User): Promise<void>;
129
- /** Read a feature gate. Returns false until identify() resolves. */
129
+ /**
130
+ * Read a feature gate. Returns false until FlagsBoundary mounts (SSR-safe).
131
+ * After mount, URL overrides (?se_ks_*) apply even without a configured client.
132
+ */
130
133
  get(name: string): boolean;
131
134
  getConfig<T = unknown>(name: string, decode?: (raw: unknown) => T): T | undefined;
132
135
  getExperiment<P extends Record<string, unknown>>(name: string, defaultParams: P, decode?: (raw: unknown) => P, variants?: Record<string, Partial<P>>): ExperimentResult<P>;
133
136
  track(eventName: string, props?: Record<string, unknown>): void;
134
137
  flush(): Promise<void>;
138
+ /**
139
+ * Called by FlagsBoundary after React hydration to unlock flag reads.
140
+ * Dispatches se:override:change so subscribers (FlagsBoundary) re-render
141
+ * once with real values — URL overrides and server-evaluated flags.
142
+ */
143
+ notifyMounted(): void;
135
144
  /** Subscribe for change notifications (identify/override). Used by framework adapters. */
136
145
  subscribe(listener: () => void): () => void;
137
146
  /** True once identify() has completed and flags are available. */
@@ -168,7 +177,7 @@ declare const i18n: {
168
177
  * Falls back to a plain translated string if `createElement` was not
169
178
  * configured (e.g. server-side or in non-JSX contexts).
170
179
  */
171
- tEl(key: string, variables?: Record<string, string | number>, desc?: string): any;
180
+ tEl(key: string, fallback: string, variables?: Record<string, string | number>, desc?: string): any;
172
181
  /** Wire up the element creator once at app startup (call before any tEl use). */
173
182
  configure(opts: {
174
183
  createElement: (tag: string, props: object, children: string) => any;
@@ -659,6 +659,16 @@ function _resetShipeasyForTests() {
659
659
  _client?.destroy();
660
660
  _client = null;
661
661
  }
662
+ var _mountedAndReady = false;
663
+ var _standaloneListeners = /* @__PURE__ */ new Set();
664
+ var _standaloneOverrideWired = false;
665
+ function wireStandaloneOverride() {
666
+ if (_standaloneOverrideWired || typeof window === "undefined") return;
667
+ _standaloneOverrideWired = true;
668
+ window.addEventListener("se:override:change", () => {
669
+ for (const cb of _standaloneListeners) cb();
670
+ });
671
+ }
662
672
  var flags = {
663
673
  configure(opts) {
664
674
  configureShipeasy(opts);
@@ -670,12 +680,26 @@ var flags = {
670
680
  }
671
681
  return _client.identify(user);
672
682
  },
673
- /** Read a feature gate. Returns false until identify() resolves. */
683
+ /**
684
+ * Read a feature gate. Returns false until FlagsBoundary mounts (SSR-safe).
685
+ * After mount, URL overrides (?se_ks_*) apply even without a configured client.
686
+ */
674
687
  get(name) {
675
- return _client?.getFlag(name) ?? false;
688
+ if (!_mountedAndReady) return false;
689
+ if (_client) return _client.getFlag(name);
690
+ return readGateOverride(name) ?? false;
676
691
  },
677
692
  getConfig(name, decode) {
678
- return _client?.getConfig(name, decode);
693
+ if (!_mountedAndReady) return void 0;
694
+ if (_client) return _client.getConfig(name, decode);
695
+ const ov = readConfigOverride(name);
696
+ if (ov === void 0) return void 0;
697
+ if (!decode) return ov;
698
+ try {
699
+ return decode(ov);
700
+ } catch {
701
+ return void 0;
702
+ }
679
703
  },
680
704
  getExperiment(name, defaultParams, decode, variants) {
681
705
  return _client?.getExperiment(name, defaultParams, decode, variants) ?? {
@@ -690,11 +714,24 @@ var flags = {
690
714
  flush() {
691
715
  return _client?.flush() ?? Promise.resolve();
692
716
  },
717
+ /**
718
+ * Called by FlagsBoundary after React hydration to unlock flag reads.
719
+ * Dispatches se:override:change so subscribers (FlagsBoundary) re-render
720
+ * once with real values — URL overrides and server-evaluated flags.
721
+ */
722
+ notifyMounted() {
723
+ if (_mountedAndReady) return;
724
+ _mountedAndReady = true;
725
+ if (typeof window !== "undefined") {
726
+ window.dispatchEvent(new CustomEvent("se:override:change"));
727
+ }
728
+ },
693
729
  /** Subscribe for change notifications (identify/override). Used by framework adapters. */
694
730
  subscribe(listener) {
695
- if (!_client) return () => {
696
- };
697
- return _client.subscribe(listener);
731
+ if (_client) return _client.subscribe(listener);
732
+ _standaloneListeners.add(listener);
733
+ wireStandaloneOverride();
734
+ return () => _standaloneListeners.delete(listener);
698
735
  },
699
736
  /** True once identify() has completed and flags are available. */
700
737
  get ready() {
@@ -732,9 +769,8 @@ var i18n = {
732
769
  * Falls back to a plain translated string if `createElement` was not
733
770
  * configured (e.g. server-side or in non-JSX contexts).
734
771
  */
735
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
736
- tEl(key, variables, desc) {
737
- const text = this.t(key, variables);
772
+ tEl(key, fallback, variables, desc) {
773
+ const text = this.t(key, variables) || fallback;
738
774
  if (!_createElement) return text;
739
775
  return _createElement("span", labelAttrs(key, variables, desc), text);
740
776
  },
@@ -617,6 +617,16 @@ function _resetShipeasyForTests() {
617
617
  _client?.destroy();
618
618
  _client = null;
619
619
  }
620
+ var _mountedAndReady = false;
621
+ var _standaloneListeners = /* @__PURE__ */ new Set();
622
+ var _standaloneOverrideWired = false;
623
+ function wireStandaloneOverride() {
624
+ if (_standaloneOverrideWired || typeof window === "undefined") return;
625
+ _standaloneOverrideWired = true;
626
+ window.addEventListener("se:override:change", () => {
627
+ for (const cb of _standaloneListeners) cb();
628
+ });
629
+ }
620
630
  var flags = {
621
631
  configure(opts) {
622
632
  configureShipeasy(opts);
@@ -628,12 +638,26 @@ var flags = {
628
638
  }
629
639
  return _client.identify(user);
630
640
  },
631
- /** Read a feature gate. Returns false until identify() resolves. */
641
+ /**
642
+ * Read a feature gate. Returns false until FlagsBoundary mounts (SSR-safe).
643
+ * After mount, URL overrides (?se_ks_*) apply even without a configured client.
644
+ */
632
645
  get(name) {
633
- return _client?.getFlag(name) ?? false;
646
+ if (!_mountedAndReady) return false;
647
+ if (_client) return _client.getFlag(name);
648
+ return readGateOverride(name) ?? false;
634
649
  },
635
650
  getConfig(name, decode) {
636
- return _client?.getConfig(name, decode);
651
+ if (!_mountedAndReady) return void 0;
652
+ if (_client) return _client.getConfig(name, decode);
653
+ const ov = readConfigOverride(name);
654
+ if (ov === void 0) return void 0;
655
+ if (!decode) return ov;
656
+ try {
657
+ return decode(ov);
658
+ } catch {
659
+ return void 0;
660
+ }
637
661
  },
638
662
  getExperiment(name, defaultParams, decode, variants) {
639
663
  return _client?.getExperiment(name, defaultParams, decode, variants) ?? {
@@ -648,11 +672,24 @@ var flags = {
648
672
  flush() {
649
673
  return _client?.flush() ?? Promise.resolve();
650
674
  },
675
+ /**
676
+ * Called by FlagsBoundary after React hydration to unlock flag reads.
677
+ * Dispatches se:override:change so subscribers (FlagsBoundary) re-render
678
+ * once with real values — URL overrides and server-evaluated flags.
679
+ */
680
+ notifyMounted() {
681
+ if (_mountedAndReady) return;
682
+ _mountedAndReady = true;
683
+ if (typeof window !== "undefined") {
684
+ window.dispatchEvent(new CustomEvent("se:override:change"));
685
+ }
686
+ },
651
687
  /** Subscribe for change notifications (identify/override). Used by framework adapters. */
652
688
  subscribe(listener) {
653
- if (!_client) return () => {
654
- };
655
- return _client.subscribe(listener);
689
+ if (_client) return _client.subscribe(listener);
690
+ _standaloneListeners.add(listener);
691
+ wireStandaloneOverride();
692
+ return () => _standaloneListeners.delete(listener);
656
693
  },
657
694
  /** True once identify() has completed and flags are available. */
658
695
  get ready() {
@@ -690,9 +727,8 @@ var i18n = {
690
727
  * Falls back to a plain translated string if `createElement` was not
691
728
  * configured (e.g. server-side or in non-JSX contexts).
692
729
  */
693
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
694
- tEl(key, variables, desc) {
695
- const text = this.t(key, variables);
730
+ tEl(key, fallback, variables, desc) {
731
+ const text = this.t(key, variables) || fallback;
696
732
  if (!_createElement) return text;
697
733
  return _createElement("span", labelAttrs(key, variables, desc), text);
698
734
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shipeasy/sdk",
3
- "version": "1.2.0",
3
+ "version": "2.0.1",
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",