@launchdarkly/js-client-sdk-common 1.2.0 → 1.3.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/CHANGELOG.md +22 -0
- package/dist/LDClientImpl.d.ts +40 -32
- package/dist/LDClientImpl.d.ts.map +1 -1
- package/dist/LDClientImpl.js +179 -127
- package/dist/LDClientImpl.js.map +1 -1
- package/dist/api/ConnectionMode.d.ts +3 -1
- package/dist/api/ConnectionMode.d.ts.map +1 -1
- package/dist/api/LDClient.d.ts +1 -1
- package/dist/api/LDClient.d.ts.map +1 -1
- package/dist/api/LDOptions.d.ts +13 -0
- package/dist/api/LDOptions.d.ts.map +1 -1
- package/dist/configuration/Configuration.d.ts +2 -1
- package/dist/configuration/Configuration.d.ts.map +1 -1
- package/dist/configuration/Configuration.js +3 -1
- package/dist/configuration/Configuration.js.map +1 -1
- package/dist/configuration/validators.d.ts.map +1 -1
- package/dist/configuration/validators.js +4 -2
- package/dist/configuration/validators.js.map +1 -1
- package/dist/context/addAutoEnv.d.ts.map +1 -0
- package/dist/{utils → context}/addAutoEnv.js +4 -2
- package/dist/context/addAutoEnv.js.map +1 -0
- package/dist/{utils → context}/ensureKey.d.ts +1 -2
- package/dist/context/ensureKey.d.ts.map +1 -0
- package/dist/{utils → context}/ensureKey.js +6 -3
- package/dist/context/ensureKey.js.map +1 -0
- package/dist/flag-manager/ContextIndex.d.ts +39 -0
- package/dist/flag-manager/ContextIndex.d.ts.map +1 -0
- package/dist/flag-manager/ContextIndex.js +64 -0
- package/dist/flag-manager/ContextIndex.js.map +1 -0
- package/dist/flag-manager/FlagManager.d.ts +58 -0
- package/dist/flag-manager/FlagManager.d.ts.map +1 -0
- package/dist/flag-manager/FlagManager.js +74 -0
- package/dist/flag-manager/FlagManager.js.map +1 -0
- package/dist/flag-manager/FlagPersistence.d.ts +42 -0
- package/dist/flag-manager/FlagPersistence.d.ts.map +1 -0
- package/dist/flag-manager/FlagPersistence.js +120 -0
- package/dist/flag-manager/FlagPersistence.js.map +1 -0
- package/dist/flag-manager/FlagStore.d.ts +29 -0
- package/dist/flag-manager/FlagStore.d.ts.map +1 -0
- package/dist/flag-manager/FlagStore.js +28 -0
- package/dist/flag-manager/FlagStore.js.map +1 -0
- package/dist/flag-manager/FlagUpdater.d.ts +39 -0
- package/dist/flag-manager/FlagUpdater.d.ts.map +1 -0
- package/dist/flag-manager/FlagUpdater.js +69 -0
- package/dist/flag-manager/FlagUpdater.js.map +1 -0
- package/dist/flag-manager/ItemDescriptor.d.ts +10 -0
- package/dist/flag-manager/ItemDescriptor.d.ts.map +1 -0
- package/dist/flag-manager/ItemDescriptor.js +3 -0
- package/dist/flag-manager/ItemDescriptor.js.map +1 -0
- package/dist/flag-manager/calculateChangedKeys.d.ts +6 -0
- package/dist/flag-manager/calculateChangedKeys.d.ts.map +1 -0
- package/dist/flag-manager/calculateChangedKeys.js +22 -0
- package/dist/flag-manager/calculateChangedKeys.js.map +1 -0
- package/dist/polling/PollingProcessor.d.ts +3 -0
- package/dist/polling/PollingProcessor.d.ts.map +1 -0
- package/dist/polling/PollingProcessor.js +79 -0
- package/dist/polling/PollingProcessor.js.map +1 -0
- package/dist/polling/Requestor.d.ts +6 -0
- package/dist/polling/Requestor.d.ts.map +1 -0
- package/dist/polling/Requestor.js +50 -0
- package/dist/polling/Requestor.js.map +1 -0
- package/dist/storage/getOrGenerateKey.d.ts +11 -0
- package/dist/storage/getOrGenerateKey.d.ts.map +1 -0
- package/dist/storage/getOrGenerateKey.js +21 -0
- package/dist/storage/getOrGenerateKey.js.map +1 -0
- package/dist/storage/namespaceUtils.d.ts +20 -0
- package/dist/storage/namespaceUtils.d.ts.map +1 -0
- package/dist/storage/namespaceUtils.js +61 -0
- package/dist/storage/namespaceUtils.js.map +1 -0
- package/package.json +2 -2
- package/dist/utils/addAutoEnv.d.ts.map +0 -1
- package/dist/utils/addAutoEnv.js.map +0 -1
- package/dist/utils/calculateFlagChanges.d.ts +0 -3
- package/dist/utils/calculateFlagChanges.d.ts.map +0 -1
- package/dist/utils/calculateFlagChanges.js +0 -23
- package/dist/utils/calculateFlagChanges.js.map +0 -1
- package/dist/utils/ensureKey.d.ts.map +0 -1
- package/dist/utils/ensureKey.js.map +0 -1
- package/dist/utils/getOrGenerateKey.d.ts +0 -5
- package/dist/utils/getOrGenerateKey.d.ts.map +0 -1
- package/dist/utils/getOrGenerateKey.js +0 -29
- package/dist/utils/getOrGenerateKey.js.map +0 -1
- package/dist/utils/index.d.ts +0 -5
- package/dist/utils/index.d.ts.map +0 -1
- package/dist/utils/index.js +0 -10
- package/dist/utils/index.js.map +0 -1
- /package/dist/{utils → context}/addAutoEnv.d.ts +0 -0
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { Context, LDLogger, Platform } from '@launchdarkly/js-sdk-common';
|
|
2
|
+
import FlagStore from './FlagStore';
|
|
3
|
+
import FlagUpdater from './FlagUpdater';
|
|
4
|
+
import { ItemDescriptor } from './ItemDescriptor';
|
|
5
|
+
/**
|
|
6
|
+
* This class handles persisting and loading flag values from a persistent
|
|
7
|
+
* store. It intercepts updates and forwards them to the flag updater and
|
|
8
|
+
* then persists changes after the updater has completed.
|
|
9
|
+
*/
|
|
10
|
+
export default class FlagPersistence {
|
|
11
|
+
private readonly platform;
|
|
12
|
+
private readonly environmentNamespace;
|
|
13
|
+
private readonly maxCachedContexts;
|
|
14
|
+
private readonly flagStore;
|
|
15
|
+
private readonly flagUpdater;
|
|
16
|
+
private readonly logger;
|
|
17
|
+
private readonly timeStamper;
|
|
18
|
+
private contextIndex;
|
|
19
|
+
private indexKey;
|
|
20
|
+
constructor(platform: Platform, environmentNamespace: string, maxCachedContexts: number, flagStore: FlagStore, flagUpdater: FlagUpdater, logger: LDLogger, timeStamper?: () => number);
|
|
21
|
+
/**
|
|
22
|
+
* Inits flag persistence for the provided context with the provided flags. This will result
|
|
23
|
+
* in the underlying {@link FlagUpdater} switching its active context.
|
|
24
|
+
*/
|
|
25
|
+
init(context: Context, newFlags: {
|
|
26
|
+
[key: string]: ItemDescriptor;
|
|
27
|
+
}): Promise<void>;
|
|
28
|
+
/**
|
|
29
|
+
* Upserts a flag into the {@link FlagUpdater} and stores that to persistence if the upsert
|
|
30
|
+
* was successful / accepted. An upsert may be rejected if the provided context is not
|
|
31
|
+
* the active context.
|
|
32
|
+
*/
|
|
33
|
+
upsert(context: Context, key: string, item: ItemDescriptor): Promise<boolean>;
|
|
34
|
+
/**
|
|
35
|
+
* Loads the flags from persistence for the provided context and gives those to the
|
|
36
|
+
* {@link FlagUpdater} this {@link FlagPersistence} was constructed with.
|
|
37
|
+
*/
|
|
38
|
+
loadCached(context: Context): Promise<boolean>;
|
|
39
|
+
private loadIndex;
|
|
40
|
+
private storeCache;
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=FlagPersistence.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FlagPersistence.d.ts","sourceRoot":"","sources":["../../src/flag-manager/FlagPersistence.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,6BAA6B,CAAC;AAK1E,OAAO,SAAS,MAAM,aAAa,CAAC;AACpC,OAAO,WAAW,MAAM,eAAe,CAAC;AACxC,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAElD;;;;GAIG;AACH,MAAM,CAAC,OAAO,OAAO,eAAe;IAKhC,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,oBAAoB;IACrC,OAAO,CAAC,QAAQ,CAAC,iBAAiB;IAClC,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,WAAW;IAC5B,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,WAAW;IAV9B,OAAO,CAAC,YAAY,CAA2B;IAC/C,OAAO,CAAC,QAAQ,CAAS;gBAGN,QAAQ,EAAE,QAAQ,EAClB,oBAAoB,EAAE,MAAM,EAC5B,iBAAiB,EAAE,MAAM,EACzB,SAAS,EAAE,SAAS,EACpB,WAAW,EAAE,WAAW,EACxB,MAAM,EAAE,QAAQ,EAChB,WAAW,GAAE,MAAM,MAAyB;IAK/D;;;OAGG;IACG,IAAI,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE;QAAE,CAAC,GAAG,EAAE,MAAM,GAAG,cAAc,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAKxF;;;;OAIG;IACG,MAAM,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,cAAc,GAAG,OAAO,CAAC,OAAO,CAAC;IAQnF;;;OAGG;IACG,UAAU,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;YA4CtC,SAAS;YAqBT,UAAU;CA4BzB"}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const namespaceUtils_1 = require("../storage/namespaceUtils");
|
|
4
|
+
const ContextIndex_1 = require("./ContextIndex");
|
|
5
|
+
/**
|
|
6
|
+
* This class handles persisting and loading flag values from a persistent
|
|
7
|
+
* store. It intercepts updates and forwards them to the flag updater and
|
|
8
|
+
* then persists changes after the updater has completed.
|
|
9
|
+
*/
|
|
10
|
+
class FlagPersistence {
|
|
11
|
+
constructor(platform, environmentNamespace, maxCachedContexts, flagStore, flagUpdater, logger, timeStamper = () => Date.now()) {
|
|
12
|
+
this.platform = platform;
|
|
13
|
+
this.environmentNamespace = environmentNamespace;
|
|
14
|
+
this.maxCachedContexts = maxCachedContexts;
|
|
15
|
+
this.flagStore = flagStore;
|
|
16
|
+
this.flagUpdater = flagUpdater;
|
|
17
|
+
this.logger = logger;
|
|
18
|
+
this.timeStamper = timeStamper;
|
|
19
|
+
this.indexKey = (0, namespaceUtils_1.namespaceForContextIndex)(this.environmentNamespace);
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Inits flag persistence for the provided context with the provided flags. This will result
|
|
23
|
+
* in the underlying {@link FlagUpdater} switching its active context.
|
|
24
|
+
*/
|
|
25
|
+
async init(context, newFlags) {
|
|
26
|
+
this.flagUpdater.init(context, newFlags);
|
|
27
|
+
await this.storeCache(context);
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Upserts a flag into the {@link FlagUpdater} and stores that to persistence if the upsert
|
|
31
|
+
* was successful / accepted. An upsert may be rejected if the provided context is not
|
|
32
|
+
* the active context.
|
|
33
|
+
*/
|
|
34
|
+
async upsert(context, key, item) {
|
|
35
|
+
if (this.flagUpdater.upsert(context, key, item)) {
|
|
36
|
+
await this.storeCache(context);
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Loads the flags from persistence for the provided context and gives those to the
|
|
43
|
+
* {@link FlagUpdater} this {@link FlagPersistence} was constructed with.
|
|
44
|
+
*/
|
|
45
|
+
async loadCached(context) {
|
|
46
|
+
var _a, _b, _c, _d;
|
|
47
|
+
const storageKey = (0, namespaceUtils_1.namespaceForContextData)(this.platform.crypto, this.environmentNamespace, context);
|
|
48
|
+
let flagsJson = await ((_a = this.platform.storage) === null || _a === void 0 ? void 0 : _a.get(storageKey));
|
|
49
|
+
if (flagsJson === null || flagsJson === undefined) {
|
|
50
|
+
// Fallback: in version <10.3.1 flag data was stored under the canonical key, check
|
|
51
|
+
// to see if data is present and migrate the data if present.
|
|
52
|
+
flagsJson = await ((_b = this.platform.storage) === null || _b === void 0 ? void 0 : _b.get(context.canonicalKey));
|
|
53
|
+
if (flagsJson === null || flagsJson === undefined) {
|
|
54
|
+
// return false indicating cache did not load if flag json is still absent
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
// migrate data from version <10.3.1 and cleanup data that was under canonical key
|
|
58
|
+
await ((_c = this.platform.storage) === null || _c === void 0 ? void 0 : _c.set(storageKey, flagsJson));
|
|
59
|
+
await ((_d = this.platform.storage) === null || _d === void 0 ? void 0 : _d.clear(context.canonicalKey));
|
|
60
|
+
}
|
|
61
|
+
try {
|
|
62
|
+
const flags = JSON.parse(flagsJson);
|
|
63
|
+
// mapping flags to item descriptors
|
|
64
|
+
const descriptors = Object.entries(flags).reduce((acc, [key, flag]) => {
|
|
65
|
+
acc[key] = { version: flag.version, flag };
|
|
66
|
+
return acc;
|
|
67
|
+
}, {});
|
|
68
|
+
this.flagUpdater.initCached(context, descriptors);
|
|
69
|
+
this.logger.debug('Loaded cached flag evaluations from persistent storage');
|
|
70
|
+
return true;
|
|
71
|
+
}
|
|
72
|
+
catch (e) {
|
|
73
|
+
this.logger.warn(`Could not load cached flag evaluations from persistent storage: ${e.message}`);
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
async loadIndex() {
|
|
78
|
+
var _a;
|
|
79
|
+
if (this.contextIndex !== undefined) {
|
|
80
|
+
return this.contextIndex;
|
|
81
|
+
}
|
|
82
|
+
const json = await ((_a = this.platform.storage) === null || _a === void 0 ? void 0 : _a.get(this.indexKey));
|
|
83
|
+
if (!json) {
|
|
84
|
+
this.contextIndex = new ContextIndex_1.default();
|
|
85
|
+
return this.contextIndex;
|
|
86
|
+
}
|
|
87
|
+
try {
|
|
88
|
+
this.contextIndex = ContextIndex_1.default.fromJson(json);
|
|
89
|
+
this.logger.debug('Loaded context index from persistent storage');
|
|
90
|
+
}
|
|
91
|
+
catch (e) {
|
|
92
|
+
this.logger.warn(`Could not load index from persistent storage: ${e.message}`);
|
|
93
|
+
this.contextIndex = new ContextIndex_1.default();
|
|
94
|
+
}
|
|
95
|
+
return this.contextIndex;
|
|
96
|
+
}
|
|
97
|
+
async storeCache(context) {
|
|
98
|
+
var _a, _b;
|
|
99
|
+
const index = await this.loadIndex();
|
|
100
|
+
const storageKey = (0, namespaceUtils_1.namespaceForContextData)(this.platform.crypto, this.environmentNamespace, context);
|
|
101
|
+
index.notice(storageKey, this.timeStamper());
|
|
102
|
+
const pruned = index.prune(this.maxCachedContexts);
|
|
103
|
+
await Promise.all(pruned.map(async (it) => { var _a; return (_a = this.platform.storage) === null || _a === void 0 ? void 0 : _a.clear(it.id); }));
|
|
104
|
+
// store index
|
|
105
|
+
await ((_a = this.platform.storage) === null || _a === void 0 ? void 0 : _a.set(this.indexKey, index.toJson()));
|
|
106
|
+
const allFlags = this.flagStore.getAll();
|
|
107
|
+
// mapping item descriptors to flags
|
|
108
|
+
const flags = Object.entries(allFlags).reduce((acc, [key, descriptor]) => {
|
|
109
|
+
if (descriptor.flag !== null && descriptor.flag !== undefined) {
|
|
110
|
+
acc[key] = descriptor.flag;
|
|
111
|
+
}
|
|
112
|
+
return acc;
|
|
113
|
+
}, {});
|
|
114
|
+
const jsonAll = JSON.stringify(flags);
|
|
115
|
+
// store flag data
|
|
116
|
+
await ((_b = this.platform.storage) === null || _b === void 0 ? void 0 : _b.set(storageKey, jsonAll));
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
exports.default = FlagPersistence;
|
|
120
|
+
//# sourceMappingURL=FlagPersistence.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FlagPersistence.js","sourceRoot":"","sources":["../../src/flag-manager/FlagPersistence.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { ItemDescriptor } from './ItemDescriptor';
|
|
2
|
+
/**
|
|
3
|
+
* This interface exists for testing purposes
|
|
4
|
+
*/
|
|
5
|
+
export default interface FlagStore {
|
|
6
|
+
init(newFlags: {
|
|
7
|
+
[key: string]: ItemDescriptor;
|
|
8
|
+
}): void;
|
|
9
|
+
insertOrUpdate(key: string, update: ItemDescriptor): void;
|
|
10
|
+
get(key: string): ItemDescriptor | undefined;
|
|
11
|
+
getAll(): {
|
|
12
|
+
[key: string]: ItemDescriptor;
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* In memory flag store.
|
|
17
|
+
*/
|
|
18
|
+
export declare class DefaultFlagStore implements FlagStore {
|
|
19
|
+
private flags;
|
|
20
|
+
init(newFlags: {
|
|
21
|
+
[key: string]: ItemDescriptor;
|
|
22
|
+
}): void;
|
|
23
|
+
insertOrUpdate(key: string, update: ItemDescriptor): void;
|
|
24
|
+
get(key: string): ItemDescriptor | undefined;
|
|
25
|
+
getAll(): {
|
|
26
|
+
[key: string]: ItemDescriptor;
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=FlagStore.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FlagStore.d.ts","sourceRoot":"","sources":["../../src/flag-manager/FlagStore.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAElD;;GAEG;AACH,MAAM,CAAC,OAAO,WAAW,SAAS;IAChC,IAAI,CAAC,QAAQ,EAAE;QAAE,CAAC,GAAG,EAAE,MAAM,GAAG,cAAc,CAAA;KAAE,GAAG,IAAI,CAAC;IACxD,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,cAAc,GAAG,IAAI,CAAC;IAC1D,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS,CAAC;IAC7C,MAAM,IAAI;QAAE,CAAC,GAAG,EAAE,MAAM,GAAG,cAAc,CAAA;KAAE,CAAC;CAC7C;AAED;;GAEG;AACH,qBAAa,gBAAiB,YAAW,SAAS;IAChD,OAAO,CAAC,KAAK,CAAyC;IAEtD,IAAI,CAAC,QAAQ,EAAE;QAAE,CAAC,GAAG,EAAE,MAAM,GAAG,cAAc,CAAA;KAAE;IAUhD,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,cAAc;IAIlD,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS;IAI5C,MAAM,IAAI;QAAE,CAAC,GAAG,EAAE,MAAM,GAAG,cAAc,CAAA;KAAE;CAG5C"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DefaultFlagStore = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* In memory flag store.
|
|
6
|
+
*/
|
|
7
|
+
class DefaultFlagStore {
|
|
8
|
+
constructor() {
|
|
9
|
+
this.flags = {};
|
|
10
|
+
}
|
|
11
|
+
init(newFlags) {
|
|
12
|
+
this.flags = Object.entries(newFlags).reduce((acc, [key, flag]) => {
|
|
13
|
+
acc[key] = flag;
|
|
14
|
+
return acc;
|
|
15
|
+
}, {});
|
|
16
|
+
}
|
|
17
|
+
insertOrUpdate(key, update) {
|
|
18
|
+
this.flags[key] = update;
|
|
19
|
+
}
|
|
20
|
+
get(key) {
|
|
21
|
+
return this.flags[key];
|
|
22
|
+
}
|
|
23
|
+
getAll() {
|
|
24
|
+
return this.flags;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
exports.DefaultFlagStore = DefaultFlagStore;
|
|
28
|
+
//# sourceMappingURL=FlagStore.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FlagStore.js","sourceRoot":"","sources":["../../src/flag-manager/FlagStore.ts"],"names":[],"mappings":";;;AAYA;;GAEG;AACH,MAAa,gBAAgB;IAA7B;QACU,UAAK,GAAsC,EAAE,CAAC;IAuBxD,CAAC;IArBC,IAAI,CAAC,QAA2C;QAC9C,IAAI,CAAC,KAAK,GAAG,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,MAAM,CAC1C,CAAC,GAAoC,EAAE,CAAC,GAAG,EAAE,IAAI,CAAC,EAAE,EAAE;YACpD,GAAG,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC;YAChB,OAAO,GAAG,CAAC;QACb,CAAC,EACD,EAAE,CACH,CAAC;IACJ,CAAC;IAED,cAAc,CAAC,GAAW,EAAE,MAAsB;QAChD,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC;IAC3B,CAAC;IAED,GAAG,CAAC,GAAW;QACb,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACzB,CAAC;IAED,MAAM;QACJ,OAAO,IAAI,CAAC,KAAK,CAAC;IACpB,CAAC;CACF;AAxBD,4CAwBC"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { Context, LDLogger } from '@launchdarkly/js-sdk-common';
|
|
2
|
+
import FlagStore from './FlagStore';
|
|
3
|
+
import { ItemDescriptor } from './ItemDescriptor';
|
|
4
|
+
/**
|
|
5
|
+
* This callback indicates that the details associated with one or more flags
|
|
6
|
+
* have changed.
|
|
7
|
+
*
|
|
8
|
+
* This could be the value of the flag, but it could also include changes
|
|
9
|
+
* to the evaluation reason, such as being included in an experiment.
|
|
10
|
+
*
|
|
11
|
+
* It can include new or deleted flags as well, so an evaluation may result
|
|
12
|
+
* in a FLAG_NOT_FOUND reason.
|
|
13
|
+
*
|
|
14
|
+
* This event does not include the value of the flag. It is expected that you
|
|
15
|
+
* will call a variation method for flag values which you require.
|
|
16
|
+
*/
|
|
17
|
+
export type FlagsChangeCallback = (context: Context, flagKeys: Array<string>) => void;
|
|
18
|
+
/**
|
|
19
|
+
* The flag updater handles logic required during the flag update process.
|
|
20
|
+
* It handles versions checking to handle out of order flag updates and
|
|
21
|
+
* also handles flag comparisons for change notification.
|
|
22
|
+
*/
|
|
23
|
+
export default class FlagUpdater {
|
|
24
|
+
private flagStore;
|
|
25
|
+
private logger;
|
|
26
|
+
private activeContextKey;
|
|
27
|
+
private changeCallbacks;
|
|
28
|
+
constructor(flagStore: FlagStore, logger: LDLogger);
|
|
29
|
+
init(context: Context, newFlags: {
|
|
30
|
+
[key: string]: ItemDescriptor;
|
|
31
|
+
}): void;
|
|
32
|
+
initCached(context: Context, newFlags: {
|
|
33
|
+
[key: string]: ItemDescriptor;
|
|
34
|
+
}): void;
|
|
35
|
+
upsert(context: Context, key: string, item: ItemDescriptor): boolean;
|
|
36
|
+
on(callback: FlagsChangeCallback): void;
|
|
37
|
+
off(callback: FlagsChangeCallback): void;
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=FlagUpdater.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FlagUpdater.d.ts","sourceRoot":"","sources":["../../src/flag-manager/FlagUpdater.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,6BAA6B,CAAC;AAGhE,OAAO,SAAS,MAAM,aAAa,CAAC;AACpC,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAElD;;;;;;;;;;;;GAYG;AACH,MAAM,MAAM,mBAAmB,GAAG,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,KAAK,CAAC,MAAM,CAAC,KAAK,IAAI,CAAC;AAEtF;;;;GAIG;AACH,MAAM,CAAC,OAAO,OAAO,WAAW;IAC9B,OAAO,CAAC,SAAS,CAAY;IAC7B,OAAO,CAAC,MAAM,CAAW;IACzB,OAAO,CAAC,gBAAgB,CAAqB;IAC7C,OAAO,CAAC,eAAe,CAAoC;gBAE/C,SAAS,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ;IAKlD,IAAI,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE;QAAE,CAAC,GAAG,EAAE,MAAM,GAAG,cAAc,CAAA;KAAE;IAgBlE,UAAU,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE;QAAE,CAAC,GAAG,EAAE,MAAM,GAAG,cAAc,CAAA;KAAE;IAQxE,MAAM,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,cAAc,GAAG,OAAO;IAuBpE,EAAE,CAAC,QAAQ,EAAE,mBAAmB,GAAG,IAAI;IAIvC,GAAG,CAAC,QAAQ,EAAE,mBAAmB,GAAG,IAAI;CAMzC"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const calculateChangedKeys_1 = require("./calculateChangedKeys");
|
|
4
|
+
/**
|
|
5
|
+
* The flag updater handles logic required during the flag update process.
|
|
6
|
+
* It handles versions checking to handle out of order flag updates and
|
|
7
|
+
* also handles flag comparisons for change notification.
|
|
8
|
+
*/
|
|
9
|
+
class FlagUpdater {
|
|
10
|
+
constructor(flagStore, logger) {
|
|
11
|
+
this.changeCallbacks = new Array();
|
|
12
|
+
this.flagStore = flagStore;
|
|
13
|
+
this.logger = logger;
|
|
14
|
+
}
|
|
15
|
+
init(context, newFlags) {
|
|
16
|
+
this.activeContextKey = context.canonicalKey;
|
|
17
|
+
const oldFlags = this.flagStore.getAll();
|
|
18
|
+
this.flagStore.init(newFlags);
|
|
19
|
+
const changed = (0, calculateChangedKeys_1.default)(oldFlags, newFlags);
|
|
20
|
+
if (changed.length > 0) {
|
|
21
|
+
this.changeCallbacks.forEach((callback) => {
|
|
22
|
+
try {
|
|
23
|
+
callback(context, changed);
|
|
24
|
+
}
|
|
25
|
+
catch (err) {
|
|
26
|
+
/* intentionally empty */
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
initCached(context, newFlags) {
|
|
32
|
+
if (this.activeContextKey === context.canonicalKey) {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
this.init(context, newFlags);
|
|
36
|
+
}
|
|
37
|
+
upsert(context, key, item) {
|
|
38
|
+
if (this.activeContextKey !== context.canonicalKey) {
|
|
39
|
+
this.logger.warn('Received an update for an inactive context.');
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
const currentValue = this.flagStore.get(key);
|
|
43
|
+
if (currentValue !== undefined && currentValue.version >= item.version) {
|
|
44
|
+
// this is an out of order update that can be ignored
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
this.flagStore.insertOrUpdate(key, item);
|
|
48
|
+
this.changeCallbacks.forEach((callback) => {
|
|
49
|
+
try {
|
|
50
|
+
callback(context, [key]);
|
|
51
|
+
}
|
|
52
|
+
catch (err) {
|
|
53
|
+
/* intentionally empty */
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
return true;
|
|
57
|
+
}
|
|
58
|
+
on(callback) {
|
|
59
|
+
this.changeCallbacks.push(callback);
|
|
60
|
+
}
|
|
61
|
+
off(callback) {
|
|
62
|
+
const index = this.changeCallbacks.indexOf(callback);
|
|
63
|
+
if (index > -1) {
|
|
64
|
+
this.changeCallbacks.splice(index, 1);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
exports.default = FlagUpdater;
|
|
69
|
+
//# sourceMappingURL=FlagUpdater.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FlagUpdater.js","sourceRoot":"","sources":["../../src/flag-manager/FlagUpdater.ts"],"names":[],"mappings":";;AAEA,iEAA0D;AAmB1D;;;;GAIG;AACH,MAAqB,WAAW;IAM9B,YAAY,SAAoB,EAAE,MAAgB;QAF1C,oBAAe,GAAG,IAAI,KAAK,EAAuB,CAAC;QAGzD,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;QAC3B,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACvB,CAAC;IAED,IAAI,CAAC,OAAgB,EAAE,QAA2C;QAChE,IAAI,CAAC,gBAAgB,GAAG,OAAO,CAAC,YAAY,CAAC;QAC7C,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,CAAC;QACzC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC9B,MAAM,OAAO,GAAG,IAAA,8BAAoB,EAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;QACzD,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE;YACtB,IAAI,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC,QAAQ,EAAE,EAAE;gBACxC,IAAI;oBACF,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;iBAC5B;gBAAC,OAAO,GAAG,EAAE;oBACZ,yBAAyB;iBAC1B;YACH,CAAC,CAAC,CAAC;SACJ;IACH,CAAC;IAED,UAAU,CAAC,OAAgB,EAAE,QAA2C;QACtE,IAAI,IAAI,CAAC,gBAAgB,KAAK,OAAO,CAAC,YAAY,EAAE;YAClD,OAAO;SACR;QAED,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;IAC/B,CAAC;IAED,MAAM,CAAC,OAAgB,EAAE,GAAW,EAAE,IAAoB;QACxD,IAAI,IAAI,CAAC,gBAAgB,KAAK,OAAO,CAAC,YAAY,EAAE;YAClD,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,6CAA6C,CAAC,CAAC;YAChE,OAAO,KAAK,CAAC;SACd;QAED,MAAM,YAAY,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAC7C,IAAI,YAAY,KAAK,SAAS,IAAI,YAAY,CAAC,OAAO,IAAI,IAAI,CAAC,OAAO,EAAE;YACtE,qDAAqD;YACrD,OAAO,KAAK,CAAC;SACd;QAED,IAAI,CAAC,SAAS,CAAC,cAAc,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;QACzC,IAAI,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC,QAAQ,EAAE,EAAE;YACxC,IAAI;gBACF,QAAQ,CAAC,OAAO,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;aAC1B;YAAC,OAAO,GAAG,EAAE;gBACZ,yBAAyB;aAC1B;QACH,CAAC,CAAC,CAAC;QACH,OAAO,IAAI,CAAC;IACd,CAAC;IAED,EAAE,CAAC,QAA6B;QAC9B,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACtC,CAAC;IAED,GAAG,CAAC,QAA6B;QAC/B,MAAM,KAAK,GAAG,IAAI,CAAC,eAAe,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QACrD,IAAI,KAAK,GAAG,CAAC,CAAC,EAAE;YACd,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;SACvC;IACH,CAAC;CACF;AApED,8BAoEC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Flag } from '../types';
|
|
2
|
+
/**
|
|
3
|
+
* An item descriptor is an abstraction that allows for Flag data to be
|
|
4
|
+
* handled using the same type in both a put or a patch.
|
|
5
|
+
*/
|
|
6
|
+
export interface ItemDescriptor {
|
|
7
|
+
version: number;
|
|
8
|
+
flag: Flag;
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=ItemDescriptor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ItemDescriptor.d.ts","sourceRoot":"","sources":["../../src/flag-manager/ItemDescriptor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,UAAU,CAAC;AAEhC;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,IAAI,CAAC;CACZ"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ItemDescriptor.js","sourceRoot":"","sources":["../../src/flag-manager/ItemDescriptor.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"calculateChangedKeys.d.ts","sourceRoot":"","sources":["../../src/flag-manager/calculateChangedKeys.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,OAAO,UAAU,oBAAoB,CAC1C,cAAc,EAAE;IAAE,CAAC,CAAC,EAAE,MAAM,GAAG,GAAG,CAAA;CAAE,EACpC,SAAS,EAAE;IAAE,CAAC,CAAC,EAAE,MAAM,GAAG,GAAG,CAAA;CAAE,YAoBhC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const js_sdk_common_1 = require("@launchdarkly/js-sdk-common");
|
|
4
|
+
function calculateChangedKeys(existingObject, newObject) {
|
|
5
|
+
const changedKeys = [];
|
|
6
|
+
// property deleted or updated
|
|
7
|
+
Object.entries(existingObject).forEach(([k, f]) => {
|
|
8
|
+
const subObject = newObject[k];
|
|
9
|
+
if (!subObject || !(0, js_sdk_common_1.fastDeepEqual)(f, subObject)) {
|
|
10
|
+
changedKeys.push(k);
|
|
11
|
+
}
|
|
12
|
+
});
|
|
13
|
+
// property added
|
|
14
|
+
Object.keys(newObject).forEach((k) => {
|
|
15
|
+
if (!existingObject[k]) {
|
|
16
|
+
changedKeys.push(k);
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
return changedKeys;
|
|
20
|
+
}
|
|
21
|
+
exports.default = calculateChangedKeys;
|
|
22
|
+
//# sourceMappingURL=calculateChangedKeys.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"calculateChangedKeys.js","sourceRoot":"","sources":["../../src/flag-manager/calculateChangedKeys.ts"],"names":[],"mappings":";;AAAA,+DAA4D;AAE5D,SAAwB,oBAAoB,CAC1C,cAAoC,EACpC,SAA+B;IAE/B,MAAM,WAAW,GAAa,EAAE,CAAC;IAEjC,8BAA8B;IAC9B,MAAM,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE;QAChD,MAAM,SAAS,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC;QAC/B,IAAI,CAAC,SAAS,IAAI,CAAC,IAAA,6BAAa,EAAC,CAAC,EAAE,SAAS,CAAC,EAAE;YAC9C,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;SACrB;IACH,CAAC,CAAC,CAAC;IAEH,iBAAiB;IACjB,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE;QACnC,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC,EAAE;YACtB,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;SACrB;IACH,CAAC,CAAC,CAAC;IAEH,OAAO,WAAW,CAAC;AACrB,CAAC;AAtBD,uCAsBC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PollingProcessor.d.ts","sourceRoot":"","sources":["../../src/polling/PollingProcessor.ts"],"names":[],"mappings":"AAAA,OAAO,EAOL,cAAc,EAIf,MAAM,6BAA6B,CAAC;AAKrC,MAAM,MAAM,mBAAmB,GAAG,CAAC,GAAG,EAAE,cAAc,KAAK,IAAI,CAAC"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const js_sdk_common_1 = require("@launchdarkly/js-sdk-common");
|
|
4
|
+
const Requestor_1 = require("./Requestor");
|
|
5
|
+
/**
|
|
6
|
+
* @internal
|
|
7
|
+
*/
|
|
8
|
+
class PollingProcessor {
|
|
9
|
+
constructor(sdkKey, requests, info, uriPath, config, dataHandler, errorHandler) {
|
|
10
|
+
this.dataHandler = dataHandler;
|
|
11
|
+
this.errorHandler = errorHandler;
|
|
12
|
+
this.stopped = false;
|
|
13
|
+
const uri = `${config.serviceEndpoints.polling}${uriPath}`;
|
|
14
|
+
this.logger = config.logger;
|
|
15
|
+
this.pollInterval = config.pollInterval;
|
|
16
|
+
this.requestor = new Requestor_1.default(sdkKey, requests, info, uri, config.useReport, config.tags);
|
|
17
|
+
}
|
|
18
|
+
async poll() {
|
|
19
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
20
|
+
if (this.stopped) {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
const reportJsonError = (data) => {
|
|
24
|
+
var _a, _b, _c;
|
|
25
|
+
(_a = this.logger) === null || _a === void 0 ? void 0 : _a.error('Polling received invalid data');
|
|
26
|
+
(_b = this.logger) === null || _b === void 0 ? void 0 : _b.debug(`Invalid JSON follows: ${data}`);
|
|
27
|
+
(_c = this.errorHandler) === null || _c === void 0 ? void 0 : _c.call(this, new js_sdk_common_1.LDPollingError('Malformed JSON data in polling response'));
|
|
28
|
+
};
|
|
29
|
+
(_a = this.logger) === null || _a === void 0 ? void 0 : _a.debug('Polling LaunchDarkly for feature flag updates');
|
|
30
|
+
const startTime = Date.now();
|
|
31
|
+
try {
|
|
32
|
+
const res = await this.requestor.requestPayload();
|
|
33
|
+
try {
|
|
34
|
+
const flags = JSON.parse(res);
|
|
35
|
+
try {
|
|
36
|
+
(_b = this.dataHandler) === null || _b === void 0 ? void 0 : _b.call(this, flags);
|
|
37
|
+
}
|
|
38
|
+
catch (err) {
|
|
39
|
+
(_c = this.logger) === null || _c === void 0 ? void 0 : _c.error(`Exception from data handler: ${err}`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
catch (_h) {
|
|
43
|
+
reportJsonError(res);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
catch (err) {
|
|
47
|
+
const requestError = err;
|
|
48
|
+
if (requestError.status !== undefined) {
|
|
49
|
+
if (!(0, js_sdk_common_1.isHttpRecoverable)(requestError.status)) {
|
|
50
|
+
(_d = this.logger) === null || _d === void 0 ? void 0 : _d.error((0, js_sdk_common_1.httpErrorMessage)(err, 'polling request'));
|
|
51
|
+
(_e = this.errorHandler) === null || _e === void 0 ? void 0 : _e.call(this, new js_sdk_common_1.LDPollingError(requestError.message, requestError.status));
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
(_f = this.logger) === null || _f === void 0 ? void 0 : _f.error((0, js_sdk_common_1.httpErrorMessage)(err, 'polling request', 'will retry'));
|
|
56
|
+
}
|
|
57
|
+
const elapsed = Date.now() - startTime;
|
|
58
|
+
const sleepFor = Math.max(this.pollInterval * 1000 - elapsed, 0);
|
|
59
|
+
(_g = this.logger) === null || _g === void 0 ? void 0 : _g.debug('Elapsed: %d ms, sleeping for %d ms', elapsed, sleepFor);
|
|
60
|
+
this.timeoutHandle = setTimeout(() => {
|
|
61
|
+
this.poll();
|
|
62
|
+
}, sleepFor);
|
|
63
|
+
}
|
|
64
|
+
start() {
|
|
65
|
+
this.poll();
|
|
66
|
+
}
|
|
67
|
+
stop() {
|
|
68
|
+
if (this.timeoutHandle) {
|
|
69
|
+
clearTimeout(this.timeoutHandle);
|
|
70
|
+
this.timeoutHandle = undefined;
|
|
71
|
+
}
|
|
72
|
+
this.stopped = true;
|
|
73
|
+
}
|
|
74
|
+
close() {
|
|
75
|
+
this.stop();
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
exports.default = PollingProcessor;
|
|
79
|
+
//# sourceMappingURL=PollingProcessor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PollingProcessor.js","sourceRoot":"","sources":["../../src/polling/PollingProcessor.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Requestor.d.ts","sourceRoot":"","sources":["../../src/polling/Requestor.ts"],"names":[],"mappings":"AACA,OAAO,EAGL,iBAAiB,EAGlB,MAAM,6BAA6B,CAAC;AAMrC,qBAAa,cAAe,SAAQ,KAAM,YAAW,iBAAiB;IAC7D,MAAM,CAAC,EAAE,MAAM,CAAC;gBAEX,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM;CAK7C"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.LDRequestError = void 0;
|
|
4
|
+
// eslint-disable-next-line max-classes-per-file
|
|
5
|
+
const js_sdk_common_1 = require("@launchdarkly/js-sdk-common");
|
|
6
|
+
function isOk(status) {
|
|
7
|
+
return status >= 200 && status <= 299;
|
|
8
|
+
}
|
|
9
|
+
class LDRequestError extends Error {
|
|
10
|
+
constructor(message, status) {
|
|
11
|
+
super(message);
|
|
12
|
+
this.status = status;
|
|
13
|
+
this.name = 'LaunchDarklyRequestError';
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
exports.LDRequestError = LDRequestError;
|
|
17
|
+
/**
|
|
18
|
+
* Note: The requestor is implemented independently from polling such that it can be used to
|
|
19
|
+
* make a one-off request.
|
|
20
|
+
*
|
|
21
|
+
* @internal
|
|
22
|
+
*/
|
|
23
|
+
class Requestor {
|
|
24
|
+
constructor(sdkKey, requests, info, uri, useReport, tags) {
|
|
25
|
+
this.requests = requests;
|
|
26
|
+
this.uri = uri;
|
|
27
|
+
this.headers = (0, js_sdk_common_1.defaultHeaders)(sdkKey, info, tags);
|
|
28
|
+
this.verb = useReport ? 'REPORT' : 'GET';
|
|
29
|
+
}
|
|
30
|
+
async requestPayload() {
|
|
31
|
+
let status;
|
|
32
|
+
try {
|
|
33
|
+
const res = await this.requests.fetch(this.uri, {
|
|
34
|
+
method: this.verb,
|
|
35
|
+
headers: this.headers,
|
|
36
|
+
});
|
|
37
|
+
if (isOk(res.status)) {
|
|
38
|
+
return await res.text();
|
|
39
|
+
}
|
|
40
|
+
// Assigning so it can be thrown after the try/catch.
|
|
41
|
+
status = res.status;
|
|
42
|
+
}
|
|
43
|
+
catch (err) {
|
|
44
|
+
throw new LDRequestError(err === null || err === void 0 ? void 0 : err.message);
|
|
45
|
+
}
|
|
46
|
+
throw new LDRequestError(`Unexpected status code: ${status}`, status);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
exports.default = Requestor;
|
|
50
|
+
//# sourceMappingURL=Requestor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Requestor.js","sourceRoot":"","sources":["../../src/polling/Requestor.ts"],"names":[],"mappings":";;;AAAA,gDAAgD;AAChD,+DAMqC;AAErC,SAAS,IAAI,CAAC,MAAc;IAC1B,OAAO,MAAM,IAAI,GAAG,IAAI,MAAM,IAAI,GAAG,CAAC;AACxC,CAAC;AAED,MAAa,cAAe,SAAQ,KAAK;IAGvC,YAAY,OAAe,EAAE,MAAe;QAC1C,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,IAAI,GAAG,0BAA0B,CAAC;IACzC,CAAC;CACF;AARD,wCAQC;AAED;;;;;GAKG;AACH,MAAqB,SAAS;IAI5B,YACE,MAAc,EACN,QAAkB,EAC1B,IAAU,EACO,GAAW,EAC5B,SAAkB,EAClB,IAAqB;QAJb,aAAQ,GAAR,QAAQ,CAAU;QAET,QAAG,GAAH,GAAG,CAAQ;QAI5B,IAAI,CAAC,OAAO,GAAG,IAAA,8BAAc,EAAC,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;QAClD,IAAI,CAAC,IAAI,GAAG,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC;IAC3C,CAAC;IAED,KAAK,CAAC,cAAc;QAClB,IAAI,MAA0B,CAAC;QAC/B,IAAI;YACF,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE;gBAC9C,MAAM,EAAE,IAAI,CAAC,IAAI;gBACjB,OAAO,EAAE,IAAI,CAAC,OAAO;aACtB,CAAC,CAAC;YACH,IAAI,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE;gBACpB,OAAO,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;aACzB;YACD,qDAAqD;YACrD,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC;SACrB;QAAC,OAAO,GAAQ,EAAE;YACjB,MAAM,IAAI,cAAc,CAAC,GAAG,aAAH,GAAG,uBAAH,GAAG,CAAE,OAAO,CAAC,CAAC;SACxC;QACD,MAAM,IAAI,cAAc,CAAC,2BAA2B,MAAM,EAAE,EAAE,MAAM,CAAC,CAAC;IACxE,CAAC;CACF;AAjCD,4BAiCC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Platform } from '@launchdarkly/js-sdk-common';
|
|
2
|
+
/**
|
|
3
|
+
* This function will retrieve a previously generated key for the given {@link storageKey} if it
|
|
4
|
+
* exists or generate and store one on the fly if it does not already exist.
|
|
5
|
+
* @param storageKey keyed storage location where the generated key should live. See {@link namespaceForGeneratedContextKey}
|
|
6
|
+
* for related exmaples of generating a storage key and usage.
|
|
7
|
+
* @param platform crypto and storage implementations for necessary operations
|
|
8
|
+
* @returns the generated key
|
|
9
|
+
*/
|
|
10
|
+
export declare const getOrGenerateKey: (storageKey: string, { crypto, storage }: Platform) => Promise<string>;
|
|
11
|
+
//# sourceMappingURL=getOrGenerateKey.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"getOrGenerateKey.d.ts","sourceRoot":"","sources":["../../src/storage/getOrGenerateKey.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,6BAA6B,CAAC;AAKvD;;;;;;;GAOG;AACH,eAAO,MAAM,gBAAgB,eAAsB,MAAM,uBAAuB,QAAQ,oBASvF,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getOrGenerateKey = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* This function will retrieve a previously generated key for the given {@link storageKey} if it
|
|
6
|
+
* exists or generate and store one on the fly if it does not already exist.
|
|
7
|
+
* @param storageKey keyed storage location where the generated key should live. See {@link namespaceForGeneratedContextKey}
|
|
8
|
+
* for related exmaples of generating a storage key and usage.
|
|
9
|
+
* @param platform crypto and storage implementations for necessary operations
|
|
10
|
+
* @returns the generated key
|
|
11
|
+
*/
|
|
12
|
+
const getOrGenerateKey = async (storageKey, { crypto, storage }) => {
|
|
13
|
+
let generatedKey = await (storage === null || storage === void 0 ? void 0 : storage.get(storageKey));
|
|
14
|
+
if (!generatedKey) {
|
|
15
|
+
generatedKey = crypto.randomUUID();
|
|
16
|
+
await (storage === null || storage === void 0 ? void 0 : storage.set(storageKey, generatedKey));
|
|
17
|
+
}
|
|
18
|
+
return generatedKey;
|
|
19
|
+
};
|
|
20
|
+
exports.getOrGenerateKey = getOrGenerateKey;
|
|
21
|
+
//# sourceMappingURL=getOrGenerateKey.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"getOrGenerateKey.js","sourceRoot":"","sources":["../../src/storage/getOrGenerateKey.ts"],"names":[],"mappings":";;;AAKA;;;;;;;GAOG;AACI,MAAM,gBAAgB,GAAG,KAAK,EAAE,UAAkB,EAAE,EAAE,MAAM,EAAE,OAAO,EAAY,EAAE,EAAE;IAC1F,IAAI,YAAY,GAAG,MAAM,CAAA,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,GAAG,CAAC,UAAU,CAAC,CAAA,CAAC;IAElD,IAAI,CAAC,YAAY,EAAE;QACjB,YAAY,GAAG,MAAM,CAAC,UAAU,EAAE,CAAC;QACnC,MAAM,CAAA,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,GAAG,CAAC,UAAU,EAAE,YAAY,CAAC,CAAA,CAAC;KAC9C;IAED,OAAO,YAAY,CAAC;AACtB,CAAC,CAAC;AATW,QAAA,gBAAgB,oBAS3B"}
|