@npaw/forge-react-native 9.6.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.
Files changed (74) hide show
  1. package/LICENSE +10 -0
  2. package/NPAWForgeReactNative.podspec +44 -0
  3. package/README.md +51 -0
  4. package/android/build.gradle +77 -0
  5. package/android/src/main/AndroidManifest.xml +14 -0
  6. package/android/src/main/java/com/npaw/forge/reactnative/ForgeDtoMapper.java +295 -0
  7. package/android/src/main/java/com/npaw/forge/reactnative/ForgeInstanceRegistry.java +61 -0
  8. package/android/src/main/java/com/npaw/forge/reactnative/NPAWForgeModule.java +735 -0
  9. package/android/src/main/java/com/npaw/forge/reactnative/NPAWForgePackage.java +53 -0
  10. package/android/src/test/java/com/npaw/forge/reactnative/ForgeDtoMapperTest.java +133 -0
  11. package/ios/ForgeApple/ForgeKernelFFI.xcframework/Info.plist +82 -0
  12. package/ios/ForgeApple/ForgeKernelFFI.xcframework/ios-arm64/Headers/ForgeKernelFFI.h +17 -0
  13. package/ios/ForgeApple/ForgeKernelFFI.xcframework/ios-arm64/Headers/forge_kernelFFI.h +873 -0
  14. package/ios/ForgeApple/ForgeKernelFFI.xcframework/ios-arm64/Headers/module.modulemap +7 -0
  15. package/ios/ForgeApple/ForgeKernelFFI.xcframework/ios-arm64/libForgeKernelFFI.a +0 -0
  16. package/ios/ForgeApple/ForgeKernelFFI.xcframework/ios-arm64_x86_64-simulator/Headers/ForgeKernelFFI.h +17 -0
  17. package/ios/ForgeApple/ForgeKernelFFI.xcframework/ios-arm64_x86_64-simulator/Headers/forge_kernelFFI.h +873 -0
  18. package/ios/ForgeApple/ForgeKernelFFI.xcframework/ios-arm64_x86_64-simulator/Headers/module.modulemap +7 -0
  19. package/ios/ForgeApple/ForgeKernelFFI.xcframework/ios-arm64_x86_64-simulator/libForgeKernelFFI.a +0 -0
  20. package/ios/ForgeApple/ForgeKernelFFI.xcframework/tvos-arm64/Headers/ForgeKernelFFI.h +17 -0
  21. package/ios/ForgeApple/ForgeKernelFFI.xcframework/tvos-arm64/Headers/forge_kernelFFI.h +873 -0
  22. package/ios/ForgeApple/ForgeKernelFFI.xcframework/tvos-arm64/Headers/module.modulemap +7 -0
  23. package/ios/ForgeApple/ForgeKernelFFI.xcframework/tvos-arm64/libForgeKernelFFI.a +0 -0
  24. package/ios/ForgeApple/ForgeKernelFFI.xcframework/tvos-arm64-simulator/Headers/ForgeKernelFFI.h +17 -0
  25. package/ios/ForgeApple/ForgeKernelFFI.xcframework/tvos-arm64-simulator/Headers/forge_kernelFFI.h +873 -0
  26. package/ios/ForgeApple/ForgeKernelFFI.xcframework/tvos-arm64-simulator/Headers/module.modulemap +7 -0
  27. package/ios/ForgeApple/ForgeKernelFFI.xcframework/tvos-arm64-simulator/libForgeKernelFFI.a +0 -0
  28. package/ios/ForgeApple/Sources/ForgeKernelUniFFI/forge_kernel.swift +3931 -0
  29. package/ios/ForgeApple/Sources/ForgeKernelUniFFI/forge_kernelFFI.h +873 -0
  30. package/ios/ForgeApple/Sources/ForgeKernelUniFFI/forge_kernelFFI.modulemap +7 -0
  31. package/ios/ForgeApple/Sources/NPAWForgeAVPlayerAdapter/AVPlayerAccessLogFacts.swift +152 -0
  32. package/ios/ForgeApple/Sources/NPAWForgeAVPlayerAdapter/AVPlayerAdapter.swift +877 -0
  33. package/ios/ForgeApple/Sources/NPAWForgeCapture/AttributionCapture.swift +158 -0
  34. package/ios/ForgeApple/Sources/NPAWForgeCapture/CapturePlanGate.swift +189 -0
  35. package/ios/ForgeApple/Sources/NPAWForgeCapture/CrashCapture.swift +120 -0
  36. package/ios/ForgeApple/Sources/NPAWForgeCapture/ForgeAutomaticCapture.swift +639 -0
  37. package/ios/ForgeApple/Sources/NPAWForgeCapture/SectionsCapture.swift +1176 -0
  38. package/ios/ForgeApple/Sources/NPAWForgeCapture/SectionsEngine.swift +674 -0
  39. package/ios/ForgeApple/Sources/NPAWForgeCapture/SectionsMarkers.swift +105 -0
  40. package/ios/ForgeApple/Sources/NPAWForgeCapture/SectionsSwiftUI.swift +284 -0
  41. package/ios/ForgeApple/Sources/NPAWForgeCapture/SwiftUICapture.swift +44 -0
  42. package/ios/ForgeApple/Sources/NPAWForgeCore/AdAdapter.swift +199 -0
  43. package/ios/ForgeApple/Sources/NPAWForgeCore/AdSemantics.swift +136 -0
  44. package/ios/ForgeApple/Sources/NPAWForgeCore/ForgeApple.swift +596 -0
  45. package/ios/ForgeApple/Sources/NPAWForgeCore/ForgeCapturePlan.swift +297 -0
  46. package/ios/ForgeApple/Sources/NPAWForgeCore/ForgeClock.swift +29 -0
  47. package/ios/ForgeApple/Sources/NPAWForgeCore/ForgeLogging.swift +109 -0
  48. package/ios/ForgeApple/Sources/NPAWForgeCore/ForgeOutcome.swift +89 -0
  49. package/ios/ForgeApple/Sources/NPAWForgeCore/ForgeRuntime.swift +291 -0
  50. package/ios/ForgeApple/Sources/NPAWForgeCore/ForgeStorage.swift +70 -0
  51. package/ios/ForgeApple/Sources/NPAWForgeCore/ManualAdReporter.swift +180 -0
  52. package/ios/ForgeApple/Sources/NPAWForgeCore/NPAWForge.swift +453 -0
  53. package/ios/ForgeApple/Sources/NPAWForgeCore/NativeUniFFIRuntime.swift +594 -0
  54. package/ios/ForgeApple/Sources/NPAWForgeCore/NetworkRequestRecord.swift +191 -0
  55. package/ios/ForgeApple/Sources/NPAWForgeCore/ObjectiveCWrappers.swift +191 -0
  56. package/ios/ForgeApple/Sources/NPAWForgeCore/PlayerAdapter.swift +299 -0
  57. package/ios/ForgeApple/Sources/NPAWForgeCore/PlayerSemantics.swift +266 -0
  58. package/ios/ForgeApple/Sources/NPAWForgeCore/PrivacySanitizer.swift +59 -0
  59. package/ios/ForgeApple/Sources/NPAWForgeCore/VideoAdapter.swift +478 -0
  60. package/ios/ForgeApple/Sources/NPAWForgeCore/VideoOptions.swift +431 -0
  61. package/ios/ForgeApple/Sources/NPAWForgeNetworkCapture/ForgeNetworkCapture.swift +116 -0
  62. package/ios/ForgeApple/Sources/NPAWForgeNetworkCaptureObjC/NPAWForgeNetworkSwizzler.m +293 -0
  63. package/ios/ForgeApple/Sources/NPAWForgeNetworkCaptureObjC/include/NPAWForgeNetworkSwizzler.h +33 -0
  64. package/ios/ForgeModuleImpl.swift +1131 -0
  65. package/ios/NPAWForgeTurboModule.mm +399 -0
  66. package/package.json +33 -0
  67. package/src/ForgeAdReporter.ts +101 -0
  68. package/src/ForgeVideo.ts +169 -0
  69. package/src/NPAWForge.ts +177 -0
  70. package/src/capture.ts +192 -0
  71. package/src/events.ts +40 -0
  72. package/src/index.ts +37 -0
  73. package/src/specs/NativeNPAWForge.ts +168 -0
  74. package/src/types.ts +114 -0
