@statsig/js-local-overrides 3.15.0 → 3.15.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@statsig/js-local-overrides",
3
- "version": "3.15.0",
3
+ "version": "3.15.2",
4
4
  "license": "ISC",
5
5
  "homepage": "https://github.com/statsig-io/js-client-monorepo",
6
6
  "repository": {
@@ -9,7 +9,7 @@
9
9
  "directory": "packages/js-local-overrides"
10
10
  },
11
11
  "dependencies": {
12
- "@statsig/client-core": "3.15.0"
12
+ "@statsig/client-core": "3.15.2"
13
13
  },
14
14
  "type": "commonjs",
15
15
  "main": "./src/index.js",
@@ -10,9 +10,10 @@ export declare class LocalOverrideAdapter implements OverrideAdapter {
10
10
  private _sdkKey;
11
11
  constructor(sdkKey?: string);
12
12
  private _getLocalOverridesStorageKey;
13
- private _loadOverridesFromStorage;
13
+ loadFromStorage(): Promise<void>;
14
14
  private _saveOverridesToStorage;
15
15
  overrideGate(name: string, value: boolean): void;
16
+ private _warnIfStorageNotReady;
16
17
  removeGateOverride(name: string): void;
17
18
  getGateOverride(current: FeatureGate, _user: StatsigUser): FeatureGate | null;
18
19
  overrideDynamicConfig(name: string, value: Record<string, unknown>): void;
@@ -27,4 +28,5 @@ export declare class LocalOverrideAdapter implements OverrideAdapter {
27
28
  removeAllOverrides(): void;
28
29
  getLayerOverride(current: Layer, _user: StatsigUser): Layer | null;
29
30
  private _getConfigOverride;
31
+ private _hasInMemoryOverrides;
30
32
  }
@@ -1,4 +1,13 @@
1
1
  "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
2
11
  Object.defineProperty(exports, "__esModule", { value: true });
3
12
  exports.LocalOverrideAdapter = void 0;
4
13
  const client_core_1 = require("@statsig/client-core");
@@ -11,25 +20,51 @@ function _makeEmptyStore() {
11
20
  layer: {},
12
21
  };
13
22
  }
