@goliapkg/sentori-react-native 6.3.0 → 6.4.1
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-core/com/sentori/SentoriConfig.kt +1 -1
- package/android/src/main/java-core/com/sentori/SentoriPush.kt +57 -13
- package/android/src/test/java-core/com/sentori/SentoriPushTest.kt +69 -0
- package/lib/push.d.ts.map +1 -1
- package/lib/push.js +144 -8
- package/lib/push.js.map +1 -1
- package/lib/transport.js +1 -1
- package/package.json +1 -1
- package/src/__tests__/iron-rule.test.ts +118 -0
- package/src/__tests__/push.test.ts +249 -0
- package/src/push.ts +137 -8
- package/src/transport.ts +1 -1
|
@@ -273,17 +273,24 @@ object SentoriPush {
|
|
|
273
273
|
*/
|
|
274
274
|
private fun identityChanged(appContext: Context) {
|
|
275
275
|
val config = SentoriConfig.current ?: return
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
276
|
+
// Everything, including the preference reads, on the worker.
|
|
277
|
+
// `Sentori.user` is synchronous and is called from wherever
|
|
278
|
+
// the host signs someone in — usually the main thread — and
|
|
279
|
+
// reading preferences there is disk I/O on the thread that
|
|
280
|
+
// draws frames. Warm in practice, since this listener is only
|
|
281
|
+
// installed after a registration has already touched them; on
|
|
282
|
+
// the worker it cannot be anything else.
|
|
283
|
+
worker.execute {
|
|
284
|
+
val prefs = appContext.getSharedPreferences(PREFS, Context.MODE_PRIVATE)
|
|
285
|
+
val token = prefs.getString(TOKEN_KEY, null) ?: return@execute
|
|
286
|
+
if (prefs.getString(HANDLE_KEY, null) == null) return@execute
|
|
279
287
|
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
288
|
+
// `Sentori.user` is a verb an app may call on every screen,
|
|
289
|
+
// and one request per call is not free to a host.
|
|
290
|
+
val identity = "${SentoriScope.userKey}\u0000${SentoriScope.traits}"
|
|
291
|
+
if (identity == lastSentIdentity) return@execute
|
|
292
|
+
lastSentIdentity = identity
|
|
285
293
|
|
|
286
|
-
worker.execute {
|
|
287
294
|
when (val r = registerWithServer(appContext, token, config)) {
|
|
288
295
|
is Result.Success -> rememberHandle(appContext, r.handle, token)
|
|
289
296
|
is Result.Failure -> {
|
|
@@ -319,6 +326,9 @@ object SentoriPush {
|
|
|
319
326
|
}
|
|
320
327
|
}
|
|
321
328
|
|
|
329
|
+
/** Hand one drained batch to the host's callbacks, for tests. */
|
|
330
|
+
internal fun drainForTests(state: Map<String, Any?>) = flush(state)
|
|
331
|
+
|
|
322
332
|
/** This installation's id, minted on first use and kept after. */
|
|
323
333
|
internal fun installId(ctx: Context): String {
|
|
324
334
|
val prefs = ctx.applicationContext.getSharedPreferences(PREFS, Context.MODE_PRIVATE)
|
|
@@ -350,7 +360,12 @@ object SentoriPush {
|
|
|
350
360
|
"push register failed (${result.reason.reason}): ${result.message}",
|
|
351
361
|
)
|
|
352
362
|
}
|
|
353
|
-
|
|
363
|
+
// The host's callback is the host's code. A registration that
|
|
364
|
+
// succeeded must not be undone by what the host does with the
|
|
365
|
+
// news, and this runs on the single worker thread — an
|
|
366
|
+
// exception here takes the thread, and with it every push
|
|
367
|
+
// request the process makes afterwards.
|
|
368
|
+
safely("register callback") { completion(result) }
|
|
354
369
|
}
|
|
355
370
|
|
|
356
371
|
private fun finishRegister(
|
|
@@ -558,7 +573,11 @@ object SentoriPush {
|
|
|
558
573
|
if (drainTask != null) return
|
|
559
574
|
drainTask =
|
|
560
575
|
worker.scheduleWithFixedDelay(
|
|
561
|
-
|
|
576
|
+
// Belt as well as braces: `flush` contains the
|
|
577
|
+
// host's code, and this contains everything else,
|
|
578
|
+
// because a task that throws is a task the
|
|
579
|
+
// executor never runs again.
|
|
580
|
+
{ safely("drain") { flush(SentoriPushNotifications.drainState()) } },
|
|
562
581
|
1,
|
|
563
582
|
1,
|
|
564
583
|
TimeUnit.SECONDS,
|
|
@@ -567,11 +586,36 @@ object SentoriPush {
|
|
|
567
586
|
}
|
|
568
587
|
|
|
569
588
|
@Suppress("UNCHECKED_CAST")
|
|
589
|
+
/**
|
|
590
|
+
* Hand a drained batch to the host, without letting the host reach
|
|
591
|
+
* back.
|
|
592
|
+
*
|
|
593
|
+
* Push is allowed to fail silently; it is never allowed to make
|
|
594
|
+
* the host app fail. The handlers are the host's own code running
|
|
595
|
+
* inside our loop, and the loop is a `scheduleWithFixedDelay` —
|
|
596
|
+
* where an exception out of the task makes the executor **cancel
|
|
597
|
+
* every future run**, silently. One throwing `onMessage` ended
|
|
598
|
+
* push delivery for the life of the process, and nothing anywhere
|
|
599
|
+
* said so.
|
|
600
|
+
*/
|
|
570
601
|
private fun flush(state: Map<String, Any?>) {
|
|
571
602
|
val (message, tap) = synchronized(lock) { onMessage to onTap }
|
|
572
603
|
if (message == null && tap == null) return
|
|
573
|
-
(state["notifications"] as? List<Map<String, Any?>>)?.forEach {
|
|
574
|
-
|
|
604
|
+
(state["notifications"] as? List<Map<String, Any?>>)?.forEach {
|
|
605
|
+
safely("onMessage") { message?.invoke(it) }
|
|
606
|
+
}
|
|
607
|
+
(state["taps"] as? List<Map<String, Any?>>)?.forEach {
|
|
608
|
+
safely("onTap") { tap?.invoke(it) }
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
/** Run the host's own code without letting it reach back into ours. */
|
|
613
|
+
private inline fun safely(what: String, body: () -> Unit) {
|
|
614
|
+
try {
|
|
615
|
+
body()
|
|
616
|
+
} catch (t: Throwable) {
|
|
617
|
+
android.util.Log.w("sentori", "push $what threw — carrying on", t)
|
|
618
|
+
}
|
|
575
619
|
}
|
|
576
620
|
|
|
577
621
|
internal fun resetForTests() {
|
|
@@ -704,4 +704,73 @@ class SentoriPushTest {
|
|
|
704
704
|
assertEquals(0, rec.bodies.size)
|
|
705
705
|
}
|
|
706
706
|
}
|
|
707
|
+
|
|
708
|
+
/**
|
|
709
|
+
* Push is allowed to fail silently. It is never allowed to make
|
|
710
|
+
* the host app fail.
|
|
711
|
+
*
|
|
712
|
+
* The host's own handlers are the sharp edge: they are its code
|
|
713
|
+
* and they run inside our loop. Worse here than anywhere — the
|
|
714
|
+
* drain is a `scheduleWithFixedDelay`, and an exception out of a
|
|
715
|
+
* scheduled task makes the executor **cancel every future run**,
|
|
716
|
+
* silently. One throwing `onMessage` used to end push delivery for
|
|
717
|
+
* the life of the process, with nothing anywhere to say so.
|
|
718
|
+
*/
|
|
719
|
+
@Test
|
|
720
|
+
fun aThrowingHandlerDoesNotStopDelivery() {
|
|
721
|
+
val ctx = ApplicationProvider.getApplicationContext<android.content.Context>()
|
|
722
|
+
SentoriConfig.set(
|
|
723
|
+
SentoriConfig(token = "st_test", ingestUrl = "http://127.0.0.1:9",
|
|
724
|
+
release = "app@1.0.0", environment = "test"),
|
|
725
|
+
)
|
|
726
|
+
val seen = mutableListOf<String>()
|
|
727
|
+
val latch = CountDownLatch(1)
|
|
728
|
+
SentoriPush.register(
|
|
729
|
+
ctx,
|
|
730
|
+
activity = null,
|
|
731
|
+
timeoutMs = 200,
|
|
732
|
+
onMessage = {
|
|
733
|
+
seen.add(it["title"] as? String ?: "?")
|
|
734
|
+
if (it["title"] == "one") error("the host threw")
|
|
735
|
+
},
|
|
736
|
+
onTap = { seen.add("tap") },
|
|
737
|
+
) { latch.countDown() }
|
|
738
|
+
assertTrue(latch.await(30, TimeUnit.SECONDS))
|
|
739
|
+
|
|
740
|
+
// One bad handler must not swallow the rest of the batch.
|
|
741
|
+
SentoriPush.drainForTests(
|
|
742
|
+
mapOf(
|
|
743
|
+
"notifications" to listOf(
|
|
744
|
+
mapOf("title" to "one"),
|
|
745
|
+
mapOf("title" to "two"),
|
|
746
|
+
),
|
|
747
|
+
"taps" to listOf(mapOf<String, Any?>("tapped" to true)),
|
|
748
|
+
),
|
|
749
|
+
)
|
|
750
|
+
assertEquals(listOf("one", "two", "tap"), seen)
|
|
751
|
+
|
|
752
|
+
// And the next batch still arrives, which is what would have
|
|
753
|
+
// been lost when the executor cancelled the task.
|
|
754
|
+
SentoriPush.drainForTests(
|
|
755
|
+
mapOf("notifications" to listOf(mapOf("title" to "three")), "taps" to null),
|
|
756
|
+
)
|
|
757
|
+
assertEquals(listOf("one", "two", "tap", "three"), seen)
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
/** A host whose completion throws must not take the worker with it. */
|
|
761
|
+
@Test
|
|
762
|
+
fun aThrowingCompletionIsContained() {
|
|
763
|
+
val ctx = ApplicationProvider.getApplicationContext<android.content.Context>()
|
|
764
|
+
SentoriConfig.set(
|
|
765
|
+
SentoriConfig(token = "st_test", ingestUrl = "http://127.0.0.1:9",
|
|
766
|
+
release = "app@1.0.0", environment = "test"),
|
|
767
|
+
)
|
|
768
|
+
val second = CountDownLatch(1)
|
|
769
|
+
SentoriPush.register(ctx, activity = null, timeoutMs = 200) { error("the host threw") }
|
|
770
|
+
Thread.sleep(300)
|
|
771
|
+
// The worker is a single thread. If the first completion took
|
|
772
|
+
// it down, nothing after it ever runs.
|
|
773
|
+
SentoriPush.register(ctx, activity = null, timeoutMs = 200) { second.countDown() }
|
|
774
|
+
assertTrue("the worker did not survive a throwing completion", second.await(30, TimeUnit.SECONDS))
|
|
775
|
+
}
|
|
707
776
|
}
|
package/lib/push.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"push.d.ts","sourceRoot":"","sources":["../src/push.ts"],"names":[],"mappings":"AAqCA,OAAO,EAEL,aAAa,EAEb,qBAAqB,EAEtB,MAAM,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"push.d.ts","sourceRoot":"","sources":["../src/push.ts"],"names":[],"mappings":"AAqCA,OAAO,EAEL,aAAa,EAEb,qBAAqB,EAEtB,MAAM,aAAa,CAAA;AAsCpB,MAAM,MAAM,mBAAmB,GAAG;IAChC;;;;;;mEAM+D;IAC/D,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAClC;kDAC8C;IAC9C,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,uBAAuB,KAAK,IAAI,CAAA;IACtD,sDAAsD;IACtD,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAA;IAC/B;iEAC6D;IAC7D,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAA;IAC/B;;yBAEqB;IACrB,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,KAAK,KAAK,IAAI,CAAA;IAC9B;;;kBAGc;IACd,cAAc,CAAC,EAAE,MAAM,CAAA;CACxB,CAAA;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,MAAM,mBAAmB,GAC3B,iBAAiB,GACjB,cAAc,GACd,mBAAmB,GACnB,iBAAiB,GACjB,eAAe,CAAA;AAEnB;;;;mCAImC;AACnC,MAAM,MAAM,kBAAkB,GAC1B;IACE,EAAE,EAAE,IAAI,CAAA;IACR;;oDAEgD;IAChD,GAAG,EAAE,MAAM,CAAA;CACZ,GACD;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,mBAAmB,CAAA;CAAE,CAAA;AAE/D,MAAM,MAAM,uBAAuB,GAAG;IACpC,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAClC,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB,CAAA;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,QAAQ,CAAC,IAAI,GAAE,mBAAwB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CA6C1F;AAmDD;;;GAGG;AACH,wBAAsB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CAoBhD;AAED;4DAC4D;AAC5D,wBAAgB,YAAY,IAAI,IAAI,GAAG,MAAM,CAE5C;AAED,sDAAsD;AACtD,OAAO,EAAE,aAAa,IAAI,SAAS,EAAE,qBAAqB,IAAI,iBAAiB,EAAE,CAAA;AA+RjF;oEACoE;AACpE,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,IAAI,GAAG,MAAM,GAAG,IAAI,CAEhE;AA4HD;;+DAE+D;AAC/D,wBAAgB,eAAe,IAAI,IAAI,CAWtC;AAID;qCACqC;AACrC,wBAAgB,qBAAqB,CAAC,CAAC,EAAE,KAAK,GAAG,SAAS,GAAG,SAAS,GAAG,IAAI,GAAG,IAAI,CAEnF"}
|
package/lib/push.js
CHANGED
|
@@ -24,6 +24,14 @@ import { logger, pushSignal } from '@goliapkg/sentori-core';
|
|
|
24
24
|
import { currentUserKey, currentUserTraits, onIdentityChange } from './scope.js';
|
|
25
25
|
import { pushDrainState, pushGetStatus, pushRegister as nativePushRegister, pushRequestPermission, pushUnregister as nativePushUnregister, } from './native.js';
|
|
26
26
|
const STORAGE_KEY = 'sentori.push.ipt';
|
|
27
|
+
/// Which installation this is.
|
|
28
|
+
///
|
|
29
|
+
/// Minted once and kept, so the server can key the device row on it
|
|
30
|
+
/// rather than on the vendor's token. Without one a rotation writes a
|
|
31
|
+
/// *new* row under a *new* spToken, and every backend holding the old
|
|
32
|
+
/// one is addressing nothing — with nothing to tell it. The native
|
|
33
|
+
/// SDKs were given this; this package registers from JS and was not.
|
|
34
|
+
const INSTALL_KEY = 'sentori.push.install';
|
|
27
35
|
let _cachedIpt = null;
|
|
28
36
|
// The last token the vendor handed us, kept so a sign-in can re-send
|
|
29
37
|
// the registration without asking the OS for a token again.
|
|
@@ -33,6 +41,7 @@ let _lastOptions = {};
|
|
|
33
41
|
// the same thing is not sent: `user()` is a verb an app may call on
|
|
34
42
|
// every screen, and one HTTP request per call is not free to a host.
|
|
35
43
|
let _lastSentIdentity = null;
|
|
44
|
+
let _installId = null;
|
|
36
45
|
let _drainInterval = null;
|
|
37
46
|
let _appStateSubscription = null;
|
|
38
47
|
let _backgrounded = false;
|
|
@@ -88,7 +97,11 @@ export async function register(opts = {}) {
|
|
|
88
97
|
_lastNativeToken = token;
|
|
89
98
|
_lastOptions = opts;
|
|
90
99
|
void persistIpt(ipt);
|
|
91
|
-
|
|
100
|
+
// The registration reached the server and came back with a handle.
|
|
101
|
+
// What the host does with it afterwards is not our outcome — this
|
|
102
|
+
// used to sit inside the try, so a throwing `onToken` reported a
|
|
103
|
+
// successful registration as `server-rejected`.
|
|
104
|
+
safely('onToken', () => opts.onToken?.(ipt));
|
|
92
105
|
bindBufferDrain(opts.onMessage, opts.onTap);
|
|
93
106
|
// From here on, signing in or out updates the row by itself.
|
|
94
107
|
onIdentityChange(reRegisterAfterIdentityChange);
|
|
@@ -104,7 +117,10 @@ export async function register(opts = {}) {
|
|
|
104
117
|
* wants a callback, hand back the reason. */
|
|
105
118
|
function fail(opts, reason, message) {
|
|
106
119
|
logger.warn('push', `register failed (${reason}):`, message);
|
|
107
|
-
|
|
120
|
+
// `register()` never throws is the contract, and this is the one
|
|
121
|
+
// path that hands the host an error to look at — so it is also the
|
|
122
|
+
// one place where the host's own code could have broken it.
|
|
123
|
+
safely('onError', () => opts.onError?.(new Error(message)));
|
|
108
124
|
return { ok: false, message, reason };
|
|
109
125
|
}
|
|
110
126
|
/**
|
|
@@ -218,6 +234,10 @@ async function registerWithServer(cfg, nativeToken, opts) {
|
|
|
218
234
|
// `kind`, not `provider` — the server's field name, which this
|
|
219
235
|
// sent for a year as `provider` and got a 422 for every time.
|
|
220
236
|
const body = {
|
|
237
|
+
// Which installation this is. The server keys the row on it, so a
|
|
238
|
+
// rotated token updates this device rather than creating a second
|
|
239
|
+
// one under a new address.
|
|
240
|
+
installId: await installId(),
|
|
221
241
|
kind: isAndroid ? 'fcm' : 'apns',
|
|
222
242
|
nativeToken,
|
|
223
243
|
// The same salted identity hash every event carries, so the
|
|
@@ -287,20 +307,87 @@ function teardownBufferDrain() {
|
|
|
287
307
|
_ackQueue = [];
|
|
288
308
|
}
|
|
289
309
|
async function pumpOnce() {
|
|
290
|
-
|
|
291
|
-
|
|
310
|
+
// Nothing in a tick may reject. The caller is `void pumpOnce()` on
|
|
311
|
+
// an interval, so a rejection here is an unhandled rejection in the
|
|
312
|
+
// host's process — a red box in development, and on some setups
|
|
313
|
+
// worse — for something the host is entitled to have fail quietly.
|
|
314
|
+
try {
|
|
315
|
+
const state = await pushDrainState();
|
|
316
|
+
flushBuffered(state.notifications, state.taps);
|
|
317
|
+
await reportRotationIfAny(state.token);
|
|
318
|
+
}
|
|
319
|
+
catch (e) {
|
|
320
|
+
logger.warn('push', 'a drain tick failed — the next one still runs', e);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
/// Tell the server when the vendor has handed the app a different
|
|
324
|
+
/// token from the one it registered with.
|
|
325
|
+
///
|
|
326
|
+
/// Both native layers already route a rotation into the *native*
|
|
327
|
+
/// `SentoriPush.handleRotatedToken`, and that refuses to act on a
|
|
328
|
+
/// device the native side never registered — which, for a React
|
|
329
|
+
/// Native app, is every device: registration goes through JS and the
|
|
330
|
+
/// handle lives in AsyncStorage. So the rotation arrived nowhere, and
|
|
331
|
+
/// the server held a dead token until the host next called
|
|
332
|
+
/// `register()`. For an app that stays resident that is not a bounded
|
|
333
|
+
/// wait.
|
|
334
|
+
///
|
|
335
|
+
/// This is the drain loop that already runs at 1 Hz and already reads
|
|
336
|
+
/// the token; it threw it away. Foreground only, like the rest of the
|
|
337
|
+
/// loop — a rotation while backgrounded is reported on the next tick
|
|
338
|
+
/// after the app comes back, which is bounded.
|
|
339
|
+
async function reportRotationIfAny(token) {
|
|
340
|
+
if (token == null || token === '' || token === _lastNativeToken)
|
|
341
|
+
return;
|
|
342
|
+
// Only a device that has registered: a token arriving for one that
|
|
343
|
+
// never did is not something to act on unasked, which is the rule
|
|
344
|
+
// both native SDKs follow.
|
|
345
|
+
if (_lastNativeToken == null)
|
|
346
|
+
return;
|
|
347
|
+
const cfg = tryGetRuntimeConfig();
|
|
348
|
+
if (cfg == null)
|
|
349
|
+
return;
|
|
350
|
+
const previous = _lastNativeToken;
|
|
351
|
+
_lastNativeToken = token;
|
|
352
|
+
try {
|
|
353
|
+
const ipt = await registerWithServer(cfg, token, _lastOptions);
|
|
354
|
+
_cachedIpt = ipt;
|
|
355
|
+
void persistIpt(ipt);
|
|
356
|
+
}
|
|
357
|
+
catch (e) {
|
|
358
|
+
// Put it back, or one failed report means the rotation is never
|
|
359
|
+
// mentioned again and the device is unreachable for good.
|
|
360
|
+
_lastNativeToken = previous;
|
|
361
|
+
logger.warn('push', 'reporting a rotated token failed', e);
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
/// Run the host's own code without letting it reach back into ours.
|
|
365
|
+
///
|
|
366
|
+
/// A push that does not arrive is a product decision the host can live
|
|
367
|
+
/// with. An exception out of an SDK it merely opted into is not — and
|
|
368
|
+
/// the host's handlers are the sharp edge here: they are its code,
|
|
369
|
+
/// they run inside our loop, and JavaScript throws for a living. One
|
|
370
|
+
/// throwing handler used to take the rest of the batch with it, then
|
|
371
|
+
/// the tick, then every later tick as an unhandled rejection.
|
|
372
|
+
function safely(what, fn) {
|
|
373
|
+
try {
|
|
374
|
+
fn();
|
|
375
|
+
}
|
|
376
|
+
catch (e) {
|
|
377
|
+
logger.warn('push', `${what} threw — carrying on`, e);
|
|
378
|
+
}
|
|
292
379
|
}
|
|
293
380
|
function flushBuffered(notifications, taps) {
|
|
294
381
|
for (const raw of notifications) {
|
|
295
382
|
// v2.26 — Observability link-through (rule #4). If the server
|
|
296
383
|
// injected `_sentori.msgId` in v2.25+, drop a `push` breadcrumb,
|
|
297
384
|
// emit `sentori.push.received` track, and queue the ack.
|
|
298
|
-
autoCorrelate(raw, 'received');
|
|
299
|
-
_onMessage?.(coerceNotification(raw));
|
|
385
|
+
safely('autoCorrelate', () => autoCorrelate(raw, 'received'));
|
|
386
|
+
safely('onMessage', () => _onMessage?.(coerceNotification(raw)));
|
|
300
387
|
}
|
|
301
388
|
for (const raw of taps) {
|
|
302
|
-
autoCorrelate(raw, 'opened');
|
|
303
|
-
_onTap?.(raw.userInfo ?? raw);
|
|
389
|
+
safely('autoCorrelate', () => autoCorrelate(raw, 'opened'));
|
|
390
|
+
safely('onTap', () => _onTap?.(raw.userInfo ?? raw));
|
|
304
391
|
}
|
|
305
392
|
}
|
|
306
393
|
/** v2.26 — process one drained notification or tap for downstream
|
|
@@ -407,6 +494,54 @@ function startAppStateWatch() {
|
|
|
407
494
|
/* react-native unavailable (unit test) */
|
|
408
495
|
}
|
|
409
496
|
}
|
|
497
|
+
/// This installation's id, minted on first use and kept after.
|
|
498
|
+
///
|
|
499
|
+
/// Falls back to a module-scoped value when there is no AsyncStorage,
|
|
500
|
+
/// which is the same fallback the handle uses: worse than persistent,
|
|
501
|
+
/// better than nothing, and it still keeps one launch's rotations
|
|
502
|
+
/// pointed at one row.
|
|
503
|
+
async function installId() {
|
|
504
|
+
if (_installId != null)
|
|
505
|
+
return _installId;
|
|
506
|
+
const storage = await tryAsyncStorage();
|
|
507
|
+
if (storage) {
|
|
508
|
+
try {
|
|
509
|
+
const seen = await storage.getItem(INSTALL_KEY);
|
|
510
|
+
if (typeof seen === 'string' && seen.length > 0) {
|
|
511
|
+
_installId = seen;
|
|
512
|
+
return seen;
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
catch {
|
|
516
|
+
// An unreadable store is a store with nothing in it.
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
const fresh = randomId();
|
|
520
|
+
_installId = fresh;
|
|
521
|
+
if (storage) {
|
|
522
|
+
try {
|
|
523
|
+
await storage.setItem(INSTALL_KEY, fresh);
|
|
524
|
+
}
|
|
525
|
+
catch (e) {
|
|
526
|
+
logger.warn('push', 'AsyncStorage.setItem failed', e);
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
return fresh;
|
|
530
|
+
}
|
|
531
|
+
/// A uuid, without requiring one to exist.
|
|
532
|
+
///
|
|
533
|
+
/// `crypto.randomUUID` is present on newer Hermes and absent on older
|
|
534
|
+
/// ones, and this must not be the line that decides whether push
|
|
535
|
+
/// works on an older engine.
|
|
536
|
+
function randomId() {
|
|
537
|
+
const c = globalThis.crypto;
|
|
538
|
+
if (typeof c?.randomUUID === 'function')
|
|
539
|
+
return c.randomUUID();
|
|
540
|
+
let out = '';
|
|
541
|
+
for (let i = 0; i < 32; i++)
|
|
542
|
+
out += Math.floor(Math.random() * 16).toString(16);
|
|
543
|
+
return out;
|
|
544
|
+
}
|
|
410
545
|
async function persistIpt(ipt) {
|
|
411
546
|
const storage = await tryAsyncStorage();
|
|
412
547
|
if (!storage)
|
|
@@ -455,6 +590,7 @@ function joinUrl(base, path) {
|
|
|
455
590
|
* into the next. Production code paths must not call this. */
|
|
456
591
|
export function __resetForTests() {
|
|
457
592
|
_cachedIpt = null;
|
|
593
|
+
_installId = null;
|
|
458
594
|
_onMessage = undefined;
|
|
459
595
|
_onTap = undefined;
|
|
460
596
|
_ackQueue = [];
|
package/lib/push.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"push.js","sourceRoot":"","sources":["../src/push.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"push.js","sourceRoot":"","sources":["../src/push.ts"],"names":[],"mappings":"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"}
|
package/lib/transport.js
CHANGED
|
@@ -15,7 +15,7 @@ const MAX_RETRY = 3;
|
|
|
15
15
|
const STORAGE_KEY = '@sentori/pending';
|
|
16
16
|
const MAX_PERSISTED = 1000;
|
|
17
17
|
// Pinned to package.json by a test — bump both together.
|
|
18
|
-
const SDK_VERSION = '6.
|
|
18
|
+
const SDK_VERSION = '6.4.1';
|
|
19
19
|
let _queue = [];
|
|
20
20
|
let _assertStats = new Map();
|
|
21
21
|
let _flushTimer = null;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@goliapkg/sentori-react-native",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.4.1",
|
|
4
4
|
"description": "Sentori SDK for React Native — JS-layer error capture, native crash handlers (iOS / Android), batched transport, fetch + react-navigation tracing.",
|
|
5
5
|
"license": "Apache-2.0 OR MIT",
|
|
6
6
|
"author": "GOLIA K.K. <takagi@golia.jp> (https://golia.jp)",
|
|
@@ -17,6 +17,14 @@ import {
|
|
|
17
17
|
startTransport,
|
|
18
18
|
} from '../transport';
|
|
19
19
|
import { verbs } from '../verbs';
|
|
20
|
+
import { __setNativeForTests as setNative } from '../native';
|
|
21
|
+
import {
|
|
22
|
+
__resetForTests as resetPush,
|
|
23
|
+
__setPlatformForTests as setPlatform,
|
|
24
|
+
getCachedIpt,
|
|
25
|
+
register as pushRegister,
|
|
26
|
+
unregister as pushUnregister,
|
|
27
|
+
} from '../push';
|
|
20
28
|
|
|
21
29
|
const baseConfig = {
|
|
22
30
|
token: 'st_test',
|
|
@@ -126,3 +134,113 @@ describe('iron rule: init timing', () => {
|
|
|
126
134
|
expect(ms).toBeLessThan(50);
|
|
127
135
|
});
|
|
128
136
|
});
|
|
137
|
+
|
|
138
|
+
// Dimension 3, for push specifically.
|
|
139
|
+
//
|
|
140
|
+
// The rule was written down and the gate did not reach here: this file
|
|
141
|
+
// covered the five event verbs and mentioned push nowhere, while push
|
|
142
|
+
// is the surface with the most ways to go wrong — an OS permission, a
|
|
143
|
+
// vendor token, a network round trip, a background loop, and the
|
|
144
|
+
// host's own handlers running inside it.
|
|
145
|
+
//
|
|
146
|
+
// A notification that does not arrive is something the host can live
|
|
147
|
+
// with. An exception out of an SDK it merely opted into is not.
|
|
148
|
+
describe('iron rule: push never reaches the host', () => {
|
|
149
|
+
beforeEach(() => {
|
|
150
|
+
resetAll();
|
|
151
|
+
resetPush();
|
|
152
|
+
setPlatform('ios');
|
|
153
|
+
setConfig(baseConfig);
|
|
154
|
+
});
|
|
155
|
+
afterEach(() => {
|
|
156
|
+
globalThis.fetch = originalFetch;
|
|
157
|
+
setPlatform(null);
|
|
158
|
+
setNative(undefined);
|
|
159
|
+
resetPush();
|
|
160
|
+
resetAll();
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
const granting = {
|
|
164
|
+
pushRequestPermission: () => Promise.resolve('granted'),
|
|
165
|
+
pushGetStatus: () => Promise.resolve('granted'),
|
|
166
|
+
pushRegister: () => undefined,
|
|
167
|
+
pushUnregister: () => undefined,
|
|
168
|
+
pushDrainState: () => Promise.resolve({ notifications: [], taps: [], token: 'abc' }),
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
it('a server that answers 500 is an outcome, not an exception', async () => {
|
|
172
|
+
setNative(granting);
|
|
173
|
+
globalThis.fetch = (async () => new Response('boom', { status: 500 })) as typeof fetch;
|
|
174
|
+
const r = await pushRegister();
|
|
175
|
+
expect(r.ok).toBe(false);
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
it('a network that is down is an outcome, not an exception', async () => {
|
|
179
|
+
setNative(granting);
|
|
180
|
+
globalThis.fetch = (async () => {
|
|
181
|
+
throw new Error('offline');
|
|
182
|
+
}) as typeof fetch;
|
|
183
|
+
const r = await pushRegister();
|
|
184
|
+
expect(r.ok).toBe(false);
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
it('a server that answers nonsense is an outcome, not an exception', async () => {
|
|
188
|
+
setNative(granting);
|
|
189
|
+
globalThis.fetch = (async () => new Response('<html>not json</html>', { status: 200 })) as typeof fetch;
|
|
190
|
+
const r = await pushRegister();
|
|
191
|
+
expect(r.ok).toBe(false);
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
it('a native module that throws is an outcome, not an exception', async () => {
|
|
195
|
+
setNative({
|
|
196
|
+
...granting,
|
|
197
|
+
pushRequestPermission: () => {
|
|
198
|
+
throw new Error('bridge died');
|
|
199
|
+
},
|
|
200
|
+
});
|
|
201
|
+
const r = await pushRegister();
|
|
202
|
+
expect(r.ok).toBe(false);
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
it('fuzz: the push verbs accept garbage without throwing', async () => {
|
|
206
|
+
setNative(granting);
|
|
207
|
+
globalThis.fetch = (async () =>
|
|
208
|
+
new Response(JSON.stringify({ spToken: 'dev-1' }), { status: 200 })) as typeof fetch;
|
|
209
|
+
const circular: Record<string, unknown> = { a: 1 };
|
|
210
|
+
circular.self = circular;
|
|
211
|
+
const garbage = [
|
|
212
|
+
undefined,
|
|
213
|
+
null,
|
|
214
|
+
{},
|
|
215
|
+
{ metadata: circular },
|
|
216
|
+
{ metadata: { huge: 'x'.repeat(200_000) } },
|
|
217
|
+
{ onMessage: 'not a function' },
|
|
218
|
+
{ tokenTimeoutMs: -1 },
|
|
219
|
+
{ tokenTimeoutMs: Number.NaN },
|
|
220
|
+
];
|
|
221
|
+
for (const g of garbage) {
|
|
222
|
+
const r = await pushRegister(g as never);
|
|
223
|
+
expect(typeof r.ok).toBe('boolean');
|
|
224
|
+
}
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
it('every push verb before init is a quiet outcome', async () => {
|
|
228
|
+
resetConfig();
|
|
229
|
+
setNative(granting);
|
|
230
|
+
const r = await pushRegister();
|
|
231
|
+
expect(r).toEqual({ ok: false, message: 'sentori.init() has not run', reason: 'not-initialised' });
|
|
232
|
+
await pushUnregister();
|
|
233
|
+
expect(await getCachedIpt()).toBe(null);
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
it('unregister cannot fail, whatever the server does', async () => {
|
|
237
|
+
setNative(granting);
|
|
238
|
+
globalThis.fetch = (async () =>
|
|
239
|
+
new Response(JSON.stringify({ spToken: 'dev-1' }), { status: 200 })) as typeof fetch;
|
|
240
|
+
await pushRegister();
|
|
241
|
+
globalThis.fetch = (async () => {
|
|
242
|
+
throw new Error('offline');
|
|
243
|
+
}) as typeof fetch;
|
|
244
|
+
await pushUnregister();
|
|
245
|
+
});
|
|
246
|
+
});
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
__resetForTests as resetPush,
|
|
14
14
|
__setPlatformForTests as setPlatform,
|
|
15
15
|
register,
|
|
16
|
+
unregister,
|
|
16
17
|
} from '../push';
|
|
17
18
|
import { __resetForTests as resetConfig, setConfig } from '../config';
|
|
18
19
|
import { __resetForTests as resetScope, setUser } from '../scope';
|
|
@@ -300,3 +301,251 @@ describe('push registration follows the person', () => {
|
|
|
300
301
|
expect(seen).toHaveLength(0);
|
|
301
302
|
});
|
|
302
303
|
});
|
|
304
|
+
|
|
305
|
+
// A vendor token is not an address. It rotates — on a reinstall, a
|
|
306
|
+
// restore, cleared app data — and the server keys the row on it, so a
|
|
307
|
+
// rotation wrote a *new* row under a *new* spToken and every backend
|
|
308
|
+
// holding the old one was addressing nothing.
|
|
309
|
+
//
|
|
310
|
+
// The native SDKs were given an installId and a rotation report. This
|
|
311
|
+
// package was not: its registration goes through JS, and the native
|
|
312
|
+
// rotation hook it does have refuses to act on a device the *native*
|
|
313
|
+
// side never registered — which, for a React Native app, is every
|
|
314
|
+
// device.
|
|
315
|
+
describe('push survives the vendor rotating its token', () => {
|
|
316
|
+
function recordingFetch(): Array<Record<string, unknown>> {
|
|
317
|
+
const seen: Array<Record<string, unknown>> = [];
|
|
318
|
+
globalThis.fetch = ((_url: string, init?: { body?: string }) => {
|
|
319
|
+
if (init?.body != null) {
|
|
320
|
+
seen.push(JSON.parse(init.body) as Record<string, unknown>);
|
|
321
|
+
}
|
|
322
|
+
return Promise.resolve({
|
|
323
|
+
ok: true,
|
|
324
|
+
status: 202,
|
|
325
|
+
json: () => Promise.resolve({ spToken: 'dev-1', token_id: 'dev-1' }),
|
|
326
|
+
});
|
|
327
|
+
}) as unknown as typeof fetch;
|
|
328
|
+
return seen;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
beforeEach(() => {
|
|
332
|
+
resetPush();
|
|
333
|
+
resetConfig();
|
|
334
|
+
resetScope();
|
|
335
|
+
setPlatform('ios');
|
|
336
|
+
setConfig(baseConfig);
|
|
337
|
+
});
|
|
338
|
+
afterEach(() => {
|
|
339
|
+
resetPush();
|
|
340
|
+
resetConfig();
|
|
341
|
+
resetScope();
|
|
342
|
+
setPlatform(null);
|
|
343
|
+
__setNativeForTests(undefined);
|
|
344
|
+
globalThis.fetch = realFetch;
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
it('tells the server which installation this is', async () => {
|
|
348
|
+
__setNativeForTests(grantingNative());
|
|
349
|
+
const seen = recordingFetch();
|
|
350
|
+
await register();
|
|
351
|
+
const installId = seen[0]?.installId;
|
|
352
|
+
expect(typeof installId).toBe('string');
|
|
353
|
+
expect(String(installId).length).toBeGreaterThan(8);
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
it('keeps the same installation across registrations', async () => {
|
|
357
|
+
__setNativeForTests(grantingNative());
|
|
358
|
+
const seen = recordingFetch();
|
|
359
|
+
await register();
|
|
360
|
+
await register();
|
|
361
|
+
expect(seen).toHaveLength(2);
|
|
362
|
+
// Asserting equality alone passes when neither call sent one —
|
|
363
|
+
// `undefined === undefined` — which is the state this test
|
|
364
|
+
// exists to reject. So: a string, and the same string.
|
|
365
|
+
expect(typeof seen[0]?.installId).toBe('string');
|
|
366
|
+
expect(seen[1]?.installId).toBe(seen[0]?.installId);
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
it('reports a token that rotated while the app was running', async () => {
|
|
370
|
+
// The vendor hands back a different token on the second drain,
|
|
371
|
+
// which is what a rotation looks like from here.
|
|
372
|
+
let handed = 0;
|
|
373
|
+
__setNativeForTests(
|
|
374
|
+
grantingNative({
|
|
375
|
+
pushDrainState: () => {
|
|
376
|
+
handed += 1;
|
|
377
|
+
return Promise.resolve({
|
|
378
|
+
notifications: [],
|
|
379
|
+
taps: [],
|
|
380
|
+
token: handed <= 1 ? 'abc123' : 'rotated456',
|
|
381
|
+
});
|
|
382
|
+
},
|
|
383
|
+
}),
|
|
384
|
+
);
|
|
385
|
+
const seen = recordingFetch();
|
|
386
|
+
await register();
|
|
387
|
+
expect(seen).toHaveLength(1);
|
|
388
|
+
|
|
389
|
+
// One turn of the drain loop, which already runs at 1 Hz and
|
|
390
|
+
// already reads the token — it just threw it away.
|
|
391
|
+
await new Promise((r) => setTimeout(r, 1400));
|
|
392
|
+
|
|
393
|
+
expect(seen).toHaveLength(2);
|
|
394
|
+
expect(seen[1]?.nativeToken).toBe('rotated456');
|
|
395
|
+
// Same installation, or the server writes a second row and the
|
|
396
|
+
// rotation has achieved exactly what it used to.
|
|
397
|
+
expect(seen[1]?.installId).toBe(seen[0]?.installId);
|
|
398
|
+
});
|
|
399
|
+
|
|
400
|
+
it('does not report the same token twice', async () => {
|
|
401
|
+
__setNativeForTests(grantingNative());
|
|
402
|
+
const seen = recordingFetch();
|
|
403
|
+
await register();
|
|
404
|
+
await new Promise((r) => setTimeout(r, 2400));
|
|
405
|
+
expect(seen).toHaveLength(1);
|
|
406
|
+
});
|
|
407
|
+
});
|
|
408
|
+
|
|
409
|
+
// Push is allowed to fail silently. It is never allowed to make the
|
|
410
|
+
// host app fail — a notification that does not arrive is a product
|
|
411
|
+
// decision the host can live with; an exception out of an SDK the host
|
|
412
|
+
// merely opted into is not.
|
|
413
|
+
//
|
|
414
|
+
// The host's own handlers are the sharp edge: they are its code, they
|
|
415
|
+
// run inside our loop, and JavaScript throws for a living.
|
|
416
|
+
describe('nothing the host does can reach back into the app', () => {
|
|
417
|
+
const boom = () => {
|
|
418
|
+
throw new Error('the host threw');
|
|
419
|
+
};
|
|
420
|
+
|
|
421
|
+
/** Hands the batch over once and is empty after, the way the real
|
|
422
|
+
* native drain is — a stub that keeps re-delivering makes a second
|
|
423
|
+
* tick look like a bug in the SDK. */
|
|
424
|
+
function drainingNative(notifications: unknown[], taps: unknown[]) {
|
|
425
|
+
let drained = false;
|
|
426
|
+
return grantingNative({
|
|
427
|
+
pushDrainState: () => {
|
|
428
|
+
const batch = drained
|
|
429
|
+
? { notifications: [], taps: [] }
|
|
430
|
+
: { notifications, taps };
|
|
431
|
+
drained = true;
|
|
432
|
+
return Promise.resolve({ ...batch, token: 'abc123' });
|
|
433
|
+
},
|
|
434
|
+
});
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
function okFetch(): void {
|
|
438
|
+
globalThis.fetch = (() =>
|
|
439
|
+
Promise.resolve({
|
|
440
|
+
ok: true,
|
|
441
|
+
status: 202,
|
|
442
|
+
json: () => Promise.resolve({ spToken: 'dev-1', token_id: 'dev-1' }),
|
|
443
|
+
})) as unknown as typeof fetch;
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
beforeEach(() => {
|
|
447
|
+
resetPush();
|
|
448
|
+
resetConfig();
|
|
449
|
+
resetScope();
|
|
450
|
+
setPlatform('ios');
|
|
451
|
+
setConfig(baseConfig);
|
|
452
|
+
okFetch();
|
|
453
|
+
});
|
|
454
|
+
afterEach(() => {
|
|
455
|
+
resetPush();
|
|
456
|
+
resetConfig();
|
|
457
|
+
resetScope();
|
|
458
|
+
setPlatform(null);
|
|
459
|
+
__setNativeForTests(undefined);
|
|
460
|
+
globalThis.fetch = realFetch;
|
|
461
|
+
});
|
|
462
|
+
|
|
463
|
+
it('keeps delivering after a handler throws', async () => {
|
|
464
|
+
const seen: string[] = [];
|
|
465
|
+
__setNativeForTests(
|
|
466
|
+
drainingNative(
|
|
467
|
+
[{ title: 'one' }, { title: 'two' }],
|
|
468
|
+
[{ tapped: true }],
|
|
469
|
+
),
|
|
470
|
+
);
|
|
471
|
+
const r = await register({
|
|
472
|
+
onMessage: (n) => {
|
|
473
|
+
seen.push(String(n.title));
|
|
474
|
+
// The first one throws. The second one, and the tap after it,
|
|
475
|
+
// still have to arrive: one bad handler must not swallow the
|
|
476
|
+
// rest of the batch.
|
|
477
|
+
if (n.title === 'one') boom();
|
|
478
|
+
},
|
|
479
|
+
onTap: () => seen.push('tap'),
|
|
480
|
+
});
|
|
481
|
+
expect(r.ok).toBe(true);
|
|
482
|
+
await new Promise((res) => setTimeout(res, 1400));
|
|
483
|
+
expect(seen).toEqual(['one', 'two', 'tap']);
|
|
484
|
+
});
|
|
485
|
+
|
|
486
|
+
it('keeps ticking after a handler throws', async () => {
|
|
487
|
+
let ticks = 0;
|
|
488
|
+
__setNativeForTests(
|
|
489
|
+
grantingNative({
|
|
490
|
+
pushDrainState: () => {
|
|
491
|
+
ticks += 1;
|
|
492
|
+
return Promise.resolve({
|
|
493
|
+
notifications: [{ title: 'n' }],
|
|
494
|
+
taps: [],
|
|
495
|
+
token: 'abc123',
|
|
496
|
+
});
|
|
497
|
+
},
|
|
498
|
+
}),
|
|
499
|
+
);
|
|
500
|
+
await register({ onMessage: boom });
|
|
501
|
+
const first = ticks;
|
|
502
|
+
await new Promise((res) => setTimeout(res, 2400));
|
|
503
|
+
// A throwing handler that stops the loop is worse than one that
|
|
504
|
+
// drops a notification: every later push is gone too, silently.
|
|
505
|
+
expect(ticks).toBeGreaterThan(first + 1);
|
|
506
|
+
});
|
|
507
|
+
|
|
508
|
+
it('survives a native module that rejects', async () => {
|
|
509
|
+
let ticks = 0;
|
|
510
|
+
__setNativeForTests(
|
|
511
|
+
grantingNative({
|
|
512
|
+
pushDrainState: () => {
|
|
513
|
+
ticks += 1;
|
|
514
|
+
return ticks === 1
|
|
515
|
+
? Promise.resolve({ notifications: [], taps: [], token: 'abc123' })
|
|
516
|
+
: Promise.reject(new Error('bridge died'));
|
|
517
|
+
},
|
|
518
|
+
}),
|
|
519
|
+
);
|
|
520
|
+
await register();
|
|
521
|
+
await new Promise((res) => setTimeout(res, 2400));
|
|
522
|
+
expect(ticks).toBeGreaterThan(2);
|
|
523
|
+
});
|
|
524
|
+
|
|
525
|
+
it('does not report a failure because onToken threw', async () => {
|
|
526
|
+
__setNativeForTests(grantingNative());
|
|
527
|
+
const r = await register({ onToken: boom });
|
|
528
|
+
// The registration reached the server and came back with a handle.
|
|
529
|
+
// What the host did with it afterwards is not our outcome.
|
|
530
|
+
expect(r).toEqual({ ok: true, ipt: 'dev-1' });
|
|
531
|
+
});
|
|
532
|
+
|
|
533
|
+
it('still resolves when onError throws', async () => {
|
|
534
|
+
// `register()` never throws is the documented contract, and the
|
|
535
|
+
// one path that reports failure is the one that hands the host an
|
|
536
|
+
// error to look at.
|
|
537
|
+
__setNativeForTests(
|
|
538
|
+
grantingNative({ pushRequestPermission: () => Promise.resolve('denied') }),
|
|
539
|
+
);
|
|
540
|
+
const r = await register({ onError: boom });
|
|
541
|
+
expect(r.ok).toBe(false);
|
|
542
|
+
});
|
|
543
|
+
|
|
544
|
+
it('unregister does not throw when the server is unreachable', async () => {
|
|
545
|
+
__setNativeForTests(grantingNative());
|
|
546
|
+
await register();
|
|
547
|
+
globalThis.fetch = (() => Promise.reject(new Error('offline'))) as unknown as typeof fetch;
|
|
548
|
+
// Signing out must not be something that can fail.
|
|
549
|
+
await unregister();
|
|
550
|
+
});
|
|
551
|
+
});
|
package/src/push.ts
CHANGED
|
@@ -44,6 +44,14 @@ import {
|
|
|
44
44
|
} from './native.js'
|
|
45
45
|
|
|
46
46
|
const STORAGE_KEY = 'sentori.push.ipt'
|
|
47
|
+
/// Which installation this is.
|
|
48
|
+
///
|
|
49
|
+
/// Minted once and kept, so the server can key the device row on it
|
|
50
|
+
/// rather than on the vendor's token. Without one a rotation writes a
|
|
51
|
+
/// *new* row under a *new* spToken, and every backend holding the old
|
|
52
|
+
/// one is addressing nothing — with nothing to tell it. The native
|
|
53
|
+
/// SDKs were given this; this package registers from JS and was not.
|
|
54
|
+
const INSTALL_KEY = 'sentori.push.install'
|
|
47
55
|
|
|
48
56
|
let _cachedIpt: null | string = null
|
|
49
57
|
// The last token the vendor handed us, kept so a sign-in can re-send
|
|
@@ -54,6 +62,7 @@ let _lastOptions: PushRegisterOptions = {}
|
|
|
54
62
|
// the same thing is not sent: `user()` is a verb an app may call on
|
|
55
63
|
// every screen, and one HTTP request per call is not free to a host.
|
|
56
64
|
let _lastSentIdentity: string | null = null
|
|
65
|
+
let _installId: null | string = null
|
|
57
66
|
let _drainInterval: ReturnType<typeof setInterval> | null = null
|
|
58
67
|
let _appStateSubscription: { remove: () => void } | null = null
|
|
59
68
|
let _backgrounded = false
|
|
@@ -187,7 +196,11 @@ export async function register(opts: PushRegisterOptions = {}): Promise<PushRegi
|
|
|
187
196
|
_lastNativeToken = token
|
|
188
197
|
_lastOptions = opts
|
|
189
198
|
void persistIpt(ipt)
|
|
190
|
-
|
|
199
|
+
// The registration reached the server and came back with a handle.
|
|
200
|
+
// What the host does with it afterwards is not our outcome — this
|
|
201
|
+
// used to sit inside the try, so a throwing `onToken` reported a
|
|
202
|
+
// successful registration as `server-rejected`.
|
|
203
|
+
safely('onToken', () => opts.onToken?.(ipt))
|
|
191
204
|
bindBufferDrain(opts.onMessage, opts.onTap)
|
|
192
205
|
// From here on, signing in or out updates the row by itself.
|
|
193
206
|
onIdentityChange(reRegisterAfterIdentityChange)
|
|
@@ -207,7 +220,10 @@ function fail(
|
|
|
207
220
|
message: string,
|
|
208
221
|
): PushRegisterResult {
|
|
209
222
|
logger.warn('push', `register failed (${reason}):`, message)
|
|
210
|
-
|
|
223
|
+
// `register()` never throws is the contract, and this is the one
|
|
224
|
+
// path that hands the host an error to look at — so it is also the
|
|
225
|
+
// one place where the host's own code could have broken it.
|
|
226
|
+
safely('onError', () => opts.onError?.(new Error(message)))
|
|
211
227
|
return { ok: false, message, reason }
|
|
212
228
|
}
|
|
213
229
|
|
|
@@ -333,6 +349,10 @@ async function registerWithServer(
|
|
|
333
349
|
// `kind`, not `provider` — the server's field name, which this
|
|
334
350
|
// sent for a year as `provider` and got a 422 for every time.
|
|
335
351
|
const body: Record<string, unknown> = {
|
|
352
|
+
// Which installation this is. The server keys the row on it, so a
|
|
353
|
+
// rotated token updates this device rather than creating a second
|
|
354
|
+
// one under a new address.
|
|
355
|
+
installId: await installId(),
|
|
336
356
|
kind: isAndroid ? 'fcm' : 'apns',
|
|
337
357
|
nativeToken,
|
|
338
358
|
// The same salted identity hash every event carries, so the
|
|
@@ -404,8 +424,71 @@ function teardownBufferDrain(): void {
|
|
|
404
424
|
}
|
|
405
425
|
|
|
406
426
|
async function pumpOnce(): Promise<void> {
|
|
407
|
-
|
|
408
|
-
|
|
427
|
+
// Nothing in a tick may reject. The caller is `void pumpOnce()` on
|
|
428
|
+
// an interval, so a rejection here is an unhandled rejection in the
|
|
429
|
+
// host's process — a red box in development, and on some setups
|
|
430
|
+
// worse — for something the host is entitled to have fail quietly.
|
|
431
|
+
try {
|
|
432
|
+
const state = await pushDrainState()
|
|
433
|
+
flushBuffered(state.notifications, state.taps)
|
|
434
|
+
await reportRotationIfAny(state.token)
|
|
435
|
+
} catch (e) {
|
|
436
|
+
logger.warn('push', 'a drain tick failed — the next one still runs', e)
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
/// Tell the server when the vendor has handed the app a different
|
|
441
|
+
/// token from the one it registered with.
|
|
442
|
+
///
|
|
443
|
+
/// Both native layers already route a rotation into the *native*
|
|
444
|
+
/// `SentoriPush.handleRotatedToken`, and that refuses to act on a
|
|
445
|
+
/// device the native side never registered — which, for a React
|
|
446
|
+
/// Native app, is every device: registration goes through JS and the
|
|
447
|
+
/// handle lives in AsyncStorage. So the rotation arrived nowhere, and
|
|
448
|
+
/// the server held a dead token until the host next called
|
|
449
|
+
/// `register()`. For an app that stays resident that is not a bounded
|
|
450
|
+
/// wait.
|
|
451
|
+
///
|
|
452
|
+
/// This is the drain loop that already runs at 1 Hz and already reads
|
|
453
|
+
/// the token; it threw it away. Foreground only, like the rest of the
|
|
454
|
+
/// loop — a rotation while backgrounded is reported on the next tick
|
|
455
|
+
/// after the app comes back, which is bounded.
|
|
456
|
+
async function reportRotationIfAny(token: string | undefined): Promise<void> {
|
|
457
|
+
if (token == null || token === '' || token === _lastNativeToken) return
|
|
458
|
+
// Only a device that has registered: a token arriving for one that
|
|
459
|
+
// never did is not something to act on unasked, which is the rule
|
|
460
|
+
// both native SDKs follow.
|
|
461
|
+
if (_lastNativeToken == null) return
|
|
462
|
+
const cfg = tryGetRuntimeConfig()
|
|
463
|
+
if (cfg == null) return
|
|
464
|
+
const previous = _lastNativeToken
|
|
465
|
+
_lastNativeToken = token
|
|
466
|
+
try {
|
|
467
|
+
const ipt = await registerWithServer(cfg, token, _lastOptions)
|
|
468
|
+
_cachedIpt = ipt
|
|
469
|
+
void persistIpt(ipt)
|
|
470
|
+
} catch (e) {
|
|
471
|
+
// Put it back, or one failed report means the rotation is never
|
|
472
|
+
// mentioned again and the device is unreachable for good.
|
|
473
|
+
_lastNativeToken = previous
|
|
474
|
+
logger.warn('push', 'reporting a rotated token failed', e)
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
/// Run the host's own code without letting it reach back into ours.
|
|
479
|
+
///
|
|
480
|
+
/// A push that does not arrive is a product decision the host can live
|
|
481
|
+
/// with. An exception out of an SDK it merely opted into is not — and
|
|
482
|
+
/// the host's handlers are the sharp edge here: they are its code,
|
|
483
|
+
/// they run inside our loop, and JavaScript throws for a living. One
|
|
484
|
+
/// throwing handler used to take the rest of the batch with it, then
|
|
485
|
+
/// the tick, then every later tick as an unhandled rejection.
|
|
486
|
+
function safely(what: string, fn: () => void): void {
|
|
487
|
+
try {
|
|
488
|
+
fn()
|
|
489
|
+
} catch (e) {
|
|
490
|
+
logger.warn('push', `${what} threw — carrying on`, e)
|
|
491
|
+
}
|
|
409
492
|
}
|
|
410
493
|
|
|
411
494
|
function flushBuffered(
|
|
@@ -416,12 +499,12 @@ function flushBuffered(
|
|
|
416
499
|
// v2.26 — Observability link-through (rule #4). If the server
|
|
417
500
|
// injected `_sentori.msgId` in v2.25+, drop a `push` breadcrumb,
|
|
418
501
|
// emit `sentori.push.received` track, and queue the ack.
|
|
419
|
-
autoCorrelate(raw, 'received')
|
|
420
|
-
_onMessage?.(coerceNotification(raw))
|
|
502
|
+
safely('autoCorrelate', () => autoCorrelate(raw, 'received'))
|
|
503
|
+
safely('onMessage', () => _onMessage?.(coerceNotification(raw)))
|
|
421
504
|
}
|
|
422
505
|
for (const raw of taps) {
|
|
423
|
-
autoCorrelate(raw, 'opened')
|
|
424
|
-
_onTap?.(raw.userInfo ?? raw)
|
|
506
|
+
safely('autoCorrelate', () => autoCorrelate(raw, 'opened'))
|
|
507
|
+
safely('onTap', () => _onTap?.(raw.userInfo ?? raw))
|
|
425
508
|
}
|
|
426
509
|
}
|
|
427
510
|
|
|
@@ -531,6 +614,51 @@ function startAppStateWatch(): void {
|
|
|
531
614
|
}
|
|
532
615
|
}
|
|
533
616
|
|
|
617
|
+
/// This installation's id, minted on first use and kept after.
|
|
618
|
+
///
|
|
619
|
+
/// Falls back to a module-scoped value when there is no AsyncStorage,
|
|
620
|
+
/// which is the same fallback the handle uses: worse than persistent,
|
|
621
|
+
/// better than nothing, and it still keeps one launch's rotations
|
|
622
|
+
/// pointed at one row.
|
|
623
|
+
async function installId(): Promise<string> {
|
|
624
|
+
if (_installId != null) return _installId
|
|
625
|
+
const storage = await tryAsyncStorage()
|
|
626
|
+
if (storage) {
|
|
627
|
+
try {
|
|
628
|
+
const seen = await storage.getItem(INSTALL_KEY)
|
|
629
|
+
if (typeof seen === 'string' && seen.length > 0) {
|
|
630
|
+
_installId = seen
|
|
631
|
+
return seen
|
|
632
|
+
}
|
|
633
|
+
} catch {
|
|
634
|
+
// An unreadable store is a store with nothing in it.
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
const fresh = randomId()
|
|
638
|
+
_installId = fresh
|
|
639
|
+
if (storage) {
|
|
640
|
+
try {
|
|
641
|
+
await storage.setItem(INSTALL_KEY, fresh)
|
|
642
|
+
} catch (e) {
|
|
643
|
+
logger.warn('push', 'AsyncStorage.setItem failed', e)
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
return fresh
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
/// A uuid, without requiring one to exist.
|
|
650
|
+
///
|
|
651
|
+
/// `crypto.randomUUID` is present on newer Hermes and absent on older
|
|
652
|
+
/// ones, and this must not be the line that decides whether push
|
|
653
|
+
/// works on an older engine.
|
|
654
|
+
function randomId(): string {
|
|
655
|
+
const c = (globalThis as { crypto?: { randomUUID?: () => string } }).crypto
|
|
656
|
+
if (typeof c?.randomUUID === 'function') return c.randomUUID()
|
|
657
|
+
let out = ''
|
|
658
|
+
for (let i = 0; i < 32; i++) out += Math.floor(Math.random() * 16).toString(16)
|
|
659
|
+
return out
|
|
660
|
+
}
|
|
661
|
+
|
|
534
662
|
async function persistIpt(ipt: string): Promise<void> {
|
|
535
663
|
const storage = await tryAsyncStorage()
|
|
536
664
|
if (!storage) return
|
|
@@ -586,6 +714,7 @@ function joinUrl(base: string, path: string): string {
|
|
|
586
714
|
* into the next. Production code paths must not call this. */
|
|
587
715
|
export function __resetForTests(): void {
|
|
588
716
|
_cachedIpt = null
|
|
717
|
+
_installId = null
|
|
589
718
|
_onMessage = undefined
|
|
590
719
|
_onTap = undefined
|
|
591
720
|
_ackQueue = []
|
package/src/transport.ts
CHANGED
|
@@ -20,7 +20,7 @@ const STORAGE_KEY = '@sentori/pending';
|
|
|
20
20
|
const MAX_PERSISTED = 1000;
|
|
21
21
|
|
|
22
22
|
// Pinned to package.json by a test — bump both together.
|
|
23
|
-
const SDK_VERSION = '6.
|
|
23
|
+
const SDK_VERSION = '6.4.1';
|
|
24
24
|
|
|
25
25
|
let _queue: WireEvent[] = [];
|
|
26
26
|
let _assertStats = new Map<string, AssertStat>();
|