@@ -0,0 +1,297 @@
1
+ /**
2
+ * Copyright (C) NPAW - All Rights Reserved
3
+ *
4
+ * This source code is protected under international copyright law. All rights
5
+ * reserved and protected by the copyright holders.
6
+ * This file is confidential and only available to authorized individuals with the
7
+ * permission of the copyright holders. If you encounter this file and do not have
8
+ * permission, please contact the copyright holders and delete this file.
9
+ *
10
+ *
11
+ * @author: Victor Soto <victor.soto@nicepeopleatwork.com>
12
+ * @description: Typed kernel-derived Capture Plan surfaced to Apple observers. The
13
+ * plan tells the binding which capture observer families are worth
14
+ * installing (bitmask), plus the focus/limit knobs and remote behavior
15
+ * hints for adapters. The kernel intake gates remain the semantic
16
+ * authority; the plan is an optimization signal, never the only gate.
17
+ */
18
+
19
+ import Foundation
20
+
21
+ /// Kernel-computed capture plan delivered whenever the kernel-derived plan
22
+ /// hash changes (first session start onward, and after remote-config
23
+ /// refreshes). Test bits with ``isEnabled(_:)`` and the ``Bits`` constants.
24
+ public struct ForgeCapturePlan: Equatable {
25
+ /// Monotonic emission sequence for the runtime instance.
26
+ public let planSeq: UInt64
27
+ /// Family bitmask; see ``Bits``.
28
+ public let bits: UInt32
29
+ /// Remote config version the plan was derived from, when known.
30
+ public let configVersion: String?
31
+ public let focus: Focus
32
+ public let limits: Limits
33
+ public let behavior: Behavior
34
+ public let sections: Sections
35
+ public let attribution: Attribution
36
+ /// Canonical plan hash as a decimal string (full 64-bit value).
37
+ public let hash: String
38
+
39
+ public init(
40
+ planSeq: UInt64,
41
+ bits: UInt32,
42
+ configVersion: String? = nil,
43
+ focus: Focus,
44
+ limits: Limits,
45
+ behavior: Behavior = Behavior(),
46
+ sections: Sections = Sections(),
47
+ attribution: Attribution = Attribution(),
48
+ hash: String
49
+ ) {
50
+ self.planSeq = planSeq
51
+ self.bits = bits
52
+ self.configVersion = configVersion
53
+ self.focus = focus
54
+ self.limits = limits
55
+ self.behavior = behavior
56
+ self.sections = sections
57
+ self.attribution = attribution
58
+ self.hash = hash
59
+ }
60
+
61
+ public func isEnabled(_ bit: UInt32) -> Bool {
62
+ bits & bit != 0
63
+ }
64
+
65
+ /// Focus observer knobs the binding needs to run dwell detection.
66
+ public struct Focus: Equatable {
67
+ public let dwellThresholdMs: Int64
68
+ public let dedupeMs: Int64
69
+ public let repeatUnchangedDwell: Bool
70
+
71
+ public init(dwellThresholdMs: Int64, dedupeMs: Int64, repeatUnchangedDwell: Bool) {
72
+ self.dwellThresholdMs = dwellThresholdMs
73
+ self.dedupeMs = dedupeMs
74
+ self.repeatUnchangedDwell = repeatUnchangedDwell
75
+ }
76
+ }
77
+
78
+ /// Privacy-derived extraction limits applied while building payloads.
79
+ public struct Limits: Equatable {
80
+ public let maxTargetTextLength: Int
81
+ public let maxPayloadFields: Int
82
+
83
+ public init(maxTargetTextLength: Int, maxPayloadFields: Int) {
84
+ self.maxTargetTextLength = maxTargetTextLength
85
+ self.maxPayloadFields = maxPayloadFields
86
+ }
87
+ }
88
+
89
+ /// Remote behavior hints observed by adapters (`bh.v`); applied at the
90
+ /// next view start, never hot-swapped mid-view. `joinMethod` uses the
91
+ /// canonical tokens `ff`, `ph`, `rd`, or `auto`.
92
+ public struct Behavior: Equatable {
93
+ public let joinMethod: String?
94
+ public let seekHeuristicEnabled: Bool?
95
+ public let seekJumpThresholdMs: Int64?
96
+ public let metadataSamplingMs: Int64?
97
+
98
+ public init(
99
+ joinMethod: String? = nil,
100
+ seekHeuristicEnabled: Bool? = nil,
101
+ seekJumpThresholdMs: Int64? = nil,
102
+ metadataSamplingMs: Int64? = nil
103
+ ) {
104
+ self.joinMethod = joinMethod
105
+ self.seekHeuristicEnabled = seekHeuristicEnabled
106
+ self.seekJumpThresholdMs = seekJumpThresholdMs
107
+ self.metadataSamplingMs = metadataSamplingMs
108
+ }
109
+ }
110
+
111
+ /// F3 sections auto-capture knobs (`raw_section_visible`,
112
+ /// `raw_section_hidden`, `raw_content_impression`, `raw_content_dwell`).
113
+ /// The CSS selectors are Web-only and carried purely for contract parity;
114
+ /// Apple discovers sections through explicit registrations,
115
+ /// `accessibilityIdentifier` markers, and the native-id remote lists.
116
+ public struct Sections: Equatable {
117
+ /// Minimum visible-area ratio (0-100) for a section/item to count as visible.
118
+ public let visibilityThresholdPct: Int
119
+ /// Continuous visible time an item must sustain before an impression fires.
120
+ public let impressionMinVisibleMs: Int64
121
+ /// Focus time on a tracked item before a dwell fires.
122
+ public let dwellMs: Int64
123
+ /// Maximum distinct items tracked per screen; extra targets are ignored.
124
+ public let maxTrackedItems: Int
125
+ /// Maximum distinct sections tracked per screen; extra targets are ignored.
126
+ public let maxSections: Int
127
+ /// Web-only CSS selector; never used on Apple.
128
+ public let sectionSelector: String?
129
+ /// Web-only CSS selector; never used on Apple.
130
+ public let itemSelector: String?
131
+ /// Exact `accessibilityIdentifier` matches adopted as sections.
132
+ public let sectionNativeIds: [String]
133
+ /// Exact `accessibilityIdentifier` matches adopted as items.
134
+ public let itemNativeIds: [String]
135
+ /// Remote naming hint (`auto`, `hd`, ...); Apple names come from
136
+ /// registrations/markers, so this stays a diagnostic passthrough.
137
+ public let nameFrom: String
138
+
139
+ public init(
140
+ visibilityThresholdPct: Int = 50,
141
+ impressionMinVisibleMs: Int64 = 1000,
142
+ dwellMs: Int64 = 1000,
143
+ maxTrackedItems: Int = 200,
144
+ maxSections: Int = 20,
145
+ sectionSelector: String? = nil,
146
+ itemSelector: String? = nil,
147
+ sectionNativeIds: [String] = [],
148
+ itemNativeIds: [String] = [],
149
+ nameFrom: String = "auto"
150
+ ) {
151
+ self.visibilityThresholdPct = visibilityThresholdPct
152
+ self.impressionMinVisibleMs = impressionMinVisibleMs
153
+ self.dwellMs = dwellMs
154
+ self.maxTrackedItems = maxTrackedItems
155
+ self.maxSections = maxSections
156
+ self.sectionSelector = sectionSelector
157
+ self.itemSelector = itemSelector
158
+ self.sectionNativeIds = sectionNativeIds
159
+ self.itemNativeIds = itemNativeIds
160
+ self.nameFrom = nameFrom
161
+ }
162
+ }
163
+
164
+ /// F4 attribution auto-capture knobs (`raw_attribution`, kernel-gated by
165
+ /// `rc.at`). `extraQueryKeys` EXTENDS the built-in click-id list (gclid,
166
+ /// fbclid, ttclid, msclkid, dclid, twclid); `utm_*` handling is always on
167
+ /// while the family bit is enabled. The kernel's Install Referrer knob is
168
+ /// Android-only and intentionally not surfaced on Apple (see
169
+ /// ``fromUniFFIFlat``).
170
+ public struct Attribution: Equatable {
171
+ /// Extra query-parameter keys captured verbatim on top of the
172
+ /// built-in click-id list; never replaces `utm_*` handling.
173
+ public let extraQueryKeys: [String]
174
+ /// Whether the redacted landing/deep-link URL rides along as `utmUrl`.
175
+ public let includeLandingUrl: Bool
176
+ /// Whether incoming deep links are parsed into `raw_attribution`.
177
+ public let deriveFromDeepLinks: Bool
178
+
179
+ public init(
180
+ extraQueryKeys: [String] = [],
181
+ includeLandingUrl: Bool = true,
182
+ deriveFromDeepLinks: Bool = true
183
+ ) {
184
+ self.extraQueryKeys = extraQueryKeys
185
+ self.includeLandingUrl = includeLandingUrl
186
+ self.deriveFromDeepLinks = deriveFromDeepLinks
187
+ }
188
+ }
189
+
190
+ /// Frozen wire contract mirroring `forge_kernel::capture_plan::bits`;
191
+ /// values must never be reassigned.
192
+ public enum Bits {
193
+ public static let navigation: UInt32 = 1 << 0
194
+ public static let pageView: UInt32 = 1 << 1
195
+ public static let clicks: UInt32 = 1 << 2
196
+ public static let scrolls: UInt32 = 1 << 3
197
+ public static let lifecycle: UInt32 = 1 << 4
198
+ public static let deepLink: UInt32 = 1 << 5
199
+ public static let focus: UInt32 = 1 << 6
200
+ public static let remoteKey: UInt32 = 1 << 7
201
+ public static let errors: UInt32 = 1 << 8
202
+ public static let webVitals: UInt32 = 1 << 9
203
+ public static let network: UInt32 = 1 << 10
204
+ // Bit positions `1 << 11` and `1 << 12` are permanently RESERVED. They
205
+ // carried the removed request/response body-content capture flags. The
206
+ // kernel never sets them and this binding must ignore them; the
207
+ // positions stay unassigned so existing bits never renumber.
208
+ public static let sections: UInt32 = 1 << 13
209
+ public static let attribution: UInt32 = 1 << 14
210
+ }
211
+ }
212
+
213
+ extension ForgeCapturePlan {
214
+ /// Flat-field factory mirroring the UniFFI `AppleCapturePlan` dictionary
215
+ /// shape. The native runtime mapping and the plan-gating tests share it so
216
+ /// the typed plan and the wire dictionary cannot drift silently.
217
+ /// `targetDataAttributes` is a Web-only knob and is intentionally dropped
218
+ /// on Apple; the sections CSS selectors are carried for contract parity
219
+ /// but never drive Apple capture. `attributionInstallReferrerEnabled` is
220
+ /// accepted for the one-to-one passthrough but dropped: the Install
221
+ /// Referrer source is the Android Play Store API and has no Apple
222
+ /// counterpart, so no Apple observer can ever consume the knob.
223
+ static func fromUniFFIFlat(
224
+ planSeq: UInt64,
225
+ bits: UInt32,
226
+ configVersion: String?,
227
+ focusDwellThresholdMs: UInt64,
228
+ focusDedupeMs: UInt64,
229
+ focusRepeatUnchangedDwell: Bool,
230
+ maxTargetTextLength: UInt32,
231
+ maxPayloadFields: UInt32,
232
+ joinMethod: String?,
233
+ seekHeuristicEnabled: Bool?,
234
+ seekJumpThresholdMs: UInt64?,
235
+ metadataSamplingMs: UInt64?,
236
+ sectionsVisibilityThresholdPct: UInt32,
237
+ sectionsImpressionMinVisibleMs: UInt64,
238
+ sectionsDwellMs: UInt64,
239
+ sectionsMaxTrackedItems: UInt32,
240
+ sectionsMaxSections: UInt32,
241
+ sectionsSectionSelector: String?,
242
+ sectionsItemSelector: String?,
243
+ sectionsSectionNativeIds: [String],
244
+ sectionsItemNativeIds: [String],
245
+ sectionsNameFrom: String,
246
+ targetDataAttributes: [String],
247
+ attributionExtraQueryKeys: [String],
248
+ attributionIncludeLandingUrl: Bool,
249
+ attributionDeriveFromDeepLinks: Bool,
250
+ attributionInstallReferrerEnabled: Bool,
251
+ hash: String
252
+ ) -> ForgeCapturePlan {
253
+ func i64(_ value: UInt64) -> Int64 {
254
+ value > UInt64(Int64.max) ? Int64.max : Int64(value)
255
+ }
256
+ return ForgeCapturePlan(
257
+ planSeq: planSeq,
258
+ bits: bits,
259
+ configVersion: configVersion,
260
+ focus: Focus(
261
+ dwellThresholdMs: i64(focusDwellThresholdMs),
262
+ dedupeMs: i64(focusDedupeMs),
263
+ repeatUnchangedDwell: focusRepeatUnchangedDwell
264
+ ),
265
+ limits: Limits(
266
+ maxTargetTextLength: Int(maxTargetTextLength),
267
+ maxPayloadFields: Int(maxPayloadFields)
268
+ ),
269
+ behavior: Behavior(
270
+ joinMethod: joinMethod,
271
+ seekHeuristicEnabled: seekHeuristicEnabled,
272
+ seekJumpThresholdMs: seekJumpThresholdMs.map(i64),
273
+ metadataSamplingMs: metadataSamplingMs.map(i64)
274
+ ),
275
+ sections: Sections(
276
+ visibilityThresholdPct: Int(sectionsVisibilityThresholdPct),
277
+ impressionMinVisibleMs: i64(sectionsImpressionMinVisibleMs),
278
+ dwellMs: i64(sectionsDwellMs),
279
+ maxTrackedItems: Int(sectionsMaxTrackedItems),
280
+ maxSections: Int(sectionsMaxSections),
281
+ sectionSelector: sectionsSectionSelector,
282
+ itemSelector: sectionsItemSelector,
283
+ sectionNativeIds: sectionsSectionNativeIds,
284
+ itemNativeIds: sectionsItemNativeIds,
285
+ nameFrom: sectionsNameFrom
286
+ ),
287
+ // attributionInstallReferrerEnabled is dropped here: Android-only
288
+ // (Play Install Referrer); Apple has no equivalent source.
289
+ attribution: Attribution(
290
+ extraQueryKeys: attributionExtraQueryKeys,
291
+ includeLandingUrl: attributionIncludeLandingUrl,
292
+ deriveFromDeepLinks: attributionDeriveFromDeepLinks
293
+ ),
294
+ hash: hash
295
+ )
296
+ }
297
+ }
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Copyright (C) NPAW - All Rights Reserved
3
+ *
4
+ * This source code is protected under international copyright law. All rights
5
+ * reserved and protected by the copyright holders.
6
+ * This file is confidential and only available to authorized individuals with the
7
+ * permission of the copyright holders. If you encounter this file and do not have
8
+ * permission, please contact the copyright holders and delete this file.
9
+ *
10
+ *
11
+ * @author: Victor Soto <victor.soto@nicepeopleatwork.com>
12
+ * @description: Wall-clock and monotonic timing helpers for Apple binding events.
13
+ */
14
+
15
+ import Foundation
16
+
17
+ public enum ForgeClock {
18
+ public static func nowMs() -> Int64 {
19
+ Int64(Date().timeIntervalSince1970 * 1000)
20
+ }
21
+
22
+ public static func elapsedRealtimeMs() -> Int64 {
23
+ Int64(ProcessInfo.processInfo.systemUptime * 1000)
24
+ }
25
+
26
+ public static func elapsedRealtimeMs(anchorTimestampMs: Int64) -> Int64 {
27
+ elapsedRealtimeMs()
28
+ }
29
+ }
@@ -0,0 +1,109 @@
1
+ /**
2
+ * Copyright (C) NPAW - All Rights Reserved
3
+ *
4
+ * This source code is protected under international copyright law. All rights
5
+ * reserved and protected by the copyright holders.
6
+ * This file is confidential and only available to authorized individuals with the
7
+ * permission of the copyright holders. If you encounter this file and do not have
8
+ * permission, please contact the copyright holders and delete this file.
9
+ *
10
+ *
11
+ * @author: Victor Soto <victor.soto@nicepeopleatwork.com>
12
+ * @description: Apple binding log levels, families, and default platform sink.
13
+ */
14
+
15
+ import Foundation
16
+
17
+ @objc(NPAWForgeLogLevel)
18
+ public enum ForgeLogLevel: Int {
19
+ case off
20
+ case error
21
+ case warning
22
+ case info
23
+ case debug
24
+ case trace
25
+
26
+ public var wireName: String {
27
+ switch self {
28
+ case .off: return "off"
29
+ case .error: return "error"
30
+ case .warning: return "warning"
31
+ case .info: return "info"
32
+ case .debug: return "debug"
33
+ case .trace: return "trace"
34
+ }
35
+ }
36
+
37
+ public static func from(_ value: Any?, default defaultLevel: ForgeLogLevel = .warning) -> ForgeLogLevel {
38
+ if let level = value as? ForgeLogLevel {
39
+ return level
40
+ }
41
+ guard let raw = value.map({ String(describing: $0).lowercased() }) else {
42
+ return defaultLevel
43
+ }
44
+ switch raw {
45
+ case "off": return .off
46
+ case "error": return .error
47
+ case "warn", "warning": return .warning
48
+ case "info": return .info
49
+ case "debug": return .debug
50
+ case "trace": return .trace
51
+ default: return defaultLevel
52
+ }
53
+ }
54
+ }
55
+
56
+ public enum ForgeLogFamily: String {
57
+ case video = "Video"
58
+ case ad = "Ad"
59
+ case session = "Session"
60
+ case capture = "Capture"
61
+ case network = "Network"
62
+ case config = "Config"
63
+ case lifecycle = "Lifecycle"
64
+ case transport = "Transport"
65
+ case storage = "Storage"
66
+ case diagnostics = "Diagnostics"
67
+ }
68
+
69
+ public protocol ForgeLogSink: AnyObject {
70
+ func log(level: ForgeLogLevel, family: ForgeLogFamily, message: String, attributes: [String: String])
71
+ }
72
+
73
+ public final class OSLogForgeLogSink: ForgeLogSink {
74
+ public init() {}
75
+
76
+ public func log(level: ForgeLogLevel, family: ForgeLogFamily, message: String, attributes: [String: String]) {
77
+ let rendered = "[\(family.rawValue)] \(message) \(attributes)"
78
+ guard level != .off else { return }
79
+ NSLog("[NPAW-Forge-Apple] %@", rendered)
80
+ }
81
+ }
82
+
83
+ public final class ForgeLogger {
84
+ private var level: ForgeLogLevel
85
+ private let sink: ForgeLogSink?
86
+
87
+ public init(level: ForgeLogLevel = .warning, sink: ForgeLogSink? = OSLogForgeLogSink()) {
88
+ self.level = level
89
+ self.sink = sink
90
+ }
91
+
92
+ public func setLevel(_ level: ForgeLogLevel) {
93
+ self.level = level
94
+ }
95
+
96
+ public func effectiveLevel() -> ForgeLogLevel {
97
+ level
98
+ }
99
+
100
+ public func warning(family: ForgeLogFamily, action: String, details: [String: String] = [:]) {
101
+ guard level.rawValue >= ForgeLogLevel.warning.rawValue else { return }
102
+ sink?.log(level: .warning, family: family, message: action, attributes: details)
103
+ }
104
+
105
+ public func error(family: ForgeLogFamily, action: String, details: [String: String] = [:]) {
106
+ guard level.rawValue >= ForgeLogLevel.error.rawValue else { return }
107
+ sink?.log(level: .error, family: family, message: action, attributes: details)
108
+ }
109
+ }
@@ -0,0 +1,89 @@
1
+ /**
2
+ * Copyright (C) NPAW - All Rights Reserved
3
+ *
4
+ * This source code is protected under international copyright law. All rights
5
+ * reserved and protected by the copyright holders.
6
+ * This file is confidential and only available to authorized individuals with the
7
+ * permission of the copyright holders. If you encounter this file and do not have
8
+ * permission, please contact the copyright holders and delete this file.
9
+ *
10
+ *
11
+ * @author: Victor Soto <victor.soto@nicepeopleatwork.com>
12
+ * @description: Public outcome and diagnostic types returned by the Forge Apple binding.
13
+ */
14
+
15
+ import Foundation
16
+
17
+ @objc(NPAWForgeOutcomeCode)
18
+ public enum ForgeOutcomeCode: Int, Equatable {
19
+ case accepted
20
+ case deferred
21
+ case dropped
22
+ case ignoredDuplicate
23
+ case ignoredInvalidState
24
+ case blockedDependency
25
+ case unsupportedCapability
26
+ case failedWithDiagnostic
27
+ case ignoredBeforeSessionStart
28
+
29
+ public var wireName: String {
30
+ switch self {
31
+ case .accepted: return "accepted"
32
+ case .deferred: return "deferred"
33
+ case .dropped: return "dropped"
34
+ case .ignoredDuplicate: return "ignored_duplicate"
35
+ case .ignoredBeforeSessionStart: return "ignored_before_session_start"
36
+ case .ignoredInvalidState: return "ignored_invalid_state"
37
+ case .blockedDependency: return "blocked_dependency"
38
+ case .unsupportedCapability: return "unsupported_capability"
39
+ case .failedWithDiagnostic: return "failed_with_diagnostic"
40
+ }
41
+ }
42
+ }
43
+
44
+ @objc(NPAWForgeOutcome)
45
+ public final class ForgeOutcome: NSObject {
46
+ @objc public let code: ForgeOutcomeCode
47
+ @objc public let reason: String?
48
+ public let details: [String: String]
49
+
50
+ public var isAccepted: Bool { code == .accepted }
51
+
52
+ public init(code: ForgeOutcomeCode, reason: String? = nil, details: [String: String] = [:]) {
53
+ self.code = code
54
+ self.reason = reason
55
+ self.details = details
56
+ }
57
+
58
+ @objc public static let accepted = ForgeOutcome(code: .accepted)
59
+ @objc public static let deferred = ForgeOutcome(code: .deferred)
60
+ @objc public static let dropped = ForgeOutcome(code: .dropped)
61
+ @objc public static let ignoredDuplicate = ForgeOutcome(code: .ignoredDuplicate)
62
+ @objc public static let ignoredBeforeSessionStart = ForgeOutcome(code: .ignoredBeforeSessionStart)
63
+ @objc public static let ignoredInvalidState = ForgeOutcome(code: .ignoredInvalidState)
64
+ @objc public static let blockedDependency = ForgeOutcome(code: .blockedDependency)
65
+ @objc public static let unsupportedCapability = ForgeOutcome(code: .unsupportedCapability)
66
+
67
+ @objc public static func failedWithDiagnostic(_ reason: String) -> ForgeOutcome {
68
+ ForgeOutcome(code: .failedWithDiagnostic, reason: reason)
69
+ }
70
+
71
+ public override var description: String {
72
+ if let reason {
73
+ return "\(code.wireName):\(reason)"
74
+ }
75
+ return code.wireName
76
+ }
77
+ }
78
+
79
+ public struct ForgeDiagnostic: Equatable {
80
+ public let code: String
81
+ public let message: String
82
+ public let attributes: [String: String]
83
+
84
+ public init(code: String, message: String, attributes: [String: String] = [:]) {
85
+ self.code = code
86
+ self.message = message
87
+ self.attributes = attributes
88
+ }
89
+ }