pulse-updates 1.0.14 → 1.0.16
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/lib/commonjs/config.js +364 -0
- package/lib/commonjs/config.js.map +1 -0
- package/lib/commonjs/index.js +12 -0
- package/lib/commonjs/index.js.map +1 -1
- package/lib/module/config.js +342 -0
- package/lib/module/config.js.map +1 -0
- package/lib/module/index.js +1 -0
- package/lib/module/index.js.map +1 -1
- package/lib/typescript/config.d.ts +124 -0
- package/lib/typescript/config.d.ts.map +1 -0
- package/lib/typescript/index.d.ts +1 -0
- package/lib/typescript/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/config.ts +410 -0
- package/src/index.ts +1 -0
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pulse Config — the client half of the Firebase Remote Config replacement.
|
|
3
|
+
*
|
|
4
|
+
* Why this exists: from 2026-09-01 Firebase bills every Remote Config fetch above
|
|
5
|
+
* 100k/day and throttles projects that stay on the free plan. Worse than the bill is
|
|
6
|
+
* the failure mode — the Firebase SDK yields the type's zero value for a key it does
|
|
7
|
+
* not hold, so a throttled fetch reads as "every flag is false" and, for an ads
|
|
8
|
+
* config, quietly turns the revenue off.
|
|
9
|
+
*
|
|
10
|
+
* The shape here deliberately mirrors what the apps already do with Remote Config —
|
|
11
|
+
* defaults, fetch, activate, typed getters — so swapping the source is a change of
|
|
12
|
+
* adapter, not of every call site. Three things are different on purpose:
|
|
13
|
+
*
|
|
14
|
+
* 1. A missing key falls back to the registered default, never to false/0. That is
|
|
15
|
+
* the exact bug this replaces.
|
|
16
|
+
* 2. The last good values are persisted and reloaded at boot, so a failed fetch
|
|
17
|
+
* degrades to yesterday's config rather than to nothing.
|
|
18
|
+
* 3. Fetching is cheap (our server, no per-request price), so it happens on launch,
|
|
19
|
+
* on every return from foreground, and on a timer — with ETag, so an unchanged
|
|
20
|
+
* config costs a 304 with no body.
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Everything the server may ever want to target on. Sent on every request even when
|
|
25
|
+
* no rule uses it yet: the client can only start sending a new field with a store
|
|
26
|
+
* build, so a contract that is incomplete today cannot be completed for months.
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
const STORAGE_KEY = 'pulse.config.v1';
|
|
30
|
+
const DEFAULT_POLL_MS = 5 * 60 * 1000;
|
|
31
|
+
const DEFAULT_TIMEOUT_MS = 10_000;
|
|
32
|
+
let options = null;
|
|
33
|
+
let defaults = {};
|
|
34
|
+
let values = {};
|
|
35
|
+
let etag = null;
|
|
36
|
+
let fetchedAt = 0;
|
|
37
|
+
let source = 'defaults';
|
|
38
|
+
let pending = null;
|
|
39
|
+
let inFlight = null;
|
|
40
|
+
let pollTimer = null;
|
|
41
|
+
const listeners = new Set();
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Registers defaults and reloads the last good payload from storage, synchronously,
|
|
45
|
+
* so the very first read at boot already has real values instead of a blank config
|
|
46
|
+
* while the network call is in flight.
|
|
47
|
+
*/
|
|
48
|
+
export function configureConfig(opts) {
|
|
49
|
+
options = opts;
|
|
50
|
+
defaults = {
|
|
51
|
+
...(opts.defaults ?? {})
|
|
52
|
+
};
|
|
53
|
+
const cached = readCache(opts.storage);
|
|
54
|
+
if (cached) {
|
|
55
|
+
values = cached.values;
|
|
56
|
+
etag = cached.etag;
|
|
57
|
+
fetchedAt = cached.fetchedAt;
|
|
58
|
+
source = 'cache';
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** Merge more defaults after configure (a late-loading module registering its own). */
|
|
63
|
+
export function setConfigDefaults(more) {
|
|
64
|
+
defaults = {
|
|
65
|
+
...defaults,
|
|
66
|
+
...more
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Fetch, and adopt the payload if it changed. Returns true when new values were
|
|
72
|
+
* applied — false for "nothing changed" (304) and false for a failure, which are
|
|
73
|
+
* deliberately indistinguishable to callers: in both cases the current values stand.
|
|
74
|
+
* Errors surface through onError instead of rejecting, because no caller should have
|
|
75
|
+
* to wrap a config refresh in a try/catch to keep the app running.
|
|
76
|
+
*/
|
|
77
|
+
export async function fetchConfig() {
|
|
78
|
+
if (!options) return false;
|
|
79
|
+
// Collapse concurrent calls (launch + resume can land together) onto one request.
|
|
80
|
+
if (inFlight) return inFlight;
|
|
81
|
+
const minInterval = options.minimumFetchIntervalMs ?? 0;
|
|
82
|
+
if (minInterval > 0 && fetchedAt > 0 && Date.now() - fetchedAt < minInterval) {
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
inFlight = (async () => {
|
|
86
|
+
const opts = options;
|
|
87
|
+
const controller = typeof AbortController !== 'undefined' ? new AbortController() : null;
|
|
88
|
+
const timer = controller ? setTimeout(() => controller.abort(), opts.timeoutMs ?? DEFAULT_TIMEOUT_MS) : null;
|
|
89
|
+
try {
|
|
90
|
+
const response = await fetch(opts.url, {
|
|
91
|
+
method: 'GET',
|
|
92
|
+
headers: buildHeaders(opts, etag),
|
|
93
|
+
signal: controller?.signal
|
|
94
|
+
});
|
|
95
|
+
if (response.status === 304) return false;
|
|
96
|
+
if (!response.ok) throw new Error(`Pulse config HTTP ${response.status}`);
|
|
97
|
+
const payload = await response.json();
|
|
98
|
+
const nextValues = payload?.values;
|
|
99
|
+
// A payload without a values object is a broken response, not "no keys": adopting
|
|
100
|
+
// it would blank every flag at once.
|
|
101
|
+
if (!nextValues || typeof nextValues !== 'object' || Array.isArray(nextValues)) {
|
|
102
|
+
throw new Error('Pulse config: malformed payload');
|
|
103
|
+
}
|
|
104
|
+
etag = response.headers?.get?.('etag') ?? null;
|
|
105
|
+
fetchedAt = Date.now();
|
|
106
|
+
if (opts.activateOnFetch === false) {
|
|
107
|
+
pending = nextValues;
|
|
108
|
+
return false;
|
|
109
|
+
}
|
|
110
|
+
return applyValues(nextValues, opts);
|
|
111
|
+
} catch (error) {
|
|
112
|
+
// A failed refresh must never clear what we have: the whole point of the cache
|
|
113
|
+
// is that an unreachable server degrades to the last good config.
|
|
114
|
+
opts.onError?.(error);
|
|
115
|
+
return false;
|
|
116
|
+
} finally {
|
|
117
|
+
if (timer) clearTimeout(timer);
|
|
118
|
+
inFlight = null;
|
|
119
|
+
}
|
|
120
|
+
})();
|
|
121
|
+
return inFlight;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Swap in the values from the last fetch made with activateOnFetch: false.
|
|
126
|
+
* Returns true when something actually changed.
|
|
127
|
+
*/
|
|
128
|
+
export function activateConfig() {
|
|
129
|
+
if (!pending || !options) return false;
|
|
130
|
+
const next = pending;
|
|
131
|
+
pending = null;
|
|
132
|
+
return applyValues(next, options);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/** Whether a fetched-but-not-yet-applied payload is waiting. */
|
|
136
|
+
export function hasPendingConfig() {
|
|
137
|
+
return pending !== null;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Fetch on launch, on every return to the foreground, and on a timer.
|
|
142
|
+
* Returns a function that stops both.
|
|
143
|
+
*/
|
|
144
|
+
export function startConfigAutoRefresh(appState) {
|
|
145
|
+
void fetchConfig();
|
|
146
|
+
const subscription = appState?.addEventListener('change', state => {
|
|
147
|
+
if (state === 'active') void fetchConfig();
|
|
148
|
+
});
|
|
149
|
+
const interval = options?.pollIntervalMs ?? DEFAULT_POLL_MS;
|
|
150
|
+
if (interval > 0) {
|
|
151
|
+
stopPolling();
|
|
152
|
+
pollTimer = setInterval(() => void fetchConfig(), interval);
|
|
153
|
+
}
|
|
154
|
+
return () => {
|
|
155
|
+
subscription?.remove();
|
|
156
|
+
stopPolling();
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
function applyValues(next, opts) {
|
|
160
|
+
const changed = !shallowEqual(values, next);
|
|
161
|
+
values = next;
|
|
162
|
+
source = 'remote';
|
|
163
|
+
writeCache(opts.storage, {
|
|
164
|
+
values,
|
|
165
|
+
etag,
|
|
166
|
+
fetchedAt
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
// Listeners drive re-renders and gate re-evaluation: firing them for an identical
|
|
170
|
+
// payload is pure churn, and the 200-with-same-content case is common enough
|
|
171
|
+
// (a proxy that drops the ETag) to be worth checking.
|
|
172
|
+
if (changed) notify();
|
|
173
|
+
return changed;
|
|
174
|
+
}
|
|
175
|
+
function shallowEqual(a, b) {
|
|
176
|
+
const aKeys = Object.keys(a);
|
|
177
|
+
const bKeys = Object.keys(b);
|
|
178
|
+
if (aKeys.length !== bKeys.length) return false;
|
|
179
|
+
return aKeys.every(k => {
|
|
180
|
+
const av = a[k];
|
|
181
|
+
const bv = b[k];
|
|
182
|
+
if (av !== null && bv !== null && typeof av === 'object' && typeof bv === 'object') {
|
|
183
|
+
return JSON.stringify(av) === JSON.stringify(bv);
|
|
184
|
+
}
|
|
185
|
+
return av === bv;
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
function stopPolling() {
|
|
189
|
+
if (pollTimer) {
|
|
190
|
+
clearInterval(pollTimer);
|
|
191
|
+
pollTimer = null;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// ─── Reads ────────────────────────────────────────────────────────────────────
|
|
196
|
+
|
|
197
|
+
/** The served value, or the registered default. Never a type's zero value. */
|
|
198
|
+
export function getConfigValue(key) {
|
|
199
|
+
if (Object.prototype.hasOwnProperty.call(values, key) && values[key] !== null) {
|
|
200
|
+
return values[key];
|
|
201
|
+
}
|
|
202
|
+
const fallback = defaults[key];
|
|
203
|
+
return fallback === undefined ? null : fallback;
|
|
204
|
+
}
|
|
205
|
+
export function getConfigBoolean(key) {
|
|
206
|
+
const value = getConfigValue(key);
|
|
207
|
+
if (typeof value === 'boolean') return value;
|
|
208
|
+
// A server that sends "true" as a string must not read as false.
|
|
209
|
+
if (typeof value === 'string') return value.toLowerCase() === 'true';
|
|
210
|
+
if (typeof value === 'number') return value !== 0;
|
|
211
|
+
return false;
|
|
212
|
+
}
|
|
213
|
+
export function getConfigNumber(key) {
|
|
214
|
+
const value = getConfigValue(key);
|
|
215
|
+
if (typeof value === 'number') return value;
|
|
216
|
+
if (typeof value === 'string') {
|
|
217
|
+
const parsed = Number(value);
|
|
218
|
+
if (!Number.isNaN(parsed)) return parsed;
|
|
219
|
+
}
|
|
220
|
+
return 0;
|
|
221
|
+
}
|
|
222
|
+
export function getConfigString(key) {
|
|
223
|
+
const value = getConfigValue(key);
|
|
224
|
+
if (typeof value === 'string') return value;
|
|
225
|
+
if (value === null || value === undefined) return '';
|
|
226
|
+
return String(value);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/** Defaults first, then everything the server sent — what a caller would see key by key. */
|
|
230
|
+
export function getAllConfig() {
|
|
231
|
+
return {
|
|
232
|
+
...defaults,
|
|
233
|
+
...values
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/** Parsed object/array value, or the default. Null when neither is usable. */
|
|
238
|
+
export function getConfigJson(key) {
|
|
239
|
+
const value = getConfigValue(key);
|
|
240
|
+
if (value === null || value === undefined) return null;
|
|
241
|
+
if (typeof value === 'object') return value;
|
|
242
|
+
if (typeof value === 'string') {
|
|
243
|
+
try {
|
|
244
|
+
return JSON.parse(value);
|
|
245
|
+
} catch {
|
|
246
|
+
return null;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
return null;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/** Where this key's value came from — the answer to "why is this flag off?". */
|
|
253
|
+
export function getConfigSource(key) {
|
|
254
|
+
if (Object.prototype.hasOwnProperty.call(values, key) && values[key] !== null) return 'remote';
|
|
255
|
+
if (Object.prototype.hasOwnProperty.call(defaults, key)) return 'default';
|
|
256
|
+
return 'missing';
|
|
257
|
+
}
|
|
258
|
+
export function getConfigKeys() {
|
|
259
|
+
return Object.keys(getAllConfig()).sort();
|
|
260
|
+
}
|
|
261
|
+
export function onConfigChange(listener) {
|
|
262
|
+
listeners.add(listener);
|
|
263
|
+
return () => listeners.delete(listener);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/** For diagnostics: where the current values came from and how old they are. */
|
|
267
|
+
export function getConfigInfo() {
|
|
268
|
+
return {
|
|
269
|
+
source,
|
|
270
|
+
fetchedAt,
|
|
271
|
+
etag,
|
|
272
|
+
keyCount: Object.keys(getAllConfig()).length
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/** Test seam: drops every piece of module state. */
|
|
277
|
+
export function resetConfigForTests() {
|
|
278
|
+
options = null;
|
|
279
|
+
defaults = {};
|
|
280
|
+
values = {};
|
|
281
|
+
etag = null;
|
|
282
|
+
fetchedAt = 0;
|
|
283
|
+
source = 'defaults';
|
|
284
|
+
inFlight = null;
|
|
285
|
+
pending = null;
|
|
286
|
+
listeners.clear();
|
|
287
|
+
stopPolling();
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// ─── Internals ────────────────────────────────────────────────────────────────
|
|
291
|
+
|
|
292
|
+
function buildHeaders(opts, currentEtag) {
|
|
293
|
+
const ctx = opts.getContext?.() ?? {};
|
|
294
|
+
const headers = {
|
|
295
|
+
Accept: 'application/json'
|
|
296
|
+
};
|
|
297
|
+
if (ctx.platform) headers['X-Pulse-Platform'] = ctx.platform;
|
|
298
|
+
if (ctx.appVersion) headers['X-Pulse-App-Version'] = ctx.appVersion;
|
|
299
|
+
if (ctx.osVersion) headers['X-Pulse-Os-Version'] = ctx.osVersion;
|
|
300
|
+
if (ctx.language) headers['X-Pulse-Language'] = ctx.language;
|
|
301
|
+
if (ctx.deviceId) headers['Pulse-Device-Id'] = ctx.deviceId;
|
|
302
|
+
if (ctx.userAttributes && Object.keys(ctx.userAttributes).length > 0) {
|
|
303
|
+
headers['X-Pulse-User-Attributes'] = JSON.stringify(ctx.userAttributes);
|
|
304
|
+
}
|
|
305
|
+
// Country is deliberately absent: the server reads it from the edge, which a client
|
|
306
|
+
// header cannot spoof to opt into a targeted rollout.
|
|
307
|
+
|
|
308
|
+
if (currentEtag) headers['If-None-Match'] = currentEtag;
|
|
309
|
+
return headers;
|
|
310
|
+
}
|
|
311
|
+
function readCache(storage) {
|
|
312
|
+
if (!storage) return null;
|
|
313
|
+
try {
|
|
314
|
+
const raw = storage.getString(STORAGE_KEY);
|
|
315
|
+
if (!raw) return null;
|
|
316
|
+
const parsed = JSON.parse(raw);
|
|
317
|
+
if (!parsed || typeof parsed !== 'object' || !parsed.values) return null;
|
|
318
|
+
return parsed;
|
|
319
|
+
} catch {
|
|
320
|
+
// A corrupt cache is not worth a crash at boot: fall back to defaults.
|
|
321
|
+
return null;
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
function writeCache(storage, payload) {
|
|
325
|
+
if (!storage) return;
|
|
326
|
+
try {
|
|
327
|
+
storage.set(STORAGE_KEY, JSON.stringify(payload));
|
|
328
|
+
} catch {
|
|
329
|
+
// Persistence is an optimisation; failing to write must not fail the fetch.
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
function notify() {
|
|
333
|
+
const snapshot = getAllConfig();
|
|
334
|
+
listeners.forEach(listener => {
|
|
335
|
+
try {
|
|
336
|
+
listener(snapshot);
|
|
337
|
+
} catch {
|
|
338
|
+
// One bad listener must not stop the others from being told.
|
|
339
|
+
}
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +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":[]}
|
package/lib/module/index.js
CHANGED
package/lib/module/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["usePulseUpdates","initializeAssetResolver","updateLocalAssets"],"sourceRoot":"../../src","sources":["index.ts"],"mappings":"AAAA,cAAc,gBAAgB;AAC9B,cAAc,SAAS;AACvB,SAASA,eAAe,QAAQ,mBAAmB;AACnD,SAASC,uBAAuB,EAAEC,iBAAiB,QAAQ,iBAAiB","ignoreList":[]}
|
|
1
|
+
{"version":3,"names":["usePulseUpdates","initializeAssetResolver","updateLocalAssets"],"sourceRoot":"../../src","sources":["index.ts"],"mappings":"AAAA,cAAc,gBAAgB;AAC9B,cAAc,SAAS;AACvB,SAASA,eAAe,QAAQ,mBAAmB;AACnD,SAASC,uBAAuB,EAAEC,iBAAiB,QAAQ,iBAAiB;AAC5E,cAAc,UAAU","ignoreList":[]}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pulse Config — the client half of the Firebase Remote Config replacement.
|
|
3
|
+
*
|
|
4
|
+
* Why this exists: from 2026-09-01 Firebase bills every Remote Config fetch above
|
|
5
|
+
* 100k/day and throttles projects that stay on the free plan. Worse than the bill is
|
|
6
|
+
* the failure mode — the Firebase SDK yields the type's zero value for a key it does
|
|
7
|
+
* not hold, so a throttled fetch reads as "every flag is false" and, for an ads
|
|
8
|
+
* config, quietly turns the revenue off.
|
|
9
|
+
*
|
|
10
|
+
* The shape here deliberately mirrors what the apps already do with Remote Config —
|
|
11
|
+
* defaults, fetch, activate, typed getters — so swapping the source is a change of
|
|
12
|
+
* adapter, not of every call site. Three things are different on purpose:
|
|
13
|
+
*
|
|
14
|
+
* 1. A missing key falls back to the registered default, never to false/0. That is
|
|
15
|
+
* the exact bug this replaces.
|
|
16
|
+
* 2. The last good values are persisted and reloaded at boot, so a failed fetch
|
|
17
|
+
* degrades to yesterday's config rather than to nothing.
|
|
18
|
+
* 3. Fetching is cheap (our server, no per-request price), so it happens on launch,
|
|
19
|
+
* on every return from foreground, and on a timer — with ETag, so an unchanged
|
|
20
|
+
* config costs a 304 with no body.
|
|
21
|
+
*/
|
|
22
|
+
export type ConfigValue = boolean | number | string | null | object;
|
|
23
|
+
export interface ConfigStorage {
|
|
24
|
+
getString(key: string): string | null | undefined;
|
|
25
|
+
set(key: string, value: string): void;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Everything the server may ever want to target on. Sent on every request even when
|
|
29
|
+
* no rule uses it yet: the client can only start sending a new field with a store
|
|
30
|
+
* build, so a contract that is incomplete today cannot be completed for months.
|
|
31
|
+
*/
|
|
32
|
+
export interface ConfigContext {
|
|
33
|
+
platform?: string;
|
|
34
|
+
appVersion?: string;
|
|
35
|
+
osVersion?: string;
|
|
36
|
+
language?: string;
|
|
37
|
+
deviceId?: string;
|
|
38
|
+
/** Free-form user attributes a rule can compare (registration date, plan, counters). */
|
|
39
|
+
userAttributes?: Record<string, string>;
|
|
40
|
+
}
|
|
41
|
+
export interface ConfigOptions {
|
|
42
|
+
/** Full manifest-style URL, e.g. https://pulse.example.com/pulse/config/esound */
|
|
43
|
+
url: string;
|
|
44
|
+
/** Values used when the server has never been reached, or has no such key. */
|
|
45
|
+
defaults?: Record<string, ConfigValue>;
|
|
46
|
+
/** Persistence for the last good payload. Without it the cache is memory-only. */
|
|
47
|
+
storage?: ConfigStorage;
|
|
48
|
+
/** Read fresh on every request: the country or plan can change between launches. */
|
|
49
|
+
getContext?: () => ConfigContext;
|
|
50
|
+
/** Foreground poll interval. 0 disables polling (launch + resume still fetch). */
|
|
51
|
+
pollIntervalMs?: number;
|
|
52
|
+
/**
|
|
53
|
+
* Skip a fetch that lands within this window of the last successful one. 0 (the
|
|
54
|
+
* default) means never skip: unlike Firebase there is no per-request price, so the
|
|
55
|
+
* throttle exists only for callers who want it.
|
|
56
|
+
*/
|
|
57
|
+
minimumFetchIntervalMs?: number;
|
|
58
|
+
/**
|
|
59
|
+
* When false, a fetch stores the payload without applying it — call activateConfig()
|
|
60
|
+
* to swap it in. Mirrors Firebase's fetch/activate split, and matters here because
|
|
61
|
+
* the foreground poll would otherwise flip a flag under a user mid-session: an ad
|
|
62
|
+
* gate or a playback engine changing while a track plays is worse than being a few
|
|
63
|
+
* minutes stale. Default true, matching fetchAndActivate.
|
|
64
|
+
*/
|
|
65
|
+
activateOnFetch?: boolean;
|
|
66
|
+
/** Network timeout per request. */
|
|
67
|
+
timeoutMs?: number;
|
|
68
|
+
onError?: (error: unknown) => void;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Registers defaults and reloads the last good payload from storage, synchronously,
|
|
72
|
+
* so the very first read at boot already has real values instead of a blank config
|
|
73
|
+
* while the network call is in flight.
|
|
74
|
+
*/
|
|
75
|
+
export declare function configureConfig(opts: ConfigOptions): void;
|
|
76
|
+
/** Merge more defaults after configure (a late-loading module registering its own). */
|
|
77
|
+
export declare function setConfigDefaults(more: Record<string, ConfigValue>): void;
|
|
78
|
+
/**
|
|
79
|
+
* Fetch, and adopt the payload if it changed. Returns true when new values were
|
|
80
|
+
* applied — false for "nothing changed" (304) and false for a failure, which are
|
|
81
|
+
* deliberately indistinguishable to callers: in both cases the current values stand.
|
|
82
|
+
* Errors surface through onError instead of rejecting, because no caller should have
|
|
83
|
+
* to wrap a config refresh in a try/catch to keep the app running.
|
|
84
|
+
*/
|
|
85
|
+
export declare function fetchConfig(): Promise<boolean>;
|
|
86
|
+
/**
|
|
87
|
+
* Swap in the values from the last fetch made with activateOnFetch: false.
|
|
88
|
+
* Returns true when something actually changed.
|
|
89
|
+
*/
|
|
90
|
+
export declare function activateConfig(): boolean;
|
|
91
|
+
/** Whether a fetched-but-not-yet-applied payload is waiting. */
|
|
92
|
+
export declare function hasPendingConfig(): boolean;
|
|
93
|
+
/**
|
|
94
|
+
* Fetch on launch, on every return to the foreground, and on a timer.
|
|
95
|
+
* Returns a function that stops both.
|
|
96
|
+
*/
|
|
97
|
+
export declare function startConfigAutoRefresh(appState?: {
|
|
98
|
+
addEventListener: (type: 'change', handler: (state: string) => void) => {
|
|
99
|
+
remove: () => void;
|
|
100
|
+
};
|
|
101
|
+
}): () => void;
|
|
102
|
+
/** The served value, or the registered default. Never a type's zero value. */
|
|
103
|
+
export declare function getConfigValue(key: string): ConfigValue;
|
|
104
|
+
export declare function getConfigBoolean(key: string): boolean;
|
|
105
|
+
export declare function getConfigNumber(key: string): number;
|
|
106
|
+
export declare function getConfigString(key: string): string;
|
|
107
|
+
/** Defaults first, then everything the server sent — what a caller would see key by key. */
|
|
108
|
+
export declare function getAllConfig(): Record<string, ConfigValue>;
|
|
109
|
+
/** Parsed object/array value, or the default. Null when neither is usable. */
|
|
110
|
+
export declare function getConfigJson<T = unknown>(key: string): T | null;
|
|
111
|
+
/** Where this key's value came from — the answer to "why is this flag off?". */
|
|
112
|
+
export declare function getConfigSource(key: string): 'remote' | 'default' | 'missing';
|
|
113
|
+
export declare function getConfigKeys(): string[];
|
|
114
|
+
export declare function onConfigChange(listener: (values: Record<string, ConfigValue>) => void): () => void;
|
|
115
|
+
/** For diagnostics: where the current values came from and how old they are. */
|
|
116
|
+
export declare function getConfigInfo(): {
|
|
117
|
+
source: 'defaults' | 'cache' | 'remote';
|
|
118
|
+
fetchedAt: number;
|
|
119
|
+
etag: string | null;
|
|
120
|
+
keyCount: number;
|
|
121
|
+
};
|
|
122
|
+
/** Test seam: drops every piece of module state. */
|
|
123
|
+
export declare function resetConfigForTests(): void;
|
|
124
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/config.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,SAAS,CAAC;AACxB,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,uBAAuB,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,SAAS,CAAC;AACxB,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,uBAAuB,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAC7E,cAAc,UAAU,CAAC"}
|