posthog-node 5.11.2 → 5.12.0
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/LICENSE +56 -2
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +3 -2
- package/dist/client.mjs +3 -2
- package/dist/experimental.d.ts +11 -0
- package/dist/experimental.d.ts.map +1 -0
- package/dist/experimental.js +18 -0
- package/dist/experimental.mjs +0 -0
- package/dist/extensions/feature-flags/cache.d.ts +110 -0
- package/dist/extensions/feature-flags/cache.d.ts.map +1 -0
- package/dist/extensions/feature-flags/cache.js +18 -0
- package/dist/extensions/feature-flags/cache.mjs +0 -0
- package/dist/extensions/feature-flags/feature-flags.d.ts +15 -1
- package/dist/extensions/feature-flags/feature-flags.d.ts.map +1 -1
- package/dist/extensions/feature-flags/feature-flags.js +70 -9
- package/dist/extensions/feature-flags/feature-flags.mjs +70 -9
- package/dist/types.d.ts +27 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/version.mjs +1 -1
- package/package.json +5 -1
- package/src/client.ts +2 -1
- package/src/experimental.ts +11 -0
- package/src/extensions/feature-flags/cache.ts +114 -0
- package/src/extensions/feature-flags/feature-flags.ts +138 -12
- package/src/types.ts +28 -0
- package/src/version.ts +1 -1
package/LICENSE
CHANGED
|
@@ -219,7 +219,7 @@ limitations under the License.
|
|
|
219
219
|
|
|
220
220
|
---
|
|
221
221
|
|
|
222
|
-
Some files in this codebase contain code from getsentry/sentry-javascript by Software, Inc. dba Sentry.
|
|
222
|
+
Some files in this codebase contain code from getsentry/sentry-javascript or getsentry/sentry-react-native by Software, Inc. dba Sentry.
|
|
223
223
|
In such cases it is explicitly stated in the file header. This license only applies to the relevant code in such cases.
|
|
224
224
|
|
|
225
225
|
MIT License
|
|
@@ -242,4 +242,58 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
|
242
242
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
243
243
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
244
244
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
245
|
-
SOFTWARE.
|
|
245
|
+
SOFTWARE.
|
|
246
|
+
|
|
247
|
+
---
|
|
248
|
+
|
|
249
|
+
Some files in this codebase contain code from facebook/metro by Meta Platforms, Inc. and affiliates.
|
|
250
|
+
In such cases it is explicitly stated in the file header. This license only applies to the relevant code in such cases.
|
|
251
|
+
|
|
252
|
+
MIT License
|
|
253
|
+
|
|
254
|
+
Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
255
|
+
|
|
256
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
257
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
258
|
+
in the Software without restriction, including without limitation the rights
|
|
259
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
260
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
261
|
+
furnished to do so, subject to the following conditions:
|
|
262
|
+
|
|
263
|
+
The above copyright notice and this permission notice shall be included in all
|
|
264
|
+
copies or substantial portions of the Software.
|
|
265
|
+
|
|
266
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
267
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
268
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
269
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
270
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
271
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
272
|
+
SOFTWARE.
|
|
273
|
+
|
|
274
|
+
---
|
|
275
|
+
|
|
276
|
+
Some files in this codebase contain code from expo/expo by 650 Industries, Inc. (aka Expo).
|
|
277
|
+
In such cases it is explicitly stated in the file header. This license only applies to the relevant code in such cases.
|
|
278
|
+
|
|
279
|
+
The MIT License (MIT)
|
|
280
|
+
|
|
281
|
+
Copyright (c) 2015-present 650 Industries, Inc. (aka Expo)
|
|
282
|
+
|
|
283
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
284
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
285
|
+
in the Software without restriction, including without limitation the rights
|
|
286
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
287
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
288
|
+
furnished to do so, subject to the following conditions:
|
|
289
|
+
|
|
290
|
+
The above copyright notice and this permission notice shall be included in all
|
|
291
|
+
copies or substantial portions of the Software.
|
|
292
|
+
|
|
293
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
294
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
295
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
296
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
297
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
298
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
299
|
+
SOFTWARE.
|
package/dist/client.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,QAAQ,EACR,oBAAoB,EAEpB,mBAAmB,EACnB,oBAAoB,EACpB,+BAA+B,EAC/B,wBAAwB,EAExB,qBAAqB,EAGtB,MAAM,eAAe,CAAA;AACtB,OAAO,EACL,YAAY,EACZ,oBAAoB,EACpB,eAAe,EACf,QAAQ,EACR,cAAc,EAEf,MAAM,SAAS,CAAA;AAChB,OAAO,EAAqB,gBAAgB,EAAuB,MAAM,eAAe,CAAA;AAMxF,OAAO,aAAa,MAAM,6BAA6B,CAAA;AACvD,OAAO,EAAkB,sBAAsB,EAAE,MAAM,eAAe,CAAA;AAUtE,8BAAsB,oBAAqB,SAAQ,oBAAqB,YAAW,QAAQ;IACzF,OAAO,CAAC,cAAc,CAA6B;IAEnD,OAAO,CAAC,kBAAkB,CAAC,CAAoB;IAC/C,SAAS,CAAC,aAAa,EAAE,aAAa,CAAA;IACtC,OAAO,CAAC,YAAY,CAAQ;IAC5B,SAAgB,OAAO,EAAE,cAAc,CAAA;IAEvC,0BAA0B,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAA;IAEpD;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA4BG;gBACS,MAAM,EAAE,MAAM,EAAE,OAAO,GAAE,cAAmB;
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
|
package/dist/client.js
CHANGED
|
@@ -65,7 +65,8 @@ class PostHogBackendClient extends core_namespaceObject.PostHogCoreStateless {
|
|
|
65
65
|
onLoad: (count)=>{
|
|
66
66
|
this._events.emit('localEvaluationFlagsLoaded', count);
|
|
67
67
|
},
|
|
68
|
-
customHeaders: this.getCustomHeaders()
|
|
68
|
+
customHeaders: this.getCustomHeaders(),
|
|
69
|
+
cacheProvider: options.flagDefinitionCacheProvider
|
|
69
70
|
});
|
|
70
71
|
}
|
|
71
72
|
this.errorTracking = new (index_js_default())(this, options, this._logger);
|
|
@@ -299,7 +300,7 @@ class PostHogBackendClient extends core_namespaceObject.PostHogCoreStateless {
|
|
|
299
300
|
await this.featureFlagsPoller?.loadFeatureFlags(true);
|
|
300
301
|
}
|
|
301
302
|
async _shutdown(shutdownTimeoutMs) {
|
|
302
|
-
this.featureFlagsPoller?.stopPoller();
|
|
303
|
+
this.featureFlagsPoller?.stopPoller(shutdownTimeoutMs);
|
|
303
304
|
this.errorTracking.shutdown();
|
|
304
305
|
return super._shutdown(shutdownTimeoutMs);
|
|
305
306
|
}
|
package/dist/client.mjs
CHANGED
|
@@ -27,7 +27,8 @@ class PostHogBackendClient extends PostHogCoreStateless {
|
|
|
27
27
|
onLoad: (count)=>{
|
|
28
28
|
this._events.emit('localEvaluationFlagsLoaded', count);
|
|
29
29
|
},
|
|
30
|
-
customHeaders: this.getCustomHeaders()
|
|
30
|
+
customHeaders: this.getCustomHeaders(),
|
|
31
|
+
cacheProvider: options.flagDefinitionCacheProvider
|
|
31
32
|
});
|
|
32
33
|
}
|
|
33
34
|
this.errorTracking = new error_tracking(this, options, this._logger);
|
|
@@ -261,7 +262,7 @@ class PostHogBackendClient extends PostHogCoreStateless {
|
|
|
261
262
|
await this.featureFlagsPoller?.loadFeatureFlags(true);
|
|
262
263
|
}
|
|
263
264
|
async _shutdown(shutdownTimeoutMs) {
|
|
264
|
-
this.featureFlagsPoller?.stopPoller();
|
|
265
|
+
this.featureFlagsPoller?.stopPoller(shutdownTimeoutMs);
|
|
265
266
|
this.errorTracking.shutdown();
|
|
266
267
|
return super._shutdown(shutdownTimeoutMs);
|
|
267
268
|
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Experimental APIs
|
|
3
|
+
*
|
|
4
|
+
* This module exports experimental features that may change or be removed in minor versions.
|
|
5
|
+
* Use these APIs with caution and be prepared for breaking changes.
|
|
6
|
+
*
|
|
7
|
+
* @packageDocumentation
|
|
8
|
+
* @experimental
|
|
9
|
+
*/
|
|
10
|
+
export type { FlagDefinitionCacheProvider, FlagDefinitionCacheData } from './extensions/feature-flags/cache';
|
|
11
|
+
//# sourceMappingURL=experimental.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"experimental.d.ts","sourceRoot":"","sources":["../src/experimental.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,YAAY,EAAE,2BAA2B,EAAE,uBAAuB,EAAE,MAAM,kCAAkC,CAAA"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __webpack_require__ = {};
|
|
3
|
+
(()=>{
|
|
4
|
+
__webpack_require__.r = (exports1)=>{
|
|
5
|
+
if ('undefined' != typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, {
|
|
6
|
+
value: 'Module'
|
|
7
|
+
});
|
|
8
|
+
Object.defineProperty(exports1, '__esModule', {
|
|
9
|
+
value: true
|
|
10
|
+
});
|
|
11
|
+
};
|
|
12
|
+
})();
|
|
13
|
+
var __webpack_exports__ = {};
|
|
14
|
+
__webpack_require__.r(__webpack_exports__);
|
|
15
|
+
for(var __webpack_i__ in __webpack_exports__)exports[__webpack_i__] = __webpack_exports__[__webpack_i__];
|
|
16
|
+
Object.defineProperty(exports, '__esModule', {
|
|
17
|
+
value: true
|
|
18
|
+
});
|
|
File without changes
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import type { PostHogFeatureFlag, PropertyGroup } from '../../types';
|
|
2
|
+
/**
|
|
3
|
+
* Represents the complete set of feature flag data needed for local evaluation.
|
|
4
|
+
*
|
|
5
|
+
* This includes flag definitions, group type mappings, and cohort property groups.
|
|
6
|
+
*/
|
|
7
|
+
export interface FlagDefinitionCacheData {
|
|
8
|
+
/** Array of feature flag definitions */
|
|
9
|
+
flags: PostHogFeatureFlag[];
|
|
10
|
+
/** Mapping of group type index to group name */
|
|
11
|
+
groupTypeMapping: Record<string, string>;
|
|
12
|
+
/** Cohort property groups for local evaluation */
|
|
13
|
+
cohorts: Record<string, PropertyGroup>;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* @experimental This API is experimental and may change in minor versions.
|
|
17
|
+
*
|
|
18
|
+
* Provider interface for caching feature flag definitions.
|
|
19
|
+
*
|
|
20
|
+
* Implementations can use this to control when flag definitions are fetched
|
|
21
|
+
* and how they're cached (Redis, database, filesystem, etc.).
|
|
22
|
+
*
|
|
23
|
+
* This interface is designed for server-side environments where multiple workers
|
|
24
|
+
* need to share flag definitions and coordinate fetching to reduce API calls.
|
|
25
|
+
*
|
|
26
|
+
* All methods may throw errors - the poller will catch and log them gracefully,
|
|
27
|
+
* ensuring cache provider errors never break flag evaluation.
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* ```typescript
|
|
31
|
+
* import { FlagDefinitionCacheProvider } from 'posthog-node/experimental'
|
|
32
|
+
*
|
|
33
|
+
* class RedisFlagCache implements FlagDefinitionCacheProvider {
|
|
34
|
+
* constructor(private redis: Redis, private teamKey: string) { }
|
|
35
|
+
*
|
|
36
|
+
* async getFlagDefinitions(): Promise<FlagDefinitionCacheData | undefined> {
|
|
37
|
+
* const cached = await this.redis.get(`posthog:flags:${this.teamKey}`)
|
|
38
|
+
* return cached ? JSON.parse(cached) : undefined
|
|
39
|
+
* }
|
|
40
|
+
*
|
|
41
|
+
* async shouldFetchFlagDefinitions(): Promise<boolean> {
|
|
42
|
+
* // Acquire distributed lock - only one worker fetches
|
|
43
|
+
* const acquired = await this.redis.set(`posthog:flags:${this.teamKey}:lock`, '1', 'EX', 60, 'NX')
|
|
44
|
+
* return acquired === 'OK'
|
|
45
|
+
* }
|
|
46
|
+
*
|
|
47
|
+
* async onFlagDefinitionsReceived(data: FlagDefinitionCacheData): Promise<void> {
|
|
48
|
+
* await this.redis.set(`posthog:flags:${this.teamKey}`, JSON.stringify(data), 'EX', 300)
|
|
49
|
+
* await this.redis.del(`posthog:flags:${this.teamKey}:lock`)
|
|
50
|
+
* }
|
|
51
|
+
*
|
|
52
|
+
* async shutdown(): Promise<void> {
|
|
53
|
+
* await this.redis.del(`posthog:flags:${this.teamKey}:lock`)
|
|
54
|
+
* }
|
|
55
|
+
* }
|
|
56
|
+
* ```
|
|
57
|
+
*/
|
|
58
|
+
export interface FlagDefinitionCacheProvider {
|
|
59
|
+
/**
|
|
60
|
+
* Retrieve cached flag definitions.
|
|
61
|
+
*
|
|
62
|
+
* Called when the poller is refreshing in-memory flag definitions. If this returns undefined
|
|
63
|
+
* (or throws an error), the poller will fetch fresh data from the PostHog API if no flag
|
|
64
|
+
* definitions are in memory. Otherwise, stale cache data is used until the next poll cycle.
|
|
65
|
+
*
|
|
66
|
+
* @returns cached definitions if available, undefined if cache is empty
|
|
67
|
+
* @throws if an error occurs while accessing the cache (error will be logged)
|
|
68
|
+
*/
|
|
69
|
+
getFlagDefinitions(): Promise<FlagDefinitionCacheData | undefined> | FlagDefinitionCacheData | undefined;
|
|
70
|
+
/**
|
|
71
|
+
* Determines whether this instance should fetch new flag definitions.
|
|
72
|
+
*
|
|
73
|
+
* Use this to implement distributed coordination (e.g., via distributed locks)
|
|
74
|
+
* to ensure only one instance fetches at a time in a multi-worker setup.
|
|
75
|
+
*
|
|
76
|
+
* When multiple workers share a cache, typically only one should fetch while
|
|
77
|
+
* others use cached data. Implementations can use Redis locks, database locks,
|
|
78
|
+
* or other coordination mechanisms.
|
|
79
|
+
*
|
|
80
|
+
* @returns true if this instance should fetch, false to skip and read cache
|
|
81
|
+
* @throws if coordination backend is unavailable (error will be logged, fetch continues)
|
|
82
|
+
*/
|
|
83
|
+
shouldFetchFlagDefinitions(): Promise<boolean> | boolean;
|
|
84
|
+
/**
|
|
85
|
+
* Called after successfully receiving new flag definitions from PostHog.
|
|
86
|
+
*
|
|
87
|
+
* Store the definitions in your cache backend here. This is called only
|
|
88
|
+
* after a successful API response with valid flag data.
|
|
89
|
+
*
|
|
90
|
+
* If this method throws, the error is logged but flag definitions are still
|
|
91
|
+
* stored in memory, ensuring local evaluation can still be performed.
|
|
92
|
+
*
|
|
93
|
+
* @param data - The complete flag definition data from PostHog
|
|
94
|
+
* @throws if storage backend is unavailable (error will be logged)
|
|
95
|
+
*/
|
|
96
|
+
onFlagDefinitionsReceived(data: FlagDefinitionCacheData): Promise<void> | void;
|
|
97
|
+
/**
|
|
98
|
+
* Called when the PostHog client shuts down.
|
|
99
|
+
*
|
|
100
|
+
* Release any held locks, close connections, or clean up resources here.
|
|
101
|
+
*
|
|
102
|
+
* Both sync and async cleanup are supported. Async cleanup has a timeout
|
|
103
|
+
* (default 30s, configurable via client shutdown options) to prevent the
|
|
104
|
+
* process shutdown from hanging indefinitely.
|
|
105
|
+
*
|
|
106
|
+
* @returns Promise that resolves when cleanup is complete, or void for sync cleanup
|
|
107
|
+
*/
|
|
108
|
+
shutdown(): Promise<void> | void;
|
|
109
|
+
}
|
|
110
|
+
//# sourceMappingURL=cache.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../../../src/extensions/feature-flags/cache.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAEpE;;;;GAIG;AACH,MAAM,WAAW,uBAAuB;IACtC,wCAAwC;IACxC,KAAK,EAAE,kBAAkB,EAAE,CAAA;IAC3B,gDAAgD;IAChD,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACxC,kDAAkD;IAClD,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAA;CACvC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AACH,MAAM,WAAW,2BAA2B;IAC1C;;;;;;;;;OASG;IACH,kBAAkB,IAAI,OAAO,CAAC,uBAAuB,GAAG,SAAS,CAAC,GAAG,uBAAuB,GAAG,SAAS,CAAA;IAExG;;;;;;;;;;;;OAYG;IACH,0BAA0B,IAAI,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAA;IAExD;;;;;;;;;;;OAWG;IACH,yBAAyB,CAAC,IAAI,EAAE,uBAAuB,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAA;IAE9E;;;;;;;;;;OAUG;IACH,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAA;CACjC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __webpack_require__ = {};
|
|
3
|
+
(()=>{
|
|
4
|
+
__webpack_require__.r = (exports1)=>{
|
|
5
|
+
if ('undefined' != typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, {
|
|
6
|
+
value: 'Module'
|
|
7
|
+
});
|
|
8
|
+
Object.defineProperty(exports1, '__esModule', {
|
|
9
|
+
value: true
|
|
10
|
+
});
|
|
11
|
+
};
|
|
12
|
+
})();
|
|
13
|
+
var __webpack_exports__ = {};
|
|
14
|
+
__webpack_require__.r(__webpack_exports__);
|
|
15
|
+
for(var __webpack_i__ in __webpack_exports__)exports[__webpack_i__] = __webpack_exports__[__webpack_i__];
|
|
16
|
+
Object.defineProperty(exports, '__esModule', {
|
|
17
|
+
value: true
|
|
18
|
+
});
|
|
File without changes
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { FeatureFlagCondition, PostHogFeatureFlag, PropertyGroup } from '../../types';
|
|
2
2
|
import type { FeatureFlagValue, JsonType, PostHogFetchOptions, PostHogFetchResponse } from '@posthog/core';
|
|
3
|
+
import { FlagDefinitionCacheProvider } from './cache';
|
|
3
4
|
declare class ClientError extends Error {
|
|
4
5
|
constructor(message: string);
|
|
5
6
|
}
|
|
@@ -21,6 +22,7 @@ type FeatureFlagsPollerOptions = {
|
|
|
21
22
|
customHeaders?: {
|
|
22
23
|
[key: string]: string;
|
|
23
24
|
};
|
|
25
|
+
cacheProvider?: FlagDefinitionCacheProvider;
|
|
24
26
|
};
|
|
25
27
|
declare class FeatureFlagsPoller {
|
|
26
28
|
pollingInterval: number;
|
|
@@ -43,6 +45,9 @@ declare class FeatureFlagsPoller {
|
|
|
43
45
|
shouldBeginExponentialBackoff: boolean;
|
|
44
46
|
backOffCount: number;
|
|
45
47
|
onLoad?: (count: number) => void;
|
|
48
|
+
private cacheProvider?;
|
|
49
|
+
private hasAttemptedCacheLoad;
|
|
50
|
+
private loadingPromise?;
|
|
46
51
|
constructor({ pollingInterval, personalApiKey, projectApiKey, timeout, host, customHeaders, ...options }: FeatureFlagsPollerOptions);
|
|
47
52
|
debug(enabled?: boolean): void;
|
|
48
53
|
private logMsgIfDebug;
|
|
@@ -68,6 +73,15 @@ declare class FeatureFlagsPoller {
|
|
|
68
73
|
valueMax: number;
|
|
69
74
|
key: string;
|
|
70
75
|
}[];
|
|
76
|
+
/**
|
|
77
|
+
* Updates the internal flag state with the provided flag data.
|
|
78
|
+
*/
|
|
79
|
+
private updateFlagState;
|
|
80
|
+
/**
|
|
81
|
+
* Attempts to load flags from cache and update internal state.
|
|
82
|
+
* Returns true if flags were successfully loaded from cache, false otherwise.
|
|
83
|
+
*/
|
|
84
|
+
private loadFromCache;
|
|
71
85
|
loadFeatureFlags(forceReload?: boolean): Promise<void>;
|
|
72
86
|
/**
|
|
73
87
|
* Returns true if the feature flags poller has loaded successfully at least once and has more than 0 feature flags.
|
|
@@ -84,7 +98,7 @@ declare class FeatureFlagsPoller {
|
|
|
84
98
|
_loadFeatureFlags(): Promise<void>;
|
|
85
99
|
private getPersonalApiKeyRequestOptions;
|
|
86
100
|
_requestFeatureFlagDefinitions(): Promise<PostHogFetchResponse>;
|
|
87
|
-
stopPoller(): void
|
|
101
|
+
stopPoller(timeoutMs?: number): Promise<void>;
|
|
88
102
|
}
|
|
89
103
|
declare function matchProperty(property: FeatureFlagCondition['properties'][number], propertyValues: Record<string, any>, warnFunction?: (msg: string) => void): boolean;
|
|
90
104
|
declare function relativeDateParseForFeatureFlagMatching(value: string): Date | null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"feature-flags.d.ts","sourceRoot":"","sources":["../../../src/extensions/feature-flags/feature-flags.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAmC,kBAAkB,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AACtH,OAAO,KAAK,EAAE,gBAAgB,EAAE,QAAQ,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAA;
|
|
1
|
+
{"version":3,"file":"feature-flags.d.ts","sourceRoot":"","sources":["../../../src/extensions/feature-flags/feature-flags.ts"],"names":[],"mappings":"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"}
|
|
@@ -68,6 +68,7 @@ class FeatureFlagsPoller {
|
|
|
68
68
|
this.debugMode = false;
|
|
69
69
|
this.shouldBeginExponentialBackoff = false;
|
|
70
70
|
this.backOffCount = 0;
|
|
71
|
+
this.hasAttemptedCacheLoad = false;
|
|
71
72
|
this.pollingInterval = pollingInterval;
|
|
72
73
|
this.personalApiKey = personalApiKey;
|
|
73
74
|
this.featureFlags = [];
|
|
@@ -83,6 +84,7 @@ class FeatureFlagsPoller {
|
|
|
83
84
|
this.onError = options.onError;
|
|
84
85
|
this.customHeaders = customHeaders;
|
|
85
86
|
this.onLoad = options.onLoad;
|
|
87
|
+
this.cacheProvider = options.cacheProvider;
|
|
86
88
|
this.loadFeatureFlags();
|
|
87
89
|
}
|
|
88
90
|
debug(enabled = true) {
|
|
@@ -270,8 +272,39 @@ class FeatureFlagsPoller {
|
|
|
270
272
|
});
|
|
271
273
|
return lookupTable;
|
|
272
274
|
}
|
|
275
|
+
updateFlagState(flagData) {
|
|
276
|
+
this.featureFlags = flagData.flags;
|
|
277
|
+
this.featureFlagsByKey = flagData.flags.reduce((acc, curr)=>(acc[curr.key] = curr, acc), {});
|
|
278
|
+
this.groupTypeMapping = flagData.groupTypeMapping;
|
|
279
|
+
this.cohorts = flagData.cohorts;
|
|
280
|
+
this.loadedSuccessfullyOnce = true;
|
|
281
|
+
}
|
|
282
|
+
async loadFromCache(debugMessage) {
|
|
283
|
+
if (!this.cacheProvider) return false;
|
|
284
|
+
try {
|
|
285
|
+
const cached = await this.cacheProvider.getFlagDefinitions();
|
|
286
|
+
if (cached) {
|
|
287
|
+
this.updateFlagState(cached);
|
|
288
|
+
this.logMsgIfDebug(()=>console.debug(`[FEATURE FLAGS] ${debugMessage} (${cached.flags.length} flags)`));
|
|
289
|
+
this.onLoad?.(this.featureFlags.length);
|
|
290
|
+
return true;
|
|
291
|
+
}
|
|
292
|
+
return false;
|
|
293
|
+
} catch (err) {
|
|
294
|
+
this.onError?.(new Error(`Failed to load from cache: ${err}`));
|
|
295
|
+
return false;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
273
298
|
async loadFeatureFlags(forceReload = false) {
|
|
274
|
-
if (
|
|
299
|
+
if (this.cacheProvider && !this.hasAttemptedCacheLoad) {
|
|
300
|
+
this.hasAttemptedCacheLoad = true;
|
|
301
|
+
await this.loadFromCache('Loaded flags from cache');
|
|
302
|
+
}
|
|
303
|
+
if (this.loadingPromise) return this.loadingPromise;
|
|
304
|
+
if (!this.loadedSuccessfullyOnce || forceReload) {
|
|
305
|
+
this.loadingPromise = this._loadFeatureFlags();
|
|
306
|
+
await this.loadingPromise;
|
|
307
|
+
}
|
|
275
308
|
}
|
|
276
309
|
isLocalEvaluationReady() {
|
|
277
310
|
return (this.loadedSuccessfullyOnce ?? false) && (this.featureFlags?.length ?? 0) > 0;
|
|
@@ -287,6 +320,17 @@ class FeatureFlagsPoller {
|
|
|
287
320
|
}
|
|
288
321
|
this.poller = setTimeout(()=>this._loadFeatureFlags(), this.getPollingInterval());
|
|
289
322
|
try {
|
|
323
|
+
let shouldFetch = true;
|
|
324
|
+
if (this.cacheProvider) try {
|
|
325
|
+
shouldFetch = await this.cacheProvider.shouldFetchFlagDefinitions();
|
|
326
|
+
} catch (err) {
|
|
327
|
+
this.onError?.(new Error(`Error in shouldFetchFlagDefinitions: ${err}`));
|
|
328
|
+
}
|
|
329
|
+
if (!shouldFetch) {
|
|
330
|
+
const loaded = await this.loadFromCache('Loaded flags from cache (skipped fetch)');
|
|
331
|
+
if (loaded) return;
|
|
332
|
+
if (this.loadedSuccessfullyOnce) return;
|
|
333
|
+
}
|
|
290
334
|
const res = await this._requestFeatureFlagDefinitions();
|
|
291
335
|
if (!res) return;
|
|
292
336
|
switch(res.status){
|
|
@@ -313,13 +357,19 @@ class FeatureFlagsPoller {
|
|
|
313
357
|
{
|
|
314
358
|
const responseJson = await res.json() ?? {};
|
|
315
359
|
if (!('flags' in responseJson)) return void this.onError?.(new Error(`Invalid response when getting feature flags: ${JSON.stringify(responseJson)}`));
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
360
|
+
const flagData = {
|
|
361
|
+
flags: responseJson.flags ?? [],
|
|
362
|
+
groupTypeMapping: responseJson.group_type_mapping || {},
|
|
363
|
+
cohorts: responseJson.cohorts || {}
|
|
364
|
+
};
|
|
365
|
+
this.updateFlagState(flagData);
|
|
321
366
|
this.shouldBeginExponentialBackoff = false;
|
|
322
367
|
this.backOffCount = 0;
|
|
368
|
+
if (this.cacheProvider && shouldFetch) try {
|
|
369
|
+
await this.cacheProvider.onFlagDefinitionsReceived(flagData);
|
|
370
|
+
} catch (err) {
|
|
371
|
+
this.onError?.(new Error(`Failed to store in cache: ${err}`));
|
|
372
|
+
}
|
|
323
373
|
this.onLoad?.(this.featureFlags.length);
|
|
324
374
|
break;
|
|
325
375
|
}
|
|
@@ -328,6 +378,8 @@ class FeatureFlagsPoller {
|
|
|
328
378
|
}
|
|
329
379
|
} catch (err) {
|
|
330
380
|
if (err instanceof ClientError) this.onError?.(err);
|
|
381
|
+
} finally{
|
|
382
|
+
this.loadingPromise = void 0;
|
|
331
383
|
}
|
|
332
384
|
}
|
|
333
385
|
getPersonalApiKeyRequestOptions(method = 'GET') {
|
|
@@ -340,7 +392,7 @@ class FeatureFlagsPoller {
|
|
|
340
392
|
}
|
|
341
393
|
};
|
|
342
394
|
}
|
|
343
|
-
|
|
395
|
+
_requestFeatureFlagDefinitions() {
|
|
344
396
|
const url = `${this.host}/api/feature_flag/local_evaluation?token=${this.projectApiKey}&send_cohorts`;
|
|
345
397
|
const options = this.getPersonalApiKeyRequestOptions();
|
|
346
398
|
let abortTimeout = null;
|
|
@@ -352,13 +404,22 @@ class FeatureFlagsPoller {
|
|
|
352
404
|
options.signal = controller.signal;
|
|
353
405
|
}
|
|
354
406
|
try {
|
|
355
|
-
return
|
|
407
|
+
return this.fetch(url, options);
|
|
356
408
|
} finally{
|
|
357
409
|
clearTimeout(abortTimeout);
|
|
358
410
|
}
|
|
359
411
|
}
|
|
360
|
-
stopPoller() {
|
|
412
|
+
async stopPoller(timeoutMs = 30000) {
|
|
361
413
|
clearTimeout(this.poller);
|
|
414
|
+
if (this.cacheProvider) try {
|
|
415
|
+
const shutdownResult = this.cacheProvider.shutdown();
|
|
416
|
+
if (shutdownResult instanceof Promise) await Promise.race([
|
|
417
|
+
shutdownResult,
|
|
418
|
+
new Promise((_, reject)=>setTimeout(()=>reject(new Error(`Cache shutdown timeout after ${timeoutMs}ms`)), timeoutMs))
|
|
419
|
+
]);
|
|
420
|
+
} catch (err) {
|
|
421
|
+
this.onError?.(new Error(`Error during cache shutdown: ${err}`));
|
|
422
|
+
}
|
|
362
423
|
}
|
|
363
424
|
}
|
|
364
425
|
async function _hash(key, distinctId, salt = '') {
|
|
@@ -35,6 +35,7 @@ class FeatureFlagsPoller {
|
|
|
35
35
|
this.debugMode = false;
|
|
36
36
|
this.shouldBeginExponentialBackoff = false;
|
|
37
37
|
this.backOffCount = 0;
|
|
38
|
+
this.hasAttemptedCacheLoad = false;
|
|
38
39
|
this.pollingInterval = pollingInterval;
|
|
39
40
|
this.personalApiKey = personalApiKey;
|
|
40
41
|
this.featureFlags = [];
|
|
@@ -50,6 +51,7 @@ class FeatureFlagsPoller {
|
|
|
50
51
|
this.onError = options.onError;
|
|
51
52
|
this.customHeaders = customHeaders;
|
|
52
53
|
this.onLoad = options.onLoad;
|
|
54
|
+
this.cacheProvider = options.cacheProvider;
|
|
53
55
|
this.loadFeatureFlags();
|
|
54
56
|
}
|
|
55
57
|
debug(enabled = true) {
|
|
@@ -237,8 +239,39 @@ class FeatureFlagsPoller {
|
|
|
237
239
|
});
|
|
238
240
|
return lookupTable;
|
|
239
241
|
}
|
|
242
|
+
updateFlagState(flagData) {
|
|
243
|
+
this.featureFlags = flagData.flags;
|
|
244
|
+
this.featureFlagsByKey = flagData.flags.reduce((acc, curr)=>(acc[curr.key] = curr, acc), {});
|
|
245
|
+
this.groupTypeMapping = flagData.groupTypeMapping;
|
|
246
|
+
this.cohorts = flagData.cohorts;
|
|
247
|
+
this.loadedSuccessfullyOnce = true;
|
|
248
|
+
}
|
|
249
|
+
async loadFromCache(debugMessage) {
|
|
250
|
+
if (!this.cacheProvider) return false;
|
|
251
|
+
try {
|
|
252
|
+
const cached = await this.cacheProvider.getFlagDefinitions();
|
|
253
|
+
if (cached) {
|
|
254
|
+
this.updateFlagState(cached);
|
|
255
|
+
this.logMsgIfDebug(()=>console.debug(`[FEATURE FLAGS] ${debugMessage} (${cached.flags.length} flags)`));
|
|
256
|
+
this.onLoad?.(this.featureFlags.length);
|
|
257
|
+
return true;
|
|
258
|
+
}
|
|
259
|
+
return false;
|
|
260
|
+
} catch (err) {
|
|
261
|
+
this.onError?.(new Error(`Failed to load from cache: ${err}`));
|
|
262
|
+
return false;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
240
265
|
async loadFeatureFlags(forceReload = false) {
|
|
241
|
-
if (
|
|
266
|
+
if (this.cacheProvider && !this.hasAttemptedCacheLoad) {
|
|
267
|
+
this.hasAttemptedCacheLoad = true;
|
|
268
|
+
await this.loadFromCache('Loaded flags from cache');
|
|
269
|
+
}
|
|
270
|
+
if (this.loadingPromise) return this.loadingPromise;
|
|
271
|
+
if (!this.loadedSuccessfullyOnce || forceReload) {
|
|
272
|
+
this.loadingPromise = this._loadFeatureFlags();
|
|
273
|
+
await this.loadingPromise;
|
|
274
|
+
}
|
|
242
275
|
}
|
|
243
276
|
isLocalEvaluationReady() {
|
|
244
277
|
return (this.loadedSuccessfullyOnce ?? false) && (this.featureFlags?.length ?? 0) > 0;
|
|
@@ -254,6 +287,17 @@ class FeatureFlagsPoller {
|
|
|
254
287
|
}
|
|
255
288
|
this.poller = setTimeout(()=>this._loadFeatureFlags(), this.getPollingInterval());
|
|
256
289
|
try {
|
|
290
|
+
let shouldFetch = true;
|
|
291
|
+
if (this.cacheProvider) try {
|
|
292
|
+
shouldFetch = await this.cacheProvider.shouldFetchFlagDefinitions();
|
|
293
|
+
} catch (err) {
|
|
294
|
+
this.onError?.(new Error(`Error in shouldFetchFlagDefinitions: ${err}`));
|
|
295
|
+
}
|
|
296
|
+
if (!shouldFetch) {
|
|
297
|
+
const loaded = await this.loadFromCache('Loaded flags from cache (skipped fetch)');
|
|
298
|
+
if (loaded) return;
|
|
299
|
+
if (this.loadedSuccessfullyOnce) return;
|
|
300
|
+
}
|
|
257
301
|
const res = await this._requestFeatureFlagDefinitions();
|
|
258
302
|
if (!res) return;
|
|
259
303
|
switch(res.status){
|
|
@@ -280,13 +324,19 @@ class FeatureFlagsPoller {
|
|
|
280
324
|
{
|
|
281
325
|
const responseJson = await res.json() ?? {};
|
|
282
326
|
if (!('flags' in responseJson)) return void this.onError?.(new Error(`Invalid response when getting feature flags: ${JSON.stringify(responseJson)}`));
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
327
|
+
const flagData = {
|
|
328
|
+
flags: responseJson.flags ?? [],
|
|
329
|
+
groupTypeMapping: responseJson.group_type_mapping || {},
|
|
330
|
+
cohorts: responseJson.cohorts || {}
|
|
331
|
+
};
|
|
332
|
+
this.updateFlagState(flagData);
|
|
288
333
|
this.shouldBeginExponentialBackoff = false;
|
|
289
334
|
this.backOffCount = 0;
|
|
335
|
+
if (this.cacheProvider && shouldFetch) try {
|
|
336
|
+
await this.cacheProvider.onFlagDefinitionsReceived(flagData);
|
|
337
|
+
} catch (err) {
|
|
338
|
+
this.onError?.(new Error(`Failed to store in cache: ${err}`));
|
|
339
|
+
}
|
|
290
340
|
this.onLoad?.(this.featureFlags.length);
|
|
291
341
|
break;
|
|
292
342
|
}
|
|
@@ -295,6 +345,8 @@ class FeatureFlagsPoller {
|
|
|
295
345
|
}
|
|
296
346
|
} catch (err) {
|
|
297
347
|
if (err instanceof ClientError) this.onError?.(err);
|
|
348
|
+
} finally{
|
|
349
|
+
this.loadingPromise = void 0;
|
|
298
350
|
}
|
|
299
351
|
}
|
|
300
352
|
getPersonalApiKeyRequestOptions(method = 'GET') {
|
|
@@ -307,7 +359,7 @@ class FeatureFlagsPoller {
|
|
|
307
359
|
}
|
|
308
360
|
};
|
|
309
361
|
}
|
|
310
|
-
|
|
362
|
+
_requestFeatureFlagDefinitions() {
|
|
311
363
|
const url = `${this.host}/api/feature_flag/local_evaluation?token=${this.projectApiKey}&send_cohorts`;
|
|
312
364
|
const options = this.getPersonalApiKeyRequestOptions();
|
|
313
365
|
let abortTimeout = null;
|
|
@@ -319,13 +371,22 @@ class FeatureFlagsPoller {
|
|
|
319
371
|
options.signal = controller.signal;
|
|
320
372
|
}
|
|
321
373
|
try {
|
|
322
|
-
return
|
|
374
|
+
return this.fetch(url, options);
|
|
323
375
|
} finally{
|
|
324
376
|
clearTimeout(abortTimeout);
|
|
325
377
|
}
|
|
326
378
|
}
|
|
327
|
-
stopPoller() {
|
|
379
|
+
async stopPoller(timeoutMs = 30000) {
|
|
328
380
|
clearTimeout(this.poller);
|
|
381
|
+
if (this.cacheProvider) try {
|
|
382
|
+
const shutdownResult = this.cacheProvider.shutdown();
|
|
383
|
+
if (shutdownResult instanceof Promise) await Promise.race([
|
|
384
|
+
shutdownResult,
|
|
385
|
+
new Promise((_, reject)=>setTimeout(()=>reject(new Error(`Cache shutdown timeout after ${timeoutMs}ms`)), timeoutMs))
|
|
386
|
+
]);
|
|
387
|
+
} catch (err) {
|
|
388
|
+
this.onError?.(new Error(`Error during cache shutdown: ${err}`));
|
|
389
|
+
}
|
|
329
390
|
}
|
|
330
391
|
}
|
|
331
392
|
async function _hash(key, distinctId, salt = '') {
|
package/dist/types.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { PostHogCoreOptions, FeatureFlagValue, JsonType, PostHogFetchOptions, PostHogFetchResponse } from '@posthog/core';
|
|
2
|
+
import type { FlagDefinitionCacheProvider } from './extensions/feature-flags/cache';
|
|
2
3
|
export interface IdentifyMessage {
|
|
3
4
|
distinctId: string;
|
|
4
5
|
properties?: Record<string | number, any>;
|
|
@@ -52,6 +53,32 @@ export type PostHogOptions = PostHogCoreOptions & {
|
|
|
52
53
|
maxCacheSize?: number;
|
|
53
54
|
fetch?: (url: string, options: PostHogFetchOptions) => Promise<PostHogFetchResponse>;
|
|
54
55
|
enableLocalEvaluation?: boolean;
|
|
56
|
+
/**
|
|
57
|
+
* @experimental This API is experimental and may change in minor versions.
|
|
58
|
+
*
|
|
59
|
+
* Optional cache provider for feature flag definitions.
|
|
60
|
+
*
|
|
61
|
+
* Allows custom caching strategies (Redis, database, etc.) for flag definitions
|
|
62
|
+
* in multi-worker environments. If not provided, defaults to in-memory cache.
|
|
63
|
+
*
|
|
64
|
+
* This enables distributed coordination where only one worker fetches flags while
|
|
65
|
+
* others use cached data, reducing API calls and improving performance.
|
|
66
|
+
*
|
|
67
|
+
* @example
|
|
68
|
+
* ```typescript
|
|
69
|
+
* import { FlagDefinitionCacheProvider } from 'posthog-node/experimental'
|
|
70
|
+
*
|
|
71
|
+
* class RedisCacheProvider implements FlagDefinitionCacheProvider {
|
|
72
|
+
* // ... implementation
|
|
73
|
+
* }
|
|
74
|
+
*
|
|
75
|
+
* const client = new PostHog('api-key', {
|
|
76
|
+
* personalApiKey: 'personal-key',
|
|
77
|
+
* flagDefinitionCacheProvider: new RedisCacheProvider(redis)
|
|
78
|
+
* })
|
|
79
|
+
* ```
|
|
80
|
+
*/
|
|
81
|
+
flagDefinitionCacheProvider?: FlagDefinitionCacheProvider;
|
|
55
82
|
/**
|
|
56
83
|
* Allows modification or dropping of events before they're sent to PostHog.
|
|
57
84
|
* If an array is provided, the functions are run in order.
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/version.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const version = "5.
|
|
1
|
+
export declare const version = "5.12.0";
|
|
2
2
|
//# sourceMappingURL=version.d.ts.map
|
package/dist/version.js
CHANGED
|
@@ -26,7 +26,7 @@ __webpack_require__.r(__webpack_exports__);
|
|
|
26
26
|
__webpack_require__.d(__webpack_exports__, {
|
|
27
27
|
version: ()=>version
|
|
28
28
|
});
|
|
29
|
-
const version = '5.
|
|
29
|
+
const version = '5.12.0';
|
|
30
30
|
exports.version = __webpack_exports__.version;
|
|
31
31
|
for(var __webpack_i__ in __webpack_exports__)if (-1 === [
|
|
32
32
|
"version"
|
package/dist/version.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const version = '5.
|
|
1
|
+
const version = '5.12.0';
|
|
2
2
|
export { version };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "posthog-node",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.12.0",
|
|
4
4
|
"description": "PostHog Node.js integration",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -30,6 +30,7 @@
|
|
|
30
30
|
"devDependencies": {
|
|
31
31
|
"@types/node": "^20.0.0",
|
|
32
32
|
"jest": "^29.7.0",
|
|
33
|
+
"@types/jest": "^29.5.0",
|
|
33
34
|
"@rslib/core": "^0.10.5",
|
|
34
35
|
"@posthog-tooling/tsconfig-base": "1.0.0"
|
|
35
36
|
},
|
|
@@ -64,6 +65,9 @@
|
|
|
64
65
|
},
|
|
65
66
|
"import": "./dist/entrypoints/index.node.mjs",
|
|
66
67
|
"require": "./dist/entrypoints/index.node.js"
|
|
68
|
+
},
|
|
69
|
+
"./experimental": {
|
|
70
|
+
"types": "./dist/experimental.d.ts"
|
|
67
71
|
}
|
|
68
72
|
},
|
|
69
73
|
"scripts": {
|
package/src/client.ts
CHANGED
|
@@ -112,6 +112,7 @@ export abstract class PostHogBackendClient extends PostHogCoreStateless implemen
|
|
|
112
112
|
this._events.emit('localEvaluationFlagsLoaded', count)
|
|
113
113
|
},
|
|
114
114
|
customHeaders: this.getCustomHeaders(),
|
|
115
|
+
cacheProvider: options.flagDefinitionCacheProvider,
|
|
115
116
|
})
|
|
116
117
|
}
|
|
117
118
|
}
|
|
@@ -1179,7 +1180,7 @@ export abstract class PostHogBackendClient extends PostHogCoreStateless implemen
|
|
|
1179
1180
|
* @returns Promise that resolves when shutdown is complete
|
|
1180
1181
|
*/
|
|
1181
1182
|
async _shutdown(shutdownTimeoutMs?: number): Promise<void> {
|
|
1182
|
-
this.featureFlagsPoller?.stopPoller()
|
|
1183
|
+
this.featureFlagsPoller?.stopPoller(shutdownTimeoutMs)
|
|
1183
1184
|
this.errorTracking.shutdown()
|
|
1184
1185
|
return super._shutdown(shutdownTimeoutMs)
|
|
1185
1186
|
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Experimental APIs
|
|
3
|
+
*
|
|
4
|
+
* This module exports experimental features that may change or be removed in minor versions.
|
|
5
|
+
* Use these APIs with caution and be prepared for breaking changes.
|
|
6
|
+
*
|
|
7
|
+
* @packageDocumentation
|
|
8
|
+
* @experimental
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
export type { FlagDefinitionCacheProvider, FlagDefinitionCacheData } from './extensions/feature-flags/cache'
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import type { PostHogFeatureFlag, PropertyGroup } from '../../types'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Represents the complete set of feature flag data needed for local evaluation.
|
|
5
|
+
*
|
|
6
|
+
* This includes flag definitions, group type mappings, and cohort property groups.
|
|
7
|
+
*/
|
|
8
|
+
export interface FlagDefinitionCacheData {
|
|
9
|
+
/** Array of feature flag definitions */
|
|
10
|
+
flags: PostHogFeatureFlag[]
|
|
11
|
+
/** Mapping of group type index to group name */
|
|
12
|
+
groupTypeMapping: Record<string, string>
|
|
13
|
+
/** Cohort property groups for local evaluation */
|
|
14
|
+
cohorts: Record<string, PropertyGroup>
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* @experimental This API is experimental and may change in minor versions.
|
|
19
|
+
*
|
|
20
|
+
* Provider interface for caching feature flag definitions.
|
|
21
|
+
*
|
|
22
|
+
* Implementations can use this to control when flag definitions are fetched
|
|
23
|
+
* and how they're cached (Redis, database, filesystem, etc.).
|
|
24
|
+
*
|
|
25
|
+
* This interface is designed for server-side environments where multiple workers
|
|
26
|
+
* need to share flag definitions and coordinate fetching to reduce API calls.
|
|
27
|
+
*
|
|
28
|
+
* All methods may throw errors - the poller will catch and log them gracefully,
|
|
29
|
+
* ensuring cache provider errors never break flag evaluation.
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* ```typescript
|
|
33
|
+
* import { FlagDefinitionCacheProvider } from 'posthog-node/experimental'
|
|
34
|
+
*
|
|
35
|
+
* class RedisFlagCache implements FlagDefinitionCacheProvider {
|
|
36
|
+
* constructor(private redis: Redis, private teamKey: string) { }
|
|
37
|
+
*
|
|
38
|
+
* async getFlagDefinitions(): Promise<FlagDefinitionCacheData | undefined> {
|
|
39
|
+
* const cached = await this.redis.get(`posthog:flags:${this.teamKey}`)
|
|
40
|
+
* return cached ? JSON.parse(cached) : undefined
|
|
41
|
+
* }
|
|
42
|
+
*
|
|
43
|
+
* async shouldFetchFlagDefinitions(): Promise<boolean> {
|
|
44
|
+
* // Acquire distributed lock - only one worker fetches
|
|
45
|
+
* const acquired = await this.redis.set(`posthog:flags:${this.teamKey}:lock`, '1', 'EX', 60, 'NX')
|
|
46
|
+
* return acquired === 'OK'
|
|
47
|
+
* }
|
|
48
|
+
*
|
|
49
|
+
* async onFlagDefinitionsReceived(data: FlagDefinitionCacheData): Promise<void> {
|
|
50
|
+
* await this.redis.set(`posthog:flags:${this.teamKey}`, JSON.stringify(data), 'EX', 300)
|
|
51
|
+
* await this.redis.del(`posthog:flags:${this.teamKey}:lock`)
|
|
52
|
+
* }
|
|
53
|
+
*
|
|
54
|
+
* async shutdown(): Promise<void> {
|
|
55
|
+
* await this.redis.del(`posthog:flags:${this.teamKey}:lock`)
|
|
56
|
+
* }
|
|
57
|
+
* }
|
|
58
|
+
* ```
|
|
59
|
+
*/
|
|
60
|
+
export interface FlagDefinitionCacheProvider {
|
|
61
|
+
/**
|
|
62
|
+
* Retrieve cached flag definitions.
|
|
63
|
+
*
|
|
64
|
+
* Called when the poller is refreshing in-memory flag definitions. If this returns undefined
|
|
65
|
+
* (or throws an error), the poller will fetch fresh data from the PostHog API if no flag
|
|
66
|
+
* definitions are in memory. Otherwise, stale cache data is used until the next poll cycle.
|
|
67
|
+
*
|
|
68
|
+
* @returns cached definitions if available, undefined if cache is empty
|
|
69
|
+
* @throws if an error occurs while accessing the cache (error will be logged)
|
|
70
|
+
*/
|
|
71
|
+
getFlagDefinitions(): Promise<FlagDefinitionCacheData | undefined> | FlagDefinitionCacheData | undefined
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Determines whether this instance should fetch new flag definitions.
|
|
75
|
+
*
|
|
76
|
+
* Use this to implement distributed coordination (e.g., via distributed locks)
|
|
77
|
+
* to ensure only one instance fetches at a time in a multi-worker setup.
|
|
78
|
+
*
|
|
79
|
+
* When multiple workers share a cache, typically only one should fetch while
|
|
80
|
+
* others use cached data. Implementations can use Redis locks, database locks,
|
|
81
|
+
* or other coordination mechanisms.
|
|
82
|
+
*
|
|
83
|
+
* @returns true if this instance should fetch, false to skip and read cache
|
|
84
|
+
* @throws if coordination backend is unavailable (error will be logged, fetch continues)
|
|
85
|
+
*/
|
|
86
|
+
shouldFetchFlagDefinitions(): Promise<boolean> | boolean
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Called after successfully receiving new flag definitions from PostHog.
|
|
90
|
+
*
|
|
91
|
+
* Store the definitions in your cache backend here. This is called only
|
|
92
|
+
* after a successful API response with valid flag data.
|
|
93
|
+
*
|
|
94
|
+
* If this method throws, the error is logged but flag definitions are still
|
|
95
|
+
* stored in memory, ensuring local evaluation can still be performed.
|
|
96
|
+
*
|
|
97
|
+
* @param data - The complete flag definition data from PostHog
|
|
98
|
+
* @throws if storage backend is unavailable (error will be logged)
|
|
99
|
+
*/
|
|
100
|
+
onFlagDefinitionsReceived(data: FlagDefinitionCacheData): Promise<void> | void
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Called when the PostHog client shuts down.
|
|
104
|
+
*
|
|
105
|
+
* Release any held locks, close connections, or clean up resources here.
|
|
106
|
+
*
|
|
107
|
+
* Both sync and async cleanup are supported. Async cleanup has a timeout
|
|
108
|
+
* (default 30s, configurable via client shutdown options) to prevent the
|
|
109
|
+
* process shutdown from hanging indefinitely.
|
|
110
|
+
*
|
|
111
|
+
* @returns Promise that resolves when cleanup is complete, or void for sync cleanup
|
|
112
|
+
*/
|
|
113
|
+
shutdown(): Promise<void> | void
|
|
114
|
+
}
|
|
@@ -2,6 +2,7 @@ import { FeatureFlagCondition, FlagProperty, FlagPropertyValue, PostHogFeatureFl
|
|
|
2
2
|
import type { FeatureFlagValue, JsonType, PostHogFetchOptions, PostHogFetchResponse } from '@posthog/core'
|
|
3
3
|
import { safeSetTimeout } from '@posthog/core'
|
|
4
4
|
import { hashSHA1 } from './crypto'
|
|
5
|
+
import { FlagDefinitionCacheProvider, FlagDefinitionCacheData } from './cache'
|
|
5
6
|
|
|
6
7
|
const SIXTY_SECONDS = 60 * 1000
|
|
7
8
|
|
|
@@ -53,6 +54,7 @@ type FeatureFlagsPollerOptions = {
|
|
|
53
54
|
onError?: (error: Error) => void
|
|
54
55
|
onLoad?: (count: number) => void
|
|
55
56
|
customHeaders?: { [key: string]: string }
|
|
57
|
+
cacheProvider?: FlagDefinitionCacheProvider
|
|
56
58
|
}
|
|
57
59
|
|
|
58
60
|
class FeatureFlagsPoller {
|
|
@@ -74,6 +76,9 @@ class FeatureFlagsPoller {
|
|
|
74
76
|
shouldBeginExponentialBackoff: boolean = false
|
|
75
77
|
backOffCount: number = 0
|
|
76
78
|
onLoad?: (count: number) => void
|
|
79
|
+
private cacheProvider?: FlagDefinitionCacheProvider
|
|
80
|
+
private hasAttemptedCacheLoad: boolean = false
|
|
81
|
+
private loadingPromise?: Promise<void>
|
|
77
82
|
|
|
78
83
|
constructor({
|
|
79
84
|
pollingInterval,
|
|
@@ -99,6 +104,7 @@ class FeatureFlagsPoller {
|
|
|
99
104
|
this.onError = options.onError
|
|
100
105
|
this.customHeaders = customHeaders
|
|
101
106
|
this.onLoad = options.onLoad
|
|
107
|
+
this.cacheProvider = options.cacheProvider
|
|
102
108
|
void this.loadFeatureFlags()
|
|
103
109
|
}
|
|
104
110
|
|
|
@@ -537,9 +543,59 @@ class FeatureFlagsPoller {
|
|
|
537
543
|
return lookupTable
|
|
538
544
|
}
|
|
539
545
|
|
|
546
|
+
/**
|
|
547
|
+
* Updates the internal flag state with the provided flag data.
|
|
548
|
+
*/
|
|
549
|
+
private updateFlagState(flagData: FlagDefinitionCacheData): void {
|
|
550
|
+
this.featureFlags = flagData.flags
|
|
551
|
+
this.featureFlagsByKey = flagData.flags.reduce(
|
|
552
|
+
(acc, curr) => ((acc[curr.key] = curr), acc),
|
|
553
|
+
<Record<string, PostHogFeatureFlag>>{}
|
|
554
|
+
)
|
|
555
|
+
this.groupTypeMapping = flagData.groupTypeMapping
|
|
556
|
+
this.cohorts = flagData.cohorts
|
|
557
|
+
this.loadedSuccessfullyOnce = true
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
/**
|
|
561
|
+
* Attempts to load flags from cache and update internal state.
|
|
562
|
+
* Returns true if flags were successfully loaded from cache, false otherwise.
|
|
563
|
+
*/
|
|
564
|
+
private async loadFromCache(debugMessage: string): Promise<boolean> {
|
|
565
|
+
if (!this.cacheProvider) {
|
|
566
|
+
return false
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
try {
|
|
570
|
+
const cached = await this.cacheProvider.getFlagDefinitions()
|
|
571
|
+
if (cached) {
|
|
572
|
+
this.updateFlagState(cached)
|
|
573
|
+
this.logMsgIfDebug(() => console.debug(`[FEATURE FLAGS] ${debugMessage} (${cached.flags.length} flags)`))
|
|
574
|
+
this.onLoad?.(this.featureFlags.length)
|
|
575
|
+
return true
|
|
576
|
+
}
|
|
577
|
+
return false
|
|
578
|
+
} catch (err) {
|
|
579
|
+
this.onError?.(new Error(`Failed to load from cache: ${err}`))
|
|
580
|
+
return false
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
|
|
540
584
|
async loadFeatureFlags(forceReload = false): Promise<void> {
|
|
585
|
+
// On first load, try to initialize from cache (if a cache provider is configured)
|
|
586
|
+
if (this.cacheProvider && !this.hasAttemptedCacheLoad) {
|
|
587
|
+
this.hasAttemptedCacheLoad = true
|
|
588
|
+
await this.loadFromCache('Loaded flags from cache')
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
// If a fetch is already in progress, wait for it
|
|
592
|
+
if (this.loadingPromise) {
|
|
593
|
+
return this.loadingPromise
|
|
594
|
+
}
|
|
595
|
+
|
|
541
596
|
if (!this.loadedSuccessfullyOnce || forceReload) {
|
|
542
|
-
|
|
597
|
+
this.loadingPromise = this._loadFeatureFlags()
|
|
598
|
+
await this.loadingPromise
|
|
543
599
|
}
|
|
544
600
|
}
|
|
545
601
|
|
|
@@ -574,6 +630,42 @@ class FeatureFlagsPoller {
|
|
|
574
630
|
this.poller = setTimeout(() => this._loadFeatureFlags(), this.getPollingInterval())
|
|
575
631
|
|
|
576
632
|
try {
|
|
633
|
+
let shouldFetch = true
|
|
634
|
+
if (this.cacheProvider) {
|
|
635
|
+
try {
|
|
636
|
+
shouldFetch = await this.cacheProvider.shouldFetchFlagDefinitions()
|
|
637
|
+
} catch (err) {
|
|
638
|
+
this.onError?.(new Error(`Error in shouldFetchFlagDefinitions: ${err}`))
|
|
639
|
+
// Important: if `shouldFetchFlagDefinitions` throws, we
|
|
640
|
+
// default to fetching.
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
if (!shouldFetch) {
|
|
645
|
+
// If we're not supposed to fetch, we assume another instance
|
|
646
|
+
// is handling it. In this case, we'll just reload from cache.
|
|
647
|
+
const loaded = await this.loadFromCache('Loaded flags from cache (skipped fetch)')
|
|
648
|
+
if (loaded) {
|
|
649
|
+
return
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
if (this.loadedSuccessfullyOnce) {
|
|
653
|
+
// Respect the decision to not fetch, even if it means
|
|
654
|
+
// keeping stale feature flags.
|
|
655
|
+
return
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
// If we've gotten here:
|
|
659
|
+
// - A cache provider is configured
|
|
660
|
+
// - We've been asked not to fetch
|
|
661
|
+
// - We failed to load from cache
|
|
662
|
+
// - We have no feature flag definitions to work with.
|
|
663
|
+
//
|
|
664
|
+
// This is the only case where we'll ignore the shouldFetch
|
|
665
|
+
// decision and proceed to fetch, because the alternative is
|
|
666
|
+
// worse: local evaluation is impossible.
|
|
667
|
+
}
|
|
668
|
+
|
|
577
669
|
const res = await this._requestFeatureFlagDefinitions()
|
|
578
670
|
|
|
579
671
|
// Handle undefined res case, this shouldn't happen, but it doesn't hurt to handle it anyway
|
|
@@ -637,16 +729,28 @@ class FeatureFlagsPoller {
|
|
|
637
729
|
return
|
|
638
730
|
}
|
|
639
731
|
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
this.
|
|
647
|
-
this.loadedSuccessfullyOnce = true
|
|
732
|
+
const flagData: FlagDefinitionCacheData = {
|
|
733
|
+
flags: (responseJson.flags as PostHogFeatureFlag[]) ?? [],
|
|
734
|
+
groupTypeMapping: (responseJson.group_type_mapping as Record<string, string>) || {},
|
|
735
|
+
cohorts: (responseJson.cohorts as Record<string, PropertyGroup>) || {},
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
this.updateFlagState(flagData)
|
|
648
739
|
this.shouldBeginExponentialBackoff = false
|
|
649
740
|
this.backOffCount = 0
|
|
741
|
+
|
|
742
|
+
if (this.cacheProvider && shouldFetch) {
|
|
743
|
+
// Only notify the cache if it's actually expecting new data
|
|
744
|
+
// E.g., if we weren't supposed to fetch but we missed the
|
|
745
|
+
// cache, we may not have a lock, so we skip this step
|
|
746
|
+
try {
|
|
747
|
+
await this.cacheProvider.onFlagDefinitionsReceived(flagData)
|
|
748
|
+
} catch (err) {
|
|
749
|
+
this.onError?.(new Error(`Failed to store in cache: ${err}`))
|
|
750
|
+
// Continue anyway, the data at least made it to memory
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
|
|
650
754
|
this.onLoad?.(this.featureFlags.length)
|
|
651
755
|
break
|
|
652
756
|
}
|
|
@@ -660,6 +764,8 @@ class FeatureFlagsPoller {
|
|
|
660
764
|
if (err instanceof ClientError) {
|
|
661
765
|
this.onError?.(err)
|
|
662
766
|
}
|
|
767
|
+
} finally {
|
|
768
|
+
this.loadingPromise = undefined
|
|
663
769
|
}
|
|
664
770
|
}
|
|
665
771
|
|
|
@@ -674,7 +780,7 @@ class FeatureFlagsPoller {
|
|
|
674
780
|
}
|
|
675
781
|
}
|
|
676
782
|
|
|
677
|
-
|
|
783
|
+
_requestFeatureFlagDefinitions(): Promise<PostHogFetchResponse> {
|
|
678
784
|
const url = `${this.host}/api/feature_flag/local_evaluation?token=${this.projectApiKey}&send_cohorts`
|
|
679
785
|
|
|
680
786
|
const options = this.getPersonalApiKeyRequestOptions()
|
|
@@ -690,14 +796,34 @@ class FeatureFlagsPoller {
|
|
|
690
796
|
}
|
|
691
797
|
|
|
692
798
|
try {
|
|
693
|
-
return
|
|
799
|
+
return this.fetch(url, options)
|
|
694
800
|
} finally {
|
|
695
801
|
clearTimeout(abortTimeout)
|
|
696
802
|
}
|
|
697
803
|
}
|
|
698
804
|
|
|
699
|
-
stopPoller(): void {
|
|
805
|
+
async stopPoller(timeoutMs: number = 30000): Promise<void> {
|
|
700
806
|
clearTimeout(this.poller)
|
|
807
|
+
|
|
808
|
+
if (this.cacheProvider) {
|
|
809
|
+
try {
|
|
810
|
+
const shutdownResult = this.cacheProvider.shutdown()
|
|
811
|
+
|
|
812
|
+
if (shutdownResult instanceof Promise) {
|
|
813
|
+
// This follows the same timeout logic defined in _shutdown.
|
|
814
|
+
// We time out after some period of time to avoid hanging the entire
|
|
815
|
+
// shutdown process if the cache provider misbehaves.
|
|
816
|
+
await Promise.race([
|
|
817
|
+
shutdownResult,
|
|
818
|
+
new Promise((_, reject) =>
|
|
819
|
+
setTimeout(() => reject(new Error(`Cache shutdown timeout after ${timeoutMs}ms`)), timeoutMs)
|
|
820
|
+
),
|
|
821
|
+
])
|
|
822
|
+
}
|
|
823
|
+
} catch (err) {
|
|
824
|
+
this.onError?.(new Error(`Error during cache shutdown: ${err}`))
|
|
825
|
+
}
|
|
826
|
+
}
|
|
701
827
|
}
|
|
702
828
|
}
|
|
703
829
|
|
package/src/types.ts
CHANGED
|
@@ -6,6 +6,8 @@ import type {
|
|
|
6
6
|
PostHogFetchResponse,
|
|
7
7
|
} from '@posthog/core'
|
|
8
8
|
|
|
9
|
+
import type { FlagDefinitionCacheProvider } from './extensions/feature-flags/cache'
|
|
10
|
+
|
|
9
11
|
export interface IdentifyMessage {
|
|
10
12
|
distinctId: string
|
|
11
13
|
properties?: Record<string | number, any>
|
|
@@ -72,6 +74,32 @@ export type PostHogOptions = PostHogCoreOptions & {
|
|
|
72
74
|
// Whether to enable feature flag polling for local evaluation by default. Defaults to true when personalApiKey is provided.
|
|
73
75
|
// We recommend setting this to false if you are only using the personalApiKey for evaluating remote config payloads via `getRemoteConfigPayload` and not using local evaluation.
|
|
74
76
|
enableLocalEvaluation?: boolean
|
|
77
|
+
/**
|
|
78
|
+
* @experimental This API is experimental and may change in minor versions.
|
|
79
|
+
*
|
|
80
|
+
* Optional cache provider for feature flag definitions.
|
|
81
|
+
*
|
|
82
|
+
* Allows custom caching strategies (Redis, database, etc.) for flag definitions
|
|
83
|
+
* in multi-worker environments. If not provided, defaults to in-memory cache.
|
|
84
|
+
*
|
|
85
|
+
* This enables distributed coordination where only one worker fetches flags while
|
|
86
|
+
* others use cached data, reducing API calls and improving performance.
|
|
87
|
+
*
|
|
88
|
+
* @example
|
|
89
|
+
* ```typescript
|
|
90
|
+
* import { FlagDefinitionCacheProvider } from 'posthog-node/experimental'
|
|
91
|
+
*
|
|
92
|
+
* class RedisCacheProvider implements FlagDefinitionCacheProvider {
|
|
93
|
+
* // ... implementation
|
|
94
|
+
* }
|
|
95
|
+
*
|
|
96
|
+
* const client = new PostHog('api-key', {
|
|
97
|
+
* personalApiKey: 'personal-key',
|
|
98
|
+
* flagDefinitionCacheProvider: new RedisCacheProvider(redis)
|
|
99
|
+
* })
|
|
100
|
+
* ```
|
|
101
|
+
*/
|
|
102
|
+
flagDefinitionCacheProvider?: FlagDefinitionCacheProvider
|
|
75
103
|
/**
|
|
76
104
|
* Allows modification or dropping of events before they're sent to PostHog.
|
|
77
105
|
* If an array is provided, the functions are run in order.
|
package/src/version.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const version = '5.
|
|
1
|
+
export const version = '5.12.0'
|