expo-observe 57.0.13 → 57.0.15
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 +11 -0
- package/android/src/main/java/expo/modules/observe/Constants.kt +1 -0
- package/android/src/main/java/expo/modules/observe/DispatchUtils.kt +15 -8
- package/android/src/main/java/expo/modules/observe/EventDispatcher.kt +2 -0
- package/android/src/main/java/expo/modules/observe/ObservabilityBackgroundWorker.kt +2 -1
- package/android/src/main/java/expo/modules/observe/ObservabilityManager.kt +138 -89
- package/android/src/main/java/expo/modules/observe/storage/ObserveDatabase.kt +18 -4
- package/android/src/main/java/expo/modules/observe/storage/PendingLogsManager.kt +5 -1
- package/android/src/main/java/expo/modules/observe/storage/PendingMetricsManager.kt +5 -1
- package/expo-module.config.json +1 -1
- package/local-maven-repo/expo/modules/observe/expo.modules.observe/57.0.15/expo.modules.observe-57.0.15-sources.jar +0 -0
- package/local-maven-repo/expo/modules/observe/expo.modules.observe/57.0.15/expo.modules.observe-57.0.15-sources.jar.md5 +1 -0
- package/local-maven-repo/expo/modules/observe/expo.modules.observe/57.0.15/expo.modules.observe-57.0.15-sources.jar.sha1 +1 -0
- package/local-maven-repo/expo/modules/observe/expo.modules.observe/57.0.15/expo.modules.observe-57.0.15-sources.jar.sha256 +1 -0
- package/local-maven-repo/expo/modules/observe/expo.modules.observe/57.0.15/expo.modules.observe-57.0.15-sources.jar.sha512 +1 -0
- package/local-maven-repo/expo/modules/observe/expo.modules.observe/57.0.15/expo.modules.observe-57.0.15.aar +0 -0
- package/local-maven-repo/expo/modules/observe/expo.modules.observe/57.0.15/expo.modules.observe-57.0.15.aar.md5 +1 -0
- package/local-maven-repo/expo/modules/observe/expo.modules.observe/57.0.15/expo.modules.observe-57.0.15.aar.sha1 +1 -0
- package/local-maven-repo/expo/modules/observe/expo.modules.observe/57.0.15/expo.modules.observe-57.0.15.aar.sha256 +1 -0
- package/local-maven-repo/expo/modules/observe/expo.modules.observe/57.0.15/expo.modules.observe-57.0.15.aar.sha512 +1 -0
- package/local-maven-repo/expo/modules/observe/expo.modules.observe/{57.0.13/expo.modules.observe-57.0.13.module → 57.0.15/expo.modules.observe-57.0.15.module} +23 -23
- package/local-maven-repo/expo/modules/observe/expo.modules.observe/57.0.15/expo.modules.observe-57.0.15.module.md5 +1 -0
- package/local-maven-repo/expo/modules/observe/expo.modules.observe/57.0.15/expo.modules.observe-57.0.15.module.sha1 +1 -0
- package/local-maven-repo/expo/modules/observe/expo.modules.observe/57.0.15/expo.modules.observe-57.0.15.module.sha256 +1 -0
- package/local-maven-repo/expo/modules/observe/expo.modules.observe/57.0.15/expo.modules.observe-57.0.15.module.sha512 +1 -0
- package/local-maven-repo/expo/modules/observe/expo.modules.observe/{57.0.13/expo.modules.observe-57.0.13.pom → 57.0.15/expo.modules.observe-57.0.15.pom} +2 -2
- package/local-maven-repo/expo/modules/observe/expo.modules.observe/57.0.15/expo.modules.observe-57.0.15.pom.md5 +1 -0
- package/local-maven-repo/expo/modules/observe/expo.modules.observe/57.0.15/expo.modules.observe-57.0.15.pom.sha1 +1 -0
- package/local-maven-repo/expo/modules/observe/expo.modules.observe/57.0.15/expo.modules.observe-57.0.15.pom.sha256 +1 -0
- package/local-maven-repo/expo/modules/observe/expo.modules.observe/57.0.15/expo.modules.observe-57.0.15.pom.sha512 +1 -0
- package/local-maven-repo/expo/modules/observe/expo.modules.observe/maven-metadata.xml +4 -4
- package/local-maven-repo/expo/modules/observe/expo.modules.observe/maven-metadata.xml.md5 +1 -1
- package/local-maven-repo/expo/modules/observe/expo.modules.observe/maven-metadata.xml.sha1 +1 -1
- package/local-maven-repo/expo/modules/observe/expo.modules.observe/maven-metadata.xml.sha256 +1 -1
- package/local-maven-repo/expo/modules/observe/expo.modules.observe/maven-metadata.xml.sha512 +1 -1
- package/package.json +4 -4
- package/local-maven-repo/expo/modules/observe/expo.modules.observe/57.0.13/expo.modules.observe-57.0.13-sources.jar +0 -0
- package/local-maven-repo/expo/modules/observe/expo.modules.observe/57.0.13/expo.modules.observe-57.0.13-sources.jar.md5 +0 -1
- package/local-maven-repo/expo/modules/observe/expo.modules.observe/57.0.13/expo.modules.observe-57.0.13-sources.jar.sha1 +0 -1
- package/local-maven-repo/expo/modules/observe/expo.modules.observe/57.0.13/expo.modules.observe-57.0.13-sources.jar.sha256 +0 -1
- package/local-maven-repo/expo/modules/observe/expo.modules.observe/57.0.13/expo.modules.observe-57.0.13-sources.jar.sha512 +0 -1
- package/local-maven-repo/expo/modules/observe/expo.modules.observe/57.0.13/expo.modules.observe-57.0.13.aar +0 -0
- package/local-maven-repo/expo/modules/observe/expo.modules.observe/57.0.13/expo.modules.observe-57.0.13.aar.md5 +0 -1
- package/local-maven-repo/expo/modules/observe/expo.modules.observe/57.0.13/expo.modules.observe-57.0.13.aar.sha1 +0 -1
- package/local-maven-repo/expo/modules/observe/expo.modules.observe/57.0.13/expo.modules.observe-57.0.13.aar.sha256 +0 -1
- package/local-maven-repo/expo/modules/observe/expo.modules.observe/57.0.13/expo.modules.observe-57.0.13.aar.sha512 +0 -1
- package/local-maven-repo/expo/modules/observe/expo.modules.observe/57.0.13/expo.modules.observe-57.0.13.module.md5 +0 -1
- package/local-maven-repo/expo/modules/observe/expo.modules.observe/57.0.13/expo.modules.observe-57.0.13.module.sha1 +0 -1
- package/local-maven-repo/expo/modules/observe/expo.modules.observe/57.0.13/expo.modules.observe-57.0.13.module.sha256 +0 -1
- package/local-maven-repo/expo/modules/observe/expo.modules.observe/57.0.13/expo.modules.observe-57.0.13.module.sha512 +0 -1
- package/local-maven-repo/expo/modules/observe/expo.modules.observe/57.0.13/expo.modules.observe-57.0.13.pom.md5 +0 -1
- package/local-maven-repo/expo/modules/observe/expo.modules.observe/57.0.13/expo.modules.observe-57.0.13.pom.sha1 +0 -1
- package/local-maven-repo/expo/modules/observe/expo.modules.observe/57.0.13/expo.modules.observe-57.0.13.pom.sha256 +0 -1
- package/local-maven-repo/expo/modules/observe/expo.modules.observe/57.0.13/expo.modules.observe-57.0.13.pom.sha512 +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -10,6 +10,17 @@
|
|
|
10
10
|
|
|
11
11
|
### 💡 Others
|
|
12
12
|
|
|
13
|
+
## 57.0.15 — 2026-08-20
|
|
14
|
+
|
|
15
|
+
### 💡 Others
|
|
16
|
+
|
|
17
|
+
- [Android] Retry a dispatch that gets HTTP 413 ([#49016](https://github.com/expo/expo/pull/49016) by [@Ubax](https://github.com/Ubax))
|
|
18
|
+
- [Android] Dispatch pending metrics and logs in bounded, oldest-first chunks without replacing active background work. ([#49012](https://github.com/expo/expo/pull/49012) by [@Ubax](https://github.com/Ubax))
|
|
19
|
+
|
|
20
|
+
## 57.0.14 — 2026-08-17
|
|
21
|
+
|
|
22
|
+
_This version does not introduce any user-facing changes._
|
|
23
|
+
|
|
13
24
|
## 57.0.13 — 2026-08-14
|
|
14
25
|
|
|
15
26
|
_This version does not introduce any user-facing changes._
|
|
@@ -11,7 +11,7 @@ import kotlin.math.pow
|
|
|
11
11
|
import kotlin.random.Random
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
|
-
* Outcome of a single dispatch attempt to the OTLP endpoint.
|
|
14
|
+
* Outcome of a single dispatch attempt to the OTLP endpoint. Five cases, modeled after the
|
|
15
15
|
* OTLP retry guidance (see https://opentelemetry.io/docs/specs/otlp/#otlphttp-response):
|
|
16
16
|
*
|
|
17
17
|
* - `Success` — server accepted the batch without rejections.
|
|
@@ -22,6 +22,7 @@ import kotlin.random.Random
|
|
|
22
22
|
* it as a drop.
|
|
23
23
|
* - `RetryableFailure` — transient failure (429/502/503/504 or transport error); retry
|
|
24
24
|
* the same batch after `retryAfterMs` or a client-computed backoff.
|
|
25
|
+
* - `PayloadTooLarge` — HTTP 413; retry immediately with fewer records.
|
|
25
26
|
* - `NonRetryableFailure` — permanent failure (4xx/5xx outside the retryable set, encoding
|
|
26
27
|
* error); drop the batch so it can't wedge the queue.
|
|
27
28
|
*
|
|
@@ -33,6 +34,7 @@ sealed class DispatchResult {
|
|
|
33
34
|
object Success : DispatchResult()
|
|
34
35
|
data class PartialSuccess(val partial: OTPartialSuccess) : DispatchResult()
|
|
35
36
|
data class RetryableFailure(val retryAfterMs: Long? = null) : DispatchResult()
|
|
37
|
+
object PayloadTooLarge : DispatchResult()
|
|
36
38
|
data class NonRetryableFailure(val reason: String) : DispatchResult()
|
|
37
39
|
}
|
|
38
40
|
|
|
@@ -43,7 +45,7 @@ sealed class DispatchResult {
|
|
|
43
45
|
*/
|
|
44
46
|
object DispatchUtils {
|
|
45
47
|
/**
|
|
46
|
-
* Pure classifier that maps an HTTP response into
|
|
48
|
+
* Pure classifier that maps an HTTP response into a dispatch outcome. Extracted
|
|
47
49
|
* from the dispatch call site so the OTLP-spec rules can be unit-tested without a real
|
|
48
50
|
* network call.
|
|
49
51
|
*
|
|
@@ -57,8 +59,6 @@ object DispatchUtils {
|
|
|
57
59
|
responseBody: String?,
|
|
58
60
|
bodyExcerpt: () -> String = { "" }
|
|
59
61
|
): DispatchResult {
|
|
60
|
-
val retryAfter = parseRetryAfter(retryAfterHeader)
|
|
61
|
-
|
|
62
62
|
if (statusCode in 200..299) {
|
|
63
63
|
// The OTLP spec allows `partial_success` to carry a warning-only payload —
|
|
64
64
|
// `rejectedCount == 0` with a non-empty `errorMessage`. Treat that as a successful send
|
|
@@ -78,7 +78,8 @@ object DispatchUtils {
|
|
|
78
78
|
}
|
|
79
79
|
|
|
80
80
|
return when (statusCode) {
|
|
81
|
-
|
|
81
|
+
413 -> DispatchResult.PayloadTooLarge
|
|
82
|
+
429, 502, 503, 504 -> DispatchResult.RetryableFailure(parseRetryAfter(retryAfterHeader))
|
|
82
83
|
else -> {
|
|
83
84
|
val excerpt = bodyExcerpt()
|
|
84
85
|
val suffix = if (excerpt.isEmpty()) "" else ": $excerpt"
|
|
@@ -97,12 +98,15 @@ object DispatchUtils {
|
|
|
97
98
|
* permanently, so retrying would produce the same answer; removing them drops the batch
|
|
98
99
|
* so it can't wedge subsequent rounds. This is the acceptance-criterion behavior: a
|
|
99
100
|
* 400/403 must not be re-sent on the next cycle.
|
|
100
|
-
* - `
|
|
101
|
+
* - `PayloadTooLarge` removes the pending ID because multi-record batches are retried with
|
|
102
|
+
* smaller chunks before this check, so reaching it means a single record exceeded the limit.
|
|
103
|
+
* - `RetryableFailure` keeps them so they can be retried.
|
|
101
104
|
*/
|
|
102
105
|
fun shouldRemovePending(result: DispatchResult): Boolean = when (result) {
|
|
103
106
|
is DispatchResult.Success,
|
|
104
107
|
is DispatchResult.PartialSuccess,
|
|
105
|
-
is DispatchResult.NonRetryableFailure
|
|
108
|
+
is DispatchResult.NonRetryableFailure,
|
|
109
|
+
is DispatchResult.PayloadTooLarge -> true
|
|
106
110
|
is DispatchResult.RetryableFailure -> false
|
|
107
111
|
}
|
|
108
112
|
|
|
@@ -218,6 +222,8 @@ object DispatchUtils {
|
|
|
218
222
|
* server-side) doesn't introduce a new pause.
|
|
219
223
|
* - `NonRetryableFailure` also resets the counter. A permanent drop isn't a sign that the
|
|
220
224
|
* server is unhealthy and shouldn't pause subsequent rounds.
|
|
225
|
+
* - `PayloadTooLarge` also resets the counter because a reachable server returned a definitive
|
|
226
|
+
* response, and leaves the gate alone because chunk-size retries are immediate.
|
|
221
227
|
* - `RetryableFailure` increments the counter and sets the gate to `now + delay`, where
|
|
222
228
|
* `delay` is the server-supplied `retryAfterMs` if present, otherwise `backoff(nextCount)`.
|
|
223
229
|
*
|
|
@@ -232,7 +238,8 @@ object DispatchUtils {
|
|
|
232
238
|
): RetryGateState = when (result) {
|
|
233
239
|
is DispatchResult.Success,
|
|
234
240
|
is DispatchResult.PartialSuccess,
|
|
235
|
-
is DispatchResult.NonRetryableFailure
|
|
241
|
+
is DispatchResult.NonRetryableFailure,
|
|
242
|
+
is DispatchResult.PayloadTooLarge ->
|
|
236
243
|
currentState.copy(consecutiveRetryableFailures = 0)
|
|
237
244
|
is DispatchResult.RetryableFailure -> {
|
|
238
245
|
val nextCount = currentState.consecutiveRetryableFailures + 1
|
|
@@ -132,6 +132,8 @@ class EventDispatcher(
|
|
|
132
132
|
)
|
|
133
133
|
is DispatchResult.RetryableFailure ->
|
|
134
134
|
Log.w(OBSERVE_TAG, "Server responded with ${response.code} (retryable) and data: $responseBody")
|
|
135
|
+
is DispatchResult.PayloadTooLarge ->
|
|
136
|
+
Log.w(OBSERVE_TAG, "Server responded with ${response.code} (payload too large) and data: $responseBody")
|
|
135
137
|
is DispatchResult.NonRetryableFailure ->
|
|
136
138
|
Log.w(
|
|
137
139
|
OBSERVE_TAG,
|
|
@@ -119,7 +119,8 @@ class ObservabilityBackgroundWorker(
|
|
|
119
119
|
.getInstance(context)
|
|
120
120
|
.enqueueUniqueWork(
|
|
121
121
|
WORK_NAME,
|
|
122
|
-
|
|
122
|
+
// Keep an in-flight dispatch; cancelling it can duplicate a request the server received.
|
|
123
|
+
ExistingWorkPolicy.KEEP,
|
|
123
124
|
periodicWork
|
|
124
125
|
)
|
|
125
126
|
}
|
|
@@ -8,6 +8,8 @@ import expo.modules.observe.storage.PendingMetricsManager
|
|
|
8
8
|
import expo.modules.appmetrics.storage.SessionManager
|
|
9
9
|
import expo.modules.appmetrics.utils.TimeUtils
|
|
10
10
|
import expo.modules.interfaces.constants.ConstantsInterface
|
|
11
|
+
import kotlinx.coroutines.currentCoroutineContext
|
|
12
|
+
import kotlinx.coroutines.isActive
|
|
11
13
|
import kotlinx.coroutines.sync.Mutex
|
|
12
14
|
import kotlinx.coroutines.sync.withLock
|
|
13
15
|
|
|
@@ -81,7 +83,8 @@ class BaseObservabilityManager(
|
|
|
81
83
|
private val deterministicUniformValueProvider: () -> Double = {
|
|
82
84
|
EASClientID.deterministicUniformValue(EASClientID(context).uuid)
|
|
83
85
|
},
|
|
84
|
-
private val currentTimeMs: () -> Long = { TimeUtils.getWallClockMillis() }
|
|
86
|
+
private val currentTimeMs: () -> Long = { TimeUtils.getWallClockMillis() },
|
|
87
|
+
private val dispatchChunkSize: Int = DISPATCH_CHUNK_SIZE
|
|
85
88
|
) {
|
|
86
89
|
private val eventDispatcher = EventDispatcher(
|
|
87
90
|
context = context,
|
|
@@ -149,8 +152,7 @@ class BaseObservabilityManager(
|
|
|
149
152
|
)
|
|
150
153
|
|
|
151
154
|
suspend fun dispatchUnsentMetrics(): Unit = metricsDispatchMutex.withLock {
|
|
152
|
-
|
|
153
|
-
if (pendingIds.isEmpty()) {
|
|
155
|
+
if (!pendingMetricsManager.hasPendingMetrics()) {
|
|
154
156
|
return
|
|
155
157
|
}
|
|
156
158
|
|
|
@@ -159,50 +161,74 @@ class BaseObservabilityManager(
|
|
|
159
161
|
}
|
|
160
162
|
|
|
161
163
|
if (!shouldDispatch()) {
|
|
162
|
-
pendingMetricsManager.
|
|
164
|
+
pendingMetricsManager.removeAllPendingMetrics()
|
|
163
165
|
return
|
|
164
166
|
}
|
|
165
167
|
|
|
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
|
-
|
|
168
|
+
var chunkSize = dispatchChunkSize
|
|
169
|
+
while (currentCoroutineContext().isActive) {
|
|
170
|
+
val pendingIds = pendingMetricsManager.getPendingMetricIds(chunkSize)
|
|
171
|
+
// Use the default for the next batch unless a 413 below overrides it. Re-discovering
|
|
172
|
+
// the limit each batch is fine: the server accepts payloads over 1 MB, so the default
|
|
173
|
+
// chunk stays far below the limit and a 413 is exceptional.
|
|
174
|
+
chunkSize = dispatchChunkSize
|
|
175
|
+
if (pendingIds.isEmpty()) {
|
|
176
|
+
break
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
val sessionsWithPendingMetrics = sessionManager.getSessionsWithMetrics(pendingIds)
|
|
180
|
+
|
|
181
|
+
// Clean up orphaned pending IDs (metrics deleted from MetricsDatabase but still in pending table)
|
|
182
|
+
val resolvedMetricIds = sessionsWithPendingMetrics.flatMap { it.metrics }.map { it.metricId }.toSet()
|
|
183
|
+
val orphanedIds = pendingIds.filter { it !in resolvedMetricIds }
|
|
184
|
+
if (orphanedIds.isNotEmpty()) {
|
|
185
|
+
pendingMetricsManager.removePendingMetrics(orphanedIds)
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
if (sessionsWithPendingMetrics.isNotEmpty()) {
|
|
189
|
+
val events = sessionsWithPendingMetrics.map { sessionWithMetrics ->
|
|
190
|
+
Event(
|
|
191
|
+
metadata = Metadata.fromSessionMetadata(sessionWithMetrics.session),
|
|
192
|
+
metrics = sessionWithMetrics.metrics.map { EASMetric.fromMetric(it) }
|
|
193
|
+
)
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
val result = eventDispatcher.dispatch(events)
|
|
197
|
+
metricsRetryGate = nextGate(metricsRetryGate, result)
|
|
198
|
+
val dispatchedMetricIds = sessionsWithPendingMetrics.flatMap { it.metrics }.map { it.metricId }
|
|
199
|
+
when (result) {
|
|
200
|
+
is DispatchResult.PartialSuccess ->
|
|
201
|
+
Log.w(
|
|
202
|
+
OBSERVE_TAG,
|
|
203
|
+
"Partial success on batch of ${dispatchedMetricIds.size} metric event(s): " +
|
|
204
|
+
"server rejected ${result.partial.rejectedCount} " +
|
|
205
|
+
"(${result.partial.errorMessage ?: "no error message"})"
|
|
206
|
+
)
|
|
207
|
+
is DispatchResult.NonRetryableFailure ->
|
|
208
|
+
Log.w(
|
|
209
|
+
OBSERVE_TAG,
|
|
210
|
+
"Dropping batch of ${dispatchedMetricIds.size} metric event(s): ${result.reason}"
|
|
211
|
+
)
|
|
212
|
+
is DispatchResult.PayloadTooLarge ->
|
|
213
|
+
if (dispatchedMetricIds.size == 1) {
|
|
214
|
+
Log.w(OBSERVE_TAG, "Dropping metric event that exceeds the server's payload limit")
|
|
215
|
+
}
|
|
216
|
+
is DispatchResult.Success, is DispatchResult.RetryableFailure -> Unit
|
|
217
|
+
}
|
|
218
|
+
if (result is DispatchResult.PayloadTooLarge && dispatchedMetricIds.size > 1) {
|
|
219
|
+
chunkSize = dispatchedMetricIds.size / 2
|
|
220
|
+
// Keep the pending metrics, but retry immediately with a smaller chunk.
|
|
221
|
+
continue
|
|
222
|
+
}
|
|
223
|
+
if (!DispatchUtils.shouldRemovePending(result)) {
|
|
224
|
+
break
|
|
225
|
+
}
|
|
226
|
+
pendingMetricsManager.removePendingMetrics(dispatchedMetricIds)
|
|
227
|
+
// A systematic rejection or an oversized record: leave the rest for the next run.
|
|
228
|
+
if (result is DispatchResult.NonRetryableFailure || result is DispatchResult.PayloadTooLarge) {
|
|
229
|
+
break
|
|
230
|
+
}
|
|
231
|
+
}
|
|
206
232
|
}
|
|
207
233
|
}
|
|
208
234
|
|
|
@@ -211,8 +237,7 @@ class BaseObservabilityManager(
|
|
|
211
237
|
* a logs failure doesn't affect the metrics pending table and vice versa.
|
|
212
238
|
*/
|
|
213
239
|
suspend fun dispatchUnsentLogs(): Unit = logsDispatchMutex.withLock {
|
|
214
|
-
|
|
215
|
-
if (pendingIds.isEmpty()) {
|
|
240
|
+
if (!pendingLogsManager.hasPendingLogs()) {
|
|
216
241
|
return
|
|
217
242
|
}
|
|
218
243
|
|
|
@@ -221,52 +246,76 @@ class BaseObservabilityManager(
|
|
|
221
246
|
}
|
|
222
247
|
|
|
223
248
|
if (!shouldDispatch()) {
|
|
224
|
-
pendingLogsManager.
|
|
249
|
+
pendingLogsManager.removeAllPendingLogs()
|
|
225
250
|
return
|
|
226
251
|
}
|
|
227
252
|
|
|
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
|
-
|
|
268
|
-
|
|
269
|
-
|
|
253
|
+
var chunkSize = dispatchChunkSize
|
|
254
|
+
while (currentCoroutineContext().isActive) {
|
|
255
|
+
val pendingIds = pendingLogsManager.getPendingLogIds(chunkSize)
|
|
256
|
+
// Use the default for the next batch unless a 413 below overrides it. Re-discovering
|
|
257
|
+
// the limit each batch is fine: the server accepts payloads over 1 MB, so the default
|
|
258
|
+
// chunk stays far below the limit and a 413 is exceptional.
|
|
259
|
+
chunkSize = dispatchChunkSize
|
|
260
|
+
if (pendingIds.isEmpty()) {
|
|
261
|
+
break
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
val sessionsWithPendingLogs = sessionManager.getSessionsWithLogs(pendingIds)
|
|
265
|
+
|
|
266
|
+
// Clean up orphaned pending IDs (logs deleted from the `logs` table but
|
|
267
|
+
// still tracked in `pending_logs`).
|
|
268
|
+
val resolvedLogIds = sessionsWithPendingLogs.flatMap { it.logs }.map { it.logId }.toSet()
|
|
269
|
+
val orphanedIds = pendingIds.filter { it !in resolvedLogIds }
|
|
270
|
+
if (orphanedIds.isNotEmpty()) {
|
|
271
|
+
pendingLogsManager.removePendingLogs(orphanedIds)
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
if (sessionsWithPendingLogs.isNotEmpty()) {
|
|
275
|
+
val events = sessionsWithPendingLogs.map { sessionWithLogs ->
|
|
276
|
+
Event(
|
|
277
|
+
metadata = Metadata.fromSessionMetadata(sessionWithLogs.session),
|
|
278
|
+
metrics = emptyList(),
|
|
279
|
+
logs = sessionWithLogs.logs.map { LogEvent.fromLogRecord(it) }
|
|
280
|
+
)
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
val result = eventDispatcher.dispatchLogs(events)
|
|
284
|
+
logsRetryGate = nextGate(logsRetryGate, result)
|
|
285
|
+
val dispatchedLogIds = sessionsWithPendingLogs.flatMap { it.logs }.map { it.logId }
|
|
286
|
+
when (result) {
|
|
287
|
+
is DispatchResult.PartialSuccess ->
|
|
288
|
+
Log.w(
|
|
289
|
+
OBSERVE_TAG,
|
|
290
|
+
"Partial success on batch of ${dispatchedLogIds.size} log event(s): " +
|
|
291
|
+
"server rejected ${result.partial.rejectedCount} " +
|
|
292
|
+
"(${result.partial.errorMessage ?: "no error message"})"
|
|
293
|
+
)
|
|
294
|
+
is DispatchResult.NonRetryableFailure ->
|
|
295
|
+
Log.w(
|
|
296
|
+
OBSERVE_TAG,
|
|
297
|
+
"Dropping batch of ${dispatchedLogIds.size} log event(s): ${result.reason}"
|
|
298
|
+
)
|
|
299
|
+
is DispatchResult.PayloadTooLarge ->
|
|
300
|
+
if (dispatchedLogIds.size == 1) {
|
|
301
|
+
Log.w(OBSERVE_TAG, "Dropping log event that exceeds the server's payload limit")
|
|
302
|
+
}
|
|
303
|
+
is DispatchResult.Success, is DispatchResult.RetryableFailure -> Unit
|
|
304
|
+
}
|
|
305
|
+
if (result is DispatchResult.PayloadTooLarge && dispatchedLogIds.size > 1) {
|
|
306
|
+
chunkSize = dispatchedLogIds.size / 2
|
|
307
|
+
// Keep the pending logs, but retry immediately with a smaller chunk.
|
|
308
|
+
continue
|
|
309
|
+
}
|
|
310
|
+
if (!DispatchUtils.shouldRemovePending(result)) {
|
|
311
|
+
break
|
|
312
|
+
}
|
|
313
|
+
pendingLogsManager.removePendingLogs(dispatchedLogIds)
|
|
314
|
+
// A systematic rejection or an oversized record: leave the rest for the next run.
|
|
315
|
+
if (result is DispatchResult.NonRetryableFailure || result is DispatchResult.PayloadTooLarge) {
|
|
316
|
+
break
|
|
317
|
+
}
|
|
318
|
+
}
|
|
270
319
|
}
|
|
271
320
|
}
|
|
272
321
|
|
|
@@ -11,6 +11,8 @@ import androidx.room.Query
|
|
|
11
11
|
import androidx.room.Room
|
|
12
12
|
import androidx.room.RoomDatabase
|
|
13
13
|
|
|
14
|
+
// TODO: Draining large backlogs from both pending tables reruns `ORDER BY addedAt`
|
|
15
|
+
// for every chunk. Consider `Index("addedAt")` and a version bump if this matters in practice.
|
|
14
16
|
@Entity(tableName = "pending_metrics")
|
|
15
17
|
data class PendingMetric(
|
|
16
18
|
@PrimaryKey val metricId: String,
|
|
@@ -30,8 +32,14 @@ interface PendingMetricDao {
|
|
|
30
32
|
@Insert(onConflict = OnConflictStrategy.IGNORE)
|
|
31
33
|
suspend fun insertAll(metrics: List<PendingMetric>)
|
|
32
34
|
|
|
33
|
-
@Query("SELECT metricId FROM pending_metrics")
|
|
34
|
-
suspend fun
|
|
35
|
+
@Query("SELECT metricId FROM pending_metrics ORDER BY addedAt ASC LIMIT :limit")
|
|
36
|
+
suspend fun getMetricIds(limit: Int): List<String>
|
|
37
|
+
|
|
38
|
+
@Query("SELECT EXISTS(SELECT 1 FROM pending_metrics)")
|
|
39
|
+
suspend fun hasMetricIds(): Boolean
|
|
40
|
+
|
|
41
|
+
@Query("DELETE FROM pending_metrics")
|
|
42
|
+
suspend fun deleteAll()
|
|
35
43
|
|
|
36
44
|
@Query("DELETE FROM pending_metrics WHERE metricId IN (:metricIds)")
|
|
37
45
|
suspend fun deleteByIds(metricIds: List<String>)
|
|
@@ -45,8 +53,14 @@ interface PendingLogDao {
|
|
|
45
53
|
@Insert(onConflict = OnConflictStrategy.IGNORE)
|
|
46
54
|
suspend fun insertAll(logs: List<PendingLog>)
|
|
47
55
|
|
|
48
|
-
@Query("SELECT logId FROM pending_logs")
|
|
49
|
-
suspend fun
|
|
56
|
+
@Query("SELECT logId FROM pending_logs ORDER BY addedAt ASC LIMIT :limit")
|
|
57
|
+
suspend fun getLogIds(limit: Int): List<String>
|
|
58
|
+
|
|
59
|
+
@Query("SELECT EXISTS(SELECT 1 FROM pending_logs)")
|
|
60
|
+
suspend fun hasLogIds(): Boolean
|
|
61
|
+
|
|
62
|
+
@Query("DELETE FROM pending_logs")
|
|
63
|
+
suspend fun deleteAll()
|
|
50
64
|
|
|
51
65
|
@Query("DELETE FROM pending_logs WHERE logId IN (:logIds)")
|
|
52
66
|
suspend fun deleteByIds(logIds: List<String>)
|
|
@@ -17,7 +17,11 @@ class PendingLogsManager(
|
|
|
17
17
|
database.pendingLogDao().insertAll(pendingLogs)
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
suspend fun
|
|
20
|
+
suspend fun getPendingLogIds(limit: Int): List<String> = database.pendingLogDao().getLogIds(limit)
|
|
21
|
+
|
|
22
|
+
suspend fun hasPendingLogs(): Boolean = database.pendingLogDao().hasLogIds()
|
|
23
|
+
|
|
24
|
+
suspend fun removeAllPendingLogs() = database.pendingLogDao().deleteAll()
|
|
21
25
|
|
|
22
26
|
suspend fun removePendingLogs(logIds: List<String>) {
|
|
23
27
|
database.withTransaction {
|
|
@@ -17,7 +17,11 @@ class PendingMetricsManager(
|
|
|
17
17
|
database.pendingMetricDao().insertAll(pendingMetrics)
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
suspend fun
|
|
20
|
+
suspend fun getPendingMetricIds(limit: Int): List<String> = database.pendingMetricDao().getMetricIds(limit)
|
|
21
|
+
|
|
22
|
+
suspend fun hasPendingMetrics(): Boolean = database.pendingMetricDao().hasMetricIds()
|
|
23
|
+
|
|
24
|
+
suspend fun removeAllPendingMetrics() = database.pendingMetricDao().deleteAll()
|
|
21
25
|
|
|
22
26
|
suspend fun removePendingMetrics(metricIds: List<String>) {
|
|
23
27
|
database.withTransaction {
|
package/expo-module.config.json
CHANGED
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
5430ae1cc22085977500f5a9be5d9770
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
a226a6afacb650059d328efa24da7d2166b2b306
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
9859b378e81b9f278044dffb84c6f6c0594e6ffc6c876b7c9471800a39ad7010
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
4ccf4718d0b3b09def0435d9bc224010235bea875a63a8c8c609bf3c2c96023cc387fcfcdb6e426357dd797eeba064d53d70513877d3abfd750f47398b7e4ff6
|
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
7e9d14ef4b143fca2a5b5f4ce8ab1b04
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
a429ffd382c02b9fdc6de948013b4080478c86dd
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
6598738fa761502e1da0e93cb5d098616755bf810557ec6e5f91030ac2d03115
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
d64aa553cc7b7116a3bfbef0b88b25b47244cece088283f0cbce543e921de5677974be9dc4ce00d9c5c7c05bcfb05151f5285b78413e24aab9a974c38ce0e7fe
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"component": {
|
|
4
4
|
"group": "expo.modules.observe",
|
|
5
5
|
"module": "expo.modules.observe",
|
|
6
|
-
"version": "57.0.
|
|
6
|
+
"version": "57.0.15",
|
|
7
7
|
"attributes": {
|
|
8
8
|
"org.gradle.status": "release"
|
|
9
9
|
}
|
|
@@ -24,13 +24,13 @@
|
|
|
24
24
|
},
|
|
25
25
|
"files": [
|
|
26
26
|
{
|
|
27
|
-
"name": "expo.modules.observe-57.0.
|
|
28
|
-
"url": "expo.modules.observe-57.0.
|
|
29
|
-
"size":
|
|
30
|
-
"sha512": "
|
|
31
|
-
"sha256": "
|
|
32
|
-
"sha1": "
|
|
33
|
-
"md5": "
|
|
27
|
+
"name": "expo.modules.observe-57.0.15.aar",
|
|
28
|
+
"url": "expo.modules.observe-57.0.15.aar",
|
|
29
|
+
"size": 279117,
|
|
30
|
+
"sha512": "d64aa553cc7b7116a3bfbef0b88b25b47244cece088283f0cbce543e921de5677974be9dc4ce00d9c5c7c05bcfb05151f5285b78413e24aab9a974c38ce0e7fe",
|
|
31
|
+
"sha256": "6598738fa761502e1da0e93cb5d098616755bf810557ec6e5f91030ac2d03115",
|
|
32
|
+
"sha1": "a429ffd382c02b9fdc6de948013b4080478c86dd",
|
|
33
|
+
"md5": "7e9d14ef4b143fca2a5b5f4ce8ab1b04"
|
|
34
34
|
}
|
|
35
35
|
]
|
|
36
36
|
},
|
|
@@ -61,7 +61,7 @@
|
|
|
61
61
|
"group": "expo.modules.appmetrics",
|
|
62
62
|
"module": "expo.modules.appmetrics",
|
|
63
63
|
"version": {
|
|
64
|
-
"requires": "57.0.
|
|
64
|
+
"requires": "57.0.13"
|
|
65
65
|
}
|
|
66
66
|
},
|
|
67
67
|
{
|
|
@@ -134,13 +134,13 @@
|
|
|
134
134
|
],
|
|
135
135
|
"files": [
|
|
136
136
|
{
|
|
137
|
-
"name": "expo.modules.observe-57.0.
|
|
138
|
-
"url": "expo.modules.observe-57.0.
|
|
139
|
-
"size":
|
|
140
|
-
"sha512": "
|
|
141
|
-
"sha256": "
|
|
142
|
-
"sha1": "
|
|
143
|
-
"md5": "
|
|
137
|
+
"name": "expo.modules.observe-57.0.15.aar",
|
|
138
|
+
"url": "expo.modules.observe-57.0.15.aar",
|
|
139
|
+
"size": 279117,
|
|
140
|
+
"sha512": "d64aa553cc7b7116a3bfbef0b88b25b47244cece088283f0cbce543e921de5677974be9dc4ce00d9c5c7c05bcfb05151f5285b78413e24aab9a974c38ce0e7fe",
|
|
141
|
+
"sha256": "6598738fa761502e1da0e93cb5d098616755bf810557ec6e5f91030ac2d03115",
|
|
142
|
+
"sha1": "a429ffd382c02b9fdc6de948013b4080478c86dd",
|
|
143
|
+
"md5": "7e9d14ef4b143fca2a5b5f4ce8ab1b04"
|
|
144
144
|
}
|
|
145
145
|
]
|
|
146
146
|
},
|
|
@@ -154,13 +154,13 @@
|
|
|
154
154
|
},
|
|
155
155
|
"files": [
|
|
156
156
|
{
|
|
157
|
-
"name": "expo.modules.observe-57.0.
|
|
158
|
-
"url": "expo.modules.observe-57.0.
|
|
159
|
-
"size":
|
|
160
|
-
"sha512": "
|
|
161
|
-
"sha256": "
|
|
162
|
-
"sha1": "
|
|
163
|
-
"md5": "
|
|
157
|
+
"name": "expo.modules.observe-57.0.15-sources.jar",
|
|
158
|
+
"url": "expo.modules.observe-57.0.15-sources.jar",
|
|
159
|
+
"size": 26681,
|
|
160
|
+
"sha512": "4ccf4718d0b3b09def0435d9bc224010235bea875a63a8c8c609bf3c2c96023cc387fcfcdb6e426357dd797eeba064d53d70513877d3abfd750f47398b7e4ff6",
|
|
161
|
+
"sha256": "9859b378e81b9f278044dffb84c6f6c0594e6ffc6c876b7c9471800a39ad7010",
|
|
162
|
+
"sha1": "a226a6afacb650059d328efa24da7d2166b2b306",
|
|
163
|
+
"md5": "5430ae1cc22085977500f5a9be5d9770"
|
|
164
164
|
}
|
|
165
165
|
]
|
|
166
166
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
1551a025e9fa6d31eefeb87480bc2a4e
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
8a8736180c22f65d736e7aa3549ae9a6c84463f0
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
8afb9eb230946a7be139a495fd0dea559f26850926ed0c95d5b5a589d9bb0e77
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
a35cfdd58aeda7b1c603212f09812f52058703309aeca000f97bb16160e3c52a7df4df36b730473e1d60a825af689b7994890a0573c645168e98787fc5ea496d
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
<modelVersion>4.0.0</modelVersion>
|
|
10
10
|
<groupId>expo.modules.observe</groupId>
|
|
11
11
|
<artifactId>expo.modules.observe</artifactId>
|
|
12
|
-
<version>57.0.
|
|
12
|
+
<version>57.0.15</version>
|
|
13
13
|
<packaging>aar</packaging>
|
|
14
14
|
<name>expo.modules.observe</name>
|
|
15
15
|
<url>https://github.com/expo/expo</url>
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
<dependency>
|
|
53
53
|
<groupId>expo.modules.appmetrics</groupId>
|
|
54
54
|
<artifactId>expo.modules.appmetrics</artifactId>
|
|
55
|
-
<version>57.0.
|
|
55
|
+
<version>57.0.13</version>
|
|
56
56
|
<scope>runtime</scope>
|
|
57
57
|
</dependency>
|
|
58
58
|
<dependency>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
6bef51574c6c822f3feb8e9cac38808e
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
9b96ab29d19ab69368357f964ffc5d3254ceb9b5
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
f7b0181f654c7427af6df84bb6954f14ef7f7f2abc949d92a82ab3ccdd970760
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
f191372e14db1e6b075d0ce5f05cca883166a74ea132143293aea3c2550e74669ab6718363e094121849e14653608ed031c2053fb1916ab4f1669f90ace954ba
|
|
@@ -3,11 +3,11 @@
|
|
|
3
3
|
<groupId>expo.modules.observe</groupId>
|
|
4
4
|
<artifactId>expo.modules.observe</artifactId>
|
|
5
5
|
<versioning>
|
|
6
|
-
<latest>57.0.
|
|
7
|
-
<release>57.0.
|
|
6
|
+
<latest>57.0.15</latest>
|
|
7
|
+
<release>57.0.15</release>
|
|
8
8
|
<versions>
|
|
9
|
-
<version>57.0.
|
|
9
|
+
<version>57.0.15</version>
|
|
10
10
|
</versions>
|
|
11
|
-
<lastUpdated>
|
|
11
|
+
<lastUpdated>20260820103851</lastUpdated>
|
|
12
12
|
</versioning>
|
|
13
13
|
</metadata>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
2c0c0f09d33976b41ea0b5fd83a0ce84
|
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
5edcb91f92b6d691970902c2445c72949c9ecc45
|
package/local-maven-repo/expo/modules/observe/expo.modules.observe/maven-metadata.xml.sha256
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
d34ffe597003279018ac71c5254be213491be648ccef19925a22960846de2e9e
|
package/local-maven-repo/expo/modules/observe/expo.modules.observe/maven-metadata.xml.sha512
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
c8095abdac192e8e4cc90dd777f9a162fc28034cb4723c75ceb7626a3af21c056adb529869b428bf0911a0b044e5616bcbbe296480baf6046ca695136096aec1
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "expo-observe",
|
|
3
3
|
"title": "Expo Observe",
|
|
4
|
-
"version": "57.0.
|
|
4
|
+
"version": "57.0.15",
|
|
5
5
|
"description": "Expo module that dispatches collected app metrics to EAS Observe",
|
|
6
6
|
"main": "src/index.ts",
|
|
7
7
|
"types": "build/index.d.ts",
|
|
@@ -35,14 +35,14 @@
|
|
|
35
35
|
"author": "650 Industries, Inc.",
|
|
36
36
|
"license": "MIT",
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"expo-app-metrics": "~57.0.
|
|
38
|
+
"expo-app-metrics": "~57.0.13",
|
|
39
39
|
"expo-eas-client": "~57.0.1"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
42
|
"@react-navigation/native": "^7.1.33",
|
|
43
43
|
"@testing-library/react-native": "^13.3.0",
|
|
44
44
|
"@types/react": "~19.2.0",
|
|
45
|
-
"expo": "57.0.
|
|
45
|
+
"expo": "57.0.15",
|
|
46
46
|
"expo-module-scripts": "56.0.3"
|
|
47
47
|
},
|
|
48
48
|
"peerDependencies": {
|
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
"optional": true
|
|
61
61
|
}
|
|
62
62
|
},
|
|
63
|
-
"gitHead": "
|
|
63
|
+
"gitHead": "499024a441d67293aa60afcbe02051999a54f1bb",
|
|
64
64
|
"scripts": {
|
|
65
65
|
"build": "expo-module build",
|
|
66
66
|
"clean": "expo-module clean",
|
|
Binary file
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
2d172433507ad60d9a463b80c5236dde
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
c2699f439715d0ed6161a5fd7b11447f7ee16806
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
b5f3c5315d5f69659945189b8481f06136430e3383c08235a7faf07e9d562501
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
6b1cd0f830b9527829365dd05a0c7eb6e5403e96e08f071fe63d1703ebf58e36ba7cc950e9b7e4b2041b80a12a7264a9519daff7bebf2a5d1e65cad0d3cf764a
|
|
Binary file
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
f59414d95986cdaebaa1684bd740f5b7
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
01c1015942ab1dca9b939bdf85dca606789f0470
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
9ca9a7d9d78365a0c95c492db0a0b02c88ddee546cf7b2758920662ef3bf1b4d
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
efb8dcee42d6457617e21778edfb37079978025d8f0031a4d11361dba511c93bc4189ad163614d93b21a1f98bb9732b8260f65f6432eb7aab57ad0bdd95eed3e
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
48b54642336c0b33093c1aa6fcb7e161
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
a50a041fbf41a72de4deaf62949d10ab080e1c07
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
720ac07ecd122cf219df7f41e06e9c30f3ccaacb7dc721b359979c3d7dd2092d
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
4435097b976e7ffae1ccb63bca65ab8f0cd97ffe24ab076debe86f552d949f726586075ead478395aea43e66bb0fcb89adb92cc60a3f10ec236c480ba22cc88d
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
6ea78debb9cad8e52caaf1aac1c1cf15
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
f32f119c445120ad8eb5bc48f0d221c5cc8362ca
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
986dc06fa1a306980e3c3fab2e618b52657a70b5fbfcd988d308ad8b2000180c
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
06fd6d39e633d3907c63cc96f747dd751c186888b22db7f166a6ab9fd794983256a26a66f098c98b615496624b2e25d35663bb4a7d7063995808fb9df0d9c45d
|