react-native-nitro-logger 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (205) hide show
  1. package/LICENSE +20 -0
  2. package/NitroLogger.podspec +29 -0
  3. package/README.md +214 -0
  4. package/android/CMakeLists.txt +24 -0
  5. package/android/build.gradle +124 -0
  6. package/android/src/main/AndroidManifest.xml +2 -0
  7. package/android/src/main/cpp/cpp-adapter.cpp +11 -0
  8. package/android/src/main/java/com/margelo/nitro/nitrologger/AndroidPlatformIo.kt +100 -0
  9. package/android/src/main/java/com/margelo/nitro/nitrologger/HybridFileSink.kt +292 -0
  10. package/android/src/main/java/com/margelo/nitro/nitrologger/HybridNativeConsoleSink.kt +23 -0
  11. package/android/src/main/java/com/margelo/nitro/nitrologger/LogFileWriter.kt +1328 -0
  12. package/android/src/main/java/com/margelo/nitro/nitrologger/LogRotationPolicy.kt +186 -0
  13. package/android/src/main/java/com/margelo/nitro/nitrologger/LogSecureFile.kt +80 -0
  14. package/android/src/main/java/com/margelo/nitro/nitrologger/LogWriterRegistry.kt +420 -0
  15. package/android/src/main/java/com/margelo/nitro/nitrologger/NativeConsoleWriter.kt +237 -0
  16. package/android/src/main/java/com/margelo/nitro/nitrologger/NitroLoggerPackage.kt +22 -0
  17. package/android/src/main/java/com/margelo/nitro/nitrologger/PlatformIo.kt +165 -0
  18. package/docs/PARITY.md +330 -0
  19. package/docs/PRIVACY.md +341 -0
  20. package/eslint-plugin/README.md +238 -0
  21. package/eslint-plugin/index.js +62 -0
  22. package/eslint-plugin/rules/literal-subsystem.js +102 -0
  23. package/eslint-plugin/rules/no-computed-metadata-key.js +174 -0
  24. package/eslint-plugin/rules/no-derived-correlation.js +171 -0
  25. package/eslint-plugin/rules/no-dynamic-message.js +60 -0
  26. package/eslint-plugin/shared.js +1948 -0
  27. package/ios/Gzip.swift +203 -0
  28. package/ios/HybridFileSink.swift +291 -0
  29. package/ios/HybridNativeConsoleSink.swift +20 -0
  30. package/ios/LogFileWriter.swift +1280 -0
  31. package/ios/LogSecureFile.swift +296 -0
  32. package/ios/LogWriterRegistry.swift +444 -0
  33. package/ios/NativeConsoleWriter.swift +180 -0
  34. package/lib/module/Logger.js +381 -0
  35. package/lib/module/Logger.js.map +1 -0
  36. package/lib/module/ScopedLogger.js +70 -0
  37. package/lib/module/ScopedLogger.js.map +1 -0
  38. package/lib/module/config.js +23 -0
  39. package/lib/module/config.js.map +1 -0
  40. package/lib/module/destinations/Batcher.js +647 -0
  41. package/lib/module/destinations/Batcher.js.map +1 -0
  42. package/lib/module/destinations/ConsoleDestination.js +51 -0
  43. package/lib/module/destinations/ConsoleDestination.js.map +1 -0
  44. package/lib/module/destinations/FileDestination.js +360 -0
  45. package/lib/module/destinations/FileDestination.js.map +1 -0
  46. package/lib/module/destinations/NativeConsoleDestination.js +170 -0
  47. package/lib/module/destinations/NativeConsoleDestination.js.map +1 -0
  48. package/lib/module/destinations/types.js +4 -0
  49. package/lib/module/destinations/types.js.map +1 -0
  50. package/lib/module/formatters/DefaultFormatter.js +136 -0
  51. package/lib/module/formatters/DefaultFormatter.js.map +1 -0
  52. package/lib/module/formatters/JsonLinesFormatter.js +162 -0
  53. package/lib/module/formatters/JsonLinesFormatter.js.map +1 -0
  54. package/lib/module/formatters/timestamp.js +15 -0
  55. package/lib/module/formatters/timestamp.js.map +1 -0
  56. package/lib/module/formatters/types.js +4 -0
  57. package/lib/module/formatters/types.js.map +1 -0
  58. package/lib/module/index.js +31 -0
  59. package/lib/module/index.js.map +1 -0
  60. package/lib/module/integrations/appState.js +81 -0
  61. package/lib/module/integrations/appState.js.map +1 -0
  62. package/lib/module/integrations/errorHandler.js +166 -0
  63. package/lib/module/integrations/errorHandler.js.map +1 -0
  64. package/lib/module/integrations/sanitizeError.js +238 -0
  65. package/lib/module/integrations/sanitizeError.js.map +1 -0
  66. package/lib/module/levels.js +35 -0
  67. package/lib/module/levels.js.map +1 -0
  68. package/lib/module/metadata.js +52 -0
  69. package/lib/module/metadata.js.map +1 -0
  70. package/lib/module/package.json +1 -0
  71. package/lib/module/privacy.js +349 -0
  72. package/lib/module/privacy.js.map +1 -0
  73. package/lib/module/specs/FileSink.nitro.js +4 -0
  74. package/lib/module/specs/FileSink.nitro.js.map +1 -0
  75. package/lib/module/specs/NativeConsoleSink.nitro.js +4 -0
  76. package/lib/module/specs/NativeConsoleSink.nitro.js.map +1 -0
  77. package/lib/module/types.js +4 -0
  78. package/lib/module/types.js.map +1 -0
  79. package/lib/module/utf8.js +42 -0
  80. package/lib/module/utf8.js.map +1 -0
  81. package/lib/typescript/package.json +1 -0
  82. package/lib/typescript/src/Logger.d.ts +122 -0
  83. package/lib/typescript/src/Logger.d.ts.map +1 -0
  84. package/lib/typescript/src/ScopedLogger.d.ts +36 -0
  85. package/lib/typescript/src/ScopedLogger.d.ts.map +1 -0
  86. package/lib/typescript/src/config.d.ts +12 -0
  87. package/lib/typescript/src/config.d.ts.map +1 -0
  88. package/lib/typescript/src/destinations/Batcher.d.ts +244 -0
  89. package/lib/typescript/src/destinations/Batcher.d.ts.map +1 -0
  90. package/lib/typescript/src/destinations/ConsoleDestination.d.ts +27 -0
  91. package/lib/typescript/src/destinations/ConsoleDestination.d.ts.map +1 -0
  92. package/lib/typescript/src/destinations/FileDestination.d.ts +200 -0
  93. package/lib/typescript/src/destinations/FileDestination.d.ts.map +1 -0
  94. package/lib/typescript/src/destinations/NativeConsoleDestination.d.ts +86 -0
  95. package/lib/typescript/src/destinations/NativeConsoleDestination.d.ts.map +1 -0
  96. package/lib/typescript/src/destinations/types.d.ts +19 -0
  97. package/lib/typescript/src/destinations/types.d.ts.map +1 -0
  98. package/lib/typescript/src/formatters/DefaultFormatter.d.ts +22 -0
  99. package/lib/typescript/src/formatters/DefaultFormatter.d.ts.map +1 -0
  100. package/lib/typescript/src/formatters/JsonLinesFormatter.d.ts +73 -0
  101. package/lib/typescript/src/formatters/JsonLinesFormatter.d.ts.map +1 -0
  102. package/lib/typescript/src/formatters/timestamp.d.ts +6 -0
  103. package/lib/typescript/src/formatters/timestamp.d.ts.map +1 -0
  104. package/lib/typescript/src/formatters/types.d.ts +27 -0
  105. package/lib/typescript/src/formatters/types.d.ts.map +1 -0
  106. package/lib/typescript/src/index.d.ts +34 -0
  107. package/lib/typescript/src/index.d.ts.map +1 -0
  108. package/lib/typescript/src/integrations/appState.d.ts +33 -0
  109. package/lib/typescript/src/integrations/appState.d.ts.map +1 -0
  110. package/lib/typescript/src/integrations/errorHandler.d.ts +55 -0
  111. package/lib/typescript/src/integrations/errorHandler.d.ts.map +1 -0
  112. package/lib/typescript/src/integrations/sanitizeError.d.ts +89 -0
  113. package/lib/typescript/src/integrations/sanitizeError.d.ts.map +1 -0
  114. package/lib/typescript/src/levels.d.ts +9 -0
  115. package/lib/typescript/src/levels.d.ts.map +1 -0
  116. package/lib/typescript/src/metadata.d.ts +23 -0
  117. package/lib/typescript/src/metadata.d.ts.map +1 -0
  118. package/lib/typescript/src/privacy.d.ts +103 -0
  119. package/lib/typescript/src/privacy.d.ts.map +1 -0
  120. package/lib/typescript/src/specs/FileSink.nitro.d.ts +131 -0
  121. package/lib/typescript/src/specs/FileSink.nitro.d.ts.map +1 -0
  122. package/lib/typescript/src/specs/NativeConsoleSink.nitro.d.ts +21 -0
  123. package/lib/typescript/src/specs/NativeConsoleSink.nitro.d.ts.map +1 -0
  124. package/lib/typescript/src/types.d.ts +37 -0
  125. package/lib/typescript/src/types.d.ts.map +1 -0
  126. package/lib/typescript/src/utf8.d.ts +15 -0
  127. package/lib/typescript/src/utf8.d.ts.map +1 -0
  128. package/nitro.json +33 -0
  129. package/nitrogen/generated/android/c++/JAppendResult.hpp +79 -0
  130. package/nitrogen/generated/android/c++/JClearOutcome.hpp +88 -0
  131. package/nitrogen/generated/android/c++/JFlushOutcome.hpp +81 -0
  132. package/nitrogen/generated/android/c++/JHybridFileSinkSpec.cpp +120 -0
  133. package/nitrogen/generated/android/c++/JHybridFileSinkSpec.hpp +69 -0
  134. package/nitrogen/generated/android/c++/JHybridNativeConsoleSinkSpec.cpp +71 -0
  135. package/nitrogen/generated/android/c++/JHybridNativeConsoleSinkSpec.hpp +64 -0
  136. package/nitrogen/generated/android/c++/JRejectReason.hpp +64 -0
  137. package/nitrogen/generated/android/c++/JRotationConfig.hpp +77 -0
  138. package/nitrogen/generated/android/c++/JSinkStatus.hpp +69 -0
  139. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrologger/AppendResult.kt +76 -0
  140. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrologger/ClearOutcome.kt +66 -0
  141. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrologger/FlushOutcome.kt +81 -0
  142. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrologger/HybridFileSinkSpec.kt +80 -0
  143. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrologger/HybridNativeConsoleSinkSpec.kt +58 -0
  144. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrologger/RejectReason.kt +25 -0
  145. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrologger/RotationConfig.kt +76 -0
  146. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrologger/SinkStatus.kt +66 -0
  147. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrologger/nitrologgerOnLoad.kt +35 -0
  148. package/nitrogen/generated/android/nitrologger+autolinking.cmake +83 -0
  149. package/nitrogen/generated/android/nitrologger+autolinking.gradle +27 -0
  150. package/nitrogen/generated/android/nitrologgerOnLoad.cpp +70 -0
  151. package/nitrogen/generated/android/nitrologgerOnLoad.hpp +34 -0
  152. package/nitrogen/generated/ios/NitroLogger+autolinking.rb +62 -0
  153. package/nitrogen/generated/ios/NitroLogger-Swift-Cxx-Bridge.cpp +50 -0
  154. package/nitrogen/generated/ios/NitroLogger-Swift-Cxx-Bridge.hpp +216 -0
  155. package/nitrogen/generated/ios/NitroLogger-Swift-Cxx-Umbrella.hpp +69 -0
  156. package/nitrogen/generated/ios/NitroLoggerAutolinking.mm +41 -0
  157. package/nitrogen/generated/ios/NitroLoggerAutolinking.swift +38 -0
  158. package/nitrogen/generated/ios/c++/HybridFileSinkSpecSwift.cpp +11 -0
  159. package/nitrogen/generated/ios/c++/HybridFileSinkSpecSwift.hpp +150 -0
  160. package/nitrogen/generated/ios/c++/HybridNativeConsoleSinkSpecSwift.cpp +11 -0
  161. package/nitrogen/generated/ios/c++/HybridNativeConsoleSinkSpecSwift.hpp +87 -0
  162. package/nitrogen/generated/ios/swift/AppendResult.swift +60 -0
  163. package/nitrogen/generated/ios/swift/ClearOutcome.swift +50 -0
  164. package/nitrogen/generated/ios/swift/FlushOutcome.swift +59 -0
  165. package/nitrogen/generated/ios/swift/HybridFileSinkSpec.swift +61 -0
  166. package/nitrogen/generated/ios/swift/HybridFileSinkSpec_cxx.swift +227 -0
  167. package/nitrogen/generated/ios/swift/HybridNativeConsoleSinkSpec.swift +56 -0
  168. package/nitrogen/generated/ios/swift/HybridNativeConsoleSinkSpec_cxx.swift +148 -0
  169. package/nitrogen/generated/ios/swift/RejectReason.swift +48 -0
  170. package/nitrogen/generated/ios/swift/RotationConfig.swift +93 -0
  171. package/nitrogen/generated/ios/swift/SinkStatus.swift +44 -0
  172. package/nitrogen/generated/shared/c++/AppendResult.hpp +105 -0
  173. package/nitrogen/generated/shared/c++/ClearOutcome.hpp +96 -0
  174. package/nitrogen/generated/shared/c++/FlushOutcome.hpp +107 -0
  175. package/nitrogen/generated/shared/c++/HybridFileSinkSpec.cpp +28 -0
  176. package/nitrogen/generated/shared/c++/HybridFileSinkSpec.hpp +84 -0
  177. package/nitrogen/generated/shared/c++/HybridNativeConsoleSinkSpec.cpp +22 -0
  178. package/nitrogen/generated/shared/c++/HybridNativeConsoleSinkSpec.hpp +64 -0
  179. package/nitrogen/generated/shared/c++/RejectReason.hpp +84 -0
  180. package/nitrogen/generated/shared/c++/RotationConfig.hpp +103 -0
  181. package/nitrogen/generated/shared/c++/SinkStatus.hpp +95 -0
  182. package/package.json +151 -0
  183. package/src/Logger.ts +417 -0
  184. package/src/ScopedLogger.ts +94 -0
  185. package/src/config.ts +25 -0
  186. package/src/destinations/Batcher.ts +805 -0
  187. package/src/destinations/ConsoleDestination.ts +60 -0
  188. package/src/destinations/FileDestination.ts +473 -0
  189. package/src/destinations/NativeConsoleDestination.ts +220 -0
  190. package/src/destinations/types.ts +19 -0
  191. package/src/formatters/DefaultFormatter.ts +150 -0
  192. package/src/formatters/JsonLinesFormatter.ts +195 -0
  193. package/src/formatters/timestamp.ts +12 -0
  194. package/src/formatters/types.ts +29 -0
  195. package/src/index.tsx +106 -0
  196. package/src/integrations/appState.ts +111 -0
  197. package/src/integrations/errorHandler.ts +223 -0
  198. package/src/integrations/sanitizeError.ts +287 -0
  199. package/src/levels.ts +35 -0
  200. package/src/metadata.ts +52 -0
  201. package/src/privacy.ts +389 -0
  202. package/src/specs/FileSink.nitro.ts +144 -0
  203. package/src/specs/NativeConsoleSink.nitro.ts +21 -0
  204. package/src/types.ts +45 -0
  205. package/src/utf8.ts +39 -0
