@chipmobilesdk/rn-payment 0.4.0 → 0.5.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 +482 -334
- package/README.md +925 -897
- package/package.json +52 -54
- package/skills/sdk-rn-payment/SKILL.md +175 -174
- package/src/adapter/createStoreProvider.ts +116 -118
- package/src/adapter/iap-adapter.ts +627 -660
- package/src/adapter/port.ts +209 -215
- package/src/config/types.ts +298 -310
- package/src/config/validate.ts +224 -232
- package/src/core/mutex.ts +25 -128
- package/src/core/retry.ts +95 -133
- package/src/core/timers.ts +272 -267
- package/src/diagnostics/errors.ts +124 -117
- package/src/diagnostics/events.ts +282 -266
- package/src/diagnostics/messageKeys.ts +93 -95
- package/src/engine/engine.ts +921 -999
- package/src/engine/finalize.ts +343 -346
- package/src/engine/foreground.ts +29 -0
- package/src/engine/products.ts +259 -259
- package/src/engine/purchase.ts +589 -614
- package/src/engine/subscriptions.ts +146 -157
- package/src/entitlements/aggregate.ts +261 -242
- package/src/entitlements/mapping.ts +67 -67
- package/src/entitlements/policy.ts +121 -129
- package/src/index.ts +5 -9
- package/src/persistence/mmkv-store.ts +158 -158
- package/src/persistence/snapshot.ts +220 -247
- package/src/react/PaymentProvider.tsx +90 -79
- package/src/react/hooks.ts +236 -274
- package/src/testing/conformance.ts +169 -0
- package/src/testing/fakeStore.ts +360 -393
- package/src/testing/index.ts +19 -10
package/src/core/timers.ts
CHANGED
|
@@ -1,267 +1,272 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Timeout, retry, and cache policy.
|
|
3
|
-
*
|
|
4
|
-
* Defaults and bounds live here rather than at each call site so there is one
|
|
5
|
-
* place to read what the package will wait for, and so consumers can read the
|
|
6
|
-
* values they are bound by instead of discovering them from a timeout in
|
|
7
|
-
* production.
|
|
8
|
-
*
|
|
9
|
-
* Out-of-band values are a configuration error rather than being silently
|
|
10
|
-
* clamped: quietly turning a 1-second timeout into 3 seconds would tell the app
|
|
11
|
-
* it configured something it did not.
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
import {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
export const
|
|
23
|
-
/**
|
|
24
|
-
export const
|
|
25
|
-
/**
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
*/
|
|
34
|
-
export const
|
|
35
|
-
/**
|
|
36
|
-
export const
|
|
37
|
-
/**
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
*
|
|
41
|
-
* the
|
|
42
|
-
*
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
export const
|
|
47
|
-
|
|
48
|
-
export const
|
|
49
|
-
export const
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
*
|
|
104
|
-
*
|
|
105
|
-
*
|
|
106
|
-
*
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
};
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
export const
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
);
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Timeout, retry, and cache policy.
|
|
3
|
+
*
|
|
4
|
+
* Defaults and bounds live here rather than at each call site so there is one
|
|
5
|
+
* place to read what the package will wait for, and so consumers can read the
|
|
6
|
+
* values they are bound by instead of discovering them from a timeout in
|
|
7
|
+
* production.
|
|
8
|
+
*
|
|
9
|
+
* Out-of-band values are a configuration error rather than being silently
|
|
10
|
+
* clamped: quietly turning a 1-second timeout into 3 seconds would tell the app
|
|
11
|
+
* it configured something it did not.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { checkRange, type Range } from '@chipmobilesdk/rn-core';
|
|
15
|
+
|
|
16
|
+
import { PaymentConfigError } from '../diagnostics/errors';
|
|
17
|
+
import type { BackoffConfig, RetryConfig, TimeoutConfig } from '../config/types';
|
|
18
|
+
|
|
19
|
+
/** Local IPC to Play Services. Slower than this means unavailable, not busy. */
|
|
20
|
+
export const DEFAULT_CONNECT_TIMEOUT_MS = 10_000;
|
|
21
|
+
/** A network round trip; a paywall that hangs longer should show its error state. */
|
|
22
|
+
export const DEFAULT_PRODUCT_QUERY_TIMEOUT_MS = 15_000;
|
|
23
|
+
/** Runs at startup, so it must not hold the first render hostage. */
|
|
24
|
+
export const DEFAULT_SYNC_TIMEOUT_MS = 20_000;
|
|
25
|
+
/**
|
|
26
|
+
* Covers opening the store surface only.
|
|
27
|
+
*
|
|
28
|
+
* Time the user spends *inside* the store's own UI is deliberately excluded
|
|
29
|
+
* (FR-035): a user reading the terms, switching to their banking app for an OTP,
|
|
30
|
+
* or simply hesitating must never have their purchase cancelled by our clock.
|
|
31
|
+
*/
|
|
32
|
+
export const DEFAULT_PURCHASE_LAUNCH_TIMEOUT_MS = 30_000;
|
|
33
|
+
/** Per finalize attempt, not per transaction — the queue owns overall persistence. */
|
|
34
|
+
export const DEFAULT_FINALIZE_ATTEMPT_TIMEOUT_MS = 15_000;
|
|
35
|
+
/** The app's backend hook. A user is waiting on a purchase result behind it. */
|
|
36
|
+
export const DEFAULT_VALIDATE_TIMEOUT_MS = 15_000;
|
|
37
|
+
/**
|
|
38
|
+
* The ceiling on a purchase flow whose outcome includes the user's own time in
|
|
39
|
+
* the store UI, so is otherwise untimed (FR-035). Not a timeout on the user: it
|
|
40
|
+
* exists only so a binding that never answers cannot hold the purchase lock for
|
|
41
|
+
* the rest of the session. A purchase that completes later still arrives through
|
|
42
|
+
* the store update channel and is granted then.
|
|
43
|
+
*/
|
|
44
|
+
export const PURCHASE_FLOW_CEILING_MS = 10 * 60_000;
|
|
45
|
+
|
|
46
|
+
export const CONNECT_TIMEOUT_BOUNDS = { min: 3_000, max: 60_000 } as const;
|
|
47
|
+
export const PRODUCT_QUERY_TIMEOUT_BOUNDS = { min: 3_000, max: 60_000 } as const;
|
|
48
|
+
export const SYNC_TIMEOUT_BOUNDS = { min: 5_000, max: 120_000 } as const;
|
|
49
|
+
export const PURCHASE_LAUNCH_TIMEOUT_BOUNDS = {
|
|
50
|
+
min: 5_000,
|
|
51
|
+
max: 120_000,
|
|
52
|
+
} as const;
|
|
53
|
+
export const FINALIZE_ATTEMPT_TIMEOUT_BOUNDS = {
|
|
54
|
+
min: 3_000,
|
|
55
|
+
max: 60_000,
|
|
56
|
+
} as const;
|
|
57
|
+
export const VALIDATE_TIMEOUT_BOUNDS = { min: 3_000, max: 120_000 } as const;
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Prices and offer eligibility are shown at the moment of purchase, so a short
|
|
61
|
+
* TTL with on-demand refresh beats a long one (FR-029).
|
|
62
|
+
*/
|
|
63
|
+
export const DEFAULT_PRODUCT_CACHE_TTL_MS = 900_000;
|
|
64
|
+
export const PRODUCT_CACHE_TTL_BOUNDS = { min: 60_000, max: 86_400_000 } as const;
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* How long a successful sync keeps entitlements trustworthy offline: **7 days**.
|
|
68
|
+
*
|
|
69
|
+
* Fixed by the specification (FR-055) after weighing both failure directions.
|
|
70
|
+
* The realistic offline cases — travel, poor coverage, a device left unused —
|
|
71
|
+
* resolve within days. Seven days covers a holiday without connectivity while
|
|
72
|
+
* bounding how long a refunded entitlement can outlive its refund to one week.
|
|
73
|
+
*
|
|
74
|
+
* Past this the entitlement becomes `unknown`, never `not_entitled` (FR-055a):
|
|
75
|
+
* the package stops asserting rather than telling a paying customer they did not
|
|
76
|
+
* pay.
|
|
77
|
+
*/
|
|
78
|
+
export const DEFAULT_STALE_AFTER_MS = 604_800_000;
|
|
79
|
+
export const STALE_AFTER_BOUNDS = {
|
|
80
|
+
min: 3_600_000,
|
|
81
|
+
max: 7_776_000_000,
|
|
82
|
+
} as const;
|
|
83
|
+
|
|
84
|
+
/** Keeps rapid app switching from becoming a request storm (PERF-002). */
|
|
85
|
+
export const DEFAULT_FOREGROUND_SYNC_MIN_INTERVAL_MS = 60_000;
|
|
86
|
+
export const FOREGROUND_SYNC_MIN_INTERVAL_BOUNDS = {
|
|
87
|
+
min: 0,
|
|
88
|
+
max: 3_600_000,
|
|
89
|
+
} as const;
|
|
90
|
+
|
|
91
|
+
/** Bounded so a dropped billing connection cannot become a retry storm (FR-020). */
|
|
92
|
+
export const DEFAULT_RECONNECT_BACKOFF: Required<BackoffConfig> = {
|
|
93
|
+
initialDelayMs: 1_000,
|
|
94
|
+
maxDelayMs: 30_000,
|
|
95
|
+
factor: 2,
|
|
96
|
+
maxAttempts: 5,
|
|
97
|
+
jitter: true,
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Finalize retry.
|
|
102
|
+
*
|
|
103
|
+
* Bounded **per session** but never permanently abandoned: Google auto-refunds
|
|
104
|
+
* an unacknowledged purchase after three days, so exhausting this session's
|
|
105
|
+
* attempts must leave the transaction queued for the next launch, not dropped
|
|
106
|
+
* (FR-045).
|
|
107
|
+
*/
|
|
108
|
+
export const DEFAULT_FINALIZE_BACKOFF: Required<BackoffConfig> = {
|
|
109
|
+
initialDelayMs: 2_000,
|
|
110
|
+
maxDelayMs: 60_000,
|
|
111
|
+
factor: 2,
|
|
112
|
+
maxAttempts: 5,
|
|
113
|
+
jitter: true,
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
export const BACKOFF_MAX_DELAY_BOUNDS = { min: 5_000, max: 600_000 } as const;
|
|
117
|
+
export const BACKOFF_ATTEMPT_BOUNDS = { min: 1, max: 10 } as const;
|
|
118
|
+
/** A first retry sooner than this is a storm; later than this, the user has given up. */
|
|
119
|
+
export const BACKOFF_INITIAL_DELAY_BOUNDS = { min: 100, max: 60_000 } as const;
|
|
120
|
+
/** 1 is a constant delay; 0 or below would retry at once, forever (CORE-05). */
|
|
121
|
+
export const BACKOFF_FACTOR_BOUNDS = { min: 1, max: 10 } as const;
|
|
122
|
+
|
|
123
|
+
function invalidConfig(label: string) {
|
|
124
|
+
return (message: string) =>
|
|
125
|
+
new PaymentConfigError({
|
|
126
|
+
code: 'PAYMENT_INVALID_CONFIG',
|
|
127
|
+
message,
|
|
128
|
+
operation: 'configure',
|
|
129
|
+
offenders: [label],
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function requireInBounds(
|
|
134
|
+
label: string,
|
|
135
|
+
value: number | undefined,
|
|
136
|
+
fallback: number,
|
|
137
|
+
bounds: Range,
|
|
138
|
+
unit = 'ms',
|
|
139
|
+
): number {
|
|
140
|
+
return value === undefined
|
|
141
|
+
? fallback
|
|
142
|
+
: checkRange(label, value, bounds, invalidConfig(label), unit);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export interface ResolvedTimeouts {
|
|
146
|
+
connectMs: number;
|
|
147
|
+
productQueryMs: number;
|
|
148
|
+
syncMs: number;
|
|
149
|
+
purchaseLaunchMs: number;
|
|
150
|
+
finalizeAttemptMs: number;
|
|
151
|
+
validateMs: number;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
export function resolveTimeouts(config?: TimeoutConfig): ResolvedTimeouts {
|
|
155
|
+
return {
|
|
156
|
+
connectMs: requireInBounds(
|
|
157
|
+
'timeouts.connectMs',
|
|
158
|
+
config?.connectMs,
|
|
159
|
+
DEFAULT_CONNECT_TIMEOUT_MS,
|
|
160
|
+
CONNECT_TIMEOUT_BOUNDS,
|
|
161
|
+
),
|
|
162
|
+
productQueryMs: requireInBounds(
|
|
163
|
+
'timeouts.productQueryMs',
|
|
164
|
+
config?.productQueryMs,
|
|
165
|
+
DEFAULT_PRODUCT_QUERY_TIMEOUT_MS,
|
|
166
|
+
PRODUCT_QUERY_TIMEOUT_BOUNDS,
|
|
167
|
+
),
|
|
168
|
+
syncMs: requireInBounds(
|
|
169
|
+
'timeouts.syncMs',
|
|
170
|
+
config?.syncMs,
|
|
171
|
+
DEFAULT_SYNC_TIMEOUT_MS,
|
|
172
|
+
SYNC_TIMEOUT_BOUNDS,
|
|
173
|
+
),
|
|
174
|
+
purchaseLaunchMs: requireInBounds(
|
|
175
|
+
'timeouts.purchaseLaunchMs',
|
|
176
|
+
config?.purchaseLaunchMs,
|
|
177
|
+
DEFAULT_PURCHASE_LAUNCH_TIMEOUT_MS,
|
|
178
|
+
PURCHASE_LAUNCH_TIMEOUT_BOUNDS,
|
|
179
|
+
),
|
|
180
|
+
finalizeAttemptMs: requireInBounds(
|
|
181
|
+
'timeouts.finalizeAttemptMs',
|
|
182
|
+
config?.finalizeAttemptMs,
|
|
183
|
+
DEFAULT_FINALIZE_ATTEMPT_TIMEOUT_MS,
|
|
184
|
+
FINALIZE_ATTEMPT_TIMEOUT_BOUNDS,
|
|
185
|
+
),
|
|
186
|
+
validateMs: requireInBounds(
|
|
187
|
+
'timeouts.validateMs',
|
|
188
|
+
config?.validateMs,
|
|
189
|
+
DEFAULT_VALIDATE_TIMEOUT_MS,
|
|
190
|
+
VALIDATE_TIMEOUT_BOUNDS,
|
|
191
|
+
),
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function resolveBackoff(
|
|
196
|
+
label: string,
|
|
197
|
+
config: BackoffConfig | undefined,
|
|
198
|
+
fallback: Required<BackoffConfig>,
|
|
199
|
+
): Required<BackoffConfig> {
|
|
200
|
+
const maxDelayMs = requireInBounds(
|
|
201
|
+
`${label}.maxDelayMs`,
|
|
202
|
+
config?.maxDelayMs,
|
|
203
|
+
fallback.maxDelayMs,
|
|
204
|
+
BACKOFF_MAX_DELAY_BOUNDS,
|
|
205
|
+
);
|
|
206
|
+
const maxAttempts = requireInBounds(
|
|
207
|
+
`${label}.maxAttempts`,
|
|
208
|
+
config?.maxAttempts,
|
|
209
|
+
fallback.maxAttempts,
|
|
210
|
+
BACKOFF_ATTEMPT_BOUNDS,
|
|
211
|
+
'',
|
|
212
|
+
);
|
|
213
|
+
const initialDelayMs = requireInBounds(
|
|
214
|
+
`${label}.initialDelayMs`,
|
|
215
|
+
config?.initialDelayMs,
|
|
216
|
+
fallback.initialDelayMs,
|
|
217
|
+
BACKOFF_INITIAL_DELAY_BOUNDS,
|
|
218
|
+
);
|
|
219
|
+
const factor = requireInBounds(
|
|
220
|
+
`${label}.factor`,
|
|
221
|
+
config?.factor,
|
|
222
|
+
fallback.factor,
|
|
223
|
+
BACKOFF_FACTOR_BOUNDS,
|
|
224
|
+
'',
|
|
225
|
+
);
|
|
226
|
+
if (initialDelayMs > maxDelayMs) {
|
|
227
|
+
throw invalidConfig(`${label}.initialDelayMs`)(
|
|
228
|
+
`${label}.initialDelayMs (${initialDelayMs}ms) exceeds ${label}.maxDelayMs (${maxDelayMs}ms)`,
|
|
229
|
+
);
|
|
230
|
+
}
|
|
231
|
+
return {
|
|
232
|
+
initialDelayMs,
|
|
233
|
+
maxDelayMs,
|
|
234
|
+
factor,
|
|
235
|
+
maxAttempts,
|
|
236
|
+
jitter: config?.jitter ?? fallback.jitter,
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
export interface ResolvedRetry {
|
|
241
|
+
reconnect: Required<BackoffConfig>;
|
|
242
|
+
finalize: Required<BackoffConfig>;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
export function resolveRetry(config?: RetryConfig): ResolvedRetry {
|
|
246
|
+
return {
|
|
247
|
+
reconnect: resolveBackoff('retry.reconnect', config?.reconnect, DEFAULT_RECONNECT_BACKOFF),
|
|
248
|
+
finalize: resolveBackoff('retry.finalize', config?.finalize, DEFAULT_FINALIZE_BACKOFF),
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
export function resolveStaleAfterMs(value?: number): number {
|
|
253
|
+
return requireInBounds('staleAfterMs', value, DEFAULT_STALE_AFTER_MS, STALE_AFTER_BOUNDS);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
export function resolveProductCacheTtlMs(value?: number): number {
|
|
257
|
+
return requireInBounds(
|
|
258
|
+
'productCacheTtlMs',
|
|
259
|
+
value,
|
|
260
|
+
DEFAULT_PRODUCT_CACHE_TTL_MS,
|
|
261
|
+
PRODUCT_CACHE_TTL_BOUNDS,
|
|
262
|
+
);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
export function resolveForegroundSyncMinIntervalMs(value?: number): number {
|
|
266
|
+
return requireInBounds(
|
|
267
|
+
'foregroundSyncMinIntervalMs',
|
|
268
|
+
value,
|
|
269
|
+
DEFAULT_FOREGROUND_SYNC_MIN_INTERVAL_MS,
|
|
270
|
+
FOREGROUND_SYNC_MIN_INTERVAL_BOUNDS,
|
|
271
|
+
);
|
|
272
|
+
}
|