23
+ function _mergeOverrides(currentValues, newValues) {
24
+ return {
25
+ gate: Object.assign({}, currentValues.gate, newValues.gate),
26
+ dynamicConfig: Object.assign({}, currentValues.dynamicConfig, newValues.dynamicConfig),
27
+ experiment: Object.assign({}, currentValues.experiment, newValues.experiment),
28
+ layer: Object.assign({}, currentValues.layer, newValues.layer),
29
+ };
30
+ }
14
31
  class LocalOverrideAdapter {
15
32
  constructor(sdkKey) {
16
33
  this._overrides = _makeEmptyStore();
17
34
  this._sdkKey = sdkKey !== null && sdkKey !== void 0 ? sdkKey : null;
18
- this._overrides = this._loadOverridesFromStorage();
19
35
  }
20
36
  _getLocalOverridesStorageKey(sdkKey) {
21
37
  return `statsig.local-overrides.${(0, client_core_1._getStorageKey)(sdkKey)}`;
22
38
  }
23
- _loadOverridesFromStorage() {
24
- if (this._sdkKey == null) {
25
- return _makeEmptyStore();
26
- }
27
- const storageKey = this._getLocalOverridesStorageKey(this._sdkKey);
28
- const storedOverrides = client_core_1.Storage.getItem(storageKey);
29
- return storedOverrides ? JSON.parse(storedOverrides) : _makeEmptyStore();
39
+ loadFromStorage() {
40
+ return __awaiter(this, void 0, void 0, function* () {
41
+ if (this._sdkKey == null) {
42
+ return;
43
+ }
44
+ if (!client_core_1.Storage.isReady()) {
45
+ yield client_core_1.Storage.isReadyResolver();
46
+ }
47
+ const storageKey = this._getLocalOverridesStorageKey(this._sdkKey);
48
+ const storedOverrides = client_core_1.Storage.getItem(storageKey);
49
+ const overrides = storedOverrides
50
+ ? (0, client_core_1._typedJsonParse)(storedOverrides, 'gate', 'LocalOverrideAdapter overrides')
51
+ : null;
52
+ const hasInMemoryOverrides = this._hasInMemoryOverrides();
53
+ if (overrides) {
54
+ // Merge overrides in memory with stored overrides.
55
+ // WARN: This is a union of overrides. Trying to remove an override
56
+ // before storage is ready won't remove the override in storage.
57
+ this._overrides = hasInMemoryOverrides
58
+ ? _mergeOverrides(overrides, this._overrides)
59
+ : overrides;
60
+ }
61
+ if (hasInMemoryOverrides) {
62
+ this._saveOverridesToStorage();
63
+ }
64
+ });
30
65
  }
31
66
  _saveOverridesToStorage() {
32
- if (this._sdkKey == null) {
67
+ if (this._sdkKey == null || !client_core_1.Storage.isReady()) {
33
68
  return;
34
69
  }
35
70
  const storageKey = this._getLocalOverridesStorageKey(this._sdkKey);
@@ -40,7 +75,13 @@ class LocalOverrideAdapter {
40
75
  this._overrides.gate[(0, client_core_1._DJB2)(name)] = value;
41
76
  this._saveOverridesToStorage();
42
77
  }
78
+ _warnIfStorageNotReady() {
79
+ if (!client_core_1.Storage.isReady()) {
80
+ client_core_1.Log.warn('Storage is not ready. Override removal may not persist.');
81
+ }
82
+ }
43
83
  removeGateOverride(name) {
84
+ this._warnIfStorageNotReady();
44
85
  delete this._overrides.gate[name];
45
86
  delete this._overrides.gate[(0, client_core_1._DJB2)(name)];
46
87
  this._saveOverridesToStorage();
@@ -59,6 +100,7 @@ class LocalOverrideAdapter {
59
100
  this._saveOverridesToStorage();
60
101
  }
61
102
  removeDynamicConfigOverride(name) {
103
+ this._warnIfStorageNotReady();
62
104
  delete this._overrides.dynamicConfig[name];
63
105
  delete this._overrides.dynamicConfig[(0, client_core_1._DJB2)(name)];
64
106
  this._saveOverridesToStorage();
@@ -72,6 +114,7 @@ class LocalOverrideAdapter {
72
114
  this._saveOverridesToStorage();
73
115
  }
74
116
  removeExperimentOverride(name) {
117
+ this._warnIfStorageNotReady();
75
118
  delete this._overrides.experiment[name];
76
119
  delete this._overrides.experiment[(0, client_core_1._DJB2)(name)];
77
120
  this._saveOverridesToStorage();
@@ -85,6 +128,7 @@ class LocalOverrideAdapter {
85
128
  this._saveOverridesToStorage();
86
129
  }
87
130
  removeLayerOverride(name) {
131
+ this._warnIfStorageNotReady();
88
132
  delete this._overrides.layer[name];
89
133
  delete this._overrides.layer[(0, client_core_1._DJB2)(name)];
90
134
  this._saveOverridesToStorage();
@@ -93,6 +137,7 @@ class LocalOverrideAdapter {
93
137
  return JSON.parse(JSON.stringify(this._overrides));
94
138
  }
95
139
  removeAllOverrides() {
140
+ this._warnIfStorageNotReady();
96
141
  this._overrides = _makeEmptyStore();
97
142
  this._saveOverridesToStorage();
98
143
  }
@@ -112,5 +157,11 @@ class LocalOverrideAdapter {
112
157
  }
113
158
  return Object.assign(Object.assign({}, current), { value: overridden, get: (0, client_core_1._makeTypedGet)(current.name, overridden), details: Object.assign(Object.assign({}, current.details), { reason: LOCAL_OVERRIDE_REASON }) });
114
159
  }
160
+ _hasInMemoryOverrides() {
161
+ return (Object.keys(this._overrides.gate).length > 0 ||
162
+ Object.keys(this._overrides.dynamicConfig).length > 0 ||
163
+ Object.keys(this._overrides.experiment).length > 0 ||
164
+ Object.keys(this._overrides.layer).length > 0);
165
+ }
115
166
  }
116
167
  exports.LocalOverrideAdapter = LocalOverrideAdapter;
@@ -0,0 +1,17 @@
1
+ import { StorageProvider } from '@statsig/client-core';
2
+ export declare class MockStorageProvider implements StorageProvider {
3
+ private ready;
4
+ data: Record<string, string>;
5
+ private _readyPromise;
6
+ private _resolveReady;
7
+ constructor(ready?: boolean);
8
+ getItem(key: string): string | null;
9
+ setItem(key: string, value: string): void;
10
+ isReady(): boolean;
11
+ getProviderName(): string;
12
+ removeItem(key: string): void;
13
+ getAllKeys(): readonly string[];
14
+ isReadyResolver(): Promise<void>;
15
+ setReady(ready: boolean): void;
16
+ reset(): void;
17
+ }
@@ -0,0 +1,53 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MockStorageProvider = void 0;
4
+ class MockStorageProvider {
5
+ constructor(ready = true) {
6
+ this.ready = ready;
7
+ this.data = {};
8
+ this._resolveReady = null;
9
+ this._readyPromise = new Promise((resolve) => {
10
+ if (this.ready) {
11
+ resolve();
12
+ }
13
+ else {
14
+ this._resolveReady = resolve;
15
+ }
16
+ });
17
+ }
18
+ getItem(key) {
19
+ return this.data[key] || null;
20
+ }
21
+ setItem(key, value) {
22
+ this.data[key] = value;
23
+ }
24
+ isReady() {
25
+ return this.ready;
26
+ }
27
+ getProviderName() {
28
+ return 'MockStorage';
29
+ }
30
+ removeItem(key) {
31
+ delete this.data[key];
32
+ }
33
+ getAllKeys() {
34
+ return Object.keys(this.data);
35
+ }
36
+ isReadyResolver() {
37
+ return this._readyPromise;
38
+ }
39
+ setReady(ready) {
40
+ this.ready = ready;
41
+ if (ready && this._resolveReady) {
42
+ this._resolveReady();
43
+ this._resolveReady = null;
44
+ }
45
+ }
46
+ reset() {
47
+ this.data = {};
48
+ this.ready = true;
49
+ this._resolveReady = null;
50
+ this._readyPromise = Promise.resolve();
51
+ }
52
+ }
53
+ exports.MockStorageProvider = MockStorageProvider;