@smthrs/journal 0.0.0-stage → 1.0.0-rc.3

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 (257) hide show
  1. package/CHANGELOG.md +149 -0
  2. package/LICENSE +21 -0
  3. package/README.md +206 -2
  4. package/dist/cjs/CallFact.d.ts +129 -0
  5. package/dist/cjs/CallFact.d.ts.map +1 -0
  6. package/dist/cjs/CallFact.js +106 -0
  7. package/dist/cjs/CallFact.js.map +7 -0
  8. package/dist/cjs/Consensus.d.ts +440 -0
  9. package/dist/cjs/Consensus.d.ts.map +1 -0
  10. package/dist/cjs/Consensus.js +251 -0
  11. package/dist/cjs/Consensus.js.map +7 -0
  12. package/dist/cjs/EngineEvent.d.ts +890 -0
  13. package/dist/cjs/EngineEvent.d.ts.map +1 -0
  14. package/dist/cjs/EngineEvent.js +383 -0
  15. package/dist/cjs/EngineEvent.js.map +7 -0
  16. package/dist/cjs/ExecutionFact.d.ts +216 -0
  17. package/dist/cjs/ExecutionFact.d.ts.map +1 -0
  18. package/dist/cjs/ExecutionFact.js +303 -0
  19. package/dist/cjs/ExecutionFact.js.map +7 -0
  20. package/dist/cjs/Journal.d.ts +617 -0
  21. package/dist/cjs/Journal.d.ts.map +1 -0
  22. package/dist/cjs/Journal.js +190 -0
  23. package/dist/cjs/Journal.js.map +7 -0
  24. package/dist/cjs/JournalEvent.d.ts +421 -0
  25. package/dist/cjs/JournalEvent.d.ts.map +1 -0
  26. package/dist/cjs/JournalEvent.js +281 -0
  27. package/dist/cjs/JournalEvent.js.map +7 -0
  28. package/dist/cjs/JournalGeneration.d.ts +34 -0
  29. package/dist/cjs/JournalGeneration.d.ts.map +1 -0
  30. package/dist/cjs/JournalGeneration.js +72 -0
  31. package/dist/cjs/JournalGeneration.js.map +7 -0
  32. package/dist/cjs/JournalMetrics.d.ts +72 -0
  33. package/dist/cjs/JournalMetrics.d.ts.map +1 -0
  34. package/dist/cjs/JournalMetrics.js +55 -0
  35. package/dist/cjs/JournalMetrics.js.map +7 -0
  36. package/dist/cjs/Migrations.d.ts +42 -0
  37. package/dist/cjs/Migrations.d.ts.map +1 -0
  38. package/dist/cjs/Migrations.js +58 -0
  39. package/dist/cjs/Migrations.js.map +7 -0
  40. package/dist/cjs/OwnerId.d.ts +40 -0
  41. package/dist/cjs/OwnerId.d.ts.map +1 -0
  42. package/dist/cjs/OwnerId.js +40 -0
  43. package/dist/cjs/OwnerId.js.map +7 -0
  44. package/dist/cjs/Projection.d.ts +29 -0
  45. package/dist/cjs/Projection.d.ts.map +1 -0
  46. package/dist/cjs/Projection.js +25 -0
  47. package/dist/cjs/Projection.js.map +7 -0
  48. package/dist/cjs/RedactedLogger.d.ts +121 -0
  49. package/dist/cjs/RedactedLogger.d.ts.map +1 -0
  50. package/dist/cjs/RedactedLogger.js +235 -0
  51. package/dist/cjs/RedactedLogger.js.map +7 -0
  52. package/dist/cjs/Redaction.d.ts +350 -0
  53. package/dist/cjs/Redaction.d.ts.map +1 -0
  54. package/dist/cjs/Redaction.js +928 -0
  55. package/dist/cjs/Redaction.js.map +7 -0
  56. package/dist/cjs/SqlConsensus.d.ts +44 -0
  57. package/dist/cjs/SqlConsensus.d.ts.map +1 -0
  58. package/dist/cjs/SqlConsensus.js +243 -0
  59. package/dist/cjs/SqlConsensus.js.map +7 -0
  60. package/dist/cjs/SqlJournal.d.ts +173 -0
  61. package/dist/cjs/SqlJournal.d.ts.map +1 -0
  62. package/dist/cjs/SqlJournal.js +1568 -0
  63. package/dist/cjs/SqlJournal.js.map +7 -0
  64. package/dist/cjs/StepFact.d.ts +66 -0
  65. package/dist/cjs/StepFact.d.ts.map +1 -0
  66. package/dist/cjs/StepFact.js +68 -0
  67. package/dist/cjs/StepFact.js.map +7 -0
  68. package/dist/cjs/index.d.ts +95 -0
  69. package/dist/cjs/index.d.ts.map +1 -0
  70. package/dist/cjs/index.js +63 -0
  71. package/dist/cjs/index.js.map +7 -0
  72. package/dist/cjs/internal/JournalInternalFault.d.ts +37 -0
  73. package/dist/cjs/internal/JournalInternalFault.d.ts.map +1 -0
  74. package/dist/cjs/internal/JournalInternalFault.js +43 -0
  75. package/dist/cjs/internal/JournalInternalFault.js.map +7 -0
  76. package/dist/cjs/migrations/0001_initial.d.ts +15 -0
  77. package/dist/cjs/migrations/0001_initial.d.ts.map +1 -0
  78. package/dist/cjs/migrations/0001_initial.js +54 -0
  79. package/dist/cjs/migrations/0001_initial.js.map +7 -0
  80. package/dist/cjs/migrations/0002_checkpoints.d.ts +27 -0
  81. package/dist/cjs/migrations/0002_checkpoints.d.ts.map +1 -0
  82. package/dist/cjs/migrations/0002_checkpoints.js +48 -0
  83. package/dist/cjs/migrations/0002_checkpoints.js.map +7 -0
  84. package/dist/cjs/migrations/0003_startup_index.d.ts +15 -0
  85. package/dist/cjs/migrations/0003_startup_index.d.ts.map +1 -0
  86. package/dist/cjs/migrations/0003_startup_index.js +40 -0
  87. package/dist/cjs/migrations/0003_startup_index.js.map +7 -0
  88. package/dist/cjs/migrations/0004_dedup.d.ts +15 -0
  89. package/dist/cjs/migrations/0004_dedup.d.ts.map +1 -0
  90. package/dist/cjs/migrations/0004_dedup.js +57 -0
  91. package/dist/cjs/migrations/0004_dedup.js.map +7 -0
  92. package/dist/cjs/migrations/0005_run_event_type.d.ts +15 -0
  93. package/dist/cjs/migrations/0005_run_event_type.d.ts.map +1 -0
  94. package/dist/cjs/migrations/0005_run_event_type.js +40 -0
  95. package/dist/cjs/migrations/0005_run_event_type.js.map +7 -0
  96. package/dist/cjs/migrations/0006_consensus.d.ts +22 -0
  97. package/dist/cjs/migrations/0006_consensus.d.ts.map +1 -0
  98. package/dist/cjs/migrations/0006_consensus.js +84 -0
  99. package/dist/cjs/migrations/0006_consensus.js.map +7 -0
  100. package/dist/cjs/package.json +1 -0
  101. package/dist/cjs/test/Notifying.d.ts +65 -0
  102. package/dist/cjs/test/Notifying.d.ts.map +1 -0
  103. package/dist/cjs/test/Notifying.js +62 -0
  104. package/dist/cjs/test/Notifying.js.map +7 -0
  105. package/dist/cjs/test/TestJournal.d.ts +43 -0
  106. package/dist/cjs/test/TestJournal.d.ts.map +1 -0
  107. package/dist/cjs/test/TestJournal.js +43 -0
  108. package/dist/cjs/test/TestJournal.js.map +7 -0
  109. package/dist/esm/CallFact.d.ts +129 -0
  110. package/dist/esm/CallFact.d.ts.map +1 -0
  111. package/dist/esm/CallFact.js +96 -0
  112. package/dist/esm/CallFact.js.map +1 -0
  113. package/dist/esm/Consensus.d.ts +440 -0
  114. package/dist/esm/Consensus.d.ts.map +1 -0
  115. package/dist/esm/Consensus.js +454 -0
  116. package/dist/esm/Consensus.js.map +1 -0
  117. package/dist/esm/EngineEvent.d.ts +890 -0
  118. package/dist/esm/EngineEvent.d.ts.map +1 -0
  119. package/dist/esm/EngineEvent.js +614 -0
  120. package/dist/esm/EngineEvent.js.map +1 -0
  121. package/dist/esm/ExecutionFact.d.ts +216 -0
  122. package/dist/esm/ExecutionFact.d.ts.map +1 -0
  123. package/dist/esm/ExecutionFact.js +374 -0
  124. package/dist/esm/ExecutionFact.js.map +1 -0
  125. package/dist/esm/Journal.d.ts +617 -0
  126. package/dist/esm/Journal.d.ts.map +1 -0
  127. package/dist/esm/Journal.js +323 -0
  128. package/dist/esm/Journal.js.map +1 -0
  129. package/dist/esm/JournalEvent.d.ts +421 -0
  130. package/dist/esm/JournalEvent.d.ts.map +1 -0
  131. package/dist/esm/JournalEvent.js +517 -0
  132. package/dist/esm/JournalEvent.js.map +1 -0
  133. package/dist/esm/JournalGeneration.d.ts +34 -0
  134. package/dist/esm/JournalGeneration.d.ts.map +1 -0
  135. package/dist/esm/JournalGeneration.js +65 -0
  136. package/dist/esm/JournalGeneration.js.map +1 -0
  137. package/dist/esm/JournalMetrics.d.ts +72 -0
  138. package/dist/esm/JournalMetrics.d.ts.map +1 -0
  139. package/dist/esm/JournalMetrics.js +79 -0
  140. package/dist/esm/JournalMetrics.js.map +1 -0
  141. package/dist/esm/Migrations.d.ts +42 -0
  142. package/dist/esm/Migrations.d.ts.map +1 -0
  143. package/dist/esm/Migrations.js +59 -0
  144. package/dist/esm/Migrations.js.map +1 -0
  145. package/dist/esm/OwnerId.d.ts +40 -0
  146. package/dist/esm/OwnerId.d.ts.map +1 -0
  147. package/dist/esm/OwnerId.js +33 -0
  148. package/dist/esm/OwnerId.js.map +1 -0
  149. package/dist/esm/Projection.d.ts +29 -0
  150. package/dist/esm/Projection.d.ts.map +1 -0
  151. package/dist/esm/Projection.js +13 -0
  152. package/dist/esm/Projection.js.map +1 -0
  153. package/dist/esm/RedactedLogger.d.ts +121 -0
  154. package/dist/esm/RedactedLogger.d.ts.map +1 -0
  155. package/dist/esm/RedactedLogger.js +445 -0
  156. package/dist/esm/RedactedLogger.js.map +1 -0
  157. package/dist/esm/Redaction.d.ts +350 -0
  158. package/dist/esm/Redaction.d.ts.map +1 -0
  159. package/dist/esm/Redaction.js +1584 -0
  160. package/dist/esm/Redaction.js.map +1 -0
  161. package/dist/esm/SqlConsensus.d.ts +44 -0
  162. package/dist/esm/SqlConsensus.d.ts.map +1 -0
  163. package/dist/esm/SqlConsensus.js +252 -0
  164. package/dist/esm/SqlConsensus.js.map +1 -0
  165. package/dist/esm/SqlJournal.d.ts +173 -0
  166. package/dist/esm/SqlJournal.d.ts.map +1 -0
  167. package/dist/esm/SqlJournal.js +1858 -0
  168. package/dist/esm/SqlJournal.js.map +1 -0
  169. package/dist/esm/StepFact.d.ts +66 -0
  170. package/dist/esm/StepFact.d.ts.map +1 -0
  171. package/dist/esm/StepFact.js +50 -0
  172. package/dist/esm/StepFact.js.map +1 -0
  173. package/dist/esm/index.d.ts +95 -0
  174. package/dist/esm/index.d.ts.map +1 -0
  175. package/dist/esm/index.js +95 -0
  176. package/dist/esm/index.js.map +1 -0
  177. package/dist/esm/internal/JournalInternalFault.d.ts +37 -0
  178. package/dist/esm/internal/JournalInternalFault.d.ts.map +1 -0
  179. package/dist/esm/internal/JournalInternalFault.js +28 -0
  180. package/dist/esm/internal/JournalInternalFault.js.map +1 -0
  181. package/dist/esm/migrations/0001_initial.d.ts +15 -0
  182. package/dist/esm/migrations/0001_initial.d.ts.map +1 -0
  183. package/dist/esm/migrations/0001_initial.js +32 -0
  184. package/dist/esm/migrations/0001_initial.js.map +1 -0
  185. package/dist/esm/migrations/0002_checkpoints.d.ts +27 -0
  186. package/dist/esm/migrations/0002_checkpoints.d.ts.map +1 -0
  187. package/dist/esm/migrations/0002_checkpoints.js +38 -0
  188. package/dist/esm/migrations/0002_checkpoints.js.map +1 -0
  189. package/dist/esm/migrations/0003_startup_index.d.ts +15 -0
  190. package/dist/esm/migrations/0003_startup_index.d.ts.map +1 -0
  191. package/dist/esm/migrations/0003_startup_index.js +18 -0
  192. package/dist/esm/migrations/0003_startup_index.js.map +1 -0
  193. package/dist/esm/migrations/0004_dedup.d.ts +15 -0
  194. package/dist/esm/migrations/0004_dedup.d.ts.map +1 -0
  195. package/dist/esm/migrations/0004_dedup.js +38 -0
  196. package/dist/esm/migrations/0004_dedup.js.map +1 -0
  197. package/dist/esm/migrations/0005_run_event_type.d.ts +15 -0
  198. package/dist/esm/migrations/0005_run_event_type.d.ts.map +1 -0
  199. package/dist/esm/migrations/0005_run_event_type.js +18 -0
  200. package/dist/esm/migrations/0005_run_event_type.js.map +1 -0
  201. package/dist/esm/migrations/0006_consensus.d.ts +22 -0
  202. package/dist/esm/migrations/0006_consensus.d.ts.map +1 -0
  203. package/dist/esm/migrations/0006_consensus.js +69 -0
  204. package/dist/esm/migrations/0006_consensus.js.map +1 -0
  205. package/dist/esm/test/Notifying.d.ts +65 -0
  206. package/dist/esm/test/Notifying.d.ts.map +1 -0
  207. package/dist/esm/test/Notifying.js +62 -0
  208. package/dist/esm/test/Notifying.js.map +1 -0
  209. package/dist/esm/test/TestJournal.d.ts +43 -0
  210. package/dist/esm/test/TestJournal.d.ts.map +1 -0
  211. package/dist/esm/test/TestJournal.js +27 -0
  212. package/dist/esm/test/TestJournal.js.map +1 -0
  213. package/docs/README.md +164 -0
  214. package/docs/api.md +639 -0
  215. package/docs/concepts/compaction.md +157 -0
  216. package/docs/concepts/idempotency.md +127 -0
  217. package/docs/concepts/owner-fence.md +124 -0
  218. package/docs/concepts/redaction.md +218 -0
  219. package/docs/concepts/state-event-authority.md +159 -0
  220. package/docs/concepts/two-channels.md +167 -0
  221. package/docs/guides/commit-state-and-entry.md +114 -0
  222. package/docs/guides/compact-a-run.md +134 -0
  223. package/docs/guides/fold-a-projection.md +106 -0
  224. package/docs/guides/read-a-run.md +139 -0
  225. package/docs/guides/redact-log-output.md +94 -0
  226. package/docs/guides/testing.md +124 -0
  227. package/docs/guides/write-lifecycle-events.md +110 -0
  228. package/docs/installation.md +133 -0
  229. package/docs/quickstart.md +143 -0
  230. package/docs/troubleshooting.md +214 -0
  231. package/package.json +263 -3
  232. package/src/CallFact.ts +117 -0
  233. package/src/Consensus.ts +683 -0
  234. package/src/EngineEvent.ts +823 -0
  235. package/src/ExecutionFact.ts +433 -0
  236. package/src/Journal.ts +688 -0
  237. package/src/JournalEvent.ts +654 -0
  238. package/src/JournalGeneration.ts +77 -0
  239. package/src/JournalMetrics.ts +89 -0
  240. package/src/Migrations.ts +62 -0
  241. package/src/OwnerId.ts +42 -0
  242. package/src/Projection.ts +31 -0
  243. package/src/RedactedLogger.ts +483 -0
  244. package/src/Redaction.ts +1784 -0
  245. package/src/SqlConsensus.ts +344 -0
  246. package/src/SqlJournal.ts +2732 -0
  247. package/src/StepFact.ts +69 -0
  248. package/src/index.ts +109 -0
  249. package/src/internal/JournalInternalFault.ts +40 -0
  250. package/src/migrations/0001_initial.ts +41 -0
  251. package/src/migrations/0002_checkpoints.ts +46 -0
  252. package/src/migrations/0003_startup_index.ts +19 -0
  253. package/src/migrations/0004_dedup.ts +43 -0
  254. package/src/migrations/0005_run_event_type.ts +19 -0
  255. package/src/migrations/0006_consensus.ts +83 -0
  256. package/src/test/Notifying.ts +101 -0
  257. package/src/test/TestJournal.ts +52 -0
