@shipeasy/sdk 2.4.0 → 2.5.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.
@@ -27,6 +27,12 @@ interface EvalResponse {
27
27
  flags: Record<string, boolean>;
28
28
  configs: Record<string, unknown>;
29
29
  experiments: Record<string, EvalExpResult>;
30
+ /**
31
+ * Killswitch state, flattened by the server. A boolean means the killswitch
32
+ * is whole-killed; an object means it's not whole-killed and carries per-
33
+ * switch booleans.
34
+ */
35
+ killswitches?: Record<string, boolean | Record<string, boolean>>;
30
36
  }
31
37
  interface AutoCollectGroups {
32
38
  vitals: boolean;
@@ -83,6 +89,12 @@ declare class FlagsClientBrowser {
83
89
  */
84
90
  installBridge(): ShipeasySdkBridge | null;
85
91
  track(eventName: string, props?: Record<string, unknown>): void;
92
+ /**
93
+ * Read a killswitch from the server's evaluated state. Without `switchKey`,
94
+ * returns true when the killswitch is whole-killed. With `switchKey`, returns
95
+ * the per-switch state. Returns false for unknown killswitches / switches.
96
+ */
97
+ getKillswitch(name: string, switchKey?: string): boolean;
86
98
  flush(): Promise<void>;
87
99
  destroy(): void;
88
100
  }
@@ -180,6 +192,12 @@ interface BootstrapPayload {
180
192
  group: string;
181
193
  params: Record<string, unknown>;
182
194
  }>;
195
+ /**
196
+ * Killswitch state, flattened by the server. A value of `boolean` means the
197
+ * killswitch is killed as a whole (no per-switch detail); a `Record` means
198
+ * the killswitch is not whole-killed and the map carries per-switch state.
199
+ */
200
+ killswitches?: Record<string, boolean | Record<string, boolean>>;
183
201
  /** Set by getBootstrapHtml() for auto-init. Not part of evaluate() output. */
184
202
  apiKey?: string;
185
203
  apiUrl?: string;
@@ -205,6 +223,16 @@ declare const flags: {
205
223
  getConfig<T = unknown>(name: string, decode?: (raw: unknown) => T): T | undefined;
206
224
  getExperiment<P extends Record<string, unknown>>(name: string, defaultParams: P, decode?: (raw: unknown) => P, variants?: Record<string, Partial<P>>): ExperimentResult<P>;
207
225
  track(eventName: string, props?: Record<string, unknown>): void;
226
+ /**
227
+ * Read a killswitch. Without `switchKey`, returns true when the killswitch is
228
+ * killed as a whole. With `switchKey`, returns true when that specific switch
229
+ * is on. Unknown killswitches / switches return false.
230
+ *
231
+ * Priority: bootstrap → CDN evalResult (post-mount) → false. Matches the
232
+ * pattern used by `flags.get` / `flags.getConfig` so SSR-hydrated values are
233
+ * available synchronously on first render.
234
+ */
235
+ ks(name: string, switchKey?: string): boolean;
208
236
  flush(): Promise<void>;
209
237
  /**
210
238
  * Called by FlagsBoundary after React hydration to unlock flag reads.
@@ -27,6 +27,12 @@ interface EvalResponse {
27
27
  flags: Record<string, boolean>;
28
28
  configs: Record<string, unknown>;
29
29
  experiments: Record<string, EvalExpResult>;
30
+ /**
31
+ * Killswitch state, flattened by the server. A boolean means the killswitch
32
+ * is whole-killed; an object means it's not whole-killed and carries per-
33
+ * switch booleans.
34
+ */
35
+ killswitches?: Record<string, boolean | Record<string, boolean>>;
30
36
  }
31
37
  interface AutoCollectGroups {
32
38
  vitals: boolean;
@@ -83,6 +89,12 @@ declare class FlagsClientBrowser {
83
89
  */
84
90
  installBridge(): ShipeasySdkBridge | null;
85
91
  track(eventName: string, props?: Record<string, unknown>): void;
92
+ /**
93
+ * Read a killswitch from the server's evaluated state. Without `switchKey`,
94
+ * returns true when the killswitch is whole-killed. With `switchKey`, returns
95
+ * the per-switch state. Returns false for unknown killswitches / switches.
96
+ */
97
+ getKillswitch(name: string, switchKey?: string): boolean;
86
98
  flush(): Promise<void>;
87
99
  destroy(): void;
88
100
  }
@@ -180,6 +192,12 @@ interface BootstrapPayload {
180
192
  group: string;
181
193
  params: Record<string, unknown>;
182
194
  }>;
195
+ /**
196
+ * Killswitch state, flattened by the server. A value of `boolean` means the
197
+ * killswitch is killed as a whole (no per-switch detail); a `Record` means
198
+ * the killswitch is not whole-killed and the map carries per-switch state.
199
+ */
200
+ killswitches?: Record<string, boolean | Record<string, boolean>>;
183
201
  /** Set by getBootstrapHtml() for auto-init. Not part of evaluate() output. */
184
202
  apiKey?: string;
185
203
  apiUrl?: string;
@@ -205,6 +223,16 @@ declare const flags: {
205
223
  getConfig<T = unknown>(name: string, decode?: (raw: unknown) => T): T | undefined;
206
224
  getExperiment<P extends Record<string, unknown>>(name: string, defaultParams: P, decode?: (raw: unknown) => P, variants?: Record<string, Partial<P>>): ExperimentResult<P>;
207
225
  track(eventName: string, props?: Record<string, unknown>): void;
226
+ /**
227
+ * Read a killswitch. Without `switchKey`, returns true when the killswitch is
228
+ * killed as a whole. With `switchKey`, returns true when that specific switch
229
+ * is on. Unknown killswitches / switches return false.
230
+ *
231
+ * Priority: bootstrap → CDN evalResult (post-mount) → false. Matches the
232
+ * pattern used by `flags.get` / `flags.getConfig` so SSR-hydrated values are
233
+ * available synchronously on first render.
234
+ */
235
+ ks(name: string, switchKey?: string): boolean;
208
236
  flush(): Promise<void>;
209
237
  /**
210
238
  * Called by FlagsBoundary after React hydration to unlock flag reads.
@@ -143,7 +143,8 @@ var EventBuffer = class {
143
143
  const batch = this.queue.splice(0);
144
144
  const body = JSON.stringify({ events: batch });
145
145
  if (useBeacon && typeof navigator !== "undefined" && navigator.sendBeacon) {
146
- navigator.sendBeacon(this.collectUrl, new Blob([body], { type: "text/plain" }));
146
+ const beaconBody = JSON.stringify({ k: this.sdkKey, events: batch });
147
+ navigator.sendBeacon(this.collectUrl, new Blob([beaconBody], { type: "text/plain" }));
147
148
  return;
148
149
  }
149
150
  fetch(this.collectUrl, {
@@ -575,6 +576,19 @@ var FlagsClientBrowser = class {
575
576
  track(eventName, props) {
576
577
  this.buffer.pushMetric(eventName, this.userId, this.anonId, props);
577
578
  }
579
+ /**
580
+ * Read a killswitch from the server's evaluated state. Without `switchKey`,
581
+ * returns true when the killswitch is whole-killed. With `switchKey`, returns
582
+ * the per-switch state. Returns false for unknown killswitches / switches.
583
+ */
584
+ getKillswitch(name, switchKey) {
585
+ if (this.evalResult === null) return false;
586
+ const ks = this.evalResult.killswitches?.[name];
587
+ if (ks === void 0) return false;
588
+ if (typeof ks === "boolean") return switchKey === void 0 ? ks : false;
589
+ if (switchKey === void 0) return false;
590
+ return ks[switchKey] === true;
591
+ }
578
592
  async flush() {
579
593
  await this.buffer.flushAsync();
580
594
  }
@@ -794,6 +808,26 @@ var flags = {
794
808
  track(eventName, props) {
795
809
  _client?.track(eventName, props);
796
810
  },
811
+ /**
812
+ * Read a killswitch. Without `switchKey`, returns true when the killswitch is
813
+ * killed as a whole. With `switchKey`, returns true when that specific switch
814
+ * is on. Unknown killswitches / switches return false.
815
+ *
816
+ * Priority: bootstrap → CDN evalResult (post-mount) → false. Matches the
817
+ * pattern used by `flags.get` / `flags.getConfig` so SSR-hydrated values are
818
+ * available synchronously on first render.
819
+ */
820
+ ks(name, switchKey) {
821
+ const bs = getBootstrap();
822
+ if (bs !== null && bs.killswitches && name in bs.killswitches) {
823
+ const ks = bs.killswitches[name];
824
+ if (typeof ks === "boolean") return switchKey === void 0 ? ks : false;
825
+ if (switchKey === void 0) return false;
826
+ return ks[switchKey] === true;
827
+ }
828
+ if (!_mountedAndReady) return false;
829
+ return _client?.getKillswitch(name, switchKey) ?? false;
830
+ },
797
831
  flush() {
798
832
  return _client?.flush() ?? Promise.resolve();
799
833
  },
@@ -100,7 +100,8 @@ var EventBuffer = class {
100
100
  const batch = this.queue.splice(0);
101
101
  const body = JSON.stringify({ events: batch });
102
102
  if (useBeacon && typeof navigator !== "undefined" && navigator.sendBeacon) {
103
- navigator.sendBeacon(this.collectUrl, new Blob([body], { type: "text/plain" }));
103
+ const beaconBody = JSON.stringify({ k: this.sdkKey, events: batch });
104
+ navigator.sendBeacon(this.collectUrl, new Blob([beaconBody], { type: "text/plain" }));
104
105
  return;
105
106
  }
106
107
  fetch(this.collectUrl, {
@@ -532,6 +533,19 @@ var FlagsClientBrowser = class {
532
533
  track(eventName, props) {
533
534
  this.buffer.pushMetric(eventName, this.userId, this.anonId, props);
534
535
  }
536
+ /**
537
+ * Read a killswitch from the server's evaluated state. Without `switchKey`,
538
+ * returns true when the killswitch is whole-killed. With `switchKey`, returns
539
+ * the per-switch state. Returns false for unknown killswitches / switches.
540
+ */
541
+ getKillswitch(name, switchKey) {
542
+ if (this.evalResult === null) return false;
543
+ const ks = this.evalResult.killswitches?.[name];
544
+ if (ks === void 0) return false;
545
+ if (typeof ks === "boolean") return switchKey === void 0 ? ks : false;
546
+ if (switchKey === void 0) return false;
547
+ return ks[switchKey] === true;
548
+ }
535
549
  async flush() {
536
550
  await this.buffer.flushAsync();
537
551
  }
@@ -751,6 +765,26 @@ var flags = {
751
765
  track(eventName, props) {
752
766
  _client?.track(eventName, props);
753
767
  },
768
+ /**
769
+ * Read a killswitch. Without `switchKey`, returns true when the killswitch is
770
+ * killed as a whole. With `switchKey`, returns true when that specific switch
771
+ * is on. Unknown killswitches / switches return false.
772
+ *
773
+ * Priority: bootstrap → CDN evalResult (post-mount) → false. Matches the
774
+ * pattern used by `flags.get` / `flags.getConfig` so SSR-hydrated values are
775
+ * available synchronously on first render.
776
+ */
777
+ ks(name, switchKey) {
778
+ const bs = getBootstrap();
779
+ if (bs !== null && bs.killswitches && name in bs.killswitches) {
780
+ const ks = bs.killswitches[name];
781
+ if (typeof ks === "boolean") return switchKey === void 0 ? ks : false;
782
+ if (switchKey === void 0) return false;
783
+ return ks[switchKey] === true;
784
+ }
785
+ if (!_mountedAndReady) return false;
786
+ return _client?.getKillswitch(name, switchKey) ?? false;
787
+ },
754
788
  flush() {
755
789
  return _client?.flush() ?? Promise.resolve();
756
790
  },
@@ -9,10 +9,36 @@ interface ExperimentResult<P> {
9
9
  group: string;
10
10
  params: P;
11
11
  }
12
+ interface GateRule {
13
+ attr: string;
14
+ op: "eq" | "neq" | "in" | "not_in" | "gt" | "gte" | "lt" | "lte" | "contains" | "regex";
15
+ value: unknown;
16
+ }
17
+ interface Gate {
18
+ rules: GateRule[];
19
+ rolloutPct: number;
20
+ salt: string;
21
+ enabled: 0 | 1 | boolean;
22
+ killswitch?: 0 | 1 | boolean;
23
+ }
24
+ interface Killswitch {
25
+ killed: 0 | 1 | boolean;
26
+ switches?: Record<string, 0 | 1 | boolean>;
27
+ }
28
+ interface FlagsBlob {
29
+ version: string;
30
+ plan: string;
31
+ gates: Record<string, Gate>;
32
+ configs: Record<string, {
33
+ value: unknown;
34
+ }>;
35
+ killswitches: Record<string, Killswitch>;
36
+ }
12
37
  interface BootstrapPayload {
13
38
  flags: Record<string, boolean>;
14
39
  configs: Record<string, unknown>;
15
40
  experiments: Record<string, ExperimentResult<Record<string, unknown>>>;
41
+ killswitches: Record<string, boolean | Record<string, boolean>>;
16
42
  }
17
43
  type FlagsClientEnv = "dev" | "staging" | "prod";
18
44
  interface FlagsClientOptions {
@@ -20,6 +46,11 @@ interface FlagsClientOptions {
20
46
  baseUrl?: string;
21
47
  /** Which published env to read values from. Defaults to "prod". */
22
48
  env?: FlagsClientEnv;
49
+ /**
50
+ * Preload the flags blob synchronously without a network fetch. Primarily
51
+ * for tests; production callers should rely on init()/initOnce().
52
+ */
53
+ initialBlob?: FlagsBlob;
23
54
  }
24
55
  declare class FlagsClient {
25
56
  private readonly apiKey;
@@ -54,6 +85,7 @@ declare class FlagsClient {
54
85
  * synchronously without waiting for identify() to resolve.
55
86
  */
56
87
  evaluate(user: User, rawUrl?: string): BootstrapPayload;
88
+ getKillswitch(name: string, switchKey?: string): boolean;
57
89
  }
58
90
  interface I18nForRequest {
59
91
  strings: Record<string, string>;
@@ -165,6 +197,12 @@ declare const flags: {
165
197
  get(name: string, user: User): boolean;
166
198
  getConfig<T = unknown>(name: string, decode?: (raw: unknown) => T): T | undefined;
167
199
  getExperiment<P extends Record<string, unknown>>(name: string, user: User, defaultParams: P, decode?: (raw: unknown) => P): ExperimentResult<P>;
200
+ /**
201
+ * Read a killswitch. Without `switchKey`, returns true when the whole
202
+ * killswitch is killed. With `switchKey`, returns true when that specific
203
+ * switch is on. Unknown killswitches / switches return false.
204
+ */
205
+ ks(name: string, switchKey?: string): boolean;
168
206
  track(userId: string, eventName: string, props?: Record<string, unknown>): void;
169
207
  /**
170
208
  * Evaluate all flags / configs / experiments for a user against the locally
@@ -9,10 +9,36 @@ interface ExperimentResult<P> {
9
9
  group: string;
10
10
  params: P;
11
11
  }
12
+ interface GateRule {
13
+ attr: string;
14
+ op: "eq" | "neq" | "in" | "not_in" | "gt" | "gte" | "lt" | "lte" | "contains" | "regex";
15
+ value: unknown;
16
+ }
17
+ interface Gate {
18
+ rules: GateRule[];
19
+ rolloutPct: number;
20
+ salt: string;
21
+ enabled: 0 | 1 | boolean;
22
+ killswitch?: 0 | 1 | boolean;
23
+ }
24
+ interface Killswitch {
25
+ killed: 0 | 1 | boolean;
26
+ switches?: Record<string, 0 | 1 | boolean>;
27
+ }
28
+ interface FlagsBlob {
29
+ version: string;
30
+ plan: string;
31
+ gates: Record<string, Gate>;
32
+ configs: Record<string, {
33
+ value: unknown;
34
+ }>;
35
+ killswitches: Record<string, Killswitch>;
36
+ }
12
37
  interface BootstrapPayload {
13
38
  flags: Record<string, boolean>;
14
39
  configs: Record<string, unknown>;
15
40
  experiments: Record<string, ExperimentResult<Record<string, unknown>>>;
41
+ killswitches: Record<string, boolean | Record<string, boolean>>;
16
42
  }
17
43
  type FlagsClientEnv = "dev" | "staging" | "prod";
18
44
  interface FlagsClientOptions {
@@ -20,6 +46,11 @@ interface FlagsClientOptions {
20
46
  baseUrl?: string;
21
47
  /** Which published env to read values from. Defaults to "prod". */
22
48
  env?: FlagsClientEnv;
49
+ /**
50
+ * Preload the flags blob synchronously without a network fetch. Primarily
51
+ * for tests; production callers should rely on init()/initOnce().
52
+ */
53
+ initialBlob?: FlagsBlob;
23
54
  }
24
55
  declare class FlagsClient {
25
56
  private readonly apiKey;
@@ -54,6 +85,7 @@ declare class FlagsClient {
54
85
  * synchronously without waiting for identify() to resolve.
55
86
  */
56
87
  evaluate(user: User, rawUrl?: string): BootstrapPayload;
88
+ getKillswitch(name: string, switchKey?: string): boolean;
57
89
  }
58
90
  interface I18nForRequest {
59
91
  strings: Record<string, string>;
@@ -165,6 +197,12 @@ declare const flags: {
165
197
  get(name: string, user: User): boolean;
166
198
  getConfig<T = unknown>(name: string, decode?: (raw: unknown) => T): T | undefined;
167
199
  getExperiment<P extends Record<string, unknown>>(name: string, user: User, defaultParams: P, decode?: (raw: unknown) => P): ExperimentResult<P>;
200
+ /**
201
+ * Read a killswitch. Without `switchKey`, returns true when the whole
202
+ * killswitch is killed. With `switchKey`, returns true when that specific
203
+ * switch is on. Unknown killswitches / switches return false.
204
+ */
205
+ ks(name: string, switchKey?: string): boolean;
168
206
  track(userId: string, eventName: string, props?: Record<string, unknown>): void;
169
207
  /**
170
208
  * Evaluate all flags / configs / experiments for a user against the locally
@@ -203,6 +203,10 @@ var FlagsClient = class {
203
203
  this.apiKey = opts.apiKey;
204
204
  this.baseUrl = (opts.baseUrl ?? "https://cdn.shipeasy.ai").replace(/\/$/, "");
205
205
  this.env = opts.env ?? "prod";
206
+ if (opts.initialBlob) {
207
+ this.flagsBlob = opts.initialBlob;
208
+ this.initialized = true;
209
+ }
206
210
  }
207
211
  async init() {
208
212
  await this.fetchAll();
@@ -343,6 +347,7 @@ var FlagsClient = class {
343
347
  const flags2 = {};
344
348
  const configs = {};
345
349
  const experiments = {};
350
+ const killswitches = {};
346
351
  for (const [name, gate] of Object.entries(this.flagsBlob?.gates ?? {})) {
347
352
  flags2[name] = evalGateInternal(gate, user);
348
353
  }
@@ -352,6 +357,15 @@ var FlagsClient = class {
352
357
  for (const [name] of Object.entries(this.expsBlob?.experiments ?? {})) {
353
358
  experiments[name] = this.getExperiment(name, user, {});
354
359
  }
360
+ for (const [name, ks] of Object.entries(this.flagsBlob?.killswitches ?? {})) {
361
+ if (ks.switches && Object.keys(ks.switches).length > 0) {
362
+ const out = {};
363
+ for (const [k, v] of Object.entries(ks.switches)) out[k] = isEnabled(v);
364
+ killswitches[name] = out;
365
+ } else {
366
+ killswitches[name] = isEnabled(ks.killed);
367
+ }
368
+ }
355
369
  if (rawUrl) {
356
370
  const ov = parseOverrides(rawUrl);
357
371
  Object.assign(flags2, ov.gates);
@@ -360,7 +374,13 @@ var FlagsClient = class {
360
374
  experiments[name] = { inExperiment: true, group, params: {} };
361
375
  }
362
376
  }
363
- return { flags: flags2, configs, experiments };
377
+ return { flags: flags2, configs, experiments, killswitches };
378
+ }
379
+ getKillswitch(name, switchKey) {
380
+ const ks = this.flagsBlob?.killswitches?.[name];
381
+ if (!ks) return false;
382
+ if (switchKey === void 0) return isEnabled(ks.killed);
383
+ return isEnabled(ks.switches?.[switchKey]);
364
384
  }
365
385
  };
366
386
  var _I18N_SSR_SYM = /* @__PURE__ */ Symbol.for("@shipeasy/sdk:ssr-i18n");
@@ -589,6 +609,14 @@ var flags = {
589
609
  params: defaultParams
590
610
  };
591
611
  },
612
+ /**
613
+ * Read a killswitch. Without `switchKey`, returns true when the whole
614
+ * killswitch is killed. With `switchKey`, returns true when that specific
615
+ * switch is on. Unknown killswitches / switches return false.
616
+ */
617
+ ks(name, switchKey) {
618
+ return _server?.getKillswitch(name, switchKey) ?? false;
619
+ },
592
620
  track(userId, eventName, props) {
593
621
  _server?.track(userId, eventName, props);
594
622
  },
@@ -598,7 +626,12 @@ var flags = {
598
626
  * overrides. Returns an empty payload when the blob hasn't been fetched yet.
599
627
  */
600
628
  evaluate(user, rawUrl) {
601
- return _server?.evaluate(user, rawUrl) ?? { flags: {}, configs: {}, experiments: {} };
629
+ return _server?.evaluate(user, rawUrl) ?? {
630
+ flags: {},
631
+ configs: {},
632
+ experiments: {},
633
+ killswitches: {}
634
+ };
602
635
  }
603
636
  };
604
637
  // Annotate the CommonJS export names for ESM import in node:
@@ -160,6 +160,10 @@ var FlagsClient = class {
160
160
  this.apiKey = opts.apiKey;
161
161
  this.baseUrl = (opts.baseUrl ?? "https://cdn.shipeasy.ai").replace(/\/$/, "");
162
162
  this.env = opts.env ?? "prod";
163
+ if (opts.initialBlob) {
164
+ this.flagsBlob = opts.initialBlob;
165
+ this.initialized = true;
166
+ }
163
167
  }
164
168
  async init() {
165
169
  await this.fetchAll();
@@ -300,6 +304,7 @@ var FlagsClient = class {
300
304
  const flags2 = {};
301
305
  const configs = {};
302
306
  const experiments = {};
307
+ const killswitches = {};
303
308
  for (const [name, gate] of Object.entries(this.flagsBlob?.gates ?? {})) {
304
309
  flags2[name] = evalGateInternal(gate, user);
305
310
  }
@@ -309,6 +314,15 @@ var FlagsClient = class {
309
314
  for (const [name] of Object.entries(this.expsBlob?.experiments ?? {})) {
310
315
  experiments[name] = this.getExperiment(name, user, {});
311
316
  }
317
+ for (const [name, ks] of Object.entries(this.flagsBlob?.killswitches ?? {})) {
318
+ if (ks.switches && Object.keys(ks.switches).length > 0) {
319
+ const out = {};
320
+ for (const [k, v] of Object.entries(ks.switches)) out[k] = isEnabled(v);
321
+ killswitches[name] = out;
322
+ } else {
323
+ killswitches[name] = isEnabled(ks.killed);
324
+ }
325
+ }
312
326
  if (rawUrl) {
313
327
  const ov = parseOverrides(rawUrl);
314
328
  Object.assign(flags2, ov.gates);
@@ -317,7 +331,13 @@ var FlagsClient = class {
317
331
  experiments[name] = { inExperiment: true, group, params: {} };
318
332
  }
319
333
  }
320
- return { flags: flags2, configs, experiments };
334
+ return { flags: flags2, configs, experiments, killswitches };
335
+ }
336
+ getKillswitch(name, switchKey) {
337
+ const ks = this.flagsBlob?.killswitches?.[name];
338
+ if (!ks) return false;
339
+ if (switchKey === void 0) return isEnabled(ks.killed);
340
+ return isEnabled(ks.switches?.[switchKey]);
321
341
  }
322
342
  };
323
343
  var _I18N_SSR_SYM = /* @__PURE__ */ Symbol.for("@shipeasy/sdk:ssr-i18n");
@@ -546,6 +566,14 @@ var flags = {
546
566
  params: defaultParams
547
567
  };
548
568
  },
569
+ /**
570
+ * Read a killswitch. Without `switchKey`, returns true when the whole
571
+ * killswitch is killed. With `switchKey`, returns true when that specific
572
+ * switch is on. Unknown killswitches / switches return false.
573
+ */
574
+ ks(name, switchKey) {
575
+ return _server?.getKillswitch(name, switchKey) ?? false;
576
+ },
549
577
  track(userId, eventName, props) {
550
578
  _server?.track(userId, eventName, props);
551
579
  },
@@ -555,7 +583,12 @@ var flags = {
555
583
  * overrides. Returns an empty payload when the blob hasn't been fetched yet.
556
584
  */
557
585
  evaluate(user, rawUrl) {
558
- return _server?.evaluate(user, rawUrl) ?? { flags: {}, configs: {}, experiments: {} };
586
+ return _server?.evaluate(user, rawUrl) ?? {
587
+ flags: {},
588
+ configs: {},
589
+ experiments: {},
590
+ killswitches: {}
591
+ };
559
592
  }
560
593
  };
561
594
  export {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shipeasy/sdk",
3
- "version": "2.4.0",
3
+ "version": "2.5.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",