pulse-updates 1.0.19 → 1.0.21
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/android/src/main/java/app/pulse/updates/PulseReactHostFactory.kt +14 -2
- package/android/src/main/java/app/pulse/updates/PulseUpdatesModule.kt +22 -12
- package/ios/PulseUpdates/PulseUpdates.swift +9 -0
- package/lib/commonjs/config.js +147 -7
- package/lib/commonjs/config.js.map +1 -1
- package/lib/module/config.js +145 -7
- package/lib/module/config.js.map +1 -1
- package/lib/typescript/config.d.ts +43 -0
- package/lib/typescript/config.d.ts.map +1 -1
- package/package.json +8 -3
- package/src/config.ts +182 -10
|
@@ -32,7 +32,8 @@ private class PulseReactHostDelegate(
|
|
|
32
32
|
override val jsRuntimeFactory: JSRuntimeFactory = HermesInstance(),
|
|
33
33
|
override val bindingsInstaller: BindingsInstaller? = null,
|
|
34
34
|
override val turboModuleManagerDelegateBuilder: ReactPackageTurboModuleManagerDelegate.Builder = DefaultTurboModuleManagerDelegate.Builder(),
|
|
35
|
-
private val exceptionHandler: (Exception) -> Unit = { throw it }
|
|
35
|
+
private val exceptionHandler: (Exception) -> Unit = { throw it },
|
|
36
|
+
private val useDevSupport: Boolean = false
|
|
36
37
|
) : ReactHostDelegate {
|
|
37
38
|
|
|
38
39
|
/**
|
|
@@ -44,6 +45,16 @@ private class PulseReactHostDelegate(
|
|
|
44
45
|
val context = weakContext.get()
|
|
45
46
|
?: throw IllegalStateException("Context is no longer available")
|
|
46
47
|
|
|
48
|
+
// A dev build takes its JS from Metro, so there is no Pulse artefact to
|
|
49
|
+
// restore and nothing to verify: the embedded asset is a plain Metro
|
|
50
|
+
// bundle, prepare() rightly returns null for it, and validating anyway
|
|
51
|
+
// threw at the first frame — an unrecoverable crash-loop that surviving
|
|
52
|
+
// app data made look permanent. Stand aside instead, exactly as an app
|
|
53
|
+
// without Pulse would, and let React Native load the asset.
|
|
54
|
+
if (useDevSupport) {
|
|
55
|
+
return JSBundleLoader.createAssetLoader(context, "assets://index.android.bundle", false)
|
|
56
|
+
}
|
|
57
|
+
|
|
47
58
|
val bundleFile = PulseUpdatesModule.getBundleFile(context)
|
|
48
59
|
|
|
49
60
|
return if (bundleFile != null) {
|
|
@@ -95,7 +106,8 @@ object PulseReactHostFactory {
|
|
|
95
106
|
val delegate = PulseReactHostDelegate(
|
|
96
107
|
weakContext = weakContext,
|
|
97
108
|
jsMainModulePath = jsMainModuleName,
|
|
98
|
-
reactPackages = packages
|
|
109
|
+
reactPackages = packages,
|
|
110
|
+
useDevSupport = useDevSupport
|
|
99
111
|
)
|
|
100
112
|
|
|
101
113
|
val componentFactory = ComponentFactory()
|
|
@@ -199,33 +199,43 @@ class PulseUpdatesModule(private val reactContext: ReactApplicationContext) :
|
|
|
199
199
|
return@runOnUiThread
|
|
200
200
|
}
|
|
201
201
|
|
|
202
|
-
//
|
|
202
|
+
// New architecture (ReactHost) first.
|
|
203
|
+
//
|
|
204
|
+
// Reached through the TYPED interface, not `javaClass.getMethod("getReactHost")`:
|
|
205
|
+
// reflection by name looks the method up on the app's own class, and in a
|
|
206
|
+
// minified release R8 has renamed it. The lookup then throws, the code falls
|
|
207
|
+
// through to the bridge path below, and that path is gone on bridgeless — the
|
|
208
|
+
// user taps "Reload" and gets `ReactInstanceManager.createReactContext is
|
|
209
|
+
// unsupported` with the update sitting there, applied only at the next cold
|
|
210
|
+
// start. `ReactApplication.getReactHost()` is part of the RN interface, so it
|
|
211
|
+
// survives minification; older RN without it throws a LINKAGE error, which is
|
|
212
|
+
// caught separately so those runtimes still reach the fallback.
|
|
203
213
|
try {
|
|
204
|
-
val reactHost = reactApplication.
|
|
214
|
+
val reactHost = reactApplication.reactHost
|
|
205
215
|
if (reactHost != null) {
|
|
206
216
|
pulseLog(TAG, "reloadAsync: using ReactHost.reload()")
|
|
207
217
|
|
|
208
|
-
//
|
|
218
|
+
// Resume first when the host is not RESUMED: reload on a paused host
|
|
219
|
+
// is a no-op (same reason expo-updates does this).
|
|
209
220
|
val currentActivity = reactContext.currentActivity
|
|
210
221
|
try {
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
.getField("RESUMED").get(null)
|
|
214
|
-
if (lifecycleState != resumedState && currentActivity != null) {
|
|
215
|
-
reactHost.javaClass.getMethod("onHostResume", android.app.Activity::class.java)
|
|
216
|
-
.invoke(reactHost, currentActivity)
|
|
222
|
+
if (reactHost.lifecycleState != com.facebook.react.common.LifecycleState.RESUMED && currentActivity != null) {
|
|
223
|
+
reactHost.onHostResume(currentActivity)
|
|
217
224
|
}
|
|
218
225
|
} catch (e: Exception) {
|
|
219
226
|
pulseLogWarn(TAG, "reloadAsync: could not check/resume lifecycle: ${e.message}")
|
|
220
227
|
}
|
|
221
228
|
|
|
222
|
-
reactHost.
|
|
223
|
-
.invoke(reactHost, "PulseUpdates reload")
|
|
229
|
+
reactHost.reload("PulseUpdates reload")
|
|
224
230
|
promise.resolve(null)
|
|
225
231
|
return@runOnUiThread
|
|
226
232
|
}
|
|
227
|
-
|
|
233
|
+
pulseLog(TAG, "reloadAsync: ReactHost is null — falling back to the bridge path")
|
|
234
|
+
} catch (e: LinkageError) {
|
|
235
|
+
// RN too old to expose ReactHost at all.
|
|
228
236
|
pulseLog(TAG, "reloadAsync: ReactHost not available: ${e.message}")
|
|
237
|
+
} catch (e: Exception) {
|
|
238
|
+
pulseLogWarn(TAG, "reloadAsync: ReactHost.reload failed: ${e.message}")
|
|
229
239
|
}
|
|
230
240
|
|
|
231
241
|
// Fall back to old architecture (ReactNativeHost)
|
|
@@ -206,6 +206,14 @@ public class PulseUpdates: RCTEventEmitter {
|
|
|
206
206
|
|
|
207
207
|
@objc
|
|
208
208
|
public static func getBundleURL() -> URL? {
|
|
209
|
+
#if DEBUG
|
|
210
|
+
// A dev build serves its JS from Metro. Handing back a Pulse asset here
|
|
211
|
+
// means the app runs neither what the developer just wrote nor anything
|
|
212
|
+
// Pulse can vouch for, so return nil and let the host fall back to its
|
|
213
|
+
// normal debug source — the Android side stands aside the same way.
|
|
214
|
+
pulseLog("getBundleURL skipped: debug build, JS comes from the dev server")
|
|
215
|
+
return nil
|
|
216
|
+
#else
|
|
209
217
|
// If controller hasn't started yet, start it now
|
|
210
218
|
if !PulseController.shared.isStarted {
|
|
211
219
|
PulseController.shared.initializeWithoutStarting()
|
|
@@ -214,6 +222,7 @@ public class PulseUpdates: RCTEventEmitter {
|
|
|
214
222
|
let url = PulseController.shared.launchAssetURL()
|
|
215
223
|
pulseLog("getBundleURL isStarted=\(PulseController.shared.isStarted) url=\(url?.lastPathComponent ?? "nil")")
|
|
216
224
|
return url
|
|
225
|
+
#endif
|
|
217
226
|
}
|
|
218
227
|
|
|
219
228
|
// MARK: - Helpers
|
package/lib/commonjs/config.js
CHANGED
|
@@ -8,6 +8,7 @@ exports.configureConfig = configureConfig;
|
|
|
8
8
|
exports.fetchConfig = fetchConfig;
|
|
9
9
|
exports.getAllConfig = getAllConfig;
|
|
10
10
|
exports.getConfigBoolean = getConfigBoolean;
|
|
11
|
+
exports.getConfigExperiments = getConfigExperiments;
|
|
11
12
|
exports.getConfigInfo = getConfigInfo;
|
|
12
13
|
exports.getConfigJson = getConfigJson;
|
|
13
14
|
exports.getConfigKeys = getConfigKeys;
|
|
@@ -15,6 +16,7 @@ exports.getConfigNumber = getConfigNumber;
|
|
|
15
16
|
exports.getConfigSource = getConfigSource;
|
|
16
17
|
exports.getConfigString = getConfigString;
|
|
17
18
|
exports.getConfigValue = getConfigValue;
|
|
19
|
+
exports.getConfigVariant = getConfigVariant;
|
|
18
20
|
exports.hasPendingConfig = hasPendingConfig;
|
|
19
21
|
exports.onConfigChange = onConfigChange;
|
|
20
22
|
exports.resetConfigForTests = resetConfigForTests;
|
|
@@ -42,22 +44,55 @@ exports.startConfigAutoRefresh = startConfigAutoRefresh;
|
|
|
42
44
|
* config costs a 304 with no body.
|
|
43
45
|
*/
|
|
44
46
|
|
|
47
|
+
/**
|
|
48
|
+
* An A/B arm this install was assigned, as the server resolved it.
|
|
49
|
+
*
|
|
50
|
+
* Carried separately from the values even though the arm has already been folded
|
|
51
|
+
* into them: a control arm and an install outside the experiment are served exactly
|
|
52
|
+
* the same config, so the values cannot say which is which. Without the name here,
|
|
53
|
+
* every analytics event would be missing the one dimension the experiment is for.
|
|
54
|
+
*/
|
|
55
|
+
|
|
45
56
|
/**
|
|
46
57
|
* Everything the server may ever want to target on. Sent on every request even when
|
|
47
58
|
* no rule uses it yet: the client can only start sending a new field with a store
|
|
48
59
|
* build, so a contract that is incomplete today cannot be completed for months.
|
|
49
60
|
*/
|
|
50
61
|
|
|
62
|
+
/**
|
|
63
|
+
* The values and the arms move as one unit, always.
|
|
64
|
+
*
|
|
65
|
+
* They are two halves of a single answer: the arm is what produced those values.
|
|
66
|
+
* Applying one without the other — on activate, on a cache read, on a partial
|
|
67
|
+
* payload — would tag events with an arm whose values are not in force, and the
|
|
68
|
+
* resulting experiment would report a difference between two groups that were both
|
|
69
|
+
* served the same thing.
|
|
70
|
+
*/
|
|
71
|
+
|
|
51
72
|
const STORAGE_KEY = 'pulse.config.v1';
|
|
52
73
|
const DEFAULT_POLL_MS = 5 * 60 * 1000;
|
|
53
74
|
const DEFAULT_TIMEOUT_MS = 10_000;
|
|
54
75
|
let options = null;
|
|
55
76
|
let defaults = {};
|
|
56
77
|
let values = {};
|
|
78
|
+
let experiments = [];
|
|
57
79
|
let etag = null;
|
|
58
80
|
let fetchedAt = 0;
|
|
81
|
+
/**
|
|
82
|
+
* When the server last confirmed these values are current — a 200 that applied a
|
|
83
|
+
* payload, or a 304 that said the one we hold is still right.
|
|
84
|
+
*
|
|
85
|
+
* Distinct from fetchedAt, which is when the values were last *changed*. A caller
|
|
86
|
+
* comparing this config against another source needs to know the difference: values
|
|
87
|
+
* restored from disk and never revalidated may be days behind what the server would
|
|
88
|
+
* answer now, and reporting them as "what Pulse says" turns a stale cache into a
|
|
89
|
+
* disagreement that nobody can act on.
|
|
90
|
+
*/
|
|
91
|
+
let verifiedAt = 0;
|
|
59
92
|
let source = 'defaults';
|
|
60
93
|
let pending = null;
|
|
94
|
+
/** The arm set already reported this session, so a poll does not re-report it. */
|
|
95
|
+
let reportedExposure = null;
|
|
61
96
|
let inFlight = null;
|
|
62
97
|
let pollTimer = null;
|
|
63
98
|
const listeners = new Set();
|
|
@@ -75,6 +110,7 @@ function configureConfig(opts) {
|
|
|
75
110
|
const cached = readCache(opts.storage);
|
|
76
111
|
if (cached) {
|
|
77
112
|
values = cached.values;
|
|
113
|
+
experiments = cached.experiments ?? [];
|
|
78
114
|
etag = cached.etag;
|
|
79
115
|
fetchedAt = cached.fetchedAt;
|
|
80
116
|
source = 'cache';
|
|
@@ -114,7 +150,13 @@ async function fetchConfig() {
|
|
|
114
150
|
headers: buildHeaders(opts, etag),
|
|
115
151
|
signal: controller?.signal
|
|
116
152
|
});
|
|
117
|
-
|
|
153
|
+
|
|
154
|
+
// Nothing changed — which is a positive answer about freshness, not a
|
|
155
|
+
// non-event: the values we hold are the ones the server would send.
|
|
156
|
+
if (response.status === 304) {
|
|
157
|
+
verifiedAt = Date.now();
|
|
158
|
+
return false;
|
|
159
|
+
}
|
|
118
160
|
if (!response.ok) throw new Error(`Pulse config HTTP ${response.status}`);
|
|
119
161
|
const payload = await response.json();
|
|
120
162
|
const nextValues = payload?.values;
|
|
@@ -125,11 +167,16 @@ async function fetchConfig() {
|
|
|
125
167
|
}
|
|
126
168
|
etag = response.headers?.get?.('etag') ?? null;
|
|
127
169
|
fetchedAt = Date.now();
|
|
170
|
+
verifiedAt = fetchedAt;
|
|
171
|
+
const snapshot = {
|
|
172
|
+
values: nextValues,
|
|
173
|
+
experiments: parseExperiments(payload?.experiments)
|
|
174
|
+
};
|
|
128
175
|
if (opts.activateOnFetch === false) {
|
|
129
|
-
pending =
|
|
176
|
+
pending = snapshot;
|
|
130
177
|
return false;
|
|
131
178
|
}
|
|
132
|
-
return
|
|
179
|
+
return applySnapshot(snapshot, opts);
|
|
133
180
|
} catch (error) {
|
|
134
181
|
// A failed refresh must never clear what we have: the whole point of the cache
|
|
135
182
|
// is that an unreachable server degrades to the last good config.
|
|
@@ -151,7 +198,7 @@ function activateConfig() {
|
|
|
151
198
|
if (!pending || !options) return false;
|
|
152
199
|
const next = pending;
|
|
153
200
|
pending = null;
|
|
154
|
-
return
|
|
201
|
+
return applySnapshot(next, options);
|
|
155
202
|
}
|
|
156
203
|
|
|
157
204
|
/** Whether a fetched-but-not-yet-applied payload is waiting. */
|
|
@@ -178,22 +225,92 @@ function startConfigAutoRefresh(appState) {
|
|
|
178
225
|
stopPolling();
|
|
179
226
|
};
|
|
180
227
|
}
|
|
181
|
-
function
|
|
182
|
-
const changed = !shallowEqual(values, next);
|
|
183
|
-
values = next;
|
|
228
|
+
function applySnapshot(next, opts) {
|
|
229
|
+
const changed = !shallowEqual(values, next.values) || !sameExperiments(experiments, next.experiments);
|
|
230
|
+
values = next.values;
|
|
231
|
+
experiments = next.experiments;
|
|
184
232
|
source = 'remote';
|
|
185
233
|
writeCache(opts.storage, {
|
|
186
234
|
values,
|
|
235
|
+
experiments,
|
|
187
236
|
etag,
|
|
188
237
|
fetchedAt
|
|
189
238
|
});
|
|
190
239
|
|
|
240
|
+
// Reported on apply, never on fetch: with activateOnFetch false a payload can sit
|
|
241
|
+
// unapplied for the rest of a session, and an arm the app is not actually serving
|
|
242
|
+
// is not an exposure.
|
|
243
|
+
void reportExposure(opts);
|
|
244
|
+
|
|
191
245
|
// Listeners drive re-renders and gate re-evaluation: firing them for an identical
|
|
192
246
|
// payload is pure churn, and the 200-with-same-content case is common enough
|
|
193
247
|
// (a proxy that drops the ETag) to be worth checking.
|
|
194
248
|
if (changed) notify();
|
|
195
249
|
return changed;
|
|
196
250
|
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Tell the server which arms this install ended up in.
|
|
254
|
+
*
|
|
255
|
+
* The server already knows how it would assign a given device, so this is not how
|
|
256
|
+
* the experiment is measured — it is how a lopsided split gets noticed. A hash over
|
|
257
|
+
* a real population is only asymptotically fair, installs without a device id sit
|
|
258
|
+
* outside every experiment, and an audience filters unevenly across platforms; all
|
|
259
|
+
* three show up here as arms that are not the size they should be.
|
|
260
|
+
*
|
|
261
|
+
* Sent once per distinct set of arms per session rather than on every poll. The
|
|
262
|
+
* server counts one row per install per session, so a five-minute poll would
|
|
263
|
+
* multiply every arm by the length of the session and make a long user look like a
|
|
264
|
+
* crowd.
|
|
265
|
+
*
|
|
266
|
+
* Failure is swallowed on purpose. This is a diagnostic, and no diagnostic is worth
|
|
267
|
+
* a rejected promise on the path that delivers config to the app.
|
|
268
|
+
*/
|
|
269
|
+
async function reportExposure(opts) {
|
|
270
|
+
if (opts.reportExposure !== true) return;
|
|
271
|
+
if (experiments.length === 0) return;
|
|
272
|
+
const signature = experiments.map(e => `${e.key}=${e.variant}`).join(',');
|
|
273
|
+
if (signature === reportedExposure) return;
|
|
274
|
+
reportedExposure = signature;
|
|
275
|
+
const context = opts.getContext?.() ?? {};
|
|
276
|
+
const assignments = {};
|
|
277
|
+
for (const entry of experiments) assignments[entry.key] = entry.variant;
|
|
278
|
+
try {
|
|
279
|
+
await fetch(`${opts.url.replace(/\/+$/, '')}/exposure`, {
|
|
280
|
+
method: 'POST',
|
|
281
|
+
headers: {
|
|
282
|
+
'Content-Type': 'application/json'
|
|
283
|
+
},
|
|
284
|
+
body: JSON.stringify({
|
|
285
|
+
platform: context.platform,
|
|
286
|
+
appVersion: context.appVersion,
|
|
287
|
+
assignments
|
|
288
|
+
})
|
|
289
|
+
});
|
|
290
|
+
} catch {
|
|
291
|
+
// A device that cannot report its arm still has the right arm.
|
|
292
|
+
reportedExposure = signature;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* The arms as the server sent them, keeping only entries that name both halves.
|
|
298
|
+
*
|
|
299
|
+
* A half-formed assignment is dropped rather than carried: an experiment key with
|
|
300
|
+
* no variant would tag every event with an empty cohort, which reads downstream as
|
|
301
|
+
* a third group that does not exist.
|
|
302
|
+
*/
|
|
303
|
+
function parseExperiments(raw) {
|
|
304
|
+
if (!Array.isArray(raw)) return [];
|
|
305
|
+
return raw.filter(entry => !!entry && typeof entry === 'object' && typeof entry.key === 'string' && typeof entry.variant === 'string' && entry.key !== '' && entry.variant !== '').map(entry => ({
|
|
306
|
+
key: entry.key,
|
|
307
|
+
variant: entry.variant
|
|
308
|
+
}));
|
|
309
|
+
}
|
|
310
|
+
function sameExperiments(a, b) {
|
|
311
|
+
if (a.length !== b.length) return false;
|
|
312
|
+
return a.every((entry, i) => entry.key === b[i].key && entry.variant === b[i].variant);
|
|
313
|
+
}
|
|
197
314
|
function shallowEqual(a, b) {
|
|
198
315
|
const aKeys = Object.keys(a);
|
|
199
316
|
const bKeys = Object.keys(b);
|
|
@@ -277,6 +394,24 @@ function getConfigSource(key) {
|
|
|
277
394
|
if (Object.prototype.hasOwnProperty.call(defaults, key)) return 'default';
|
|
278
395
|
return 'missing';
|
|
279
396
|
}
|
|
397
|
+
|
|
398
|
+
/**
|
|
399
|
+
* The A/B arms this install is in, for tagging analytics events.
|
|
400
|
+
*
|
|
401
|
+
* Empty when nothing is running — which is also what an install with no device id
|
|
402
|
+
* gets, since an install that draws a fresh arm on every request would take both
|
|
403
|
+
* sides in turn and pollute both.
|
|
404
|
+
*/
|
|
405
|
+
function getConfigExperiments() {
|
|
406
|
+
return experiments.map(entry => ({
|
|
407
|
+
...entry
|
|
408
|
+
}));
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
/** The arm for one experiment, or null when this install is not in it. */
|
|
412
|
+
function getConfigVariant(experimentKey) {
|
|
413
|
+
return experiments.find(entry => entry.key === experimentKey)?.variant ?? null;
|
|
414
|
+
}
|
|
280
415
|
function getConfigKeys() {
|
|
281
416
|
return Object.keys(getAllConfig()).sort();
|
|
282
417
|
}
|
|
@@ -290,6 +425,7 @@ function getConfigInfo() {
|
|
|
290
425
|
return {
|
|
291
426
|
source,
|
|
292
427
|
fetchedAt,
|
|
428
|
+
verifiedAt,
|
|
293
429
|
etag,
|
|
294
430
|
keyCount: Object.keys(getAllConfig()).length
|
|
295
431
|
};
|
|
@@ -299,12 +435,15 @@ function getConfigInfo() {
|
|
|
299
435
|
function resetConfigForTests() {
|
|
300
436
|
options = null;
|
|
301
437
|
defaults = {};
|
|
438
|
+
verifiedAt = 0;
|
|
302
439
|
values = {};
|
|
440
|
+
experiments = [];
|
|
303
441
|
etag = null;
|
|
304
442
|
fetchedAt = 0;
|
|
305
443
|
source = 'defaults';
|
|
306
444
|
inFlight = null;
|
|
307
445
|
pending = null;
|
|
446
|
+
reportedExposure = null;
|
|
308
447
|
listeners.clear();
|
|
309
448
|
stopPolling();
|
|
310
449
|
}
|
|
@@ -321,6 +460,7 @@ function buildHeaders(opts, currentEtag) {
|
|
|
321
460
|
if (ctx.osVersion) headers['X-Pulse-Os-Version'] = ctx.osVersion;
|
|
322
461
|
if (ctx.language) headers['X-Pulse-Language'] = ctx.language;
|
|
323
462
|
if (ctx.deviceId) headers['Pulse-Device-Id'] = ctx.deviceId;
|
|
463
|
+
if (ctx.userId) headers['Pulse-User-Id'] = ctx.userId;
|
|
324
464
|
if (ctx.userAttributes && Object.keys(ctx.userAttributes).length > 0) {
|
|
325
465
|
headers['X-Pulse-User-Attributes'] = JSON.stringify(ctx.userAttributes);
|
|
326
466
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["STORAGE_KEY","DEFAULT_POLL_MS","DEFAULT_TIMEOUT_MS","options","defaults","values","etag","fetchedAt","source","pending","inFlight","pollTimer","listeners","Set","configureConfig","opts","cached","readCache","storage","setConfigDefaults","more","fetchConfig","minInterval","minimumFetchIntervalMs","Date","now","controller","AbortController","timer","setTimeout","abort","timeoutMs","response","fetch","url","method","headers","buildHeaders","signal","status","ok","Error","payload","json","nextValues","Array","isArray","get","activateOnFetch","applyValues","error","onError","clearTimeout","activateConfig","next","hasPendingConfig","startConfigAutoRefresh","appState","subscription","addEventListener","state","interval","pollIntervalMs","stopPolling","setInterval","remove","changed","shallowEqual","writeCache","notify","a","b","aKeys","Object","keys","bKeys","length","every","k","av","bv","JSON","stringify","clearInterval","getConfigValue","key","prototype","hasOwnProperty","call","fallback","undefined","getConfigBoolean","value","toLowerCase","getConfigNumber","parsed","Number","isNaN","getConfigString","String","getAllConfig","getConfigJson","parse","getConfigSource","getConfigKeys","sort","onConfigChange","listener","add","delete","getConfigInfo","keyCount","resetConfigForTests","clear","currentEtag","ctx","getContext","Accept","platform","appVersion","osVersion","language","deviceId","userAttributes","raw","getString","set","snapshot","forEach"],"sourceRoot":"../../src","sources":["config.ts"],"mappings":"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","ignoreList":[]}
|
|
1
|
+
{"version":3,"names":["STORAGE_KEY","DEFAULT_POLL_MS","DEFAULT_TIMEOUT_MS","options","defaults","values","experiments","etag","fetchedAt","verifiedAt","source","pending","reportedExposure","inFlight","pollTimer","listeners","Set","configureConfig","opts","cached","readCache","storage","setConfigDefaults","more","fetchConfig","minInterval","minimumFetchIntervalMs","Date","now","controller","AbortController","timer","setTimeout","abort","timeoutMs","response","fetch","url","method","headers","buildHeaders","signal","status","ok","Error","payload","json","nextValues","Array","isArray","get","snapshot","parseExperiments","activateOnFetch","applySnapshot","error","onError","clearTimeout","activateConfig","next","hasPendingConfig","startConfigAutoRefresh","appState","subscription","addEventListener","state","interval","pollIntervalMs","stopPolling","setInterval","remove","changed","shallowEqual","sameExperiments","writeCache","reportExposure","notify","length","signature","map","e","key","variant","join","context","getContext","assignments","entry","replace","body","JSON","stringify","platform","appVersion","raw","filter","a","b","every","i","aKeys","Object","keys","bKeys","k","av","bv","clearInterval","getConfigValue","prototype","hasOwnProperty","call","fallback","undefined","getConfigBoolean","value","toLowerCase","getConfigNumber","parsed","Number","isNaN","getConfigString","String","getAllConfig","getConfigJson","parse","getConfigSource","getConfigExperiments","getConfigVariant","experimentKey","find","getConfigKeys","sort","onConfigChange","listener","add","delete","getConfigInfo","keyCount","resetConfigForTests","clear","currentEtag","ctx","Accept","osVersion","language","deviceId","userId","userAttributes","getString","set","forEach"],"sourceRoot":"../../src","sources":["config.ts"],"mappings":"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","ignoreList":[]}
|
package/lib/module/config.js
CHANGED
|
@@ -20,22 +20,55 @@
|
|
|
20
20
|
* config costs a 304 with no body.
|
|
21
21
|
*/
|
|
22
22
|
|
|
23
|
+
/**
|
|
24
|
+
* An A/B arm this install was assigned, as the server resolved it.
|
|
25
|
+
*
|
|
26
|
+
* Carried separately from the values even though the arm has already been folded
|
|
27
|
+
* into them: a control arm and an install outside the experiment are served exactly
|
|
28
|
+
* the same config, so the values cannot say which is which. Without the name here,
|
|
29
|
+
* every analytics event would be missing the one dimension the experiment is for.
|
|
30
|
+
*/
|
|
31
|
+
|
|
23
32
|
/**
|
|
24
33
|
* Everything the server may ever want to target on. Sent on every request even when
|
|
25
34
|
* no rule uses it yet: the client can only start sending a new field with a store
|
|
26
35
|
* build, so a contract that is incomplete today cannot be completed for months.
|
|
27
36
|
*/
|
|
28
37
|
|
|
38
|
+
/**
|
|
39
|
+
* The values and the arms move as one unit, always.
|
|
40
|
+
*
|
|
41
|
+
* They are two halves of a single answer: the arm is what produced those values.
|
|
42
|
+
* Applying one without the other — on activate, on a cache read, on a partial
|
|
43
|
+
* payload — would tag events with an arm whose values are not in force, and the
|
|
44
|
+
* resulting experiment would report a difference between two groups that were both
|
|
45
|
+
* served the same thing.
|
|
46
|
+
*/
|
|
47
|
+
|
|
29
48
|
const STORAGE_KEY = 'pulse.config.v1';
|
|
30
49
|
const DEFAULT_POLL_MS = 5 * 60 * 1000;
|
|
31
50
|
const DEFAULT_TIMEOUT_MS = 10_000;
|
|
32
51
|
let options = null;
|
|
33
52
|
let defaults = {};
|
|
34
53
|
let values = {};
|
|
54
|
+
let experiments = [];
|
|
35
55
|
let etag = null;
|
|
36
56
|
let fetchedAt = 0;
|
|
57
|
+
/**
|
|
58
|
+
* When the server last confirmed these values are current — a 200 that applied a
|
|
59
|
+
* payload, or a 304 that said the one we hold is still right.
|
|
60
|
+
*
|
|
61
|
+
* Distinct from fetchedAt, which is when the values were last *changed*. A caller
|
|
62
|
+
* comparing this config against another source needs to know the difference: values
|
|
63
|
+
* restored from disk and never revalidated may be days behind what the server would
|
|
64
|
+
* answer now, and reporting them as "what Pulse says" turns a stale cache into a
|
|
65
|
+
* disagreement that nobody can act on.
|
|
66
|
+
*/
|
|
67
|
+
let verifiedAt = 0;
|
|
37
68
|
let source = 'defaults';
|
|
38
69
|
let pending = null;
|
|
70
|
+
/** The arm set already reported this session, so a poll does not re-report it. */
|
|
71
|
+
let reportedExposure = null;
|
|
39
72
|
let inFlight = null;
|
|
40
73
|
let pollTimer = null;
|
|
41
74
|
const listeners = new Set();
|
|
@@ -53,6 +86,7 @@ export function configureConfig(opts) {
|
|
|
53
86
|
const cached = readCache(opts.storage);
|
|
54
87
|
if (cached) {
|
|
55
88
|
values = cached.values;
|
|
89
|
+
experiments = cached.experiments ?? [];
|
|
56
90
|
etag = cached.etag;
|
|
57
91
|
fetchedAt = cached.fetchedAt;
|
|
58
92
|
source = 'cache';
|
|
@@ -92,7 +126,13 @@ export async function fetchConfig() {
|
|
|
92
126
|
headers: buildHeaders(opts, etag),
|
|
93
127
|
signal: controller?.signal
|
|
94
128
|
});
|
|
95
|
-
|
|
129
|
+
|
|
130
|
+
// Nothing changed — which is a positive answer about freshness, not a
|
|
131
|
+
// non-event: the values we hold are the ones the server would send.
|
|
132
|
+
if (response.status === 304) {
|
|
133
|
+
verifiedAt = Date.now();
|
|
134
|
+
return false;
|
|
135
|
+
}
|
|
96
136
|
if (!response.ok) throw new Error(`Pulse config HTTP ${response.status}`);
|
|
97
137
|
const payload = await response.json();
|
|
98
138
|
const nextValues = payload?.values;
|
|
@@ -103,11 +143,16 @@ export async function fetchConfig() {
|
|
|
103
143
|
}
|
|
104
144
|
etag = response.headers?.get?.('etag') ?? null;
|
|
105
145
|
fetchedAt = Date.now();
|
|
146
|
+
verifiedAt = fetchedAt;
|
|
147
|
+
const snapshot = {
|
|
148
|
+
values: nextValues,
|
|
149
|
+
experiments: parseExperiments(payload?.experiments)
|
|
150
|
+
};
|
|
106
151
|
if (opts.activateOnFetch === false) {
|
|
107
|
-
pending =
|
|
152
|
+
pending = snapshot;
|
|
108
153
|
return false;
|
|
109
154
|
}
|
|
110
|
-
return
|
|
155
|
+
return applySnapshot(snapshot, opts);
|
|
111
156
|
} catch (error) {
|
|
112
157
|
// A failed refresh must never clear what we have: the whole point of the cache
|
|
113
158
|
// is that an unreachable server degrades to the last good config.
|
|
@@ -129,7 +174,7 @@ export function activateConfig() {
|
|
|
129
174
|
if (!pending || !options) return false;
|
|
130
175
|
const next = pending;
|
|
131
176
|
pending = null;
|
|
132
|
-
return
|
|
177
|
+
return applySnapshot(next, options);
|
|
133
178
|
}
|
|
134
179
|
|
|
135
180
|
/** Whether a fetched-but-not-yet-applied payload is waiting. */
|
|
@@ -156,22 +201,92 @@ export function startConfigAutoRefresh(appState) {
|
|
|
156
201
|
stopPolling();
|
|
157
202
|
};
|
|
158
203
|
}
|
|
159
|
-
function
|
|
160
|
-
const changed = !shallowEqual(values, next);
|
|
161
|
-
values = next;
|
|
204
|
+
function applySnapshot(next, opts) {
|
|
205
|
+
const changed = !shallowEqual(values, next.values) || !sameExperiments(experiments, next.experiments);
|
|
206
|
+
values = next.values;
|
|
207
|
+
experiments = next.experiments;
|
|
162
208
|
source = 'remote';
|
|
163
209
|
writeCache(opts.storage, {
|
|
164
210
|
values,
|
|
211
|
+
experiments,
|
|
165
212
|
etag,
|
|
166
213
|
fetchedAt
|
|
167
214
|
});
|
|
168
215
|
|
|
216
|
+
// Reported on apply, never on fetch: with activateOnFetch false a payload can sit
|
|
217
|
+
// unapplied for the rest of a session, and an arm the app is not actually serving
|
|
218
|
+
// is not an exposure.
|
|
219
|
+
void reportExposure(opts);
|
|
220
|
+
|
|
169
221
|
// Listeners drive re-renders and gate re-evaluation: firing them for an identical
|
|
170
222
|
// payload is pure churn, and the 200-with-same-content case is common enough
|
|
171
223
|
// (a proxy that drops the ETag) to be worth checking.
|
|
172
224
|
if (changed) notify();
|
|
173
225
|
return changed;
|
|
174
226
|
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Tell the server which arms this install ended up in.
|
|
230
|
+
*
|
|
231
|
+
* The server already knows how it would assign a given device, so this is not how
|
|
232
|
+
* the experiment is measured — it is how a lopsided split gets noticed. A hash over
|
|
233
|
+
* a real population is only asymptotically fair, installs without a device id sit
|
|
234
|
+
* outside every experiment, and an audience filters unevenly across platforms; all
|
|
235
|
+
* three show up here as arms that are not the size they should be.
|
|
236
|
+
*
|
|
237
|
+
* Sent once per distinct set of arms per session rather than on every poll. The
|
|
238
|
+
* server counts one row per install per session, so a five-minute poll would
|
|
239
|
+
* multiply every arm by the length of the session and make a long user look like a
|
|
240
|
+
* crowd.
|
|
241
|
+
*
|
|
242
|
+
* Failure is swallowed on purpose. This is a diagnostic, and no diagnostic is worth
|
|
243
|
+
* a rejected promise on the path that delivers config to the app.
|
|
244
|
+
*/
|
|
245
|
+
async function reportExposure(opts) {
|
|
246
|
+
if (opts.reportExposure !== true) return;
|
|
247
|
+
if (experiments.length === 0) return;
|
|
248
|
+
const signature = experiments.map(e => `${e.key}=${e.variant}`).join(',');
|
|
249
|
+
if (signature === reportedExposure) return;
|
|
250
|
+
reportedExposure = signature;
|
|
251
|
+
const context = opts.getContext?.() ?? {};
|
|
252
|
+
const assignments = {};
|
|
253
|
+
for (const entry of experiments) assignments[entry.key] = entry.variant;
|
|
254
|
+
try {
|
|
255
|
+
await fetch(`${opts.url.replace(/\/+$/, '')}/exposure`, {
|
|
256
|
+
method: 'POST',
|
|
257
|
+
headers: {
|
|
258
|
+
'Content-Type': 'application/json'
|
|
259
|
+
},
|
|
260
|
+
body: JSON.stringify({
|
|
261
|
+
platform: context.platform,
|
|
262
|
+
appVersion: context.appVersion,
|
|
263
|
+
assignments
|
|
264
|
+
})
|
|
265
|
+
});
|
|
266
|
+
} catch {
|
|
267
|
+
// A device that cannot report its arm still has the right arm.
|
|
268
|
+
reportedExposure = signature;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* The arms as the server sent them, keeping only entries that name both halves.
|
|
274
|
+
*
|
|
275
|
+
* A half-formed assignment is dropped rather than carried: an experiment key with
|
|
276
|
+
* no variant would tag every event with an empty cohort, which reads downstream as
|
|
277
|
+
* a third group that does not exist.
|
|
278
|
+
*/
|
|
279
|
+
function parseExperiments(raw) {
|
|
280
|
+
if (!Array.isArray(raw)) return [];
|
|
281
|
+
return raw.filter(entry => !!entry && typeof entry === 'object' && typeof entry.key === 'string' && typeof entry.variant === 'string' && entry.key !== '' && entry.variant !== '').map(entry => ({
|
|
282
|
+
key: entry.key,
|
|
283
|
+
variant: entry.variant
|
|
284
|
+
}));
|
|
285
|
+
}
|
|
286
|
+
function sameExperiments(a, b) {
|
|
287
|
+
if (a.length !== b.length) return false;
|
|
288
|
+
return a.every((entry, i) => entry.key === b[i].key && entry.variant === b[i].variant);
|
|
289
|
+
}
|
|
175
290
|
function shallowEqual(a, b) {
|
|
176
291
|
const aKeys = Object.keys(a);
|
|
177
292
|
const bKeys = Object.keys(b);
|
|
@@ -255,6 +370,24 @@ export function getConfigSource(key) {
|
|
|
255
370
|
if (Object.prototype.hasOwnProperty.call(defaults, key)) return 'default';
|
|
256
371
|
return 'missing';
|
|
257
372
|
}
|
|
373
|
+
|
|
374
|
+
/**
|
|
375
|
+
* The A/B arms this install is in, for tagging analytics events.
|
|
376
|
+
*
|
|
377
|
+
* Empty when nothing is running — which is also what an install with no device id
|
|
378
|
+
* gets, since an install that draws a fresh arm on every request would take both
|
|
379
|
+
* sides in turn and pollute both.
|
|
380
|
+
*/
|
|
381
|
+
export function getConfigExperiments() {
|
|
382
|
+
return experiments.map(entry => ({
|
|
383
|
+
...entry
|
|
384
|
+
}));
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
/** The arm for one experiment, or null when this install is not in it. */
|
|
388
|
+
export function getConfigVariant(experimentKey) {
|
|
389
|
+
return experiments.find(entry => entry.key === experimentKey)?.variant ?? null;
|
|
390
|
+
}
|
|
258
391
|
export function getConfigKeys() {
|
|
259
392
|
return Object.keys(getAllConfig()).sort();
|
|
260
393
|
}
|
|
@@ -268,6 +401,7 @@ export function getConfigInfo() {
|
|
|
268
401
|
return {
|
|
269
402
|
source,
|
|
270
403
|
fetchedAt,
|
|
404
|
+
verifiedAt,
|
|
271
405
|
etag,
|
|
272
406
|
keyCount: Object.keys(getAllConfig()).length
|
|
273
407
|
};
|
|
@@ -277,12 +411,15 @@ export function getConfigInfo() {
|
|
|
277
411
|
export function resetConfigForTests() {
|
|
278
412
|
options = null;
|
|
279
413
|
defaults = {};
|
|
414
|
+
verifiedAt = 0;
|
|
280
415
|
values = {};
|
|
416
|
+
experiments = [];
|
|
281
417
|
etag = null;
|
|
282
418
|
fetchedAt = 0;
|
|
283
419
|
source = 'defaults';
|
|
284
420
|
inFlight = null;
|
|
285
421
|
pending = null;
|
|
422
|
+
reportedExposure = null;
|
|
286
423
|
listeners.clear();
|
|
287
424
|
stopPolling();
|
|
288
425
|
}
|
|
@@ -299,6 +436,7 @@ function buildHeaders(opts, currentEtag) {
|
|
|
299
436
|
if (ctx.osVersion) headers['X-Pulse-Os-Version'] = ctx.osVersion;
|
|
300
437
|
if (ctx.language) headers['X-Pulse-Language'] = ctx.language;
|
|
301
438
|
if (ctx.deviceId) headers['Pulse-Device-Id'] = ctx.deviceId;
|
|
439
|
+
if (ctx.userId) headers['Pulse-User-Id'] = ctx.userId;
|
|
302
440
|
if (ctx.userAttributes && Object.keys(ctx.userAttributes).length > 0) {
|
|
303
441
|
headers['X-Pulse-User-Attributes'] = JSON.stringify(ctx.userAttributes);
|
|
304
442
|
}
|
package/lib/module/config.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["STORAGE_KEY","DEFAULT_POLL_MS","DEFAULT_TIMEOUT_MS","options","defaults","values","etag","fetchedAt","source","pending","inFlight","pollTimer","listeners","Set","configureConfig","opts","cached","readCache","storage","setConfigDefaults","more","fetchConfig","minInterval","minimumFetchIntervalMs","Date","now","controller","AbortController","timer","setTimeout","abort","timeoutMs","response","fetch","url","method","headers","buildHeaders","signal","status","ok","Error","payload","json","nextValues","Array","isArray","get","activateOnFetch","applyValues","error","onError","clearTimeout","activateConfig","next","hasPendingConfig","startConfigAutoRefresh","appState","subscription","addEventListener","state","interval","pollIntervalMs","stopPolling","setInterval","remove","changed","shallowEqual","writeCache","notify","a","b","aKeys","Object","keys","bKeys","length","every","k","av","bv","JSON","stringify","clearInterval","getConfigValue","key","prototype","hasOwnProperty","call","fallback","undefined","getConfigBoolean","value","toLowerCase","getConfigNumber","parsed","Number","isNaN","getConfigString","String","getAllConfig","getConfigJson","parse","getConfigSource","getConfigKeys","sort","onConfigChange","listener","add","delete","getConfigInfo","keyCount","resetConfigForTests","clear","currentEtag","ctx","getContext","Accept","platform","appVersion","osVersion","language","deviceId","userAttributes","raw","getString","set","snapshot","forEach"],"sourceRoot":"../../src","sources":["config.ts"],"mappings":"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","ignoreList":[]}
|
|
1
|
+
{"version":3,"names":["STORAGE_KEY","DEFAULT_POLL_MS","DEFAULT_TIMEOUT_MS","options","defaults","values","experiments","etag","fetchedAt","verifiedAt","source","pending","reportedExposure","inFlight","pollTimer","listeners","Set","configureConfig","opts","cached","readCache","storage","setConfigDefaults","more","fetchConfig","minInterval","minimumFetchIntervalMs","Date","now","controller","AbortController","timer","setTimeout","abort","timeoutMs","response","fetch","url","method","headers","buildHeaders","signal","status","ok","Error","payload","json","nextValues","Array","isArray","get","snapshot","parseExperiments","activateOnFetch","applySnapshot","error","onError","clearTimeout","activateConfig","next","hasPendingConfig","startConfigAutoRefresh","appState","subscription","addEventListener","state","interval","pollIntervalMs","stopPolling","setInterval","remove","changed","shallowEqual","sameExperiments","writeCache","reportExposure","notify","length","signature","map","e","key","variant","join","context","getContext","assignments","entry","replace","body","JSON","stringify","platform","appVersion","raw","filter","a","b","every","i","aKeys","Object","keys","bKeys","k","av","bv","clearInterval","getConfigValue","prototype","hasOwnProperty","call","fallback","undefined","getConfigBoolean","value","toLowerCase","getConfigNumber","parsed","Number","isNaN","getConfigString","String","getAllConfig","getConfigJson","parse","getConfigSource","getConfigExperiments","getConfigVariant","experimentKey","find","getConfigKeys","sort","onConfigChange","listener","add","delete","getConfigInfo","keyCount","resetConfigForTests","clear","currentEtag","ctx","Accept","osVersion","language","deviceId","userId","userAttributes","getString","set","forEach"],"sourceRoot":"../../src","sources":["config.ts"],"mappings":"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","ignoreList":[]}
|
|
@@ -20,6 +20,18 @@
|
|
|
20
20
|
* config costs a 304 with no body.
|
|
21
21
|
*/
|
|
22
22
|
export type ConfigValue = boolean | number | string | null | object;
|
|
23
|
+
/**
|
|
24
|
+
* An A/B arm this install was assigned, as the server resolved it.
|
|
25
|
+
*
|
|
26
|
+
* Carried separately from the values even though the arm has already been folded
|
|
27
|
+
* into them: a control arm and an install outside the experiment are served exactly
|
|
28
|
+
* the same config, so the values cannot say which is which. Without the name here,
|
|
29
|
+
* every analytics event would be missing the one dimension the experiment is for.
|
|
30
|
+
*/
|
|
31
|
+
export interface ConfigExperiment {
|
|
32
|
+
key: string;
|
|
33
|
+
variant: string;
|
|
34
|
+
}
|
|
23
35
|
export interface ConfigStorage {
|
|
24
36
|
getString(key: string): string | null | undefined;
|
|
25
37
|
set(key: string, value: string): void;
|
|
@@ -35,6 +47,15 @@ export interface ConfigContext {
|
|
|
35
47
|
osVersion?: string;
|
|
36
48
|
language?: string;
|
|
37
49
|
deviceId?: string;
|
|
50
|
+
/**
|
|
51
|
+
* The account this install is signed into, when it is signed into one.
|
|
52
|
+
*
|
|
53
|
+
* Only an experiment configured to assign on the user reads it, and then only to
|
|
54
|
+
* decide the arm. Sending it keeps one account in one arm across its devices,
|
|
55
|
+
* which is what a metric owned per account — revenue above all — has to be
|
|
56
|
+
* joined on. Leave it unset and every experiment stays device-level.
|
|
57
|
+
*/
|
|
58
|
+
userId?: string;
|
|
38
59
|
/** Free-form user attributes a rule can compare (registration date, plan, counters). */
|
|
39
60
|
userAttributes?: Record<string, string>;
|
|
40
61
|
}
|
|
@@ -63,6 +84,16 @@ export interface ConfigOptions {
|
|
|
63
84
|
* minutes stale. Default true, matching fetchAndActivate.
|
|
64
85
|
*/
|
|
65
86
|
activateOnFetch?: boolean;
|
|
87
|
+
/**
|
|
88
|
+
* Report the arm this install landed in. Opt-in: default false.
|
|
89
|
+
*
|
|
90
|
+
* Off by default because the two failure modes are not equally bad. An app that
|
|
91
|
+
* already posts its own exposure — Lyra does — would double every count if this
|
|
92
|
+
* defaulted on, and a doubled count is wrong in the way that matters: it looks
|
|
93
|
+
* like data. A missing count looks like a missing count. So the switch defaults to
|
|
94
|
+
* the mistake that announces itself.
|
|
95
|
+
*/
|
|
96
|
+
reportExposure?: boolean;
|
|
66
97
|
/** Network timeout per request. */
|
|
67
98
|
timeoutMs?: number;
|
|
68
99
|
onError?: (error: unknown) => void;
|
|
@@ -110,12 +141,24 @@ export declare function getAllConfig(): Record<string, ConfigValue>;
|
|
|
110
141
|
export declare function getConfigJson<T = unknown>(key: string): T | null;
|
|
111
142
|
/** Where this key's value came from — the answer to "why is this flag off?". */
|
|
112
143
|
export declare function getConfigSource(key: string): 'remote' | 'default' | 'missing';
|
|
144
|
+
/**
|
|
145
|
+
* The A/B arms this install is in, for tagging analytics events.
|
|
146
|
+
*
|
|
147
|
+
* Empty when nothing is running — which is also what an install with no device id
|
|
148
|
+
* gets, since an install that draws a fresh arm on every request would take both
|
|
149
|
+
* sides in turn and pollute both.
|
|
150
|
+
*/
|
|
151
|
+
export declare function getConfigExperiments(): ConfigExperiment[];
|
|
152
|
+
/** The arm for one experiment, or null when this install is not in it. */
|
|
153
|
+
export declare function getConfigVariant(experimentKey: string): string | null;
|
|
113
154
|
export declare function getConfigKeys(): string[];
|
|
114
155
|
export declare function onConfigChange(listener: (values: Record<string, ConfigValue>) => void): () => void;
|
|
115
156
|
/** For diagnostics: where the current values came from and how old they are. */
|
|
116
157
|
export declare function getConfigInfo(): {
|
|
117
158
|
source: 'defaults' | 'cache' | 'remote';
|
|
118
159
|
fetchedAt: number;
|
|
160
|
+
/** 0 when the server has not answered since this process started. */
|
|
161
|
+
verifiedAt: number;
|
|
119
162
|
etag: string | null;
|
|
120
163
|
keyCount: number;
|
|
121
164
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,MAAM,MAAM,WAAW,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,MAAM,CAAC;AAEpE,MAAM,WAAW,aAAa;IAC5B,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IAClD,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACvC;AAED;;;;GAIG;AACH,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,wFAAwF;IACxF,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACzC;AAED,MAAM,WAAW,aAAa;IAC5B,kFAAkF;IAClF,GAAG,EAAE,MAAM,CAAC;IACZ,8EAA8E;IAC9E,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IACvC,kFAAkF;IAClF,OAAO,CAAC,EAAE,aAAa,CAAC;IACxB,oFAAoF;IACpF,UAAU,CAAC,EAAE,MAAM,aAAa,CAAC;IACjC,kFAAkF;IAClF,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;;OAIG;IACH,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC;;;;;;OAMG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,mCAAmC;IACnC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;CACpC;
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/config.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pulse-updates",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.21",
|
|
4
4
|
"description": "OTA updates for React Native - lightweight alternative to expo-updates",
|
|
5
5
|
"main": "lib/commonjs/index.js",
|
|
6
6
|
"module": "lib/module/index.js",
|
|
@@ -60,7 +60,12 @@
|
|
|
60
60
|
"targets": [
|
|
61
61
|
"commonjs",
|
|
62
62
|
"module",
|
|
63
|
-
[
|
|
63
|
+
[
|
|
64
|
+
"typescript",
|
|
65
|
+
{
|
|
66
|
+
"project": "tsconfig.build.json"
|
|
67
|
+
}
|
|
68
|
+
]
|
|
64
69
|
]
|
|
65
70
|
}
|
|
66
|
-
}
|
|
71
|
+
}
|
package/src/config.ts
CHANGED
|
@@ -22,6 +22,19 @@
|
|
|
22
22
|
|
|
23
23
|
export type ConfigValue = boolean | number | string | null | object;
|
|
24
24
|
|
|
25
|
+
/**
|
|
26
|
+
* An A/B arm this install was assigned, as the server resolved it.
|
|
27
|
+
*
|
|
28
|
+
* Carried separately from the values even though the arm has already been folded
|
|
29
|
+
* into them: a control arm and an install outside the experiment are served exactly
|
|
30
|
+
* the same config, so the values cannot say which is which. Without the name here,
|
|
31
|
+
* every analytics event would be missing the one dimension the experiment is for.
|
|
32
|
+
*/
|
|
33
|
+
export interface ConfigExperiment {
|
|
34
|
+
key: string;
|
|
35
|
+
variant: string;
|
|
36
|
+
}
|
|
37
|
+
|
|
25
38
|
export interface ConfigStorage {
|
|
26
39
|
getString(key: string): string | null | undefined;
|
|
27
40
|
set(key: string, value: string): void;
|
|
@@ -38,6 +51,15 @@ export interface ConfigContext {
|
|
|
38
51
|
osVersion?: string;
|
|
39
52
|
language?: string;
|
|
40
53
|
deviceId?: string;
|
|
54
|
+
/**
|
|
55
|
+
* The account this install is signed into, when it is signed into one.
|
|
56
|
+
*
|
|
57
|
+
* Only an experiment configured to assign on the user reads it, and then only to
|
|
58
|
+
* decide the arm. Sending it keeps one account in one arm across its devices,
|
|
59
|
+
* which is what a metric owned per account — revenue above all — has to be
|
|
60
|
+
* joined on. Leave it unset and every experiment stays device-level.
|
|
61
|
+
*/
|
|
62
|
+
userId?: string;
|
|
41
63
|
/** Free-form user attributes a rule can compare (registration date, plan, counters). */
|
|
42
64
|
userAttributes?: Record<string, string>;
|
|
43
65
|
}
|
|
@@ -67,6 +89,16 @@ export interface ConfigOptions {
|
|
|
67
89
|
* minutes stale. Default true, matching fetchAndActivate.
|
|
68
90
|
*/
|
|
69
91
|
activateOnFetch?: boolean;
|
|
92
|
+
/**
|
|
93
|
+
* Report the arm this install landed in. Opt-in: default false.
|
|
94
|
+
*
|
|
95
|
+
* Off by default because the two failure modes are not equally bad. An app that
|
|
96
|
+
* already posts its own exposure — Lyra does — would double every count if this
|
|
97
|
+
* defaulted on, and a doubled count is wrong in the way that matters: it looks
|
|
98
|
+
* like data. A missing count looks like a missing count. So the switch defaults to
|
|
99
|
+
* the mistake that announces itself.
|
|
100
|
+
*/
|
|
101
|
+
reportExposure?: boolean;
|
|
70
102
|
/** Network timeout per request. */
|
|
71
103
|
timeoutMs?: number;
|
|
72
104
|
onError?: (error: unknown) => void;
|
|
@@ -74,10 +106,26 @@ export interface ConfigOptions {
|
|
|
74
106
|
|
|
75
107
|
interface CachedPayload {
|
|
76
108
|
values: Record<string, ConfigValue>;
|
|
109
|
+
/** Absent in caches written before experiments existed; read as "none". */
|
|
110
|
+
experiments?: ConfigExperiment[];
|
|
77
111
|
etag: string | null;
|
|
78
112
|
fetchedAt: number;
|
|
79
113
|
}
|
|
80
114
|
|
|
115
|
+
/**
|
|
116
|
+
* The values and the arms move as one unit, always.
|
|
117
|
+
*
|
|
118
|
+
* They are two halves of a single answer: the arm is what produced those values.
|
|
119
|
+
* Applying one without the other — on activate, on a cache read, on a partial
|
|
120
|
+
* payload — would tag events with an arm whose values are not in force, and the
|
|
121
|
+
* resulting experiment would report a difference between two groups that were both
|
|
122
|
+
* served the same thing.
|
|
123
|
+
*/
|
|
124
|
+
interface ConfigSnapshot {
|
|
125
|
+
values: Record<string, ConfigValue>;
|
|
126
|
+
experiments: ConfigExperiment[];
|
|
127
|
+
}
|
|
128
|
+
|
|
81
129
|
const STORAGE_KEY = 'pulse.config.v1';
|
|
82
130
|
const DEFAULT_POLL_MS = 5 * 60 * 1000;
|
|
83
131
|
const DEFAULT_TIMEOUT_MS = 10_000;
|
|
@@ -85,10 +133,24 @@ const DEFAULT_TIMEOUT_MS = 10_000;
|
|
|
85
133
|
let options: ConfigOptions | null = null;
|
|
86
134
|
let defaults: Record<string, ConfigValue> = {};
|
|
87
135
|
let values: Record<string, ConfigValue> = {};
|
|
136
|
+
let experiments: ConfigExperiment[] = [];
|
|
88
137
|
let etag: string | null = null;
|
|
89
138
|
let fetchedAt = 0;
|
|
139
|
+
/**
|
|
140
|
+
* When the server last confirmed these values are current — a 200 that applied a
|
|
141
|
+
* payload, or a 304 that said the one we hold is still right.
|
|
142
|
+
*
|
|
143
|
+
* Distinct from fetchedAt, which is when the values were last *changed*. A caller
|
|
144
|
+
* comparing this config against another source needs to know the difference: values
|
|
145
|
+
* restored from disk and never revalidated may be days behind what the server would
|
|
146
|
+
* answer now, and reporting them as "what Pulse says" turns a stale cache into a
|
|
147
|
+
* disagreement that nobody can act on.
|
|
148
|
+
*/
|
|
149
|
+
let verifiedAt = 0;
|
|
90
150
|
let source: 'defaults' | 'cache' | 'remote' = 'defaults';
|
|
91
|
-
let pending:
|
|
151
|
+
let pending: ConfigSnapshot | null = null;
|
|
152
|
+
/** The arm set already reported this session, so a poll does not re-report it. */
|
|
153
|
+
let reportedExposure: string | null = null;
|
|
92
154
|
let inFlight: Promise<boolean> | null = null;
|
|
93
155
|
let pollTimer: ReturnType<typeof setInterval> | null = null;
|
|
94
156
|
const listeners = new Set<(values: Record<string, ConfigValue>) => void>();
|
|
@@ -105,6 +167,7 @@ export function configureConfig(opts: ConfigOptions): void {
|
|
|
105
167
|
const cached = readCache(opts.storage);
|
|
106
168
|
if (cached) {
|
|
107
169
|
values = cached.values;
|
|
170
|
+
experiments = cached.experiments ?? [];
|
|
108
171
|
etag = cached.etag;
|
|
109
172
|
fetchedAt = cached.fetchedAt;
|
|
110
173
|
source = 'cache';
|
|
@@ -147,7 +210,12 @@ export async function fetchConfig(): Promise<boolean> {
|
|
|
147
210
|
signal: controller?.signal,
|
|
148
211
|
});
|
|
149
212
|
|
|
150
|
-
|
|
213
|
+
// Nothing changed — which is a positive answer about freshness, not a
|
|
214
|
+
// non-event: the values we hold are the ones the server would send.
|
|
215
|
+
if (response.status === 304) {
|
|
216
|
+
verifiedAt = Date.now();
|
|
217
|
+
return false;
|
|
218
|
+
}
|
|
151
219
|
if (!response.ok) throw new Error(`Pulse config HTTP ${response.status}`);
|
|
152
220
|
|
|
153
221
|
const payload = await response.json();
|
|
@@ -160,13 +228,19 @@ export async function fetchConfig(): Promise<boolean> {
|
|
|
160
228
|
|
|
161
229
|
etag = response.headers?.get?.('etag') ?? null;
|
|
162
230
|
fetchedAt = Date.now();
|
|
231
|
+
verifiedAt = fetchedAt;
|
|
232
|
+
|
|
233
|
+
const snapshot: ConfigSnapshot = {
|
|
234
|
+
values: nextValues as Record<string, ConfigValue>,
|
|
235
|
+
experiments: parseExperiments(payload?.experiments),
|
|
236
|
+
};
|
|
163
237
|
|
|
164
238
|
if (opts.activateOnFetch === false) {
|
|
165
|
-
pending =
|
|
239
|
+
pending = snapshot;
|
|
166
240
|
return false;
|
|
167
241
|
}
|
|
168
242
|
|
|
169
|
-
return
|
|
243
|
+
return applySnapshot(snapshot, opts);
|
|
170
244
|
} catch (error) {
|
|
171
245
|
// A failed refresh must never clear what we have: the whole point of the cache
|
|
172
246
|
// is that an unreachable server degrades to the last good config.
|
|
@@ -189,7 +263,7 @@ export function activateConfig(): boolean {
|
|
|
189
263
|
if (!pending || !options) return false;
|
|
190
264
|
const next = pending;
|
|
191
265
|
pending = null;
|
|
192
|
-
return
|
|
266
|
+
return applySnapshot(next, options);
|
|
193
267
|
}
|
|
194
268
|
|
|
195
269
|
/** Whether a fetched-but-not-yet-applied payload is waiting. */
|
|
@@ -222,12 +296,18 @@ export function startConfigAutoRefresh(appState?: {
|
|
|
222
296
|
};
|
|
223
297
|
}
|
|
224
298
|
|
|
225
|
-
function
|
|
226
|
-
const changed = !shallowEqual(values, next);
|
|
299
|
+
function applySnapshot(next: ConfigSnapshot, opts: ConfigOptions): boolean {
|
|
300
|
+
const changed = !shallowEqual(values, next.values) || !sameExperiments(experiments, next.experiments);
|
|
227
301
|
|
|
228
|
-
values = next;
|
|
302
|
+
values = next.values;
|
|
303
|
+
experiments = next.experiments;
|
|
229
304
|
source = 'remote';
|
|
230
|
-
writeCache(opts.storage, { values, etag, fetchedAt });
|
|
305
|
+
writeCache(opts.storage, { values, experiments, etag, fetchedAt });
|
|
306
|
+
|
|
307
|
+
// Reported on apply, never on fetch: with activateOnFetch false a payload can sit
|
|
308
|
+
// unapplied for the rest of a session, and an arm the app is not actually serving
|
|
309
|
+
// is not an exposure.
|
|
310
|
+
void reportExposure(opts);
|
|
231
311
|
|
|
232
312
|
// Listeners drive re-renders and gate re-evaluation: firing them for an identical
|
|
233
313
|
// payload is pure churn, and the 200-with-same-content case is common enough
|
|
@@ -236,6 +316,76 @@ function applyValues(next: Record<string, ConfigValue>, opts: ConfigOptions): bo
|
|
|
236
316
|
return changed;
|
|
237
317
|
}
|
|
238
318
|
|
|
319
|
+
/**
|
|
320
|
+
* Tell the server which arms this install ended up in.
|
|
321
|
+
*
|
|
322
|
+
* The server already knows how it would assign a given device, so this is not how
|
|
323
|
+
* the experiment is measured — it is how a lopsided split gets noticed. A hash over
|
|
324
|
+
* a real population is only asymptotically fair, installs without a device id sit
|
|
325
|
+
* outside every experiment, and an audience filters unevenly across platforms; all
|
|
326
|
+
* three show up here as arms that are not the size they should be.
|
|
327
|
+
*
|
|
328
|
+
* Sent once per distinct set of arms per session rather than on every poll. The
|
|
329
|
+
* server counts one row per install per session, so a five-minute poll would
|
|
330
|
+
* multiply every arm by the length of the session and make a long user look like a
|
|
331
|
+
* crowd.
|
|
332
|
+
*
|
|
333
|
+
* Failure is swallowed on purpose. This is a diagnostic, and no diagnostic is worth
|
|
334
|
+
* a rejected promise on the path that delivers config to the app.
|
|
335
|
+
*/
|
|
336
|
+
async function reportExposure(opts: ConfigOptions): Promise<void> {
|
|
337
|
+
if (opts.reportExposure !== true) return;
|
|
338
|
+
if (experiments.length === 0) return;
|
|
339
|
+
|
|
340
|
+
const signature = experiments.map((e) => `${e.key}=${e.variant}`).join(',');
|
|
341
|
+
if (signature === reportedExposure) return;
|
|
342
|
+
reportedExposure = signature;
|
|
343
|
+
|
|
344
|
+
const context = opts.getContext?.() ?? {};
|
|
345
|
+
const assignments: Record<string, string> = {};
|
|
346
|
+
for (const entry of experiments) assignments[entry.key] = entry.variant;
|
|
347
|
+
|
|
348
|
+
try {
|
|
349
|
+
await fetch(`${opts.url.replace(/\/+$/, '')}/exposure`, {
|
|
350
|
+
method: 'POST',
|
|
351
|
+
headers: { 'Content-Type': 'application/json' },
|
|
352
|
+
body: JSON.stringify({
|
|
353
|
+
platform: context.platform,
|
|
354
|
+
appVersion: context.appVersion,
|
|
355
|
+
assignments,
|
|
356
|
+
}),
|
|
357
|
+
});
|
|
358
|
+
} catch {
|
|
359
|
+
// A device that cannot report its arm still has the right arm.
|
|
360
|
+
reportedExposure = signature;
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
/**
|
|
365
|
+
* The arms as the server sent them, keeping only entries that name both halves.
|
|
366
|
+
*
|
|
367
|
+
* A half-formed assignment is dropped rather than carried: an experiment key with
|
|
368
|
+
* no variant would tag every event with an empty cohort, which reads downstream as
|
|
369
|
+
* a third group that does not exist.
|
|
370
|
+
*/
|
|
371
|
+
function parseExperiments(raw: unknown): ConfigExperiment[] {
|
|
372
|
+
if (!Array.isArray(raw)) return [];
|
|
373
|
+
return raw
|
|
374
|
+
.filter((entry): entry is ConfigExperiment =>
|
|
375
|
+
!!entry &&
|
|
376
|
+
typeof entry === 'object' &&
|
|
377
|
+
typeof (entry as ConfigExperiment).key === 'string' &&
|
|
378
|
+
typeof (entry as ConfigExperiment).variant === 'string' &&
|
|
379
|
+
(entry as ConfigExperiment).key !== '' &&
|
|
380
|
+
(entry as ConfigExperiment).variant !== '')
|
|
381
|
+
.map((entry) => ({ key: entry.key, variant: entry.variant }));
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
function sameExperiments(a: ConfigExperiment[], b: ConfigExperiment[]): boolean {
|
|
385
|
+
if (a.length !== b.length) return false;
|
|
386
|
+
return a.every((entry, i) => entry.key === b[i]!.key && entry.variant === b[i]!.variant);
|
|
387
|
+
}
|
|
388
|
+
|
|
239
389
|
function shallowEqual(a: Record<string, ConfigValue>, b: Record<string, ConfigValue>): boolean {
|
|
240
390
|
const aKeys = Object.keys(a);
|
|
241
391
|
const bKeys = Object.keys(b);
|
|
@@ -321,6 +471,22 @@ export function getConfigSource(key: string): 'remote' | 'default' | 'missing' {
|
|
|
321
471
|
return 'missing';
|
|
322
472
|
}
|
|
323
473
|
|
|
474
|
+
/**
|
|
475
|
+
* The A/B arms this install is in, for tagging analytics events.
|
|
476
|
+
*
|
|
477
|
+
* Empty when nothing is running — which is also what an install with no device id
|
|
478
|
+
* gets, since an install that draws a fresh arm on every request would take both
|
|
479
|
+
* sides in turn and pollute both.
|
|
480
|
+
*/
|
|
481
|
+
export function getConfigExperiments(): ConfigExperiment[] {
|
|
482
|
+
return experiments.map((entry) => ({ ...entry }));
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
/** The arm for one experiment, or null when this install is not in it. */
|
|
486
|
+
export function getConfigVariant(experimentKey: string): string | null {
|
|
487
|
+
return experiments.find((entry) => entry.key === experimentKey)?.variant ?? null;
|
|
488
|
+
}
|
|
489
|
+
|
|
324
490
|
export function getConfigKeys(): string[] {
|
|
325
491
|
return Object.keys(getAllConfig()).sort();
|
|
326
492
|
}
|
|
@@ -334,22 +500,27 @@ export function onConfigChange(listener: (values: Record<string, ConfigValue>) =
|
|
|
334
500
|
export function getConfigInfo(): {
|
|
335
501
|
source: 'defaults' | 'cache' | 'remote';
|
|
336
502
|
fetchedAt: number;
|
|
503
|
+
/** 0 when the server has not answered since this process started. */
|
|
504
|
+
verifiedAt: number;
|
|
337
505
|
etag: string | null;
|
|
338
506
|
keyCount: number;
|
|
339
507
|
} {
|
|
340
|
-
return { source, fetchedAt, etag, keyCount: Object.keys(getAllConfig()).length };
|
|
508
|
+
return { source, fetchedAt, verifiedAt, etag, keyCount: Object.keys(getAllConfig()).length };
|
|
341
509
|
}
|
|
342
510
|
|
|
343
511
|
/** Test seam: drops every piece of module state. */
|
|
344
512
|
export function resetConfigForTests(): void {
|
|
345
513
|
options = null;
|
|
346
514
|
defaults = {};
|
|
515
|
+
verifiedAt = 0;
|
|
347
516
|
values = {};
|
|
517
|
+
experiments = [];
|
|
348
518
|
etag = null;
|
|
349
519
|
fetchedAt = 0;
|
|
350
520
|
source = 'defaults';
|
|
351
521
|
inFlight = null;
|
|
352
522
|
pending = null;
|
|
523
|
+
reportedExposure = null;
|
|
353
524
|
listeners.clear();
|
|
354
525
|
stopPolling();
|
|
355
526
|
}
|
|
@@ -365,6 +536,7 @@ function buildHeaders(opts: ConfigOptions, currentEtag: string | null): Record<s
|
|
|
365
536
|
if (ctx.osVersion) headers['X-Pulse-Os-Version'] = ctx.osVersion;
|
|
366
537
|
if (ctx.language) headers['X-Pulse-Language'] = ctx.language;
|
|
367
538
|
if (ctx.deviceId) headers['Pulse-Device-Id'] = ctx.deviceId;
|
|
539
|
+
if (ctx.userId) headers['Pulse-User-Id'] = ctx.userId;
|
|
368
540
|
if (ctx.userAttributes && Object.keys(ctx.userAttributes).length > 0) {
|
|
369
541
|
headers['X-Pulse-User-Attributes'] = JSON.stringify(ctx.userAttributes);
|
|
370
542
|
}
|