@@ -0,0 +1,445 @@
1
+ /**
2
+ * The redacting logger: the journal's redaction rules applied to log output.
3
+ *
4
+ * The journal redacts on the write path, so a credential a cell logs or
5
+ * returns never reaches a committed row (`./Redaction.ts`). The operator's
6
+ * terminal was the other place the same credential surfaces: every built-in
7
+ * Effect logger renders through the fiber's `Console`, so an action that hands
8
+ * a token to `Effect.logInfo` wrote it to stderr in full and it reached
9
+ * whatever collected that stream.
10
+ *
11
+ * This module closes that half with the SAME rules rather than a second rule
12
+ * set. {@link wrap} does not re-implement redaction and does not re-format a
13
+ * log line. It redacts the log event itself — the message, the cause, and the
14
+ * log annotations — before the wrapped logger is handed it, and it hands that
15
+ * logger a view of the fiber whose `Console` is a redacting console, so
16
+ * whatever the logger renders for itself — a pretty line, a JSON document —
17
+ * passes through `Redaction.redact` on its way to the stream as well.
18
+ *
19
+ * Both halves carry load. Most built-in loggers write through the fiber's
20
+ * `Console`, which is what covers the pretty line, the `--json` document, and
21
+ * the `.flows/logs/<runId>.log` stream. But a logger is free to read the event
22
+ * instead of rendering it, and one in Effect's own default logger set does:
23
+ * `Logger.tracerLogger` publishes the message as a span event name and
24
+ * `Cause.pretty` of the cause as the span's `effect.cause` attribute, and
25
+ * never reads `Console` at all. Redacting only the console would hide a
26
+ * credential on stderr and export it in clear to whatever OTLP collector is
27
+ * configured.
28
+ *
29
+ * Cost is bounded by the rules themselves: every alternation inside a
30
+ * repetition in `Redaction.diagnosticRules` starts its branches with disjoint
31
+ * characters, so each rule is linear in the length of the line and no input
32
+ * backtracks catastrophically.
33
+ *
34
+ * One deliberate difference from the journal write path. `Redaction.redact`
35
+ * rebuilds an object from its own enumerable entries, which is right for a row
36
+ * that is about to be JSON-encoded and wrong for a terminal: an `Error` keeps
37
+ * `message` and `stack` on non-enumerable properties, so rebuilding one prints
38
+ * `{}` where the operator expected a failure. An `Error` is rebuilt as a plain
39
+ * `Error` instead, carrying its name, message, stack and own members with the
40
+ * rules applied. Its class is NOT preserved. A copy built on the original's
41
+ * prototype inherits everything the prototype defines, an own-key walk sees
42
+ * none of it, and each of `name`, `cause`, `toJSON`, `Symbol.toStringTag` and
43
+ * `nodejs.util.inspect.custom` carried a credential out that way.
44
+ *
45
+ * @since 0.1.0
46
+ */
47
+ import * as Cause from "effect/Cause";
48
+ import * as Console from "effect/Console";
49
+ import * as Context from "effect/Context";
50
+ import * as Effect from "effect/Effect";
51
+ import * as Layer from "effect/Layer";
52
+ import * as Logger from "effect/Logger";
53
+ import * as References from "effect/References";
54
+ import * as Redaction from "./Redaction.js";
55
+ /**
56
+ * Marks a logger this module already wrapped.
57
+ *
58
+ * Both the CLI and the durable runtime install {@link layer}, and a detached
59
+ * `smithers run` is both at once. Wrapping twice would be correct but pay the
60
+ * rules twice per line, so a wrapped logger is returned unchanged.
61
+ *
62
+ * @since 0.1.0
63
+ * @category type ids
64
+ */
65
+ export const TypeId = Symbol.for("@smthrs/journal/RedactedLogger");
66
+ /**
67
+ * Whether {@link wrap} already wrapped this logger.
68
+ *
69
+ * @since 0.1.0
70
+ * @category predicates
71
+ */
72
+ export const isRedacted = (logger) => TypeId in logger;
73
+ /**
74
+ * Console methods that write caller-supplied values, mapped to whether their
75
+ * arguments carry log output.
76
+ *
77
+ * Only `clear` and `groupEnd` are marked `false`, because those two take no
78
+ * arguments at all: there is nothing for the rules to read and binding them
79
+ * straight through saves a copy per call. Every other method here, `count`,
80
+ * `countReset`, `time`, and `timeEnd` included, accepts a caller-supplied
81
+ * LABEL and prints it, so a label of `api_key=sk-…` reached the terminal
82
+ * through all four of them. `timeLog` already redacted its label, so the pair
83
+ * `time` / `timeLog` also disagreed about what a timer is called; redacting all
84
+ * of them is what makes a label match itself again.
85
+ */
86
+ const consoleMethods = {
87
+ assert: true,
88
+ clear: false,
89
+ count: true,
90
+ countReset: true,
91
+ debug: true,
92
+ dir: true,
93
+ dirxml: true,
94
+ error: true,
95
+ group: true,
96
+ groupCollapsed: true,
97
+ groupEnd: false,
98
+ info: true,
99
+ log: true,
100
+ table: true,
101
+ time: true,
102
+ timeEnd: true,
103
+ timeLog: true,
104
+ trace: true,
105
+ warn: true
106
+ };
107
+ /** How deep the logger walks a value before it names it instead. */
108
+ const renderDepthLimit = 200;
109
+ /** What the logger renders in place of a value nested past the depth limit. */
110
+ const renderDepthMarker = "[Deep]";
111
+ /**
112
+ * The parts of a log message.
113
+ *
114
+ * Effect's logging API passes an array, one entry per argument, including for
115
+ * a single argument. A caller that builds `LogOptions` by hand may pass the
116
+ * message itself, so it is wrapped rather than walked.
117
+ *
118
+ * @since 0.1.0
119
+ * @category redaction
120
+ */
121
+ const toArray = (message) => Array.isArray(message) ? message : [message];
122
+ /**
123
+ * Reads one piece of text off `error` and runs it through the rules.
124
+ *
125
+ * Everything a renderer prints about an error is read here, once, and only the
126
+ * redacted result crosses to the copy. A caller's error can define any of these
127
+ * as an accessor that throws, and reading one happens while a log line is
128
+ * rendering, so a refusal costs that piece of text and nothing else.
129
+ */
130
+ const redactedText = (read, redactor, fallback) => {
131
+ try {
132
+ return String(redactor(String(read())));
133
+ }
134
+ catch {
135
+ return fallback;
136
+ }
137
+ };
138
+ /** `error.stack` through the rules, or `undefined` when it has none to give. */
139
+ const redactedStack = (error, redactor) => {
140
+ try {
141
+ const stack = error.stack;
142
+ // An `Error.stack` is optional. Under `exactOptionalPropertyTypes` an
143
+ // assignment cannot carry the `undefined` a stackless error has, so the
144
+ // copy defines the property instead of assigning it.
145
+ return typeof stack === "string" ? String(redactor(stack)) : undefined;
146
+ }
147
+ catch {
148
+ return undefined;
149
+ }
150
+ };
151
+ /**
152
+ * A member NAME with the rules applied, replaced outright when one fires.
153
+ *
154
+ * The treatment `Redaction.redact` gives an object key, for the same reason: a
155
+ * name is text a renderer prints. Rewriting one in place would change what it
156
+ * reads as and redaction would stop being a fixed point, so a name that matches
157
+ * becomes the placeholder whole.
158
+ */
159
+ const redactedName = (key, redactor) => {
160
+ const redacted = String(redactor(key));
161
+ return redacted === key ? key : Redaction.placeholder;
162
+ };
163
+ /**
164
+ * Own names the copy never carries, because a renderer CALLS them.
165
+ *
166
+ * `name`, `message` and `stack` are defined from the rules above instead. The
167
+ * rest are hooks: a redacted `toString` or `valueOf` would leave a string under
168
+ * a name the language calls, and `String(error)` would then throw from inside
169
+ * the renderer, which is the failure mode this module exists to avoid.
170
+ */
171
+ const renderHooks = new Set([
172
+ "constructor",
173
+ "message",
174
+ "name",
175
+ "stack",
176
+ "toJSON",
177
+ "toString",
178
+ "valueOf"
179
+ ]);
180
+ /**
181
+ * A copy of `error` carrying the same information with the rules applied.
182
+ *
183
+ * A PLAIN `Error`, never a copy built on the original's prototype. A prototype
184
+ * is an unbounded leak surface and an own-key walk never looks at one: an
185
+ * inherited `name`, `cause`, `toJSON`, `Symbol.toStringTag` or
186
+ * `nodejs.util.inspect.custom` each carried a credential straight to an OTLP
187
+ * span attribute or to the operator's terminal, one per review round, and a
188
+ * getter anywhere on the chain is the same hole with a different name. So
189
+ * nothing inherited crosses. Only redacted text does: the name, the message,
190
+ * the stack, and the error's own members, each read once here and stored as
191
+ * data, since a getter carried across unchanged would hand the unredacted
192
+ * value to whatever reads it next.
193
+ *
194
+ * The copy is not an instance of the original's class, so a renderer that
195
+ * special-cases `instanceof MyError` sees a plain `Error`. That is the fidelity
196
+ * trade the release policy already records for every other value, and it is
197
+ * the trade the same problem's earlier, crashing form was settled with: a copy
198
+ * on a host prototype, such as the `DOMException` an `AbortSignal` carries as
199
+ * its reason, is an impostor whose brand check throws from inside `Cause.pretty`
200
+ * and kills the run the line was describing.
201
+ */
202
+ const redactError = (error, redactor, seen, depth = 0) => {
203
+ // The memo holds the COPY, not a visited mark: returning the original on a
204
+ // repeat reference put the unredacted error back into the output, so a
205
+ // diamond (two fields naming one error) leaked on its second field. The copy
206
+ // is registered before its own members are walked, so a cycle terminates on
207
+ // it and every reference to one error is the same redacted object.
208
+ const memoized = seen.get(error);
209
+ if (memoized !== undefined)
210
+ return memoized;
211
+ const clone = new Error();
212
+ seen.set(error, clone);
213
+ const define = (key, value, enumerable) => {
214
+ Object.defineProperty(clone, key, { value, writable: true, enumerable, configurable: true });
215
+ };
216
+ define("name", redactedText(() => error.name, redactor, "Error"), false);
217
+ define("message", redactedText(() => error.message, redactor, ""), false);
218
+ define("stack", redactedStack(error, redactor), false);
219
+ // Own NAMES, not `Reflect.ownKeys`. A symbol key never crosses: its
220
+ // description is text no walk can rewrite in place, and the three hooks with
221
+ // no name to skip are symbols too, `Symbol.toPrimitive`,
222
+ // `Symbol.toStringTag`, and `nodejs.util.inspect.custom`.
223
+ for (const key of Object.getOwnPropertyNames(error)) {
224
+ if (renderHooks.has(key))
225
+ continue;
226
+ const descriptor = Object.getOwnPropertyDescriptor(error, key);
227
+ const value = "value" in descriptor
228
+ ? descriptor.value
229
+ : error[key];
230
+ define(redactedName(key, redactor),
231
+ // A credential-named member is replaced wholesale, the way the journal's
232
+ // own walk replaces one, so both halves of the one rule set answer alike.
233
+ Redaction.isSensitiveKey(key)
234
+ ? Redaction.placeholder
235
+ // The depth cap binds under an Error exactly as it does beside one: a
236
+ // deep value carried on a cause can exhaust the stack just as easily.
237
+ : depth + 1 >= renderDepthLimit
238
+ ? renderDepthMarker
239
+ : value instanceof Error
240
+ ? redactError(value, redactor, seen, depth + 1)
241
+ : redactor(value), descriptor.enumerable === true);
242
+ }
243
+ return clone;
244
+ };
245
+ /**
246
+ * Redacts one value on its way out of the logger.
247
+ *
248
+ * An `Error` is rebuilt as a plain `Error` whose name, message, stack and own
249
+ * members are redacted, so a renderer that special-cases an `Error` still gets
250
+ * one. Everything else goes through the journal's own rules, which rebuild a
251
+ * value from its plain data: a `Date`, a `Map`, a class instance or a host
252
+ * object therefore reaches the operator in that plain form rather than as
253
+ * itself.
254
+ *
255
+ * That collapse is deliberate, and it now covers the `Error` too. An earlier
256
+ * version rebuilt each value on its own prototype to keep the rendering
257
+ * faithful. Three rounds of review found three ways that could kill the run it
258
+ * was logging, because a host class keeps state a property walk cannot see and
259
+ * a copy of one throws the moment a brand check reads it, and four more found
260
+ * that the prototype an `Error` copy kept was itself the leak: everything it
261
+ * defines is read by a renderer and seen by no own-key walk. A log line is not
262
+ * worth a run, and the rendering it buys is cosmetic.
263
+ *
264
+ * @since 0.1.0
265
+ * @category redaction
266
+ */
267
+ export const redactArgument = (value, redactor) => {
268
+ try {
269
+ return value instanceof Error ? redactError(value, redactor, new WeakMap()) : redactor(value);
270
+ }
271
+ catch {
272
+ // A logged value is arbitrary and a throw here happens while the line is
273
+ // rendering, which would kill the run the line describes. The marker is
274
+ // deliberately NOT derived from the value: an earlier fallback rendered
275
+ // the value to text and printed it, and that text had never been through
276
+ // the rules, so a credential reached the terminal in clear.
277
+ return unrenderableMarker;
278
+ }
279
+ };
280
+ /** What the logger renders in place of a value that cannot be rendered. */
281
+ const unrenderableMarker = "[Unrenderable]";
282
+ /**
283
+ * A console that runs every value it is handed through `redactor` and then
284
+ * delegates to `target`.
285
+ *
286
+ * @since 0.1.0
287
+ * @category constructors
288
+ */
289
+ export const redactingConsole = (target, redactor) => {
290
+ const methods = target;
291
+ const view = {};
292
+ for (const [name, redacts] of Object.entries(consoleMethods)) {
293
+ const method = methods[name].bind(target);
294
+ view[name] = redacts
295
+ ? (...args) => {
296
+ const redacted = args.map((value) => redactArgument(value, redactor));
297
+ try {
298
+ return method(...redacted);
299
+ }
300
+ catch {
301
+ // The delegate refused what we handed it. Retry with markers, never
302
+ // with a rendering of the values: a rendering has not been through
303
+ // the rules, and printing one is how a credential reached the
304
+ // terminal before. A delegate that refuses the markers too is out of
305
+ // options, and a log line is not worth the run.
306
+ try {
307
+ return method(...redacted.map(() => unrenderableMarker));
308
+ }
309
+ catch {
310
+ return undefined;
311
+ }
312
+ }
313
+ }
314
+ : method;
315
+ }
316
+ return view;
317
+ };
318
+ /**
319
+ * A view of `fiber` whose console redacts and whose log annotations are
320
+ * already redacted.
321
+ *
322
+ * The view inherits from the fiber itself, so `id`, `currentSpan`, and every
323
+ * other member a logger reads answer exactly as before; only the two
324
+ * references a logger renders from are substituted.
325
+ */
326
+ const redactedFiber = (fiber, redactor) => {
327
+ const view = Object.create(fiber);
328
+ Object.defineProperty(view, "getRef", {
329
+ value: (reference) => {
330
+ const current = fiber.getRef(reference);
331
+ if (reference === Console.Console) {
332
+ return redactingConsole(current, redactor);
333
+ }
334
+ if (reference === References.CurrentLogAnnotations) {
335
+ // The same treatment a message part gets, so one log event obeys one
336
+ // rule and an annotated Error is redacted like a logged one.
337
+ return redactArgument(current, redactor);
338
+ }
339
+ return current;
340
+ }
341
+ });
342
+ return view;
343
+ };
344
+ /**
345
+ * Rebuilds the frame chain as redacted data. Evaluate the lazy stack here:
346
+ * carrying its original function would defer the credential past the boundary.
347
+ * The same depth cap as logged values also bounds cyclic parent chains.
348
+ */
349
+ const redactedFrame = (frame, redactor, depth = 0) => {
350
+ if (frame === undefined)
351
+ return undefined;
352
+ if (depth >= renderDepthLimit) {
353
+ return { name: renderDepthMarker, stack: () => undefined, parent: undefined };
354
+ }
355
+ const stack = redactedText(() => frame.stack() ?? "", redactor, unrenderableMarker);
356
+ return {
357
+ name: redactedText(() => frame.name, redactor, unrenderableMarker),
358
+ stack: () => stack,
359
+ parent: redactedFrame(frame.parent, redactor, depth + 1)
360
+ };
361
+ };
362
+ /**
363
+ * The log event's cause with the rules applied to failures, defects and stack
364
+ * annotations. `Cause.pretty` renders these frames after rendering the error;
365
+ * the tracer exports that text without passing through the redacting console.
366
+ * Interruptions also carry both their own and the interruptor's frame chains.
367
+ * Rebuild reasons and annotation maps so the original cause stays untouched.
368
+ */
369
+ const redactedCause = (cause, redactor) => {
370
+ if (cause.reasons.length === 0)
371
+ return cause;
372
+ return Cause.fromReasons(cause.reasons.map((reason) => {
373
+ const annotations = new Map(reason.annotations);
374
+ for (const key of [Cause.StackTrace.key, Cause.InterruptorStackTrace.key]) {
375
+ if (annotations.has(key)) {
376
+ annotations.set(key, redactedFrame(annotations.get(key), redactor));
377
+ }
378
+ }
379
+ const copy = reason._tag === "Fail"
380
+ ? Cause.makeFailReason(redactArgument(reason.error, redactor))
381
+ : reason._tag === "Die"
382
+ ? Cause.makeDieReason(redactArgument(reason.defect, redactor))
383
+ : Cause.makeInterruptReason(reason.fiberId);
384
+ return copy.annotate(Context.makeUnsafe(annotations));
385
+ }));
386
+ };
387
+ /**
388
+ * Returns `logger` with the journal's redaction rules over everything it
389
+ * renders. A logger this module already wrapped is returned unchanged.
390
+ *
391
+ * @since 0.1.0
392
+ * @category combinators
393
+ */
394
+ export const wrap = (logger, options) => {
395
+ if (isRedacted(logger))
396
+ return logger;
397
+ // A logger NAMES a too-deep value rather than throwing: a throw is caught one
398
+ // frame up and costs the operator every argument on the line, not just the
399
+ // deep one. A log line is a diagnostic, so it takes the diagnostic rules. A
400
+ // caller passing its own `onTooDeep` or `rules` still wins.
401
+ const redactor = Redaction.make({ onTooDeep: "name", rules: Redaction.diagnosticRules, ...options });
402
+ const wrapped = Logger.make((logOptions) => logger.log({
403
+ ...logOptions,
404
+ // The message is redacted here, not only behind the fiber's Console,
405
+ // because a logger is free to read it directly: `Logger.tracerLogger`
406
+ // ships in Effect's default set and publishes the message as a span
407
+ // event name, so a Console-only substitution would export a credential
408
+ // in clear to whatever collector is configured.
409
+ //
410
+ // Effect delivers the message as an array of parts, one per argument,
411
+ // including the single-argument case; a non-array reaches here only from
412
+ // a caller that builds `LogOptions` by hand, and `toArray` covers it
413
+ // without a branch no test could reach through the logging API.
414
+ message: (toArray(logOptions.message).map((part) => redactArgument(part, redactor))),
415
+ // The cause travels the same way and for the same reason. A failure is
416
+ // logged as a `Cause`, not as message parts (`Effect.logError(cause)`
417
+ // moves it out of the arguments), and `Logger.tracerLogger` renders it
418
+ // itself into the span's `effect.cause` attribute without going near the
419
+ // console, so an error message carrying a token would leave the process
420
+ // in clear.
421
+ cause: redactedCause(logOptions.cause, redactor),
422
+ fiber: redactedFiber(logOptions.fiber, redactor)
423
+ }));
424
+ Object.defineProperty(wrapped, TypeId, { value: true });
425
+ return wrapped;
426
+ };
427
+ /**
428
+ * Replaces the active logger set with redacting wrappers of the same loggers.
429
+ *
430
+ * It composes over whatever loggers are already installed rather than
431
+ * choosing one, so an operator keeps the FORMAT they had, meaning their own
432
+ * logger and its layout, though a value inside a line is rendered from its
433
+ * redacted plain data rather than as its own class, and a host that
434
+ * installed its own logger keeps it too.
435
+ *
436
+ * @since 0.1.0
437
+ * @category layers
438
+ */
439
+ export const layer = (options) => Layer.effect(Logger.CurrentLoggers, Effect.withFiber((fiber) => Effect.sync(() => {
440
+ const wrapped = new Set();
441
+ for (const logger of fiber.getRef(Logger.CurrentLoggers))
442
+ wrapped.add(wrap(logger, options));
443
+ return wrapped;
444
+ })));
445
+ //# sourceMappingURL=RedactedLogger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RedactedLogger.js","sourceRoot":"","sources":["../../src/RedactedLogger.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The redacting logger: the journal's redaction rules applied to log output.\n *\n * The journal redacts on the write path, so a credential a cell logs or\n * returns never reaches a committed row (`./Redaction.ts`). The operator's\n * terminal was the other place the same credential surfaces: every built-in\n * Effect logger renders through the fiber's `Console`, so an action that hands\n * a token to `Effect.logInfo` wrote it to stderr in full and it reached\n * whatever collected that stream.\n *\n * This module closes that half with the SAME rules rather than a second rule\n * set. {@link wrap} does not re-implement redaction and does not re-format a\n * log line. It redacts the log event itself — the message, the cause, and the\n * log annotations — before the wrapped logger is handed it, and it hands that\n * logger a view of the fiber whose `Console` is a redacting console, so\n * whatever the logger renders for itself — a pretty line, a JSON document —\n * passes through `Redaction.redact` on its way to the stream as well.\n *\n * Both halves carry load. Most built-in loggers write through the fiber's\n * `Console`, which is what covers the pretty line, the `--json` document, and\n * the `.flows/logs/<runId>.log` stream. But a logger is free to read the event\n * instead of rendering it, and one in Effect's own default logger set does:\n * `Logger.tracerLogger` publishes the message as a span event name and\n * `Cause.pretty` of the cause as the span's `effect.cause` attribute, and\n * never reads `Console` at all. Redacting only the console would hide a\n * credential on stderr and export it in clear to whatever OTLP collector is\n * configured.\n *\n * Cost is bounded by the rules themselves: every alternation inside a\n * repetition in `Redaction.diagnosticRules` starts its branches with disjoint\n * characters, so each rule is linear in the length of the line and no input\n * backtracks catastrophically.\n *\n * One deliberate difference from the journal write path. `Redaction.redact`\n * rebuilds an object from its own enumerable entries, which is right for a row\n * that is about to be JSON-encoded and wrong for a terminal: an `Error` keeps\n * `message` and `stack` on non-enumerable properties, so rebuilding one prints\n * `{}` where the operator expected a failure. An `Error` is rebuilt as a plain\n * `Error` instead, carrying its name, message, stack and own members with the\n * rules applied. Its class is NOT preserved. A copy built on the original's\n * prototype inherits everything the prototype defines, an own-key walk sees\n * none of it, and each of `name`, `cause`, `toJSON`, `Symbol.toStringTag` and\n * `nodejs.util.inspect.custom` carried a credential out that way.\n *\n * @since 0.1.0\n */\n\nimport * as Cause from \"effect/Cause\"\nimport * as Console from \"effect/Console\"\nimport * as Context from \"effect/Context\"\nimport * as Effect from \"effect/Effect\"\nimport type * as Fiber from \"effect/Fiber\"\nimport * as Layer from \"effect/Layer\"\nimport * as Logger from \"effect/Logger\"\nimport * as References from \"effect/References\"\nimport * as Redaction from \"./Redaction.ts\"\n\n/**\n * Marks a logger this module already wrapped.\n *\n * Both the CLI and the durable runtime install {@link layer}, and a detached\n * `smithers run` is both at once. Wrapping twice would be correct but pay the\n * rules twice per line, so a wrapped logger is returned unchanged.\n *\n * @since 0.1.0\n * @category type ids\n */\nexport const TypeId: unique symbol = Symbol.for(\"@smthrs/journal/RedactedLogger\")\n\n/**\n * Whether {@link wrap} already wrapped this logger.\n *\n * @since 0.1.0\n * @category predicates\n */\nexport const isRedacted = (logger: Logger.Logger<any, any>): boolean => TypeId in logger\n\n/**\n * Console methods that write caller-supplied values, mapped to whether their\n * arguments carry log output.\n *\n * Only `clear` and `groupEnd` are marked `false`, because those two take no\n * arguments at all: there is nothing for the rules to read and binding them\n * straight through saves a copy per call. Every other method here, `count`,\n * `countReset`, `time`, and `timeEnd` included, accepts a caller-supplied\n * LABEL and prints it, so a label of `api_key=sk-…` reached the terminal\n * through all four of them. `timeLog` already redacted its label, so the pair\n * `time` / `timeLog` also disagreed about what a timer is called; redacting all\n * of them is what makes a label match itself again.\n */\nconst consoleMethods = {\n assert: true,\n clear: false,\n count: true,\n countReset: true,\n debug: true,\n dir: true,\n dirxml: true,\n error: true,\n group: true,\n groupCollapsed: true,\n groupEnd: false,\n info: true,\n log: true,\n table: true,\n time: true,\n timeEnd: true,\n timeLog: true,\n trace: true,\n warn: true\n} as const\n\n/** How deep the logger walks a value before it names it instead. */\nconst renderDepthLimit = 200\n\n/** What the logger renders in place of a value nested past the depth limit. */\nconst renderDepthMarker = \"[Deep]\"\n\n/**\n * The parts of a log message.\n *\n * Effect's logging API passes an array, one entry per argument, including for\n * a single argument. A caller that builds `LogOptions` by hand may pass the\n * message itself, so it is wrapped rather than walked.\n *\n * @since 0.1.0\n * @category redaction\n */\nconst toArray = (message: unknown): ReadonlyArray<unknown> => Array.isArray(message) ? message : [message]\n\n/**\n * Reads one piece of text off `error` and runs it through the rules.\n *\n * Everything a renderer prints about an error is read here, once, and only the\n * redacted result crosses to the copy. A caller's error can define any of these\n * as an accessor that throws, and reading one happens while a log line is\n * rendering, so a refusal costs that piece of text and nothing else.\n */\nconst redactedText = (read: () => unknown, redactor: Redaction.Redactor, fallback: string): string => {\n try {\n return String(redactor(String(read())))\n } catch {\n return fallback\n }\n}\n\n/** `error.stack` through the rules, or `undefined` when it has none to give. */\nconst redactedStack = (error: Error, redactor: Redaction.Redactor): string | undefined => {\n try {\n const stack = error.stack\n // An `Error.stack` is optional. Under `exactOptionalPropertyTypes` an\n // assignment cannot carry the `undefined` a stackless error has, so the\n // copy defines the property instead of assigning it.\n return typeof stack === \"string\" ? String(redactor(stack)) : undefined\n } catch {\n return undefined\n }\n}\n\n/**\n * A member NAME with the rules applied, replaced outright when one fires.\n *\n * The treatment `Redaction.redact` gives an object key, for the same reason: a\n * name is text a renderer prints. Rewriting one in place would change what it\n * reads as and redaction would stop being a fixed point, so a name that matches\n * becomes the placeholder whole.\n */\nconst redactedName = (key: string, redactor: Redaction.Redactor): string => {\n const redacted = String(redactor(key))\n return redacted === key ? key : Redaction.placeholder\n}\n\n/**\n * Own names the copy never carries, because a renderer CALLS them.\n *\n * `name`, `message` and `stack` are defined from the rules above instead. The\n * rest are hooks: a redacted `toString` or `valueOf` would leave a string under\n * a name the language calls, and `String(error)` would then throw from inside\n * the renderer, which is the failure mode this module exists to avoid.\n */\nconst renderHooks: ReadonlySet<string> = new Set([\n \"constructor\",\n \"message\",\n \"name\",\n \"stack\",\n \"toJSON\",\n \"toString\",\n \"valueOf\"\n])\n\n/**\n * A copy of `error` carrying the same information with the rules applied.\n *\n * A PLAIN `Error`, never a copy built on the original's prototype. A prototype\n * is an unbounded leak surface and an own-key walk never looks at one: an\n * inherited `name`, `cause`, `toJSON`, `Symbol.toStringTag` or\n * `nodejs.util.inspect.custom` each carried a credential straight to an OTLP\n * span attribute or to the operator's terminal, one per review round, and a\n * getter anywhere on the chain is the same hole with a different name. So\n * nothing inherited crosses. Only redacted text does: the name, the message,\n * the stack, and the error's own members, each read once here and stored as\n * data, since a getter carried across unchanged would hand the unredacted\n * value to whatever reads it next.\n *\n * The copy is not an instance of the original's class, so a renderer that\n * special-cases `instanceof MyError` sees a plain `Error`. That is the fidelity\n * trade the release policy already records for every other value, and it is\n * the trade the same problem's earlier, crashing form was settled with: a copy\n * on a host prototype, such as the `DOMException` an `AbortSignal` carries as\n * its reason, is an impostor whose brand check throws from inside `Cause.pretty`\n * and kills the run the line was describing.\n */\nconst redactError = (\n error: Error,\n redactor: Redaction.Redactor,\n seen: WeakMap<Error, Error>,\n depth = 0\n): Error => {\n // The memo holds the COPY, not a visited mark: returning the original on a\n // repeat reference put the unredacted error back into the output, so a\n // diamond (two fields naming one error) leaked on its second field. The copy\n // is registered before its own members are walked, so a cycle terminates on\n // it and every reference to one error is the same redacted object.\n const memoized = seen.get(error)\n if (memoized !== undefined) return memoized\n const clone = new Error()\n seen.set(error, clone)\n const define = (key: string, value: unknown, enumerable: boolean): void => {\n Object.defineProperty(clone, key, { value, writable: true, enumerable, configurable: true })\n }\n define(\"name\", redactedText(() => error.name, redactor, \"Error\"), false)\n define(\"message\", redactedText(() => error.message, redactor, \"\"), false)\n define(\"stack\", redactedStack(error, redactor), false)\n // Own NAMES, not `Reflect.ownKeys`. A symbol key never crosses: its\n // description is text no walk can rewrite in place, and the three hooks with\n // no name to skip are symbols too, `Symbol.toPrimitive`,\n // `Symbol.toStringTag`, and `nodejs.util.inspect.custom`.\n for (const key of Object.getOwnPropertyNames(error)) {\n if (renderHooks.has(key)) continue\n const descriptor = Object.getOwnPropertyDescriptor(error, key)!\n const value = \"value\" in descriptor\n ? descriptor.value\n : (error as unknown as Record<PropertyKey, unknown>)[key]\n define(\n redactedName(key, redactor),\n // A credential-named member is replaced wholesale, the way the journal's\n // own walk replaces one, so both halves of the one rule set answer alike.\n Redaction.isSensitiveKey(key)\n ? Redaction.placeholder\n // The depth cap binds under an Error exactly as it does beside one: a\n // deep value carried on a cause can exhaust the stack just as easily.\n : depth + 1 >= renderDepthLimit\n ? renderDepthMarker\n : value instanceof Error\n ? redactError(value, redactor, seen, depth + 1)\n : redactor(value),\n descriptor.enumerable === true\n )\n }\n return clone\n}\n\n/**\n * Redacts one value on its way out of the logger.\n *\n * An `Error` is rebuilt as a plain `Error` whose name, message, stack and own\n * members are redacted, so a renderer that special-cases an `Error` still gets\n * one. Everything else goes through the journal's own rules, which rebuild a\n * value from its plain data: a `Date`, a `Map`, a class instance or a host\n * object therefore reaches the operator in that plain form rather than as\n * itself.\n *\n * That collapse is deliberate, and it now covers the `Error` too. An earlier\n * version rebuilt each value on its own prototype to keep the rendering\n * faithful. Three rounds of review found three ways that could kill the run it\n * was logging, because a host class keeps state a property walk cannot see and\n * a copy of one throws the moment a brand check reads it, and four more found\n * that the prototype an `Error` copy kept was itself the leak: everything it\n * defines is read by a renderer and seen by no own-key walk. A log line is not\n * worth a run, and the rendering it buys is cosmetic.\n *\n * @since 0.1.0\n * @category redaction\n */\nexport const redactArgument = (value: unknown, redactor: Redaction.Redactor): unknown => {\n try {\n return value instanceof Error ? redactError(value, redactor, new WeakMap()) : redactor(value)\n } catch {\n // A logged value is arbitrary and a throw here happens while the line is\n // rendering, which would kill the run the line describes. The marker is\n // deliberately NOT derived from the value: an earlier fallback rendered\n // the value to text and printed it, and that text had never been through\n // the rules, so a credential reached the terminal in clear.\n return unrenderableMarker\n }\n}\n\n/** What the logger renders in place of a value that cannot be rendered. */\nconst unrenderableMarker = \"[Unrenderable]\"\n\n/**\n * A console that runs every value it is handed through `redactor` and then\n * delegates to `target`.\n *\n * @since 0.1.0\n * @category constructors\n */\nexport const redactingConsole = (target: Console.Console, redactor: Redaction.Redactor): Console.Console => {\n const methods = target as unknown as Record<string, (...args: ReadonlyArray<any>) => unknown>\n const view: Record<string, unknown> = {}\n for (const [name, redacts] of Object.entries(consoleMethods)) {\n const method = methods[name]!.bind(target)\n view[name] = redacts\n ? (...args: ReadonlyArray<unknown>) => {\n const redacted = args.map((value) => redactArgument(value, redactor))\n try {\n return method(...redacted)\n } catch {\n // The delegate refused what we handed it. Retry with markers, never\n // with a rendering of the values: a rendering has not been through\n // the rules, and printing one is how a credential reached the\n // terminal before. A delegate that refuses the markers too is out of\n // options, and a log line is not worth the run.\n try {\n return method(...redacted.map(() => unrenderableMarker))\n } catch {\n return undefined\n }\n }\n }\n : method\n }\n return view as unknown as Console.Console\n}\n\n/**\n * A view of `fiber` whose console redacts and whose log annotations are\n * already redacted.\n *\n * The view inherits from the fiber itself, so `id`, `currentSpan`, and every\n * other member a logger reads answer exactly as before; only the two\n * references a logger renders from are substituted.\n */\nconst redactedFiber = (\n fiber: Fiber.Fiber<unknown, unknown>,\n redactor: Redaction.Redactor\n): Fiber.Fiber<unknown, unknown> => {\n const view = Object.create(fiber) as Fiber.Fiber<unknown, unknown>\n Object.defineProperty(view, \"getRef\", {\n value: <X>(reference: Context.Reference<X>): X => {\n const current = fiber.getRef(reference)\n if (reference === (Console.Console as unknown as Context.Reference<X>)) {\n return redactingConsole(current as Console.Console, redactor) as X\n }\n if (reference === (References.CurrentLogAnnotations as unknown as Context.Reference<X>)) {\n // The same treatment a message part gets, so one log event obeys one\n // rule and an annotated Error is redacted like a logged one.\n return redactArgument(current, redactor) as X\n }\n return current\n }\n })\n return view\n}\n\n/**\n * Rebuilds the frame chain as redacted data. Evaluate the lazy stack here:\n * carrying its original function would defer the credential past the boundary.\n * The same depth cap as logged values also bounds cyclic parent chains.\n */\nconst redactedFrame = (\n frame: References.StackFrame | undefined,\n redactor: Redaction.Redactor,\n depth = 0\n): References.StackFrame | undefined => {\n if (frame === undefined) return undefined\n if (depth >= renderDepthLimit) {\n return { name: renderDepthMarker, stack: () => undefined, parent: undefined }\n }\n const stack = redactedText(() => frame.stack() ?? \"\", redactor, unrenderableMarker)\n return {\n name: redactedText(() => frame.name, redactor, unrenderableMarker),\n stack: () => stack,\n parent: redactedFrame(frame.parent, redactor, depth + 1)\n }\n}\n\n/**\n * The log event's cause with the rules applied to failures, defects and stack\n * annotations. `Cause.pretty` renders these frames after rendering the error;\n * the tracer exports that text without passing through the redacting console.\n * Interruptions also carry both their own and the interruptor's frame chains.\n * Rebuild reasons and annotation maps so the original cause stays untouched.\n */\nconst redactedCause = (\n cause: Cause.Cause<unknown>,\n redactor: Redaction.Redactor\n): Cause.Cause<unknown> => {\n if (cause.reasons.length === 0) return cause\n return Cause.fromReasons(cause.reasons.map((reason) => {\n const annotations = new Map(reason.annotations)\n for (const key of [Cause.StackTrace.key, Cause.InterruptorStackTrace.key]) {\n if (annotations.has(key)) {\n annotations.set(key, redactedFrame(annotations.get(key) as References.StackFrame | undefined, redactor))\n }\n }\n const copy = reason._tag === \"Fail\"\n ? Cause.makeFailReason(redactArgument(reason.error, redactor))\n : reason._tag === \"Die\"\n ? Cause.makeDieReason(redactArgument(reason.defect, redactor))\n : Cause.makeInterruptReason(reason.fiberId)\n return copy.annotate(Context.makeUnsafe(annotations))\n }))\n}\n\n/**\n * Returns `logger` with the journal's redaction rules over everything it\n * renders. A logger this module already wrapped is returned unchanged.\n *\n * @since 0.1.0\n * @category combinators\n */\nexport const wrap = <Message, Output>(\n logger: Logger.Logger<Message, Output>,\n options?: Redaction.Options\n): Logger.Logger<Message, Output> => {\n if (isRedacted(logger)) return logger\n // A logger NAMES a too-deep value rather than throwing: a throw is caught one\n // frame up and costs the operator every argument on the line, not just the\n // deep one. A log line is a diagnostic, so it takes the diagnostic rules. A\n // caller passing its own `onTooDeep` or `rules` still wins.\n const redactor = Redaction.make({ onTooDeep: \"name\", rules: Redaction.diagnosticRules, ...options })\n const wrapped = Logger.make<Message, Output>((logOptions) =>\n logger.log({\n ...logOptions,\n // The message is redacted here, not only behind the fiber's Console,\n // because a logger is free to read it directly: `Logger.tracerLogger`\n // ships in Effect's default set and publishes the message as a span\n // event name, so a Console-only substitution would export a credential\n // in clear to whatever collector is configured.\n //\n // Effect delivers the message as an array of parts, one per argument,\n // including the single-argument case; a non-array reaches here only from\n // a caller that builds `LogOptions` by hand, and `toArray` covers it\n // without a branch no test could reach through the logging API.\n message: (toArray(logOptions.message).map((part) => redactArgument(part, redactor))) as Message,\n // The cause travels the same way and for the same reason. A failure is\n // logged as a `Cause`, not as message parts (`Effect.logError(cause)`\n // moves it out of the arguments), and `Logger.tracerLogger` renders it\n // itself into the span's `effect.cause` attribute without going near the\n // console, so an error message carrying a token would leave the process\n // in clear.\n cause: redactedCause(logOptions.cause, redactor),\n fiber: redactedFiber(logOptions.fiber, redactor)\n })\n )\n Object.defineProperty(wrapped, TypeId, { value: true })\n return wrapped\n}\n\n/**\n * Replaces the active logger set with redacting wrappers of the same loggers.\n *\n * It composes over whatever loggers are already installed rather than\n * choosing one, so an operator keeps the FORMAT they had, meaning their own\n * logger and its layout, though a value inside a line is rendered from its\n * redacted plain data rather than as its own class, and a host that\n * installed its own logger keeps it too.\n *\n * @since 0.1.0\n * @category layers\n */\nexport const layer = (options?: Redaction.Options): Layer.Layer<never> =>\n Layer.effect(\n Logger.CurrentLoggers,\n Effect.withFiber((fiber) =>\n Effect.sync(() => {\n const wrapped = new Set<Logger.Logger<unknown, any>>()\n for (const logger of fiber.getRef(Logger.CurrentLoggers)) wrapped.add(wrap(logger, options))\n return wrapped\n })\n )\n )\n"]}