@@ -0,0 +1,136 @@
1
+ "use strict";
2
+
3
+ import { LEVEL_TAG } from "../levels.js";
4
+ import { formatTime } from "./timestamp.js";
5
+
6
+ /**
7
+ * SwiftLogger's default layout minus the `File.swift:42` column (no call-site
8
+ * capture in JS):
9
+ *
10
+ * LEVEL | HH:mm:ss.SSS | [correlation] [subsystem] message {key=value}
11
+ *
12
+ * Metadata renders as `key=value` pairs sorted by key.
13
+ *
14
+ * A multi-line message keeps its lines, indented under the message column:
15
+ *
16
+ * ERROR | 12:15:30.842 | Unhandled TypeError
17
+ * | | at foo (bundle.js:1:2)
18
+ *
19
+ * See {@link CONTINUATION} for why that indent is load-bearing rather than
20
+ * decorative.
21
+ */
22
+ export class DefaultFormatter {
23
+ format(entry) {
24
+ let tags = '';
25
+ if (entry.correlation !== undefined) {
26
+ tags += `[${escapeControls(entry.correlation)}] `;
27
+ }
28
+ if (entry.subsystem !== undefined) {
29
+ tags += `[${escapeControls(entry.subsystem)}] `;
30
+ }
31
+ let body = `${tags}${formatMessage(entry.message)}`;
32
+ if (entry.metadata) {
33
+ const metadata = entry.metadata;
34
+ const keys = Object.keys(metadata).sort();
35
+ if (keys.length > 0) {
36
+ const pairs = keys.map(k => `${escapeControls(k)}=${escapeControls(String(metadata[k]))}`).join(', ');
37
+ body += ` {${pairs}}`;
38
+ }
39
+ }
40
+ return `${LEVEL_TAG[entry.level]} | ${formatTime(entry.timestamp)} | ${body}`;
41
+ }
42
+ }
43
+
44
+ /**
45
+ * Blanks the tag and timestamp columns, so a continuation line lines up under
46
+ * the message and cannot be read as a record of its own.
47
+ *
48
+ * ERROR | 12:15:30.842 | Unhandled TypeError
49
+ * | | at foo (bundle.js:1:2)
50
+ *
51
+ * Five spaces for the level tag, twelve for `HH:mm:ss.SSS`, and the two
52
+ * separators. A real record always carries a known tag and a timestamp made
53
+ * of digits, so a blank in either column cannot be mistaken for one.
54
+ */
55
+ const CONTINUATION = `${' '.repeat(5)} | ${' '.repeat(12)} | `;
56
+
57
+ /**
58
+ * Renders the message, keeping its lines but never letting one start a record.
59
+ *
60
+ * SwiftLogger leaves `message` completely alone, and this package did too
61
+ * until review pointed out that matching the reference is not the same as
62
+ * being safe. The forgery is the one escaping the structured fields prevents:
63
+ * an error message or an interpolated string carrying
64
+ * `"\nERROR | 00:00:00.000 | patient discharged"` writes a line a reader
65
+ * cannot tell from a real one. Reachability is not theoretical —
66
+ * `no-dynamic-message` is what normally keeps arbitrary text out of this
67
+ * field, and it cannot see a logger wired through a function call.
68
+ *
69
+ * Escaping newlines outright would fix it and ruin the crash handler, which
70
+ * logs stack traces through this field on purpose. Indenting continuation
71
+ * lines keeps them readable and still leaves nothing that parses as a header.
72
+ *
73
+ * This is a deliberate parity difference; docs/PARITY.md records it.
74
+ */
75
+ function formatMessage(message) {
76
+ // Four things besides \n break a line. A bare \r does, and can also drag
77
+ // the cursor back over what was already printed. U+0085 (NEL) does for any
78
+ // Unicode-aware reader. U+2028 and U+2029 do for anything treating this as
79
+ // JavaScript-flavoured text.
80
+ return message.split(/\r\n|[\n\r\u0085\u2028\u2029]/).map(escapeControls).join(`\n${CONTINUATION}`);
81
+ }
82
+
83
+ /**
84
+ * Renders C0, DEL, C1, and the Unicode line/paragraph separators as escapes.
85
+ *
86
+ * C1 (U+0080–U+009F) matters as much as C0 here and is easy to forget:
87
+ * U+009B is a single-character CSI, so a terminal will happily read what
88
+ * follows it as a cursor-movement or erase sequence, and U+0085 is NEL,
89
+ * which Unicode-aware readers treat as a line break.
90
+ *
91
+ * U+2028 and U+2029 are here for exactly the reason U+0085 is. They are not
92
+ * control characters and a plain `\n`-splitting reader ignores them, but a
93
+ * JavaScript one does not: `^` and `$` under the `m` flag treat both as line
94
+ * terminators, so a log viewer written in JS — the likely kind for this
95
+ * package — sees a line break where a terminal sees none. That asymmetry is
96
+ * worse than either alone, because the forged line is invisible in the tool
97
+ * someone checks the raw file with.
98
+ *
99
+ * This layout is one entry per line, so a newline inside a *structured* field
100
+ * lets whoever supplied that field forge whole log entries. A correlation ID,
101
+ * a subsystem, or a metadata key or value can all arrive from a request
102
+ * header, a username, or a URL; without this, `"a\nERROR | 00:00:00.000 | "`
103
+ * writes a convincing fake line into the log, and the reader has no way to
104
+ * tell it from a real one.
105
+ *
106
+ * The message takes a different route — see {@link formatMessage} — because
107
+ * it has to stay multi-line. Everything else is escaped outright.
108
+ *
109
+ * Clean fields — essentially all of them — are scanned once and returned
110
+ * whole, so this costs nothing in the normal case.
111
+ */
112
+ function escapeControls(field) {
113
+ // eslint-disable-next-line no-control-regex
114
+ if (!/[\u0000-\u001F\u007F-\u009F\u2028\u2029]/.test(field)) return field;
115
+ let out = '';
116
+ for (const character of field) {
117
+ switch (character) {
118
+ case '\n':
119
+ out += '\\n';
120
+ break;
121
+ case '\r':
122
+ out += '\\r';
123
+ break;
124
+ case '\t':
125
+ out += '\\t';
126
+ break;
127
+ default:
128
+ {
129
+ const code = character.codePointAt(0);
130
+ out += code < 0x20 || code >= 0x7f && code <= 0x9f || code === 0x2028 || code === 0x2029 ? `\\u{${code.toString(16).toUpperCase()}}` : character;
131
+ }
132
+ }
133
+ }
134
+ return out;
135
+ }
136
+ //# sourceMappingURL=DefaultFormatter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["LEVEL_TAG","formatTime","DefaultFormatter","format","entry","tags","correlation","undefined","escapeControls","subsystem","body","formatMessage","message","metadata","keys","Object","sort","length","pairs","map","k","String","join","level","timestamp","CONTINUATION","repeat","split","field","test","out","character","code","codePointAt","toString","toUpperCase"],"sourceRoot":"../../../src","sources":["formatters/DefaultFormatter.ts"],"mappings":"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","ignoreList":[]}
@@ -0,0 +1,162 @@
1
+ "use strict";
2
+
3
+ import { LEVEL_NAME } from "../levels.js";
4
+ import { utf8Length } from "../utf8.js";
5
+ export { utf8Length };
6
+
7
+ /**
8
+ * How the `timestamp` field renders.
9
+ *
10
+ * `iso8601` is UTC with milliseconds — `2026-07-27T12:15:30.842Z` — and is
11
+ * what the SwiftLogger goldens are written against. `epochSeconds` emits a
12
+ * JSON number of seconds, for aggregators that would only parse the string
13
+ * back again.
14
+ */
15
+
16
+ /** Instants JavaScript can represent as a Date but not as ISO 8601. */
17
+ const MAX_ISO_MS = 8.64e15;
18
+
19
+ /**
20
+ * One JSON object per line, matching SwiftLogger's `JSONLogFormatter`.
21
+ *
22
+ * Keys come out in a fixed order — `timestamp`, `level`, `message`, then the
23
+ * optional `correlation`, `subsystem`, and `metadata` — so that output diffs
24
+ * cleanly and goldens are stable. Absent optionals are omitted rather than
25
+ * written as null, and metadata values keep their JSON type.
26
+ *
27
+ * Metadata keys are sorted, which is also why the object is assembled by hand
28
+ * rather than handed to `JSON.stringify`. JavaScript orders integer-like
29
+ * string keys before all others, so `JSON.stringify({ b: 1, '2': 2 })` puts
30
+ * `2` first no matter what order the keys went in. A metadata key is whatever
31
+ * the caller wrote, `'2'` included, and the Swift side sorts every key the
32
+ * same way.
33
+ *
34
+ * See `docs/PARITY.md` for the field-by-field comparison, including the three
35
+ * fields this formatter deliberately does not emit.
36
+ */
37
+ export class JsonLinesFormatter {
38
+ /**
39
+ * Every control character is escaped, so a record cannot contain a raw
40
+ * newline and native crash-tail trimming can find record boundaries.
41
+ */
42
+ framing = 'line';
43
+ constructor(options = {}) {
44
+ this.timestampStyle = options.timestampStyle ?? 'iso8601';
45
+ }
46
+ format(entry) {
47
+ return this.render(entry, entry.message, entry.metadata, false);
48
+ }
49
+
50
+ /**
51
+ * Render within a byte budget, shedding content in a fixed order rather
52
+ * than slicing the finished string.
53
+ *
54
+ * Cutting a JSON record to length produces something no parser will accept,
55
+ * and a log line that cannot be parsed is worth less than one that is
56
+ * honestly incomplete. So: drop metadata from the end of sorted key order,
57
+ * then shorten the message, and mark the result `"truncated":true` either
58
+ * way. A reader can tell the difference between a short message and a
59
+ * shortened one.
60
+ *
61
+ * The budget counts UTF-8 bytes, because that is what the sink reserves.
62
+ * An unpaired surrogate never distorts the count here: `JSON.stringify` has
63
+ * already turned it into a six-character `\udXXX` escape by the time
64
+ * anything is measured.
65
+ *
66
+ * When even an empty message will not fit, the result comes back OVER
67
+ * budget: it is the smallest record that still identifies the entry, which
68
+ * is not quite the same as the smallest record possible. `correlation` and
69
+ * `subsystem` survive, because an entry no one can place is worth less than
70
+ * a short one — dropping them would save bytes and leave a line that says
71
+ * nothing about where it came from. Callers deciding whether an entry is
72
+ * renderable at all must therefore measure the result rather than assume it
73
+ * fits.
74
+ */
75
+ formatWithin(entry, maxBytes) {
76
+ const full = this.format(entry);
77
+ if (utf8Length(full) <= maxBytes) return full;
78
+
79
+ // Metadata first: a dropped field costs the reader one fact, whereas a
80
+ // shortened message can cost them the sentence that explains the others.
81
+ const keys = entry.metadata ? Object.keys(entry.metadata).sort() : [];
82
+ for (let keep = keys.length - 1; keep >= 0; keep -= 1) {
83
+ const subset = {};
84
+ for (const key of keys.slice(0, keep)) {
85
+ subset[key] = entry.metadata[key];
86
+ }
87
+ const candidate = this.render(entry, entry.message, keep === 0 ? undefined : subset, true);
88
+ if (utf8Length(candidate) <= maxBytes) return candidate;
89
+ }
90
+
91
+ // Then the message, by code points so a surrogate pair is never split
92
+ // into a lone half. Escaping means a code point can cost anywhere from
93
+ // one to twelve bytes, so the fit is found by measuring whole rendered
94
+ // records rather than by arithmetic on the message alone.
95
+ const points = Array.from(entry.message);
96
+ let low = 0;
97
+ let high = points.length;
98
+ while (low < high) {
99
+ const mid = Math.ceil((low + high) / 2);
100
+ const candidate = this.render(entry, points.slice(0, mid).join(''), undefined, true);
101
+ if (utf8Length(candidate) <= maxBytes) low = mid;else high = mid - 1;
102
+ }
103
+ return this.render(entry, points.slice(0, low).join(''), undefined, true);
104
+ }
105
+ render(entry, message, metadata, truncated) {
106
+ let json = '{"timestamp":';
107
+ json += this.renderTimestamp(entry.timestamp);
108
+ json += ',"level":' + JSON.stringify(LEVEL_NAME[entry.level]);
109
+ json += ',"message":' + JSON.stringify(message);
110
+ if (entry.correlation !== undefined) {
111
+ json += ',"correlation":' + JSON.stringify(entry.correlation);
112
+ }
113
+ if (entry.subsystem !== undefined) {
114
+ json += ',"subsystem":' + JSON.stringify(entry.subsystem);
115
+ }
116
+ if (metadata) {
117
+ const keys = Object.keys(metadata).sort();
118
+ if (keys.length > 0) {
119
+ json += ',"metadata":{';
120
+ for (let i = 0; i < keys.length; i += 1) {
121
+ if (i > 0) json += ',';
122
+ json += JSON.stringify(keys[i]) + ':' + renderValue(metadata[keys[i]]);
123
+ }
124
+ json += '}';
125
+ }
126
+ }
127
+ if (truncated) json += ',"truncated":true';
128
+ return json + '}';
129
+ }
130
+ renderTimestamp(epochMs) {
131
+ if (this.timestampStyle === 'epochSeconds') {
132
+ return Number.isFinite(epochMs) ? String(epochMs / 1000) : '0';
133
+ }
134
+ // `toISOString` throws past the representable range, and a formatter that
135
+ // throws takes down the log call that was trying to report a problem.
136
+ if (!Number.isFinite(epochMs) || Math.abs(epochMs) > MAX_ISO_MS) {
137
+ return '"1970-01-01T00:00:00.000Z"';
138
+ }
139
+ return JSON.stringify(new Date(epochMs).toISOString());
140
+ }
141
+ }
142
+
143
+ /**
144
+ * A metadata value as JSON, keeping its type.
145
+ *
146
+ * Non-finite numbers become quoted strings because JSON has no literal for
147
+ * them, matching what the Swift formatter does. Redaction rejects them long
148
+ * before this point, so the branch exists to keep a hand-built entry from
149
+ * producing `NaN` in the middle of a record rather than because it is
150
+ * reachable through the public API.
151
+ */
152
+ function renderValue(value) {
153
+ switch (typeof value) {
154
+ case 'string':
155
+ return JSON.stringify(value);
156
+ case 'boolean':
157
+ return value ? 'true' : 'false';
158
+ default:
159
+ return Number.isFinite(value) ? String(value) : JSON.stringify(String(value));
160
+ }
161
+ }
162
+ //# sourceMappingURL=JsonLinesFormatter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["LEVEL_NAME","utf8Length","MAX_ISO_MS","JsonLinesFormatter","framing","constructor","options","timestampStyle","format","entry","render","message","metadata","formatWithin","maxBytes","full","keys","Object","sort","keep","length","subset","key","slice","candidate","undefined","points","Array","from","low","high","mid","Math","ceil","join","truncated","json","renderTimestamp","timestamp","JSON","stringify","level","correlation","subsystem","i","renderValue","epochMs","Number","isFinite","String","abs","Date","toISOString","value"],"sourceRoot":"../../../src","sources":["formatters/JsonLinesFormatter.ts"],"mappings":"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","ignoreList":[]}
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+
3
+ /** Local-time `HH:mm:ss.SSS`, matching SwiftLogger's console timestamp.
4
+ * Takes epoch milliseconds (see `LogEntry.timestamp`); plain Date getters +
5
+ * zero-pad — none of the Swift version's caching is needed, these are cheap
6
+ * in JS. */
7
+ export function formatTime(epochMs) {
8
+ const date = new Date(epochMs);
9
+ const hh = String(date.getHours()).padStart(2, '0');
10
+ const mm = String(date.getMinutes()).padStart(2, '0');
11
+ const ss = String(date.getSeconds()).padStart(2, '0');
12
+ const ms = String(date.getMilliseconds()).padStart(3, '0');
13
+ return `${hh}:${mm}:${ss}.${ms}`;
14
+ }
15
+ //# sourceMappingURL=timestamp.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["formatTime","epochMs","date","Date","hh","String","getHours","padStart","mm","getMinutes","ss","getSeconds","ms","getMilliseconds"],"sourceRoot":"../../../src","sources":["formatters/timestamp.ts"],"mappings":";;AAAA;AACA;AACA;AACA;AACA,OAAO,SAASA,UAAUA,CAACC,OAAe,EAAU;EAClD,MAAMC,IAAI,GAAG,IAAIC,IAAI,CAACF,OAAO,CAAC;EAC9B,MAAMG,EAAE,GAAGC,MAAM,CAACH,IAAI,CAACI,QAAQ,CAAC,CAAC,CAAC,CAACC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC;EACnD,MAAMC,EAAE,GAAGH,MAAM,CAACH,IAAI,CAACO,UAAU,CAAC,CAAC,CAAC,CAACF,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC;EACrD,MAAMG,EAAE,GAAGL,MAAM,CAACH,IAAI,CAACS,UAAU,CAAC,CAAC,CAAC,CAACJ,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC;EACrD,MAAMK,EAAE,GAAGP,MAAM,CAACH,IAAI,CAACW,eAAe,CAAC,CAAC,CAAC,CAACN,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC;EAC1D,OAAO,GAAGH,EAAE,IAAII,EAAE,IAAIE,EAAE,IAAIE,EAAE,EAAE;AAClC","ignoreList":[]}
@@ -0,0 +1,4 @@
1
+ "use strict";
2
+
3
+ export {};
4
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":[],"sourceRoot":"../../../src","sources":["formatters/types.ts"],"mappings":"","ignoreList":[]}
@@ -0,0 +1,31 @@
1
+ "use strict";
2
+
3
+ import { NitroModules } from 'react-native-nitro-modules';
4
+ // ── Public API ──────────────────────────────────────────────────────────────
5
+ export { Log, Logger } from "./Logger.js";
6
+ export { ScopedLogger } from "./ScopedLogger.js";
7
+ export { pub, priv } from "./privacy.js";
8
+ export { METADATA_KEY_PATTERN_SOURCE, DROPPED_COUNT_KEY, MAX_CATALOG_SIZE, UNREADABLE_VALUE } from "./privacy.js";
9
+ export { ConsoleDestination } from "./destinations/ConsoleDestination.js";
10
+ export { FileDestination } from "./destinations/FileDestination.js";
11
+ export { NativeConsoleDestination } from "./destinations/NativeConsoleDestination.js";
12
+ export { installErrorHandler } from "./integrations/errorHandler.js";
13
+ export { ERROR_METADATA_KEYS, UNCAUGHT_ERROR_MESSAGE } from "./integrations/errorHandler.js";
14
+ export { flushOnBackground } from "./integrations/appState.js";
15
+ export { sanitizeError, DEFAULT_BUNDLE_NAMES, DEFAULT_MAX_FRAMES, REDACTED_FRAME, REDACTED_MESSAGE, UNKNOWN_ERROR_NAME, NON_ERROR_THROWN } from "./integrations/sanitizeError.js";
16
+ export { Batcher } from "./destinations/Batcher.js";
17
+ export { utf8Length } from "./utf8.js";
18
+ export { DefaultFormatter } from "./formatters/DefaultFormatter.js";
19
+ export { JsonLinesFormatter } from "./formatters/JsonLinesFormatter.js";
20
+
21
+ // ── Spike-era raw sink access ───────────────────────────────────────────────
22
+ // Used by the example app's M0 harness; becomes internal once the
23
+ // FileDestination (M4/M5) and NativeConsoleDestination (M6) wrap these.
24
+
25
+ export function createFileSink() {
26
+ return NitroModules.createHybridObject('FileSink');
27
+ }
28
+ export function createNativeConsoleSink() {
29
+ return NitroModules.createHybridObject('NativeConsoleSink');
30
+ }
31
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["NitroModules","Log","Logger","ScopedLogger","pub","priv","METADATA_KEY_PATTERN_SOURCE","DROPPED_COUNT_KEY","MAX_CATALOG_SIZE","UNREADABLE_VALUE","ConsoleDestination","FileDestination","NativeConsoleDestination","installErrorHandler","ERROR_METADATA_KEYS","UNCAUGHT_ERROR_MESSAGE","flushOnBackground","sanitizeError","DEFAULT_BUNDLE_NAMES","DEFAULT_MAX_FRAMES","REDACTED_FRAME","REDACTED_MESSAGE","UNKNOWN_ERROR_NAME","NON_ERROR_THROWN","Batcher","utf8Length","DefaultFormatter","JsonLinesFormatter","createFileSink","createHybridObject","createNativeConsoleSink"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":";;AAAA,SAASA,YAAY,QAAQ,4BAA4B;AAIzD;AACA,SAASC,GAAG,EAAEC,MAAM,QAAQ,aAAU;AAEtC,SAASC,YAAY,QAAQ,mBAAgB;AAU7C,SAASC,GAAG,EAAEC,IAAI,QAAQ,cAAW;AACrC,SACEC,2BAA2B,EAC3BC,iBAAiB,EACjBC,gBAAgB,EAChBC,gBAAgB,QACX,cAAW;AAGlB,SAASC,kBAAkB,QAAQ,sCAAmC;AACtE,SAASC,eAAe,QAAQ,mCAAgC;AAMhE,SAASC,wBAAwB,QAAQ,4CAAyC;AAKlF,SAASC,mBAAmB,QAAQ,gCAA6B;AACjE,SACEC,mBAAmB,EACnBC,sBAAsB,QACjB,gCAA6B;AAMpC,SAASC,iBAAiB,QAAQ,4BAAyB;AAK3D,SACEC,aAAa,EACbC,oBAAoB,EACpBC,kBAAkB,EAClBC,cAAc,EACdC,gBAAgB,EAChBC,kBAAkB,EAClBC,gBAAgB,QACX,iCAA8B;AAKrC,SAASC,OAAO,QAAQ,2BAAwB;AAQhD,SAASC,UAAU,QAAQ,WAAQ;AAEnC,SAASC,gBAAgB,QAAQ,kCAA+B;AAChE,SAASC,kBAAkB,QAAQ,oCAAiC;;AAMpE;AACA;AACA;;AAYA,OAAO,SAASC,cAAcA,CAAA,EAAa;EACzC,OAAO5B,YAAY,CAAC6B,kBAAkB,CAAW,UAAU,CAAC;AAC9D;AAEA,OAAO,SAASC,uBAAuBA,CAAA,EAAsB;EAC3D,OAAO9B,YAAY,CAAC6B,kBAAkB,CACpC,mBACF,CAAC;AACH","ignoreList":[]}
@@ -0,0 +1,81 @@
1
+ "use strict";
2
+
3
+ import { Log } from "../Logger.js";
4
+
5
+ /** The part of RN's `AppState` this uses. Injected in tests. */
6
+
7
+ const DEFAULT_DEADLINE_MS = 2000;
8
+
9
+ /**
10
+ * States that mean "the JS thread is about to stop being scheduled".
11
+ *
12
+ * Both, not just `background`: iOS passes through `inactive` on the way there,
13
+ * and on some transitions — a phone call, the app switcher — that is as far as
14
+ * it gets. Flushing twice costs nothing, because a flush with nothing pending
15
+ * is a length check on an empty buffer in every destination this library ships.
16
+ */
17
+ const LEAVING_FOREGROUND = new Set(['inactive', 'background']);
18
+
19
+ /**
20
+ * Flushes every destination when the app leaves the foreground.
21
+ *
22
+ * Returns an idempotent uninstall handle. Removing the listener matters more
23
+ * than it looks: `AppState` outlives any component, so a subscription left
24
+ * behind keeps a disposed logger reachable and flushes it on every transition
25
+ * for the rest of the process.
26
+ */
27
+ export function flushOnBackground(options) {
28
+ const appState = options?.appState ?? resolveAppState();
29
+ if (!appState) return () => {};
30
+ const logger = options?.logger ?? Log;
31
+ const deadlineMs = budget(options?.deadlineMs);
32
+ let subscription;
33
+ try {
34
+ subscription = appState.addEventListener('change', state => {
35
+ if (!LEAVING_FOREGROUND.has(state)) return;
36
+ try {
37
+ logger.flush(deadlineMs);
38
+ } catch {
39
+ // A destination that cannot flush must not throw out of an AppState
40
+ // listener, where React Native has nowhere to put the exception.
41
+ }
42
+ });
43
+ } catch {
44
+ return () => {};
45
+ }
46
+ let removed = false;
47
+ return () => {
48
+ if (removed) return;
49
+ removed = true;
50
+ try {
51
+ subscription?.remove();
52
+ } catch {
53
+ // Already gone.
54
+ }
55
+ };
56
+ }
57
+
58
+ /**
59
+ * `AppState` without importing `react-native` at module load.
60
+ *
61
+ * A static import would make this module — and therefore the package entry
62
+ * point — unloadable anywhere React Native is not present, which includes the
63
+ * Node process the unit tests run in.
64
+ */
65
+ function resolveAppState() {
66
+ try {
67
+ const rn = require('react-native');
68
+ const candidate = rn.AppState;
69
+ if (typeof candidate !== 'object' || candidate === null) return undefined;
70
+ const add = candidate.addEventListener;
71
+ return typeof add === 'function' ? candidate : undefined;
72
+ } catch {
73
+ return undefined;
74
+ }
75
+ }
76
+ function budget(value) {
77
+ if (value === undefined) return DEFAULT_DEADLINE_MS;
78
+ if (!Number.isFinite(value) || value < 0) return DEFAULT_DEADLINE_MS;
79
+ return value;
80
+ }
81
+ //# sourceMappingURL=appState.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["Log","DEFAULT_DEADLINE_MS","LEAVING_FOREGROUND","Set","flushOnBackground","options","appState","resolveAppState","logger","deadlineMs","budget","subscription","addEventListener","state","has","flush","removed","remove","rn","require","candidate","AppState","undefined","add","value","Number","isFinite"],"sourceRoot":"../../../src","sources":["integrations/appState.ts"],"mappings":";;AACA,SAASA,GAAG,QAAQ,cAAW;;AAG/B;;AAwBA,MAAMC,mBAAmB,GAAG,IAAI;;AAEhC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,MAAMC,kBAAuC,GAAG,IAAIC,GAAG,CAAC,CACtD,UAAU,EACV,YAAY,CACb,CAAC;;AAEF;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASC,iBAAiBA,CAC/BC,OAAkC,EACvB;EACX,MAAMC,QAAQ,GAAGD,OAAO,EAAEC,QAAQ,IAAIC,eAAe,CAAC,CAAC;EACvD,IAAI,CAACD,QAAQ,EAAE,OAAO,MAAM,CAAC,CAAC;EAE9B,MAAME,MAAM,GAAGH,OAAO,EAAEG,MAAM,IAAIR,GAAG;EACrC,MAAMS,UAAU,GAAGC,MAAM,CAACL,OAAO,EAAEI,UAAU,CAAC;EAE9C,IAAIE,YAA4C;EAChD,IAAI;IACFA,YAAY,GAAGL,QAAQ,CAACM,gBAAgB,CAAC,QAAQ,EAAGC,KAAK,IAAK;MAC5D,IAAI,CAACX,kBAAkB,CAACY,GAAG,CAACD,KAAK,CAAC,EAAE;MACpC,IAAI;QACFL,MAAM,CAACO,KAAK,CAACN,UAAU,CAAC;MAC1B,CAAC,CAAC,MAAM;QACN;QACA;MAAA;IAEJ,CAAC,CAAC;EACJ,CAAC,CAAC,MAAM;IACN,OAAO,MAAM,CAAC,CAAC;EACjB;EAEA,IAAIO,OAAO,GAAG,KAAK;EACnB,OAAO,MAAM;IACX,IAAIA,OAAO,EAAE;IACbA,OAAO,GAAG,IAAI;IACd,IAAI;MACFL,YAAY,EAAEM,MAAM,CAAC,CAAC;IACxB,CAAC,CAAC,MAAM;MACN;IAAA;EAEJ,CAAC;AACH;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAASV,eAAeA,CAAA,EAA6B;EACnD,IAAI;IACF,MAAMW,EAAE,GAAGC,OAAO,CAAC,cAAc,CAA2B;IAC5D,MAAMC,SAAS,GAAGF,EAAE,CAACG,QAAQ;IAC7B,IAAI,OAAOD,SAAS,KAAK,QAAQ,IAAIA,SAAS,KAAK,IAAI,EAAE,OAAOE,SAAS;IACzE,MAAMC,GAAG,GAAIH,SAAS,CAAoCR,gBAAgB;IAC1E,OAAO,OAAOW,GAAG,KAAK,UAAU,GAAIH,SAAS,GAAoBE,SAAS;EAC5E,CAAC,CAAC,MAAM;IACN,OAAOA,SAAS;EAClB;AACF;AAEA,SAASZ,MAAMA,CAACc,KAAyB,EAAU;EACjD,IAAIA,KAAK,KAAKF,SAAS,EAAE,OAAOrB,mBAAmB;EACnD,IAAI,CAACwB,MAAM,CAACC,QAAQ,CAACF,KAAK,CAAC,IAAIA,KAAK,GAAG,CAAC,EAAE,OAAOvB,mBAAmB;EACpE,OAAOuB,KAAK;AACd","ignoreList":[]}
@@ -0,0 +1,166 @@
1
+ "use strict";
2
+
3
+ import { Log } from "../Logger.js";
4
+ import { pub } from "../privacy.js";
5
+ import { sanitizeError } from "./sanitizeError.js";
6
+
7
+ /**
8
+ * React Native's global error hook.
9
+ *
10
+ * `ErrorUtils` is undocumented and has been de-facto stable for a decade, which
11
+ * is the whole risk: it is typed here rather than imported so the library never
12
+ * depends on a declaration file that may not exist, and every access is
13
+ * guarded so a runtime without it degrades to doing nothing.
14
+ */
15
+
16
+ /** Metadata keys this handler writes. Add them to a strict key catalog. */
17
+ export const ERROR_METADATA_KEYS = ['errorName', 'errorMessage', 'errorFrames', 'errorFrameCount', 'errorFramesTruncated', 'fatal'];
18
+
19
+ /** The message this handler logs. A literal, as the lint rule requires. */
20
+ export const UNCAUGHT_ERROR_MESSAGE = 'uncaught error';
21
+ const DEFAULT_FATAL_FLUSH_MS = 2000;
22
+
23
+ /** Undoes an install. Idempotent; safe to call after another handler took over. */
24
+
25
+ /**
26
+ * Logs uncaught errors, flushes on fatal ones, and hands the error onward.
27
+ *
28
+ * The flush is the reason this exists. A crash is precisely when the buffered
29
+ * entries explaining it are still in memory, and precisely when nothing will
30
+ * come along later to write them.
31
+ *
32
+ * Nothing about the error is trusted — see {@link sanitizeError}. What reaches
33
+ * the log is a built-in class name, a redacted message outside dev, and stack
34
+ * positions in files whose names were already known.
35
+ */
36
+ export function installErrorHandler(options) {
37
+ const errorUtils = options?.errorUtils ?? globalErrorUtils();
38
+ if (!errorUtils) {
39
+ // No hook on this runtime — a bare Node test, a web target. Returning a
40
+ // no-op uninstall keeps the caller's teardown symmetrical either way.
41
+ return () => {};
42
+ }
43
+ const logger = options?.logger ?? Log;
44
+ const chain = options?.chain ?? true;
45
+ const fatalFlushMs = flushBudget(options?.fatalFlushMs);
46
+ const previous = readPrevious(errorUtils);
47
+ const handler = (error, isFatal) => {
48
+ // Uninstalled, but still the installed handler: something installed over
49
+ // us and then restored us on its way out. Pass straight through rather
50
+ // than logging to a logger the caller has finished with.
51
+ if (INSTALLED.get(handler)?.active !== true) {
52
+ liveHandler(previous)?.(error, isFatal);
53
+ return;
54
+ }
55
+
56
+ // Every step is isolated. A handler that throws while reporting a crash
57
+ // replaces a diagnosable failure with an undiagnosable one, and takes the
58
+ // previous handler — the RedBox, the crash reporter — down with it.
59
+ try {
60
+ const sanitized = sanitizeError(error, options);
61
+ logger.error(UNCAUGHT_ERROR_MESSAGE, {
62
+ errorName: pub(sanitized.name),
63
+ errorMessage: pub(sanitized.message),
64
+ errorFrames: pub(sanitized.frames.join(' | ')),
65
+ // Wrapped for the same reason as the three above, and it was an
66
+ // oversight that they were not: every one of these is generated
67
+ // here, from a count, a flag this function was handed, and a
68
+ // decision this module made. None can carry caller data. Left bare
69
+ // they follow the privacy default, so under `privacyDefault('private')`
70
+ // a crash report renders `fatal: <private>` — which withholds
71
+ // nothing, because there was nothing to withhold, and costs the
72
+ // reader the one field that says whether the app is still running.
73
+ errorFrameCount: pub(sanitized.frameCount),
74
+ errorFramesTruncated: pub(sanitized.framesTruncated),
75
+ fatal: pub(isFatal === true)
76
+ }, options?.subsystem);
77
+ } catch {
78
+ // Reporting failed; the flush and the chain below still matter.
79
+ }
80
+ if (isFatal) {
81
+ try {
82
+ logger.flush(fatalFlushMs);
83
+ } catch {
84
+ // A destination that cannot flush must not stop the crash from
85
+ // reaching the handler that shows it.
86
+ }
87
+ }
88
+ if (chain) {
89
+ liveHandler(previous)?.(error, isFatal);
90
+ }
91
+ };
92
+ INSTALLED.set(handler, {
93
+ active: true,
94
+ previous
95
+ });
96
+ errorUtils.setGlobalHandler(handler);
97
+ return () => {
98
+ const state = INSTALLED.get(handler);
99
+ if (!state || !state.active) return;
100
+
101
+ // Marked dead first, and unconditionally. Uninstalling out of order is
102
+ // ordinary — two libraries, a hot reload — and if this only took effect
103
+ // when ours happened to be the current handler, a later uninstall that
104
+ // restored us would bring a logger back to life that its owner had
105
+ // already let go of.
106
+ state.active = false;
107
+
108
+ // Something else is installed over us. Leave it alone: putting `previous`
109
+ // back would silently uninstall theirs. The flag above makes our wrapper
110
+ // inert if they ever restore it.
111
+ if (readPrevious(errorUtils) !== handler) return;
112
+ errorUtils.setGlobalHandler(liveHandler(state.previous) ?? (() => {}));
113
+ };
114
+ }
115
+ /**
116
+ * The handlers this module installed, and whether each is still live.
117
+ *
118
+ * A WeakMap rather than a property on the function: the entry disappears with
119
+ * the handler, and nothing about our bookkeeping is visible to code that
120
+ * inspects the global handler.
121
+ */
122
+ const INSTALLED = new WeakMap();
123
+
124
+ /**
125
+ * The first handler in the chain that is still installed.
126
+ *
127
+ * Walks past our own uninstalled wrappers. Any handler we did not install is
128
+ * live by definition — we have no way to know otherwise, and assuming it is
129
+ * gone would drop the RedBox or a crash reporter.
130
+ */
131
+ function liveHandler(handler) {
132
+ const seen = new Set();
133
+ let candidate = handler;
134
+ while (candidate) {
135
+ const state = INSTALLED.get(candidate);
136
+ if (!state || state.active) return candidate;
137
+ // A cycle needs two of our own handlers pointing at each other, which
138
+ // takes a restore we did not do — but the crash path is the wrong place
139
+ // to find out the hard way.
140
+ if (seen.has(candidate)) return undefined;
141
+ seen.add(candidate);
142
+ candidate = state.previous;
143
+ }
144
+ return undefined;
145
+ }
146
+ function readPrevious(errorUtils) {
147
+ try {
148
+ return errorUtils.getGlobalHandler?.();
149
+ } catch {
150
+ return undefined;
151
+ }
152
+ }
153
+
154
+ /** The global `ErrorUtils`, if this runtime has one. */
155
+ function globalErrorUtils() {
156
+ const candidate = globalThis.ErrorUtils;
157
+ if (typeof candidate !== 'object' || candidate === null) return undefined;
158
+ const setter = candidate.setGlobalHandler;
159
+ return typeof setter === 'function' ? candidate : undefined;
160
+ }
161
+ function flushBudget(value) {
162
+ if (value === undefined) return DEFAULT_FATAL_FLUSH_MS;
163
+ if (!Number.isFinite(value) || value < 0) return DEFAULT_FATAL_FLUSH_MS;
164
+ return value;
165
+ }
166
+ //# sourceMappingURL=errorHandler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["Log","pub","sanitizeError","ERROR_METADATA_KEYS","UNCAUGHT_ERROR_MESSAGE","DEFAULT_FATAL_FLUSH_MS","installErrorHandler","options","errorUtils","globalErrorUtils","logger","chain","fatalFlushMs","flushBudget","previous","readPrevious","handler","error","isFatal","INSTALLED","get","active","liveHandler","sanitized","errorName","name","errorMessage","message","errorFrames","frames","join","errorFrameCount","frameCount","errorFramesTruncated","framesTruncated","fatal","subsystem","flush","set","setGlobalHandler","state","WeakMap","seen","Set","candidate","has","undefined","add","getGlobalHandler","globalThis","ErrorUtils","setter","value","Number","isFinite"],"sourceRoot":"../../../src","sources":["integrations/errorHandler.ts"],"mappings":"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","ignoreList":[]}