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,244 @@
1
+ import type { AppendResult, FlushOutcome, SinkStatus } from '../specs/FileSink.nitro.js';
2
+ /**
3
+ * The part of a `FileSink` the batcher drives. Narrow on purpose: it is what
4
+ * lets the whole backpressure and loss protocol be tested against a scripted
5
+ * double, with no native runtime and no filesystem.
6
+ */
7
+ export interface BatchTarget {
8
+ appendBatch(batch: string, entryCount: number): AppendResult;
9
+ getStatus(): SinkStatus;
10
+ flush(deadlineMs: number): FlushOutcome;
11
+ }
12
+ /** Entries that never reached the file, and the bytes they would have been. */
13
+ export interface LossCounts {
14
+ readonly entries: number;
15
+ readonly bytes: number;
16
+ }
17
+ /** Why the sink cut this handle off. Both are terminal for the batcher. */
18
+ export type FenceReason = 'staleGeneration' | 'closed';
19
+ export interface BatcherOptions {
20
+ /**
21
+ * Render the consolidated loss notice in the destination's format. The
22
+ * notice is a record in the log file, so only the destination knows how to
23
+ * spell it.
24
+ *
25
+ * Returning `undefined` (or throwing) leaves the loss unacknowledged rather
26
+ * than silently forgotten — it will be offered again on the next batch.
27
+ */
28
+ readonly renderNotice: (lost: LossCounts) => string | undefined;
29
+ /**
30
+ * Called once when the sink fences this handle. The batcher has already
31
+ * discarded its buffer and baselined its counters by then; the owner
32
+ * decides between rebinding and self-disabling.
33
+ */
34
+ readonly onFenced?: (reason: FenceReason) => void;
35
+ /** Bytes of pending content that trigger a batch. Default 4096. */
36
+ readonly batchBytes?: number;
37
+ /** Idle flush interval, also the backpressure poll interval. Default 100. */
38
+ readonly flushIntervalMs?: number;
39
+ /** Pending-buffer caps; whichever binds first. Default 1000 / 512 KB. */
40
+ readonly maxPendingEntries?: number;
41
+ readonly maxPendingBytes?: number;
42
+ /** Sink queue depth at which pushing pauses. Default 256 KB. */
43
+ readonly watermarkBytes?: number;
44
+ /** Largest single batch. Default 256 KB. */
45
+ readonly maxBatchBytes?: number;
46
+ }
47
+ /**
48
+ * What a bounded drain achieved.
49
+ *
50
+ * Deliberately not the sink's `FlushOutcome`: the sink reports monotonic
51
+ * per-handle totals, whereas `unreportedEntries` / `unreportedBytes` are the
52
+ * losses that still have no notice in the file. That is the number a caller
53
+ * can act on, and conflating the two is how "we already logged that" turns
54
+ * into a loss no one ever hears about.
55
+ */
56
+ export interface BatchFlushOutcome {
57
+ /** Every accepted byte reached storage, nothing is buffered, no loss is owed. */
58
+ readonly durable: boolean;
59
+ readonly timedOut: boolean;
60
+ /** Bytes not yet durable: still in the JS buffer plus still in the sink. */
61
+ readonly pendingBytes: number;
62
+ readonly unreportedEntries: number;
63
+ readonly unreportedBytes: number;
64
+ /** Sink degradation bitmask, passed through. */
65
+ readonly degraded: number;
66
+ }
67
+ /**
68
+ * Buffers formatted records and feeds them to a sink under backpressure.
69
+ *
70
+ * Three things happen here that do not happen anywhere else.
71
+ *
72
+ * **Backpressure.** The sink's queue is bounded, so once it passes the
73
+ * watermark the batcher stops pushing and polls `getStatus()` instead — which
74
+ * is why that call is specified never to wait behind writer I/O. Records
75
+ * accumulate in the JS buffer meanwhile, itself bounded by an entry count and
76
+ * a byte count.
77
+ *
78
+ * **Drop-newest.** When the JS buffer is full the arriving record is dropped,
79
+ * not the oldest one held. Under a sustained overload the two policies lose
80
+ * the same volume; they differ in what survives, and the older records are
81
+ * the ones with the context explaining how the overload started.
82
+ *
83
+ * **Loss is owed until it is written.** Every drop — here or in the sink —
84
+ * accrues to a counter that is only cleared once a notice describing it has
85
+ * been accepted, and is re-armed if the flush that should have made that
86
+ * notice durable says otherwise. A loss can therefore be reported twice; it
87
+ * cannot be reported zero times. Silence has to mean nothing was lost, or the
88
+ * counters are decoration.
89
+ */
90
+ export declare class Batcher {
91
+ private readonly target;
92
+ private readonly renderNotice;
93
+ private readonly onFenced;
94
+ private readonly batchBytes;
95
+ private readonly flushIntervalMs;
96
+ private readonly maxPendingEntries;
97
+ private readonly maxPendingBytes;
98
+ private readonly watermarkBytes;
99
+ private readonly maxBatchBytes;
100
+ private pending;
101
+ private pendingBytes;
102
+ private paused;
103
+ private fenced;
104
+ private disposed;
105
+ /**
106
+ * A notice could not be delivered on its own — unrenderable, or refused by
107
+ * the sink — and nothing about ticking again would change that.
108
+ *
109
+ * The debt stays owed and still shows in `unreported()`; what stops is the
110
+ * retrying on a timer, which would go on for the life of the process
111
+ * achieving nothing. The next record to arrive reopens it — that retry
112
+ * rides a batch that carries the record, so it cannot spin — as does an
113
+ * explicit `flush()`.
114
+ */
115
+ private noticeBlocked;
116
+ private timer;
117
+ /** Highest sink totals seen, and every byte this side has dropped. */
118
+ private observed;
119
+ /** Covered by a notice the sink has accepted. */
120
+ private acked;
121
+ /** Covered by a notice a durable flush has confirmed. */
122
+ private confirmed;
123
+ private degraded;
124
+ constructor(target: BatchTarget, options: BatcherOptions);
125
+ /**
126
+ * Buffer one formatted record. Never performs I/O beyond handing a full
127
+ * batch to the sink, and never throws: a logging call must not fail the
128
+ * code that made it.
129
+ */
130
+ add(record: string): void;
131
+ /**
132
+ * Record a loss that happened before the batcher saw it — a record that
133
+ * could not be rendered, or one too large to be rendered honestly. It is
134
+ * owed a notice on exactly the same terms as anything dropped here.
135
+ */
136
+ noteLoss(entries: number, bytes: number): void;
137
+ /** Losses with no notice in the file yet. */
138
+ unreported(): LossCounts;
139
+ /** Bytes buffered on this side, not yet handed to the sink. */
140
+ bufferedBytes(): number;
141
+ /**
142
+ * Payload-free bitmask of what has stopped working underneath — rotation,
143
+ * gzip, prune, sidecar, protection.
144
+ *
145
+ * This mirrors the sink's own mask, which accumulates bits and drops them
146
+ * only when a durable purge baselines everything. That is deliberate: these
147
+ * are things an app wants to know happened *at all*, and a mask that cleared
148
+ * itself the moment the next batch succeeded would report nothing the one
149
+ * time it mattered.
150
+ */
151
+ degradation(): number;
152
+ isFenced(): boolean;
153
+ /**
154
+ * Drain to the sink and fsync, bounded by a wall-clock deadline.
155
+ *
156
+ * Backpressure is overridden for the duration: the whole point of a flush
157
+ * is to push through a full queue rather than wait for it to clear, so a
158
+ * rejected batch is followed by a bounded sink flush and retried.
159
+ *
160
+ * The postcondition is honest rather than absolute. Under a disk that stays
161
+ * full, this returns `durable: false` with the losses preserved — the
162
+ * alternative, insisting everything is either durable or noticed, is a
163
+ * promise no implementation can keep.
164
+ */
165
+ flush(deadlineMs: number): BatchFlushOutcome;
166
+ /**
167
+ * Release timers and let the buffer go. Idempotent.
168
+ *
169
+ * Deliberately does not flush: `LogDestination` calls `flush()` before
170
+ * `dispose()`, and a dispose that quietly blocked on a stalled disk would
171
+ * turn tearing down a destination into a hang.
172
+ */
173
+ dispose(): void;
174
+ /**
175
+ * Forget everything buffered and start the loss counters over, returning
176
+ * what was discarded.
177
+ *
178
+ * This is the purge path. The counts go back to the caller instead of into
179
+ * a notice, because a "4,182 entries dropped" line written into a file that
180
+ * was just cleared for compliance reasons describes the clearing, and the
181
+ * caller already knows it cleared the file.
182
+ */
183
+ discard(): LossCounts;
184
+ /**
185
+ * Resume against a fresh generation after the owner has rebound.
186
+ *
187
+ * The sink cursors are re-read from scratch rather than carried over,
188
+ * because the old totals are not a baseline for the new handle's. Whether
189
+ * the sink resets its per-handle counters on rebind or keeps them running
190
+ * is not something this side can know — and getting it wrong is silent in
191
+ * the worse direction: keeping a stale maximum would swallow every new loss
192
+ * smaller than the pre-purge total, reporting it zero times.
193
+ *
194
+ * Local counters are deliberately left alone. Records this side dropped
195
+ * after the fence went up are real drops of real entries, and they belong
196
+ * to the new file as much as to the old one.
197
+ */
198
+ rebind(): void;
199
+ /**
200
+ * Hand at most one batch to the sink.
201
+ *
202
+ * The loss snapshot is taken BEFORE the call and acknowledged to that
203
+ * snapshot rather than to the totals the call returns. A drop that happens
204
+ * during this very append is not described by the notice riding in it, and
205
+ * acknowledging the post-call totals would bury it.
206
+ */
207
+ private pushOnce;
208
+ /**
209
+ * Remove the batch's records from the head of the buffer, optionally
210
+ * counting them as lost.
211
+ */
212
+ private loseHead;
213
+ private dropped;
214
+ private fence;
215
+ /**
216
+ * Bring the sink's counters up to date without writing anything.
217
+ *
218
+ * Cursors normally advance off append results, which means a loss the sink
219
+ * has recorded but not yet had occasion to report is invisible here. Before
220
+ * drawing a line under the past — a purge, a fence — that window has to be
221
+ * closed, or a loss from the old file crosses over and gets described as if
222
+ * it had happened to the new one.
223
+ */
224
+ private syncCounters;
225
+ /** Declare every loss seen so far as settled, reporting none of it. */
226
+ private baseline;
227
+ private noticeText;
228
+ private delta;
229
+ private owesNotice;
230
+ /** Where the drain has got to: records still buffered, loss still owed. */
231
+ private position;
232
+ /**
233
+ * Fold sink counters in. They are monotonic per handle, so the maximum
234
+ * seen is the truth; a sink that reported a smaller number than last time
235
+ * would otherwise drive the delta negative and cancel a real loss.
236
+ */
237
+ private observeCounts;
238
+ private targetFlush;
239
+ private outcome;
240
+ private ensureTimer;
241
+ private cancelTimer;
242
+ private onTick;
243
+ }
244
+ //# sourceMappingURL=Batcher.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Batcher.d.ts","sourceRoot":"","sources":["../../../../src/destinations/Batcher.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ import type { LogEntry, LogLevel } from '../types.js';
2
+ import type { LogDestination } from './types.js';
3
+ import type { LogFormatter } from '../formatters/types.js';
4
+ /**
5
+ * Renders to the JS console (Metro in dev). warning → console.warn and
6
+ * error/todo → console.error deliberately: LogBox surfacing errors in dev is
7
+ * a feature, not noise.
8
+ *
9
+ * `outputSink` mirrors SwiftLogger's test hook: when set, lines are also
10
+ * handed to the sink, and `isEnabled` stays true even with printing off.
11
+ */
12
+ export declare class ConsoleDestination implements LogDestination {
13
+ readonly label = "console";
14
+ readonly minimumLevel?: LogLevel;
15
+ printEnabled: boolean;
16
+ outputSink?: (line: string) => void;
17
+ private readonly formatter;
18
+ constructor(options?: {
19
+ minimumLevel?: LogLevel;
20
+ formatter?: LogFormatter;
21
+ });
22
+ get isEnabled(): boolean;
23
+ write(entry: LogEntry): void;
24
+ flush(): void;
25
+ dispose(): void;
26
+ }
27
+ //# sourceMappingURL=ConsoleDestination.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ConsoleDestination.d.ts","sourceRoot":"","sources":["../../../../src/destinations/ConsoleDestination.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,aAAU,CAAC;AACnD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAS,CAAC;AAC9C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAqB,CAAC;AAIxD;;;;;;;GAOG;AACH,qBAAa,kBAAmB,YAAW,cAAc;IACvD,QAAQ,CAAC,KAAK,aAAa;IAC3B,QAAQ,CAAC,YAAY,CAAC,EAAE,QAAQ,CAAC;IACjC,YAAY,UAAQ;IACpB,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAEpC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAe;gBAE7B,OAAO,CAAC,EAAE;QAAE,YAAY,CAAC,EAAE,QAAQ,CAAC;QAAC,SAAS,CAAC,EAAE,YAAY,CAAA;KAAE;IAK3E,IAAI,SAAS,IAAI,OAAO,CAEvB;IAED,KAAK,CAAC,KAAK,EAAE,QAAQ,GAAG,IAAI;IAqB5B,KAAK,IAAI,IAAI;IAIb,OAAO,IAAI,IAAI;CAGhB"}
@@ -0,0 +1,200 @@
1
+ import type { LogEntry, LogLevel } from '../types.js';
2
+ import type { LogDestination } from './types.js';
3
+ import type { LogFormatter } from '../formatters/types.js';
4
+ import type { AppendResult, ClearOutcome, FlushOutcome, RotationConfig, SinkStatus } from '../specs/FileSink.nitro.js';
5
+ import type { BatchFlushOutcome, LossCounts } from './Batcher.js';
6
+ /**
7
+ * The part of `FileSink` a `FileDestination` uses.
8
+ *
9
+ * Structural rather than the Nitro interface itself, so the destination can
10
+ * be driven by a scripted double in tests. The real `FileSink` satisfies it.
11
+ */
12
+ export interface FileSinkLike {
13
+ readonly defaultLogDirectory: string;
14
+ open(path: string, rotation?: RotationConfig, lineFramed?: boolean): void;
15
+ appendBatch(batch: string, entryCount: number): AppendResult;
16
+ getStatus(): SinkStatus;
17
+ flush(deadlineMs: number): FlushOutcome;
18
+ close(deadlineMs: number): FlushOutcome;
19
+ getLogFilePaths(): string[];
20
+ /**
21
+ * Delete every artifact and bump the writer generation, fencing every
22
+ * handle on it.
23
+ *
24
+ * On `durable && rebound` the CALLING handle is rebound to the new
25
+ * generation by this call — no second `open` follows, and none would be
26
+ * accepted, since re-opening a live writer is a config conflict. That is why
27
+ * the JS side only lifts its own fence afterwards. A `clearLogs` that bumped
28
+ * the generation without rebinding its caller would leave the destination
29
+ * writing into a stale handle, discarding every record it was handed and
30
+ * disabling itself on the first rejection.
31
+ *
32
+ * The two flags are separate because they are separate facts. `durable`
33
+ * answers the compliance question — the artifacts are gone — and a purge can
34
+ * answer it truthfully and still fail to reopen, on a directory that has
35
+ * become unwritable or a volume that filled. Only the second flag says there
36
+ * is somewhere to write.
37
+ *
38
+ * On anything else the caller stays fenced along with everyone else, so a
39
+ * deletion still in flight can never race a fresh write.
40
+ */
41
+ clearLogs(deadlineMs: number): ClearOutcome;
42
+ }
43
+ export interface FileDestinationOptions {
44
+ /** Registration key. Default 'file'. */
45
+ readonly label?: string;
46
+ readonly minimumLevel?: LogLevel;
47
+ /** Default `JsonLinesFormatter` — the one with a framing guarantee. */
48
+ readonly formatter?: LogFormatter;
49
+ /** Absolute path. Default `<defaultLogDirectory>/app.log`. */
50
+ readonly path?: string;
51
+ readonly rotation?: RotationConfig;
52
+ /**
53
+ * Largest single rendered record, in UTF-8 bytes. Default 64 KB — roomy for
54
+ * a stack trace, far under the sink's payload cap so a batch always fits.
55
+ */
56
+ readonly maxEntryBytes?: number;
57
+ readonly batchBytes?: number;
58
+ readonly flushIntervalMs?: number;
59
+ readonly maxPendingEntries?: number;
60
+ readonly maxPendingBytes?: number;
61
+ readonly watermarkBytes?: number;
62
+ }
63
+ export interface PurgeOutcome {
64
+ /** Every artifact is gone. False on any survivor or a blown deadline. */
65
+ readonly durable: boolean;
66
+ /**
67
+ * The destination is writable again, and writing has resumed.
68
+ *
69
+ * False means the deletion may well have succeeded — check `durable` — but
70
+ * the sink did not come back with a usable file, or did not say so. Either
71
+ * way this destination stays disabled until a retry.
72
+ */
73
+ readonly rebound: boolean;
74
+ readonly deletedCount: number;
75
+ readonly failedPaths: readonly string[];
76
+ /** Buffered records thrown away, plus losses that will now go unreported. */
77
+ readonly discardedEntries: number;
78
+ readonly discardedBytes: number;
79
+ }
80
+ /**
81
+ * Writes formatted records to a native file sink, batched and backpressured.
82
+ *
83
+ * The division of labour is the whole design: this side decides what to write
84
+ * and what to give up on, the sink only appends text and reports counters.
85
+ * Everything interesting — batching, drop policy, loss notices, the purge
86
+ * handshake — is therefore testable without a device.
87
+ *
88
+ * Three behaviours are worth knowing about before using it.
89
+ *
90
+ * **Oversized entries are never sliced.** A formatter that can shed structure
91
+ * is asked to re-render within the limit; one that cannot has its output
92
+ * replaced whole by a fixed notice. Cutting a rendered record to length is
93
+ * how a log file stops being parseable, and an unparseable file is worth less
94
+ * than an honestly incomplete one.
95
+ *
96
+ * **Framing is declared, not assumed.** A formatter guaranteeing one record
97
+ * per line lets the native side trim a torn trailing record after a crash.
98
+ * Without that guarantee the file is left as the crash left it, because a
99
+ * writer cannot tell a torn record from an intentional newline.
100
+ *
101
+ * **A fenced handle stays fenced.** When the sink reports that this handle's
102
+ * generation is stale — someone purged the file underneath it — the buffer is
103
+ * discarded rather than replayed into the new file, and the destination
104
+ * disables itself. Pre-purge records must not reappear after a compliance
105
+ * deletion. The handle that invoked the purge rebinds; any other one does
106
+ * not, and reacquisition is the native registry's job.
107
+ */
108
+ export declare class FileDestination implements LogDestination {
109
+ readonly label: string;
110
+ readonly minimumLevel: LogLevel | undefined;
111
+ private readonly sink;
112
+ private readonly formatter;
113
+ private readonly batcher;
114
+ private readonly maxEntryBytes;
115
+ private readonly path;
116
+ /** Held for the M5 registry's reacquisition path, which needs the same
117
+ * config the handle was opened with. */
118
+ private readonly rotation;
119
+ private fenced;
120
+ private disposed;
121
+ constructor(sink: FileSinkLike, options?: FileDestinationOptions);
122
+ /** True only while this handle may write. */
123
+ get isEnabled(): boolean;
124
+ /** Where records are going. */
125
+ get filePath(): string;
126
+ /**
127
+ * Whether the native side may trim a torn trailing record after a crash.
128
+ * Follows the formatter's declaration; nothing infers it.
129
+ */
130
+ get lineFramed(): boolean;
131
+ write(entry: LogEntry): void;
132
+ /**
133
+ * Drain to disk and fsync, bounded by a wall-clock deadline.
134
+ *
135
+ * Returns the outcome even though `LogDestination` asks only for `void`: a
136
+ * caller flushing before backgrounding or on a crash path needs to know
137
+ * whether it worked, and `durable: false` is the answer that matters.
138
+ */
139
+ flush(deadlineMs?: number): BatchFlushOutcome;
140
+ /** Current file and archives, newest first; empty if the sink refuses. */
141
+ getLogFilePaths(): string[];
142
+ /** Losses with no notice in the file yet. */
143
+ unreportedLoss(): LossCounts;
144
+ /**
145
+ * What has stopped working underneath, as a payload-free bitmask —
146
+ * rotation, gzip, prune, sidecar, protection.
147
+ *
148
+ * The mask exists because the alternative is a message, and a message built
149
+ * from an `errno` string or a path is exactly the kind of thing that carries
150
+ * a username into a log file. Bits carry no payload, so an app can surface
151
+ * "log rotation is failing" to support without surfacing anything else.
152
+ *
153
+ * Non-zero does not mean records are being lost — a failed compression or a
154
+ * failed prune costs disk, not data. Check {@link unreportedLoss} for that.
155
+ */
156
+ degradation(): number;
157
+ /**
158
+ * Delete every log artifact, then rebind.
159
+ *
160
+ * The ordering is the point. This handle is fenced first, so nothing can be
161
+ * written into the window where deletion is in flight; the JS buffer is
162
+ * discarded rather than flushed, because flushing it would write pre-purge
163
+ * records into the file a moment before deleting it — or worse, a moment
164
+ * after. Only a durable deletion lifts the fence. A partial or timed-out
165
+ * one leaves this handle disabled until an explicit retry, so a late
166
+ * deletion can never race a fresh write.
167
+ *
168
+ * Discarded counts come back here rather than going into a loss notice: a
169
+ * "4,182 entries dropped" line at the top of a file that was just cleared
170
+ * for compliance describes the clearing, which the caller already knows
171
+ * about.
172
+ */
173
+ purge(deadlineMs?: number): PurgeOutcome;
174
+ /** Idempotent: flush what is buffered, release the timer, close the sink. */
175
+ dispose(): void;
176
+ /**
177
+ * Render one entry, or account for why it could not be rendered.
178
+ *
179
+ * Returns undefined when nothing writable came out; the loss is recorded
180
+ * before returning, so an entry is either in the file or in the counters
181
+ * and never in neither.
182
+ */
183
+ private renderRecord;
184
+ private renderLossNotice;
185
+ /**
186
+ * Render one of the pipeline's own notices under the same per-entry limit
187
+ * as everything else.
188
+ *
189
+ * The limit exists so a batch always fits the sink. A notice exempt from it
190
+ * could be refused forever by a sink that will never take it, and the loss
191
+ * it describes would sit undelivered behind it — the pipeline's own
192
+ * diagnostics jamming the pipeline. Returning undefined when nothing fits
193
+ * leaves the loss counted and owed, which is the honest outcome.
194
+ */
195
+ private boundedNotice;
196
+ private formatOrUndefined;
197
+ private formatWithinOrUndefined;
198
+ private onFenced;
199
+ }
200
+ //# sourceMappingURL=FileDestination.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FileDestination.d.ts","sourceRoot":"","sources":["../../../../src/destinations/FileDestination.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,86 @@
1
+ import type { LogEntry, LogLevel } from '../types.js';
2
+ import type { LogDestination } from './types.js';
3
+ import type { LogFormatter } from '../formatters/types.js';
4
+ /**
5
+ * The part of `NativeConsoleSink` this destination uses.
6
+ *
7
+ * Structural rather than the Nitro interface itself, so tests can drive it
8
+ * with a double. The real `NativeConsoleSink` satisfies it.
9
+ */
10
+ export interface NativeConsoleSinkLike {
11
+ install(subsystem: string, category: string): void;
12
+ logBatch(levels: number[], messages: string[]): void;
13
+ }
14
+ export interface NativeConsoleDestinationOptions {
15
+ /** Registration key. Default 'native-console'. */
16
+ readonly label?: string;
17
+ readonly minimumLevel?: LogLevel;
18
+ /** Default `DefaultFormatter` — os_log wants a line, not a JSON record. */
19
+ readonly formatter?: LogFormatter;
20
+ /** Reverse-DNS, as os_log expects. Default: the bundle's, chosen natively. */
21
+ readonly subsystem?: string;
22
+ /** Default 'log'. Becomes the logcat tag on Android. */
23
+ readonly category?: string;
24
+ /** Entries per bridge crossing. Default 64. */
25
+ readonly batchSize?: number;
26
+ /** Idle coalescing window. Default 100 ms. */
27
+ readonly flushIntervalMs?: number;
28
+ /** Buffer ceiling; oldest survive, newest are dropped. Default 1000. */
29
+ readonly maxPendingEntries?: number;
30
+ }
31
+ /**
32
+ * Renders entries into the platform log stream — os_log on iOS, logcat on
33
+ * Android — so JS logs interleave with native ones in Console.app and Xcode.
34
+ *
35
+ * Batched, unlike {@link ConsoleDestination}: one bridge crossing per drain
36
+ * with two parallel primitive arrays, rather than a crossing per entry.
37
+ *
38
+ * Deliberately simpler than {@link FileDestination}. There is no backpressure
39
+ * and no loss accounting because there is nothing to push back on: os_log
40
+ * accepts what it is given and never blocks on a disk. What this destination
41
+ * does account for is its own buffer — entries dropped at the ceiling, and
42
+ * entries lost to a throwing sink — through {@link dropped}, because a
43
+ * diagnostic channel that quietly loses records is worse than one that says
44
+ * it did.
45
+ *
46
+ * The durable copy is the file destination's. Nothing here is a system of
47
+ * record, which is what makes best-effort the right posture.
48
+ */
49
+ export declare class NativeConsoleDestination implements LogDestination {
50
+ readonly label: string;
51
+ readonly minimumLevel?: LogLevel;
52
+ private readonly sink;
53
+ private readonly formatter;
54
+ private readonly batchSize;
55
+ private readonly flushIntervalMs;
56
+ private readonly maxPending;
57
+ private levels;
58
+ private messages;
59
+ private timer;
60
+ private disposed;
61
+ private failures;
62
+ private droppedEntries;
63
+ constructor(sink: NativeConsoleSinkLike, options?: NativeConsoleDestinationOptions);
64
+ /**
65
+ * False once disposed, or once the native sink has failed enough times in a
66
+ * row to be considered gone. The Logger checks this before formatting, so a
67
+ * dead sink stops costing render work as well as bridge calls.
68
+ */
69
+ get isEnabled(): boolean;
70
+ /** Entries this destination never handed to the platform log. */
71
+ dropped(): number;
72
+ write(entry: LogEntry): void;
73
+ /**
74
+ * Hands everything buffered to the platform log.
75
+ *
76
+ * The deadline is accepted and ignored, deliberately: `logBatch` does not
77
+ * wait on anything, so there is no wait to bound. Taking the parameter keeps
78
+ * the destination interchangeable with the ones that do need it.
79
+ */
80
+ flush(_deadlineMs?: number): void;
81
+ dispose(): void;
82
+ private drain;
83
+ private arm;
84
+ private cancel;
85
+ }
86
+ //# sourceMappingURL=NativeConsoleDestination.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NativeConsoleDestination.d.ts","sourceRoot":"","sources":["../../../../src/destinations/NativeConsoleDestination.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,aAAU,CAAC;AACnD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAS,CAAC;AAC9C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAqB,CAAC;AAIxD;;;;;GAKG;AACH,MAAM,WAAW,qBAAqB;IACpC,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACnD,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;CACtD;AAED,MAAM,WAAW,+BAA+B;IAC9C,kDAAkD;IAClD,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,YAAY,CAAC,EAAE,QAAQ,CAAC;IACjC,2EAA2E;IAC3E,QAAQ,CAAC,SAAS,CAAC,EAAE,YAAY,CAAC;IAClC,8EAA8E;IAC9E,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,wDAAwD;IACxD,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,+CAA+C;IAC/C,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,8CAA8C;IAC9C,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;IAClC,wEAAwE;IACxE,QAAQ,CAAC,iBAAiB,CAAC,EAAE,MAAM,CAAC;CACrC;AAcD;;;;;;;;;;;;;;;;;GAiBG;AACH,qBAAa,wBAAyB,YAAW,cAAc;IAC7D,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,YAAY,CAAC,EAAE,QAAQ,CAAC;IAEjC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAwB;IAC7C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAe;IACzC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAS;IACzC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IAEpC,OAAO,CAAC,MAAM,CAAgB;IAC9B,OAAO,CAAC,QAAQ,CAAgB;IAChC,OAAO,CAAC,KAAK,CAA4C;IACzD,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,QAAQ,CAAK;IACrB,OAAO,CAAC,cAAc,CAAK;gBAGzB,IAAI,EAAE,qBAAqB,EAC3B,OAAO,CAAC,EAAE,+BAA+B;IAkB3C;;;;OAIG;IACH,IAAI,SAAS,IAAI,OAAO,CAEvB;IAED,iEAAiE;IACjE,OAAO,IAAI,MAAM;IAIjB,KAAK,CAAC,KAAK,EAAE,QAAQ,GAAG,IAAI;IAuC5B;;;;;;OAMG;IACH,KAAK,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI;IAIjC,OAAO,IAAI,IAAI;IASf,OAAO,CAAC,KAAK;IAwBb,OAAO,CAAC,GAAG;IAQX,OAAO,CAAC,MAAM;CAKf"}
@@ -0,0 +1,19 @@
1
+ import type { LogEntry, LogLevel } from '../types.js';
2
+ /**
3
+ * A log output backend. The Logger filters on `isEnabled` and `minimumLevel`
4
+ * BEFORE evaluating a lazy message, isolates each destination's `write` (one
5
+ * throwing destination never blocks siblings), and calls `dispose()` on
6
+ * replace/remove/reset — JS garbage collection is nondeterministic, so
7
+ * timers, native handles, and listeners must be released explicitly.
8
+ */
9
+ export interface LogDestination {
10
+ readonly label: string;
11
+ readonly minimumLevel?: LogLevel;
12
+ readonly isEnabled: boolean;
13
+ write(entry: LogEntry): void;
14
+ /** Drain any buffered output synchronously (bounded by deadlineMs). */
15
+ flush(deadlineMs?: number): void;
16
+ /** Idempotent: cancel timers, flush, release native handles/listeners. */
17
+ dispose(): void;
18
+ }
19
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/destinations/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,aAAU,CAAC;AAEnD;;;;;;GAMG;AACH,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,YAAY,CAAC,EAAE,QAAQ,CAAC;IACjC,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,KAAK,CAAC,KAAK,EAAE,QAAQ,GAAG,IAAI,CAAC;IAC7B,uEAAuE;IACvE,KAAK,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,0EAA0E;IAC1E,OAAO,IAAI,IAAI,CAAC;CACjB"}
@@ -0,0 +1,22 @@
1
+ import type { LogEntry } from '../types.js';
2
+ import type { LogFormatter } from './types.js';
3
+ /**
4
+ * SwiftLogger's default layout minus the `File.swift:42` column (no call-site
5
+ * capture in JS):
6
+ *
7
+ * LEVEL | HH:mm:ss.SSS | [correlation] [subsystem] message {key=value}
8
+ *
9
+ * Metadata renders as `key=value` pairs sorted by key.
10
+ *
11
+ * A multi-line message keeps its lines, indented under the message column:
12
+ *
13
+ * ERROR | 12:15:30.842 | Unhandled TypeError
14
+ * | | at foo (bundle.js:1:2)
15
+ *
16
+ * See {@link CONTINUATION} for why that indent is load-bearing rather than
17
+ * decorative.
18
+ */
19
+ export declare class DefaultFormatter implements LogFormatter {
20
+ format(entry: LogEntry): string;
21
+ }
22
+ //# sourceMappingURL=DefaultFormatter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DefaultFormatter.d.ts","sourceRoot":"","sources":["../../../../src/formatters/DefaultFormatter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAU,CAAC;AACzC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAS,CAAC;AAI5C;;;;;;;;;;;;;;;GAeG;AACH,qBAAa,gBAAiB,YAAW,YAAY;IACnD,MAAM,CAAC,KAAK,EAAE,QAAQ,GAAG,MAAM;CAyBhC"}