@logbrew/react-native 0.1.16 → 0.1.17
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/index.cjs +3 -1
- package/index.d.cts +2 -3
- package/index.d.ts +2 -3
- package/index.js +3 -1
- package/ios/AppleDiagnostics/LBRNAppleNativeDiagnostics.swift +1 -1
- package/ios/GeneratedAppleDiagnostics/LogBrew/DurableDeliveryStore.swift +2 -2
- package/ios/GeneratedAppleDiagnostics/LogBrew/DurableDeliveryStoreRecovery.swift +11 -12
- package/ios/GeneratedAppleDiagnostics/LogBrew/IssueDiagnosticEvidence.swift +184 -0
- package/ios/GeneratedAppleDiagnostics/LogBrew/IssueDiagnostics.swift +6 -1
- package/ios/GeneratedAppleDiagnostics/LogBrew/LogBrewClient.swift +4 -47
- package/ios/GeneratedAppleDiagnostics/LogBrew/PublicTypes.swift +5 -0
- package/ios/GeneratedAppleDiagnostics/LogBrew/TelemetryContextValidation.swift +7 -2
- package/ios/GeneratedAppleDiagnostics/SOURCE-MANIFEST.json +7 -6
- package/package.json +1 -1
package/index.cjs
CHANGED
|
@@ -16,7 +16,7 @@ const {
|
|
|
16
16
|
} = require("./metadata.cjs");
|
|
17
17
|
|
|
18
18
|
const DEFAULT_SDK_NAME = "logbrew-react-native";
|
|
19
|
-
const DEFAULT_SDK_VERSION = "0.1.
|
|
19
|
+
const DEFAULT_SDK_VERSION = "0.1.17";
|
|
20
20
|
const DEFAULT_ENDPOINT = "https://api.logbrew.co/v1/events";
|
|
21
21
|
const MAX_ACTION_NAME_LENGTH = 64;
|
|
22
22
|
const MAX_PRODUCT_ANALYTICS_SURFACE_LENGTH = 256;
|
|
@@ -595,6 +595,7 @@ function createReactNavigationSpanListener(client, navigationContainer, {
|
|
|
595
595
|
|
|
596
596
|
function createReactNativeErrorEvent(error, {
|
|
597
597
|
debugIdMap,
|
|
598
|
+
evidence,
|
|
598
599
|
environment,
|
|
599
600
|
fingerprint,
|
|
600
601
|
handled = true,
|
|
@@ -618,6 +619,7 @@ function createReactNativeErrorEvent(error, {
|
|
|
618
619
|
const traceContext = resolveTraceContext(trace ?? getActiveLogBrewTrace());
|
|
619
620
|
const attributes = createIssueAttributesFromError(error?.reason ?? error?.error ?? error, {
|
|
620
621
|
debugIdMap: debugIdMap === undefined ? runtimeReactNativeDebugIdMap() : debugIdMap,
|
|
622
|
+
evidence,
|
|
621
623
|
environment,
|
|
622
624
|
fingerprint,
|
|
623
625
|
handled,
|
package/index.d.cts
CHANGED
|
@@ -20,9 +20,7 @@ export type ReactNativePlatformLike = {
|
|
|
20
20
|
OS?: string;
|
|
21
21
|
Version?: string | number;
|
|
22
22
|
isPad?: boolean;
|
|
23
|
-
constants?: {
|
|
24
|
-
isTesting?: boolean;
|
|
25
|
-
};
|
|
23
|
+
constants?: { isTesting?: boolean; [key: string]: unknown };
|
|
26
24
|
};
|
|
27
25
|
|
|
28
26
|
export type ReactNativeAppStateLike = {
|
|
@@ -287,6 +285,7 @@ export type ReactNativeErrorIdFactoryContext = {
|
|
|
287
285
|
|
|
288
286
|
export type CaptureReactNativeErrorOptions = ReactNativeContextOptions & {
|
|
289
287
|
debugIdMap?: Record<string, string>;
|
|
288
|
+
evidence?: IssueAttributes["evidence"];
|
|
290
289
|
environment?: string;
|
|
291
290
|
fingerprint?: string;
|
|
292
291
|
handled?: boolean;
|
package/index.d.ts
CHANGED
|
@@ -20,9 +20,7 @@ export type ReactNativePlatformLike = {
|
|
|
20
20
|
OS?: string;
|
|
21
21
|
Version?: string | number;
|
|
22
22
|
isPad?: boolean;
|
|
23
|
-
constants?: {
|
|
24
|
-
isTesting?: boolean;
|
|
25
|
-
};
|
|
23
|
+
constants?: { isTesting?: boolean; [key: string]: unknown };
|
|
26
24
|
};
|
|
27
25
|
|
|
28
26
|
export type ReactNativeAppStateLike = {
|
|
@@ -287,6 +285,7 @@ export type ReactNativeErrorIdFactoryContext = {
|
|
|
287
285
|
|
|
288
286
|
export type CaptureReactNativeErrorOptions = ReactNativeContextOptions & {
|
|
289
287
|
debugIdMap?: Record<string, string>;
|
|
288
|
+
evidence?: IssueAttributes["evidence"];
|
|
290
289
|
environment?: string;
|
|
291
290
|
fingerprint?: string;
|
|
292
291
|
handled?: boolean;
|
package/index.js
CHANGED
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
} from "./metadata.js";
|
|
17
17
|
|
|
18
18
|
const DEFAULT_SDK_NAME = "logbrew-react-native";
|
|
19
|
-
const DEFAULT_SDK_VERSION = "0.1.
|
|
19
|
+
const DEFAULT_SDK_VERSION = "0.1.17";
|
|
20
20
|
const DEFAULT_ENDPOINT = "https://api.logbrew.co/v1/events";
|
|
21
21
|
const MAX_ACTION_NAME_LENGTH = 64;
|
|
22
22
|
const MAX_PRODUCT_ANALYTICS_SURFACE_LENGTH = 256;
|
|
@@ -584,6 +584,7 @@ export function createReactNavigationSpanListener(client, navigationContainer, {
|
|
|
584
584
|
|
|
585
585
|
export function createReactNativeErrorEvent(error, {
|
|
586
586
|
debugIdMap,
|
|
587
|
+
evidence,
|
|
587
588
|
environment,
|
|
588
589
|
fingerprint,
|
|
589
590
|
handled = true,
|
|
@@ -607,6 +608,7 @@ export function createReactNativeErrorEvent(error, {
|
|
|
607
608
|
const traceContext = resolveTraceContext(trace ?? getActiveLogBrewTrace());
|
|
608
609
|
const attributes = createIssueAttributesFromError(error?.reason ?? error?.error ?? error, {
|
|
609
610
|
debugIdMap: debugIdMap === undefined ? runtimeReactNativeDebugIdMap() : debugIdMap,
|
|
611
|
+
evidence,
|
|
610
612
|
environment,
|
|
611
613
|
fingerprint,
|
|
612
614
|
handled,
|
|
@@ -3,7 +3,7 @@ import Foundation
|
|
|
3
3
|
@objc(LBRNAppleNativeDiagnostics)
|
|
4
4
|
public final class LBRNAppleNativeDiagnostics: NSObject, @unchecked Sendable {
|
|
5
5
|
private static let shared = LBRNAppleNativeDiagnostics()
|
|
6
|
-
private static let sdkVersion = "0.1.
|
|
6
|
+
private static let sdkVersion = "0.1.17"
|
|
7
7
|
|
|
8
8
|
private let lock = NSLock()
|
|
9
9
|
private let replayQueue = DispatchQueue(label: "co.logbrew.react-native.apple-diagnostics-replay")
|
|
@@ -76,7 +76,7 @@ final class DurableDeliveryStore: @unchecked Sendable {
|
|
|
76
76
|
recoveredEvents = recovery.events
|
|
77
77
|
recoveredPrefix = recovery.prefix
|
|
78
78
|
if let last = recovery.events.last {
|
|
79
|
-
let lastSequence = try Self.sequence(from: last)
|
|
79
|
+
let lastSequence = try Self.sequence(from: last.recordName)
|
|
80
80
|
guard lastSequence < UInt64.max else {
|
|
81
81
|
throw DurableStoreFailure.corrupt
|
|
82
82
|
}
|
|
@@ -109,7 +109,7 @@ final class DurableDeliveryStore: @unchecked Sendable {
|
|
|
109
109
|
throw DurableStoreFailure.corrupt
|
|
110
110
|
}
|
|
111
111
|
let name = Self.eventName(sequence: nextSequence)
|
|
112
|
-
let eventData = try Self.
|
|
112
|
+
let eventData = try Self.encode(event)
|
|
113
113
|
guard eventData.count == encodedBytes else {
|
|
114
114
|
throw DurableStoreFailure.corrupt
|
|
115
115
|
}
|
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
// Generated by scripts/sync-apple-native-sources.mjs.
|
|
2
2
|
// Edit the canonical Swift source, then regenerate this package boundary.
|
|
3
|
-
|
|
3
|
+
#if canImport(Musl)
|
|
4
|
+
import Musl
|
|
5
|
+
#elseif canImport(Glibc)
|
|
6
|
+
import Glibc
|
|
7
|
+
#elseif canImport(Darwin)
|
|
8
|
+
import Darwin
|
|
9
|
+
#else
|
|
10
|
+
#error("LogBrew durable delivery requires a POSIX platform")
|
|
11
|
+
#endif
|
|
4
12
|
import Foundation
|
|
5
13
|
|
|
6
14
|
extension DurableDeliveryStore {
|
|
@@ -167,13 +175,8 @@ extension DurableDeliveryStore {
|
|
|
167
175
|
String(format: "event-%020llu.json", sequence)
|
|
168
176
|
}
|
|
169
177
|
|
|
170
|
-
static func sequence(from recovered: RecoveredEvent) throws -> UInt64 {
|
|
171
|
-
try sequence(from: recovered.recordName)
|
|
172
|
-
}
|
|
173
|
-
|
|
174
178
|
static func sequence(from name: String) throws -> UInt64 {
|
|
175
|
-
let prefix = "event-"
|
|
176
|
-
let suffix = ".json"
|
|
179
|
+
let (prefix, suffix) = ("event-", ".json")
|
|
177
180
|
guard name.hasPrefix(prefix), name.hasSuffix(suffix) else {
|
|
178
181
|
throw DurableStoreFailure.corrupt
|
|
179
182
|
}
|
|
@@ -204,10 +207,6 @@ extension DurableDeliveryStore {
|
|
|
204
207
|
}
|
|
205
208
|
}
|
|
206
209
|
|
|
207
|
-
static func encodeEvent(_ event: Event) throws -> Data {
|
|
208
|
-
try encode(event)
|
|
209
|
-
}
|
|
210
|
-
|
|
211
210
|
static func checksum(_ data: Data) -> String {
|
|
212
211
|
var hash: UInt64 = 14_695_981_039_346_656_037
|
|
213
212
|
for byte in data {
|
|
@@ -259,7 +258,7 @@ extension DurableDeliveryStore {
|
|
|
259
258
|
throw DurableStoreFailure.corrupt
|
|
260
259
|
}
|
|
261
260
|
let record = try decodeEventRecord(from: file)
|
|
262
|
-
let encodedEvent = try
|
|
261
|
+
let encodedEvent = try encode(record.event)
|
|
263
262
|
guard record.version == version,
|
|
264
263
|
record.sequence == sequence,
|
|
265
264
|
record.sdk == sdk,
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
// Generated by scripts/sync-apple-native-sources.mjs.
|
|
2
|
+
// Edit the canonical Swift source, then regenerate this package boundary.
|
|
3
|
+
import Foundation
|
|
4
|
+
|
|
5
|
+
/// App-reported code location that narrows the smallest likely fix area.
|
|
6
|
+
public struct IssueLikelyFixArea: Codable, Equatable, Sendable {
|
|
7
|
+
public let component: String?
|
|
8
|
+
public let module: String?
|
|
9
|
+
public let function: String?
|
|
10
|
+
public let file: String?
|
|
11
|
+
public let line: Int?
|
|
12
|
+
public let column: Int?
|
|
13
|
+
public let inApp: Bool?
|
|
14
|
+
|
|
15
|
+
public init(
|
|
16
|
+
component: String? = nil,
|
|
17
|
+
module: String? = nil,
|
|
18
|
+
function: String? = nil,
|
|
19
|
+
file: String? = nil,
|
|
20
|
+
line: Int? = nil,
|
|
21
|
+
column: Int? = nil,
|
|
22
|
+
inApp: Bool? = nil,
|
|
23
|
+
) {
|
|
24
|
+
self.component = component
|
|
25
|
+
self.module = module
|
|
26
|
+
self.function = function
|
|
27
|
+
self.file = file
|
|
28
|
+
self.line = line
|
|
29
|
+
self.column = column
|
|
30
|
+
self.inApp = inApp
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/// App-reported impact without user identities or raw request data.
|
|
35
|
+
public struct IssueImpactEvidence: Codable, Equatable, Sendable {
|
|
36
|
+
public let affectedUserSegment: String?
|
|
37
|
+
public let failedAction: String?
|
|
38
|
+
public let userVisibleOutcome: String?
|
|
39
|
+
|
|
40
|
+
public init(
|
|
41
|
+
affectedUserSegment: String? = nil,
|
|
42
|
+
failedAction: String? = nil,
|
|
43
|
+
userVisibleOutcome: String? = nil,
|
|
44
|
+
) {
|
|
45
|
+
self.affectedUserSegment = affectedUserSegment
|
|
46
|
+
self.failedAction = failedAction
|
|
47
|
+
self.userVisibleOutcome = userVisibleOutcome
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/// Explicit app-reported cause, fix, impact, and capture-state evidence.
|
|
52
|
+
public struct IssueDiagnosticEvidence: Codable, Equatable, Sendable {
|
|
53
|
+
public let likelyRootCause: String?
|
|
54
|
+
public let likelyFixArea: IssueLikelyFixArea?
|
|
55
|
+
public let impact: IssueImpactEvidence?
|
|
56
|
+
public let capturedFields: [String]?
|
|
57
|
+
public let missingFields: [String]?
|
|
58
|
+
public let redactedFields: [String]?
|
|
59
|
+
public let truncatedFields: [String]?
|
|
60
|
+
|
|
61
|
+
public init(
|
|
62
|
+
likelyRootCause: String? = nil,
|
|
63
|
+
likelyFixArea: IssueLikelyFixArea? = nil,
|
|
64
|
+
impact: IssueImpactEvidence? = nil,
|
|
65
|
+
capturedFields: [String]? = nil,
|
|
66
|
+
missingFields: [String]? = nil,
|
|
67
|
+
redactedFields: [String]? = nil,
|
|
68
|
+
truncatedFields: [String]? = nil,
|
|
69
|
+
) {
|
|
70
|
+
self.likelyRootCause = likelyRootCause
|
|
71
|
+
self.likelyFixArea = likelyFixArea
|
|
72
|
+
self.impact = impact
|
|
73
|
+
self.capturedFields = capturedFields
|
|
74
|
+
self.missingFields = missingFields
|
|
75
|
+
self.redactedFields = redactedFields
|
|
76
|
+
self.truncatedFields = truncatedFields
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
func validateIssueDiagnosticEvidence(_ value: IssueDiagnosticEvidence) throws -> IssueDiagnosticEvidence {
|
|
81
|
+
let cause = try value.likelyRootCause.map {
|
|
82
|
+
try issueText($0, label: "issue evidence likelyRootCause", maximum: 1024)
|
|
83
|
+
}
|
|
84
|
+
let fixArea = try value.likelyFixArea.map(validateIssueLikelyFixArea)
|
|
85
|
+
let impact = try value.impact.map(validateIssueImpactEvidence)
|
|
86
|
+
var seen = Set<String>()
|
|
87
|
+
let fieldLists = try [
|
|
88
|
+
("capturedFields", value.capturedFields),
|
|
89
|
+
("missingFields", value.missingFields),
|
|
90
|
+
("redactedFields", value.redactedFields),
|
|
91
|
+
("truncatedFields", value.truncatedFields),
|
|
92
|
+
].map { name, fields in
|
|
93
|
+
try fields.map { try validateIssueEvidenceFields($0, name: name, seen: &seen) }
|
|
94
|
+
}
|
|
95
|
+
guard cause != nil || fixArea != nil || impact != nil || fieldLists.contains(where: { $0 != nil }) else {
|
|
96
|
+
throw issueValidationError("issue evidence must contain at least one field")
|
|
97
|
+
}
|
|
98
|
+
return IssueDiagnosticEvidence(
|
|
99
|
+
likelyRootCause: cause,
|
|
100
|
+
likelyFixArea: fixArea,
|
|
101
|
+
impact: impact,
|
|
102
|
+
capturedFields: fieldLists[0],
|
|
103
|
+
missingFields: fieldLists[1],
|
|
104
|
+
redactedFields: fieldLists[2],
|
|
105
|
+
truncatedFields: fieldLists[3],
|
|
106
|
+
)
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
private func validateIssueLikelyFixArea(_ value: IssueLikelyFixArea) throws -> IssueLikelyFixArea {
|
|
110
|
+
let identities = try [value.component, value.module, value.function].map { identity in
|
|
111
|
+
try identity.map {
|
|
112
|
+
try issueText($0, label: "issue evidence likelyFixArea identity", maximum: 256,
|
|
113
|
+
disallowLocationDelimiters: true)
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
let file = try value.file.map(validateIssueRelativePath)
|
|
117
|
+
let coordinates = try [value.line, value.column].map { coordinate in
|
|
118
|
+
try coordinate.map {
|
|
119
|
+
guard (1 ... Int(Int32.max)).contains($0) else {
|
|
120
|
+
throw issueValidationError("issue evidence likelyFixArea coordinate must be a positive integer")
|
|
121
|
+
}
|
|
122
|
+
return $0
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
guard identities.contains(where: { $0 != nil }) || file != nil || coordinates.contains(where: { $0 != nil }) else {
|
|
126
|
+
throw issueValidationError("issue evidence likelyFixArea must identify a code location")
|
|
127
|
+
}
|
|
128
|
+
return IssueLikelyFixArea(
|
|
129
|
+
component: identities[0], module: identities[1], function: identities[2], file: file,
|
|
130
|
+
line: coordinates[0], column: coordinates[1], inApp: value.inApp,
|
|
131
|
+
)
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
private func validateIssueImpactEvidence(_ value: IssueImpactEvidence) throws -> IssueImpactEvidence {
|
|
135
|
+
let values = try [
|
|
136
|
+
(value.affectedUserSegment, 256, true),
|
|
137
|
+
(value.failedAction, 256, true),
|
|
138
|
+
(value.userVisibleOutcome, 512, false),
|
|
139
|
+
].map { text, maximum, rejectLocation in
|
|
140
|
+
try text.map {
|
|
141
|
+
try issueText($0, label: "issue evidence impact", maximum: maximum,
|
|
142
|
+
disallowLocationDelimiters: rejectLocation)
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
guard values.contains(where: { $0 != nil }) else {
|
|
146
|
+
throw issueValidationError("issue evidence impact must contain at least one field")
|
|
147
|
+
}
|
|
148
|
+
return IssueImpactEvidence(
|
|
149
|
+
affectedUserSegment: values[0], failedAction: values[1], userVisibleOutcome: values[2],
|
|
150
|
+
)
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
private func validateIssueEvidenceFields(
|
|
154
|
+
_ fields: [String],
|
|
155
|
+
name: String,
|
|
156
|
+
seen: inout Set<String>,
|
|
157
|
+
) throws -> [String] {
|
|
158
|
+
guard (1 ... 32).contains(fields.count), Set(fields).count == fields.count,
|
|
159
|
+
fields.allSatisfy({ validMachineKey($0, maximum: 64, separators: ["_", ".", "-"], allowNumericStart: true) })
|
|
160
|
+
else {
|
|
161
|
+
throw issueValidationError("issue evidence \(name) must contain 1-32 unique bounded fields")
|
|
162
|
+
}
|
|
163
|
+
for field in fields where !seen.insert(field).inserted {
|
|
164
|
+
throw issueValidationError("issue evidence field \(field) has conflicting states")
|
|
165
|
+
}
|
|
166
|
+
return fields
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
private func validateIssueRelativePath(_ value: String) throws -> String {
|
|
170
|
+
let path = try issueText(
|
|
171
|
+
value.replacingOccurrences(of: "\\", with: "/"),
|
|
172
|
+
label: "issue evidence likelyFixArea file",
|
|
173
|
+
maximum: 256,
|
|
174
|
+
disallowLocationDelimiters: true,
|
|
175
|
+
)
|
|
176
|
+
let parts = path.split(separator: "/", omittingEmptySubsequences: false)
|
|
177
|
+
let drivePath = path.count >= 3 && path[path.index(after: path.startIndex)] == ":"
|
|
178
|
+
guard !path.hasPrefix("/"), !drivePath, !path.contains("://"),
|
|
179
|
+
parts.allSatisfy({ !$0.isEmpty && $0 != "." && $0 != ".." })
|
|
180
|
+
else {
|
|
181
|
+
throw issueValidationError("issue evidence likelyFixArea file must be a safe relative path")
|
|
182
|
+
}
|
|
183
|
+
return path
|
|
184
|
+
}
|
|
@@ -101,6 +101,7 @@ public extension IssueAttributes {
|
|
|
101
101
|
handled: Bool = true,
|
|
102
102
|
breadcrumbs: [IssueBreadcrumb]? = nil,
|
|
103
103
|
breadcrumbsTruncated: Bool? = nil,
|
|
104
|
+
evidence: IssueDiagnosticEvidence? = nil,
|
|
104
105
|
metadata: Metadata? = nil,
|
|
105
106
|
context: TelemetryContext? = nil,
|
|
106
107
|
fileID: String = #fileID,
|
|
@@ -136,6 +137,7 @@ public extension IssueAttributes {
|
|
|
136
137
|
stackFrames: [rootFrame],
|
|
137
138
|
breadcrumbs: breadcrumbs,
|
|
138
139
|
breadcrumbsTruncated: breadcrumbsTruncated,
|
|
140
|
+
evidence: evidence,
|
|
139
141
|
metadata: metadata,
|
|
140
142
|
context: context,
|
|
141
143
|
)
|
|
@@ -186,6 +188,7 @@ func normalizeIssueAttributes(_ value: IssueAttributes) throws -> IssueAttribute
|
|
|
186
188
|
}
|
|
187
189
|
let breadcrumbs = try normalizeIssueBreadcrumbs(value.breadcrumbs)
|
|
188
190
|
let breadcrumbsWereCapped = (value.breadcrumbs?.count ?? 0) > maximumIssueBreadcrumbs
|
|
191
|
+
let evidence = try value.evidence.map(validateIssueDiagnosticEvidence)
|
|
189
192
|
let context = try value.context.map { try validateTelemetryContext($0) }
|
|
190
193
|
try validateMetadata(value.metadata, label: "issue metadata")
|
|
191
194
|
return IssueAttributes(
|
|
@@ -197,6 +200,7 @@ func normalizeIssueAttributes(_ value: IssueAttributes) throws -> IssueAttribute
|
|
|
197
200
|
stackFrames: stackFrames,
|
|
198
201
|
breadcrumbs: breadcrumbs,
|
|
199
202
|
breadcrumbsTruncated: breadcrumbsWereCapped ? true : value.breadcrumbsTruncated,
|
|
203
|
+
evidence: evidence,
|
|
200
204
|
metadata: value.metadata,
|
|
201
205
|
context: context,
|
|
202
206
|
nativeStackFrames: value.nativeStackFrames,
|
|
@@ -224,6 +228,7 @@ func issueAttributes(
|
|
|
224
228
|
breadcrumbsTruncated: value.breadcrumbsTruncated == true
|
|
225
229
|
|| snapshot.truncated
|
|
226
230
|
|| combined.count > maximumIssueBreadcrumbs,
|
|
231
|
+
evidence: value.evidence,
|
|
227
232
|
metadata: value.metadata,
|
|
228
233
|
context: value.context,
|
|
229
234
|
nativeStackFrames: value.nativeStackFrames,
|
|
@@ -377,7 +382,7 @@ func issueText(
|
|
|
377
382
|
disallowLocationDelimiters: Bool = false,
|
|
378
383
|
) throws -> String {
|
|
379
384
|
let normalized = value.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
380
|
-
guard !normalized.isEmpty, normalized.count <= maximum, !containsForbiddenControl(normalized),
|
|
385
|
+
guard !normalized.isEmpty, normalized.unicodeScalars.count <= maximum, !containsForbiddenControl(normalized),
|
|
381
386
|
!disallowLocationDelimiters || (!normalized.contains("?") && !normalized.contains("#"))
|
|
382
387
|
else {
|
|
383
388
|
throw issueValidationError("\(label) is invalid")
|
|
@@ -64,7 +64,6 @@ public final class LogBrewClient: @unchecked Sendable {
|
|
|
64
64
|
let context = try resolvedContext(event: attributes.context)
|
|
65
65
|
let attributes = issueAttributes(attributes, adding: breadcrumbStore.snapshot())
|
|
66
66
|
.withContext(context)
|
|
67
|
-
.withCorrelationMetadata()
|
|
68
67
|
try pushEvent(.issue(validateIssue(attributes)), id: id, timestamp: timestamp)
|
|
69
68
|
}
|
|
70
69
|
|
|
@@ -76,7 +75,7 @@ public final class LogBrewClient: @unchecked Sendable {
|
|
|
76
75
|
public func log(_ id: String, timestamp: String, attributes: LogAttributes) throws {
|
|
77
76
|
let context = try resolvedContext(event: attributes.context)
|
|
78
77
|
try pushEvent(
|
|
79
|
-
.log(validateLog(attributes.withContext(context)
|
|
78
|
+
.log(validateLog(attributes.withContext(context))),
|
|
80
79
|
id: id,
|
|
81
80
|
timestamp: timestamp,
|
|
82
81
|
)
|
|
@@ -103,7 +102,7 @@ public final class LogBrewClient: @unchecked Sendable {
|
|
|
103
102
|
public func action(_ id: String, timestamp: String, attributes: ActionAttributes) throws {
|
|
104
103
|
let context = try resolvedContext(event: attributes.context)
|
|
105
104
|
try pushEvent(
|
|
106
|
-
.action(validateAction(attributes.withContext(context)
|
|
105
|
+
.action(validateAction(attributes.withContext(context))),
|
|
107
106
|
id: id,
|
|
108
107
|
timestamp: timestamp,
|
|
109
108
|
)
|
|
@@ -112,7 +111,7 @@ public final class LogBrewClient: @unchecked Sendable {
|
|
|
112
111
|
public func metric(_ id: String, timestamp: String, attributes: MetricAttributes) throws {
|
|
113
112
|
let context = try resolvedContext(event: attributes.context)
|
|
114
113
|
try pushEvent(
|
|
115
|
-
.metric(validateMetric(attributes.withContext(context)
|
|
114
|
+
.metric(validateMetric(attributes.withContext(context))),
|
|
116
115
|
id: id,
|
|
117
116
|
timestamp: timestamp,
|
|
118
117
|
)
|
|
@@ -209,22 +208,7 @@ private extension IssueAttributes {
|
|
|
209
208
|
stackFrames: stackFrames,
|
|
210
209
|
breadcrumbs: breadcrumbs,
|
|
211
210
|
breadcrumbsTruncated: breadcrumbsTruncated,
|
|
212
|
-
|
|
213
|
-
context: context,
|
|
214
|
-
nativeStackFrames: nativeStackFrames,
|
|
215
|
-
)
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
func withCorrelationMetadata() -> IssueAttributes {
|
|
219
|
-
IssueAttributes(
|
|
220
|
-
title: title,
|
|
221
|
-
level: level,
|
|
222
|
-
message: message,
|
|
223
|
-
exception: exception,
|
|
224
|
-
exceptionChain: exceptionChain,
|
|
225
|
-
stackFrames: stackFrames,
|
|
226
|
-
breadcrumbs: breadcrumbs,
|
|
227
|
-
breadcrumbsTruncated: breadcrumbsTruncated,
|
|
211
|
+
evidence: evidence,
|
|
228
212
|
metadata: correlationMetadata(metadata, context: context),
|
|
229
213
|
context: context,
|
|
230
214
|
nativeStackFrames: nativeStackFrames,
|
|
@@ -234,16 +218,6 @@ private extension IssueAttributes {
|
|
|
234
218
|
|
|
235
219
|
private extension LogAttributes {
|
|
236
220
|
func withContext(_ context: TelemetryContext?) -> LogAttributes {
|
|
237
|
-
LogAttributes(
|
|
238
|
-
message: message,
|
|
239
|
-
level: level,
|
|
240
|
-
logger: logger,
|
|
241
|
-
metadata: metadata,
|
|
242
|
-
context: context,
|
|
243
|
-
)
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
func withCorrelationMetadata() -> LogAttributes {
|
|
247
221
|
LogAttributes(
|
|
248
222
|
message: message,
|
|
249
223
|
level: level,
|
|
@@ -256,10 +230,6 @@ private extension LogAttributes {
|
|
|
256
230
|
|
|
257
231
|
private extension ActionAttributes {
|
|
258
232
|
func withContext(_ context: TelemetryContext?) -> ActionAttributes {
|
|
259
|
-
ActionAttributes(name: name, status: status, metadata: metadata, context: context)
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
func withCorrelationMetadata() -> ActionAttributes {
|
|
263
233
|
ActionAttributes(
|
|
264
234
|
name: name,
|
|
265
235
|
status: status,
|
|
@@ -271,19 +241,6 @@ private extension ActionAttributes {
|
|
|
271
241
|
|
|
272
242
|
private extension MetricAttributes {
|
|
273
243
|
func withContext(_ context: TelemetryContext?) -> MetricAttributes {
|
|
274
|
-
MetricAttributes(
|
|
275
|
-
name: name,
|
|
276
|
-
kind: kind,
|
|
277
|
-
value: value,
|
|
278
|
-
unit: unit,
|
|
279
|
-
temporality: temporality,
|
|
280
|
-
metadata: metadata,
|
|
281
|
-
context: context,
|
|
282
|
-
description: description,
|
|
283
|
-
)
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
func withCorrelationMetadata() -> MetricAttributes {
|
|
287
244
|
MetricAttributes(
|
|
288
245
|
name: name,
|
|
289
246
|
kind: kind,
|
|
@@ -175,6 +175,7 @@ public struct IssueAttributes: Codable, Equatable, Sendable {
|
|
|
175
175
|
public let stackFrames: [IssueStackFrame]?
|
|
176
176
|
public let breadcrumbs: [IssueBreadcrumb]?
|
|
177
177
|
public let breadcrumbsTruncated: Bool?
|
|
178
|
+
public let evidence: IssueDiagnosticEvidence?
|
|
178
179
|
public let metadata: Metadata?
|
|
179
180
|
public let context: TelemetryContext?
|
|
180
181
|
@_spi(CrashReplay) public let nativeStackFrames: [NativeStackFrame]?
|
|
@@ -188,6 +189,7 @@ public struct IssueAttributes: Codable, Equatable, Sendable {
|
|
|
188
189
|
stackFrames: [IssueStackFrame]? = nil,
|
|
189
190
|
breadcrumbs: [IssueBreadcrumb]? = nil,
|
|
190
191
|
breadcrumbsTruncated: Bool? = nil,
|
|
192
|
+
evidence: IssueDiagnosticEvidence? = nil,
|
|
191
193
|
metadata: Metadata? = nil,
|
|
192
194
|
context: TelemetryContext? = nil,
|
|
193
195
|
) {
|
|
@@ -199,6 +201,7 @@ public struct IssueAttributes: Codable, Equatable, Sendable {
|
|
|
199
201
|
self.stackFrames = stackFrames
|
|
200
202
|
self.breadcrumbs = breadcrumbs
|
|
201
203
|
self.breadcrumbsTruncated = breadcrumbsTruncated
|
|
204
|
+
self.evidence = evidence
|
|
202
205
|
self.metadata = metadata
|
|
203
206
|
self.context = context
|
|
204
207
|
nativeStackFrames = nil
|
|
@@ -214,6 +217,7 @@ public struct IssueAttributes: Codable, Equatable, Sendable {
|
|
|
214
217
|
stackFrames: [IssueStackFrame]? = nil,
|
|
215
218
|
breadcrumbs: [IssueBreadcrumb]? = nil,
|
|
216
219
|
breadcrumbsTruncated: Bool? = nil,
|
|
220
|
+
evidence: IssueDiagnosticEvidence? = nil,
|
|
217
221
|
metadata: Metadata? = nil,
|
|
218
222
|
context: TelemetryContext? = nil,
|
|
219
223
|
nativeStackFrames: [NativeStackFrame]?,
|
|
@@ -226,6 +230,7 @@ public struct IssueAttributes: Codable, Equatable, Sendable {
|
|
|
226
230
|
self.stackFrames = stackFrames
|
|
227
231
|
self.breadcrumbs = breadcrumbs
|
|
228
232
|
self.breadcrumbsTruncated = breadcrumbsTruncated
|
|
233
|
+
self.evidence = evidence
|
|
229
234
|
self.metadata = metadata
|
|
230
235
|
self.context = context
|
|
231
236
|
self.nativeStackFrames = nativeStackFrames
|
|
@@ -81,9 +81,14 @@ func validateTelemetryTraceContext(
|
|
|
81
81
|
)
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
-
func validMachineKey(
|
|
84
|
+
func validMachineKey(
|
|
85
|
+
_ value: String,
|
|
86
|
+
maximum: Int,
|
|
87
|
+
separators: Set<Character>,
|
|
88
|
+
allowNumericStart: Bool = false,
|
|
89
|
+
) -> Bool {
|
|
85
90
|
guard !value.isEmpty, value.count <= maximum, value.first?.isASCII == true,
|
|
86
|
-
value.first?.isLetter == true
|
|
91
|
+
value.first?.isLetter == true || allowNumericStart && value.first?.isNumber == true
|
|
87
92
|
else {
|
|
88
93
|
return false
|
|
89
94
|
}
|
|
@@ -7,21 +7,22 @@
|
|
|
7
7
|
"swift/logbrew-swift/Sources/LogBrew/DeliveryEngineDurable.swift": "b0d2ca07cce7fe01fc110238c7d10aa9c8ada0d28509e4c022e7fa950a4c79b9",
|
|
8
8
|
"swift/logbrew-swift/Sources/LogBrew/DeliveryEngineQueue.swift": "9c8503da4023368922b8b73dd989213685a64312806ecd73d0513958ad85f883",
|
|
9
9
|
"swift/logbrew-swift/Sources/LogBrew/DeliveryLifecycle.swift": "7b76541c2659cecf6614d9d6dcf9b61dc85b7a562d44fda03a026076afa02fcd",
|
|
10
|
-
"swift/logbrew-swift/Sources/LogBrew/DurableDeliveryStore.swift": "
|
|
11
|
-
"swift/logbrew-swift/Sources/LogBrew/DurableDeliveryStoreRecovery.swift": "
|
|
10
|
+
"swift/logbrew-swift/Sources/LogBrew/DurableDeliveryStore.swift": "7c292dda68efc3de790daf44733b25e4392226793343c21ba5df4f51fc1b402b",
|
|
11
|
+
"swift/logbrew-swift/Sources/LogBrew/DurableDeliveryStoreRecovery.swift": "79fee27975b6571954bf0b3692a50e60bbbcf53b5ab47f653a474db5c56d68ba",
|
|
12
12
|
"swift/logbrew-swift/Sources/LogBrew/EventEncoding.swift": "b88b6b2d17f7d0cee9ac1a1f34ef35b192cd33d213ef3c9e1c2d21cbcc304a97",
|
|
13
|
-
"swift/logbrew-swift/Sources/LogBrew/
|
|
13
|
+
"swift/logbrew-swift/Sources/LogBrew/IssueDiagnosticEvidence.swift": "e471f27fadaefe06971c1a98892aeb0774e840a2ce0cbb243ae9aabfa41b1c47",
|
|
14
|
+
"swift/logbrew-swift/Sources/LogBrew/IssueDiagnostics.swift": "297b8bbefd42c314a984fecb38237fb1395700c9eafcfdd1b545267bc34294cc",
|
|
14
15
|
"swift/logbrew-swift/Sources/LogBrew/IssueExceptionChain.swift": "10f81cbce95b47da0a44ef9e515b150e60d71dfcc8b04be24b037b1222a373a5",
|
|
15
16
|
"swift/logbrew-swift/Sources/LogBrew/LifecycleTrace.swift": "738cd3f129fac821a892404d1d42ee92bd806b6583d3dbb5379e88b14ab23c16",
|
|
16
|
-
"swift/logbrew-swift/Sources/LogBrew/LogBrewClient.swift": "
|
|
17
|
+
"swift/logbrew-swift/Sources/LogBrew/LogBrewClient.swift": "186cdbda3754a811f17b82818ca8e7a1711b3b75123686cd17dc3b7441fa21cb",
|
|
17
18
|
"swift/logbrew-swift/Sources/LogBrew/LogBrewLogger.swift": "d3266353a5f5139a2a1ca1a86bacce793518eb1489bf67ed9e3720b57b1c29b2",
|
|
18
19
|
"swift/logbrew-swift/Sources/LogBrew/LogBrewTrace.swift": "f67edf4ca464e6a6eb1c7a22cd4997928cd27b6c2d07535855a18a053d8de924",
|
|
19
20
|
"swift/logbrew-swift/Sources/LogBrew/Metadata.swift": "65840ebb3f3b79fbfc1f5faa3026c2583319e89ff26061676dc552c03ee15dbe",
|
|
20
21
|
"swift/logbrew-swift/Sources/LogBrew/NativeStackFrame.swift": "ecbd418969ec4a31503d9916a8bf27f22e6863356c6fabe457c1e61027a8d805",
|
|
21
22
|
"swift/logbrew-swift/Sources/LogBrew/ProductTimeline.swift": "209906aa0e8f096a347e35832840522710eadfb530053253cd73c748518e362d",
|
|
22
|
-
"swift/logbrew-swift/Sources/LogBrew/PublicTypes.swift": "
|
|
23
|
+
"swift/logbrew-swift/Sources/LogBrew/PublicTypes.swift": "a21aee058f716ec9d41d54ad032b2c7a4d64a4b99d749a9fa2be3fd2df5c6568",
|
|
23
24
|
"swift/logbrew-swift/Sources/LogBrew/TelemetryContext.swift": "ce3ac93a62a345f8c9a8f4c69ce84efc991bf02a688f49bad5e68edcff2f6e08",
|
|
24
|
-
"swift/logbrew-swift/Sources/LogBrew/TelemetryContextValidation.swift": "
|
|
25
|
+
"swift/logbrew-swift/Sources/LogBrew/TelemetryContextValidation.swift": "23524d281a185962a2ecc75f1ca1cb775171e2f72ecd39ca325ace7e45082485",
|
|
25
26
|
"swift/logbrew-swift/Sources/LogBrew/TraceEvidence.swift": "40aaa42976164c53d9f4d9e9e30d236534238da43bbe90832a673c8809ad54b6",
|
|
26
27
|
"swift/logbrew-swift/Sources/LogBrew/Transport.swift": "73eec81aaebec4bc922b2a372429c88fcd3e85606a7862cd08e1795eb2a8e6cd",
|
|
27
28
|
"swift/logbrew-swift/Sources/LogBrew/URLSessionTrace.swift": "d0076c72671716c41bd7498d0509e453b8182c418156e169196fee9f26fc66b1",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@logbrew/react-native",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.17",
|
|
4
4
|
"description": "React Native offline delivery, Apple native diagnostics, screen, error, trace, action, and network timeline helpers for LogBrew.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./index.cjs",
|