@stonyx/logs 1.0.1-alpha.18 → 1.0.1-alpha.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -58,10 +58,9 @@ const log = new Log();
58
58
  log.info('Info: sample application has started');
59
59
  log.warn('Warning: this is just a sample');
60
60
 
61
- // Writing to a file can fail, and the returned promise is the only signal that it did.
62
- // Always handle it -- an unhandled rejection terminates the process.
63
- // See "Handling write failures" below.
64
- log.error('Error: no application logic detected', true) // logs to logs/error.log file
61
+ // logs to logs/error.log; a failed write rejects, and handling that rejection is required
62
+ // see "Handling write failures" below
63
+ log.error('Error: no application logic detected', true)
65
64
  .catch(err => { /* err.code is the underlying fs error code, e.g. 'EACCES' */ });
66
65
  ```
67
66
 
@@ -100,9 +99,10 @@ log.info('Info: sample application has started')
100
99
  .catch(err => { /* see "Handling write failures" */ });
101
100
  ```
102
101
 
103
- > **Caveat:** with `logToFileByDefault: true`, *every* log call becomes a file write, and therefore
104
- > every log call returns a promise that can reject. A failing log directory turns an unhandled
105
- > `log.info()` into a process-terminating rejection. See [handling write failures](#handling-write-failures).
102
+ > **Caveat:** with `logToFileByDefault: true`, every log call *except* [`debug()`](#the-debug-method)
103
+ > becomes a file write, and therefore returns a promise that can reject. A failing log directory turns
104
+ > an unhandled `log.info()` into a process-terminating rejection. See
105
+ > [handling write failures](#handling-write-failures).
106
106
 
107
107
  ![](https://github.com/abofs/stonyx-logs/raw/main/media/examples/custom-options.jpg)
108
108
 
@@ -167,10 +167,8 @@ Color settings are handled by determining whether your input is a color name or
167
167
 
168
168
  Additionally, these methods return a promise when `logToFile` is true, allowing you to use them with `await` in an async method, or to append `then()`, `catch()` or `finally()`.
169
169
 
170
- When `logToFile` is true that promise **can reject**, and handling the rejection is **required**, not
171
- optional: the rejection is the only signal that the write failed, and an unhandled rejection
172
- terminates your process under Node's default settings. Wrap the call in `try`/`catch` (or attach
173
- `.catch()`) for every call that writes to a file.
170
+ When `logToFile` is true that promise **can reject**, and handling the rejection is **required** --
171
+ see [handling write failures](#handling-write-failures) for the full contract.
174
172
 
175
173
  ```js
176
174
  async method() {
@@ -184,8 +182,6 @@ async method() {
184
182
  }
185
183
  ```
186
184
 
187
- See [handling write failures](#handling-write-failures) for the full contract.
188
-
189
185
  ### The Debug Method
190
186
 
191
187
  **Log** allows for the `log.debug()` method to be overridden by a color setting. However, by default we do not define a color for debug and debug is handled differently. For console logging, all **debug** does is output the following:
@@ -200,9 +196,10 @@ JSON.stringify(content, null, 2);
200
196
 
201
197
  We believe that when wanting to output complicated objects or debug **typescript** applications, there are better methods than utilizing this **Log** package. But for anyone who's fully incorporated **Log** into their project, this function offers some convenience.
202
198
 
203
- `debug(content, logToFile)` writes to file on exactly the same contract as every other log type:
204
- `log.debug(content, true)` returns a promise that rejects with the underlying `fs` error when the
205
- write fails, and that rejection must be handled. See [handling write failures](#handling-write-failures).
199
+ `debug(content, logToFile)` writes to file on the same contract as every other log type -- a failed
200
+ write rejects with the underlying `fs` error and must be handled -- with one difference: `debug()`
201
+ ignores [`logToFileByDefault`](#configuration), so it writes to file only when you pass `true`.
202
+ See [handling write failures](#handling-write-failures).
206
203
 
207
204
  ### Logging Parameters
208
205
 
@@ -218,26 +215,33 @@ log.error('error message', true, false); // content, logToFile, overwrite
218
215
 
219
216
  **logToFile** will log to *<project-root>/logs* unless [configured](#configuration) differently during instantiation. <br>
220
217
 
221
- ### Handling write failures
218
+ ### Handling Write Failures
222
219
 
223
220
  Any call that writes to a file returns a promise that **rejects when the write fails**. That covers
224
- `log.error(content, true)`, `log.debug(content, true)`, and *every* log call when
225
- [`logToFileByDefault`](#configuration) is `true`.
221
+ `log.error(content, true)`, `log.debug(content, true)`, and every log call except `debug()` when
222
+ [`logToFileByDefault`](#configuration) is `true` -- `debug()` is a direct method that never consults
223
+ `logToFileByDefault`, so it only writes to file when you pass `true` explicitly.
226
224
 
227
225
  That rejection is the only actionable failure signal, so every file-writing call must be awaited
228
- inside `try`/`catch`, or have `.catch()` attached.
226
+ inside `try`/`catch`, or have `.catch()` attached. The console line is written *before* the file
227
+ write is attempted, so a rejection means only the file copy was lost -- do not re-log the message in
228
+ your handler, or it prints twice.
229
229
 
230
230
  > **A rejection is terminal. Do not retry it.**
231
231
  >
232
- > **Log** has already retried internally by the time a rejection reaches you (see
233
- > [self-healing retry](#self-healing-retry) below). A rejection means that retry ran and failed too,
234
- > so a consumer-side retry layer races the internal self-heal and only delays the disable or back-off
235
- > that your error path exists to trigger.
232
+ > For the recoverable directory-level codes -- `ENOENT`, `EACCES`, `EPERM`, `EROFS` -- **Log** has
233
+ > already retried internally by the time the rejection reaches you (see
234
+ > [self-healing retry](#self-healing-retry) below), so that retry ran and failed too. Every other
235
+ > code -- `EISDIR`, `ENOTDIR` and `ENOSPC` among them -- is never retried, because a retry cannot
236
+ > help it. Either way a consumer-side retry layer races the internal self-heal or repeats a call that
237
+ > already cannot succeed, and only delays the disable or back-off that your error path exists to
238
+ > trigger.
236
239
  >
237
- > Treat the **first** rejection as the signal to disable file logging or back off. Do not attempt the
238
- > write again.
240
+ > Treat the **first** rejection as the signal to disable file logging or back off, and do not attempt
241
+ > the write again straight away. If you do want file logging to come back without a restart, re-arm
242
+ > on a timer -- see [recovering after a latch](#recovering-after-a-latch).
239
243
 
240
- #### What the promise rejects with
244
+ #### What the Promise Rejects With
241
245
 
242
246
  The rejection value is the underlying Node `fs` error - a `NodeJS.ErrnoException` passed through
243
247
  unmodified, with `err.code`, `err.syscall`, `err.errno` and `err.path` all preserved.
@@ -277,29 +281,52 @@ async function logError(message) {
277
281
  }
278
282
  ```
279
283
 
280
- #### Self-healing retry
284
+ That latch is deliberately terminal for the lifetime of the process: nothing in it ever re-enables
285
+ file logging. Records are degraded rather than lost, because the console line is still written on
286
+ every call -- but a consumer using the file sink for audit or compliance should treat the first
287
+ rejection as an **alertable** event, or fail closed, rather than degrade silently.
288
+
289
+ #### Recovering After a Latch
290
+
291
+ If file logging must come back without a restart, re-arm the latch on a **timer**, never on the next
292
+ log call. Hold it closed for a fixed back-off window -- a minute or more, long enough that a
293
+ permissions fix or a freed disk has a chance to land -- then let exactly one write through. If that
294
+ write rejects, latch again and lengthen the window; if it succeeds, clear the latch. Anything faster
295
+ is a consumer-side retry under another name.
296
+
297
+ This also decides what your operators can see. `log-write-recovered` (see
298
+ [structured stderr notices](#structured-stderr-notices)) is emitted **only** on a subsequent
299
+ successful write, and a consumer that latches off permanently never issues one. Its entire stderr
300
+ output for the episode is a single `log-write-failed` followed by silence, which is indistinguishable
301
+ from a failure that self-healed -- so an alert-clear rule keyed on `log-write-recovered` will never
302
+ fire for it. Either treat `log-write-failed` as latching on the operator side too, or use the timed
303
+ re-arm above, which is what makes a recovery notice reachable at all.
304
+
305
+ #### Self-Healing Retry
281
306
 
282
307
  A cached log directory can outlive the directory it describes - for example, something deletes
283
308
  `logs/` while your process is running. To cover that, a failure whose `err.code` is `ENOENT`,
284
309
  `EACCES`, `EPERM` or `EROFS` drops the cached directory entry and retries the write **exactly once**.
285
- Any other code rejects on the first attempt, with no retry.
310
+ Any other code -- `EISDIR`, `ENOTDIR`, `ENOSPC`, `EMFILE` and the rest -- rejects on the first
311
+ attempt, with no retry.
286
312
 
287
- A successful retry is silent: the promise resolves normally and no notice is emitted. A rejection
288
- therefore always means the single retry has already been spent, which is why consumers must not add
289
- one of their own.
313
+ A retry that succeeds emits no *failure* notice: the promise resolves normally and the write counts
314
+ as a success, closing any failure episode already open for that directory with the usual
315
+ `log-write-recovered`. A rejection carrying one of those four codes has therefore already spent its
316
+ single retry; every other code was never retryable in the first place. The retry is per call and per
317
+ `Log` instance -- independent instances retry independently.
290
318
 
291
- #### Structured stderr notices
319
+ #### Structured stderr Notices
292
320
 
293
321
  Alongside the rejection, **Log** emits a machine-parseable notice to `stderr` so that operators have
294
322
  something to key on. The write-failure path must never re-enter the logger itself, so these notices
295
- bypass all formatting and configuration: they are written directly to `stderr` as one JSON object per
296
- line (JSONL), regardless of your color, prefix, suffix or path settings.
297
-
298
- These notices are **informational only**. They are not the failure signal - the promise rejection is.
299
- Never leave a write unhandled on the assumption that stderr covers it.
323
+ bypass all formatting and configuration: they are emitted via `console.error` as one JSON object per
324
+ line (JSONL), regardless of your color, prefix, suffix or path settings. Anything that wraps or
325
+ patches `console.error` sees them too. Notices are informational; the promise rejection is the
326
+ failure signal.
300
327
 
301
328
  | Field | Value |
302
- | :--- | :--- |
329
+ | :---: | :--- |
303
330
  | `ts` | ISO-8601 timestamp of the notice |
304
331
  | `surface` | Always `"stonyx-logs"` |
305
332
  | `sessionKey` | Always `null` |
@@ -312,7 +339,7 @@ Never leave a write unhandled on the assumption that stderr covers it.
312
339
  `log-write-failed` payload:
313
340
 
314
341
  | Field | Description |
315
- | :--- | :--- |
342
+ | :---: | :--- |
316
343
  | `targetLog` | Resolved path of the log file that could not be written |
317
344
  | `path` | Resolved log *directory*, with trailing separator - this is the dedupe key |
318
345
  | `syscall` | The failing syscall, e.g. `"mkdir"` or `"open"` |
@@ -322,10 +349,12 @@ Never leave a write unhandled on the assumption that stderr covers it.
322
349
 
323
350
  `log-write-recovered` carries `targetLog`, `path` and `suppressedCount` only.
324
351
 
325
- **Dedupe:** notices are one per failure episode, keyed on the resolved directory. The first failure
326
- for a directory emits `log-write-failed`; every subsequent failure for that same directory is silent.
327
- The next successful write to it emits a single `log-write-recovered` carrying `suppressedCount` - the
328
- number of notices that were suppressed, i.e. `N - 1` for an episode of `N` consecutive failures.
352
+ **Dedupe:** notices are one per failure episode, keyed on the resolved directory and scoped to the
353
+ `Log` instance -- the failure counters are instance state, so two instances writing to the same
354
+ directory emit one notice each. The first failure for a directory emits `log-write-failed`; every
355
+ subsequent failure for that same directory is silent. The next successful write to it emits a single
356
+ `log-write-recovered` carrying `suppressedCount` - the number of notices that were suppressed, i.e.
357
+ `N - 1` for an episode of `N` consecutive failures.
329
358
  Every failure still rejects its own promise; only the notices are deduped.
330
359
 
331
360
  Five consecutive failures followed by one success emit exactly these two records:
@@ -358,7 +387,7 @@ const log = new Log({
358
387
 
359
388
  | Option | Type | Default | Description |
360
389
  | :---: | :---: | :---: | :--- |
361
- | `logToFileByDefault` | **Boolean** | *false* | Option to change default setting for `logToFile` parameter of logging functions. When true, every log call becomes a rejectable file write -- see [handling write failures](#handling-write-failures). |
390
+ | `logToFileByDefault` | **Boolean** | *false* | Option to change default setting for `logToFile` parameter of logging functions. When true, every log call except `debug()` becomes a rejectable file write -- see [handling write failures](#handling-write-failures). |
362
391
  | `logTimestamp` | **Boolean** | *false* | Option to include timestamp in console logging. Timestamps are automatically included in file logs. |
363
392
  | `path` | **String** | *'logs/'* | Path in which to store log files. This setting is relative to your project's root directory. |
364
393
  | `prefix` | **String** | *''* | Prefix string to prepend all log messages for all log types with the exception of *debug*. |
package/dist/index.d.ts CHANGED
@@ -17,21 +17,128 @@ export default class Log {
17
17
  directoryCache: Map<string, Promise<void>>;
18
18
  writeFailures: Map<string, number>;
19
19
  [key: string]: unknown;
20
+ /**
21
+ * Logs `content` to the console in the `info` color, and to file when `logToFile` is true.
22
+ *
23
+ * **A failed file write rejects the returned promise, and that rejection is terminal - do not retry
24
+ * it.** `ENOENT`, `EACCES`, `EPERM` and `EROFS` have already been retried once internally by the
25
+ * time one reaches you; every other code (`EISDIR`, `ENOTDIR`, `ENOSPC`, `EMFILE`, ...) is never
26
+ * retried, because a retry cannot help it. Handle the first rejection by disabling file logging or
27
+ * backing off - never by writing again. The rejection value is the underlying
28
+ * `NodeJS.ErrnoException` with `err.code` preserved, and leaving it unhandled terminates the
29
+ * process.
30
+ *
31
+ * @see https://github.com/abofs/stonyx-logs#handling-write-failures
32
+ */
20
33
  info: (content: string, logToFile?: boolean, overwrite?: boolean) => Promise<void>;
34
+ /**
35
+ * Logs `content` to the console in the `warn` color, and to file when `logToFile` is true.
36
+ *
37
+ * **A failed file write rejects the returned promise, and that rejection is terminal - do not retry
38
+ * it.** `ENOENT`, `EACCES`, `EPERM` and `EROFS` have already been retried once internally by the
39
+ * time one reaches you; every other code (`EISDIR`, `ENOTDIR`, `ENOSPC`, `EMFILE`, ...) is never
40
+ * retried, because a retry cannot help it. Handle the first rejection by disabling file logging or
41
+ * backing off - never by writing again. The rejection value is the underlying
42
+ * `NodeJS.ErrnoException` with `err.code` preserved, and leaving it unhandled terminates the
43
+ * process.
44
+ *
45
+ * @see https://github.com/abofs/stonyx-logs#handling-write-failures
46
+ */
21
47
  warn: (content: string, logToFile?: boolean, overwrite?: boolean) => Promise<void>;
48
+ /**
49
+ * Logs `content` to the console in the `error` color, and to file when `logToFile` is true.
50
+ *
51
+ * **A failed file write rejects the returned promise, and that rejection is terminal - do not retry
52
+ * it.** `ENOENT`, `EACCES`, `EPERM` and `EROFS` have already been retried once internally by the
53
+ * time one reaches you; every other code (`EISDIR`, `ENOTDIR`, `ENOSPC`, `EMFILE`, ...) is never
54
+ * retried, because a retry cannot help it. Handle the first rejection by disabling file logging or
55
+ * backing off - never by writing again. The rejection value is the underlying
56
+ * `NodeJS.ErrnoException` with `err.code` preserved, and leaving it unhandled terminates the
57
+ * process.
58
+ *
59
+ * @see https://github.com/abofs/stonyx-logs#handling-write-failures
60
+ */
22
61
  error: (content: string, logToFile?: boolean, overwrite?: boolean) => Promise<void>;
23
62
  constructor(options?: Partial<LogOptions>);
24
63
  defineType(type: string, setting: ColorSetting, options?: Partial<LogOptions> | null): void;
25
64
  createConvenienceMethod(type: string): void;
65
+ /**
66
+ * Validates params and applies configuration-based defaults for logging. Every convenience method
67
+ * created by `defineType` - including `info`, `warn` and `error` - routes through here. Note that
68
+ * `debug` does not: it is a direct method that never consults `logToFileByDefault`.
69
+ *
70
+ * **A failed file write rejects the returned promise, and that rejection is terminal - do not retry
71
+ * it.** `ENOENT`, `EACCES`, `EPERM` and `EROFS` have already been retried once internally by the
72
+ * time one reaches you; every other code (`EISDIR`, `ENOTDIR`, `ENOSPC`, `EMFILE`, ...) is never
73
+ * retried, because a retry cannot help it. Handle the first rejection by disabling file logging or
74
+ * backing off - never by writing again. The rejection value is the underlying
75
+ * `NodeJS.ErrnoException` with `err.code` preserved, and leaving it unhandled terminates the
76
+ * process.
77
+ *
78
+ * @see https://github.com/abofs/stonyx-logs#handling-write-failures
79
+ */
26
80
  logAction(type: string, content: string, logToFile?: boolean, overwrite?: boolean): Promise<void>;
27
81
  getOptionForType(type: string, option: keyof LogOptions): LogOptions[keyof LogOptions];
28
82
  chalk(): ReturnType<Color['getChalkInstance']>;
83
+ /**
84
+ * Logs to console, and conditionally to file.
85
+ *
86
+ * When `logToFile` is true this awaits `writeToFile` and propagates its rejection unchanged, so
87
+ * the caller receives the underlying `NodeJS.ErrnoException`. Callers must handle it: see the
88
+ * `writeToFile` contract below.
89
+ */
29
90
  log(content: string, type: string, logToFile: boolean, overwrite: boolean): Promise<void>;
91
+ /**
92
+ * Direct hardcoded debug method (limited file logging). Unlike the `defineType` convenience
93
+ * methods this does not route through `logAction`, so it never consults `logToFileByDefault` and
94
+ * only writes to file when `logToFile` is passed explicitly.
95
+ *
96
+ * When `logToFile` is true this awaits `writeToFile` and propagates its rejection unchanged, on
97
+ * exactly the same contract as `log` - see `writeToFile` below.
98
+ */
30
99
  debug(content: unknown, logToFile?: boolean, overwrite?: boolean): Promise<void>;
100
+ /**
101
+ * Writes `content` to the resolved target for `type`, creating the target's directory on first
102
+ * use for that directory.
103
+ *
104
+ * The returned promise rejecting with the underlying `NodeJS.ErrnoException` (with `err.code`
105
+ * preserved) is the sole failure signal - callers that care must handle it. The structured
106
+ * stderr notice emitted alongside a failure is informational only and is deduped per episode.
107
+ *
108
+ * On `ENOENT`, `EACCES`, `EPERM` or `EROFS` the cached directory entry is dropped and the write
109
+ * is retried exactly once; every other code rejects on the first attempt without a retry.
110
+ *
111
+ * A rejection is terminal either way. For those four codes the self-heal has already run and
112
+ * failed by the time one escapes; the rest were never retryable, so repeating the call cannot
113
+ * change the outcome. Consumers must not layer their own retry on top - doing so either races the
114
+ * internal one or repeats a call that already cannot succeed, and delays the disable/back-off the
115
+ * rejection exists to trigger. The correct response to the first rejection is to stop writing.
116
+ * See README "Handling write failures".
117
+ */
31
118
  writeToFile(type: string, content: string, overwrite: boolean): Promise<void>;
119
+ /**
120
+ * One structured stderr notice per failure episode: an emit on the first failure, silence while
121
+ * it keeps failing, one recovery emit on the next success. A null payload records a success.
122
+ *
123
+ * Keyed on the directory rather than the resolved target, matching `directoryCache`: every code
124
+ * in the retry allowlist is a directory-level condition, so keying on the target would strand an
125
+ * un-reaped entry - and silently drop its suppressed count - whenever a `{date}` template rotates
126
+ * away from a still-failing filename.
127
+ */
32
128
  noticeWriteResult(path: string, targetLog: string, payload: Record<string, unknown> | null): void;
129
+ /**
130
+ * The write-failure path must never re-enter this logger, so notices go straight out through
131
+ * `console.error` as a single JSONL record, bypassing all of this package's formatting. Field
132
+ * order is the canonical one mandated by the framework logging schema, with the optional
133
+ * `schemaVersion` last.
134
+ */
33
135
  emitNotice(severity: Severity, event: string, payload: Record<string, unknown>): void;
34
136
  resolveFilename(template: string, type: string): string;
137
+ /**
138
+ * Ensures the target's directory exists. Both write paths auto-create the file itself, so no
139
+ * bootstrap write is needed. `targetLog` is unused but retained for signature compatibility:
140
+ * resolveFilename strips separators, so the only cached invariant is the directory.
141
+ */
35
142
  validateFileAndDirectory(path: string, targetLog: string): Promise<void>;
36
143
  sanitizePath(path: string): string;
37
144
  }
package/dist/index.js CHANGED
@@ -72,7 +72,21 @@ export default class Log {
72
72
  createConvenienceMethod(type) {
73
73
  this[type] = (content, logToFile, overwrite = false) => this.logAction(type, content, logToFile, overwrite);
74
74
  }
75
- // validates params and sets configuration-based defaults for logging
75
+ /**
76
+ * Validates params and applies configuration-based defaults for logging. Every convenience method
77
+ * created by `defineType` - including `info`, `warn` and `error` - routes through here. Note that
78
+ * `debug` does not: it is a direct method that never consults `logToFileByDefault`.
79
+ *
80
+ * **A failed file write rejects the returned promise, and that rejection is terminal - do not retry
81
+ * it.** `ENOENT`, `EACCES`, `EPERM` and `EROFS` have already been retried once internally by the
82
+ * time one reaches you; every other code (`EISDIR`, `ENOTDIR`, `ENOSPC`, `EMFILE`, ...) is never
83
+ * retried, because a retry cannot help it. Handle the first rejection by disabling file logging or
84
+ * backing off - never by writing again. The rejection value is the underlying
85
+ * `NodeJS.ErrnoException` with `err.code` preserved, and leaving it unhandled terminates the
86
+ * process.
87
+ *
88
+ * @see https://github.com/abofs/stonyx-logs#handling-write-failures
89
+ */
76
90
  logAction(type, content, logToFile, overwrite) {
77
91
  // set logToFile default based on class options when not set
78
92
  if (logToFile === undefined)
@@ -93,7 +107,7 @@ export default class Log {
93
107
  chalk() {
94
108
  return this.color.getChalkInstance();
95
109
  }
96
- /*
110
+ /**
97
111
  * Logs to console, and conditionally to file.
98
112
  *
99
113
  * When `logToFile` is true this awaits `writeToFile` and propagates its rejection unchanged, so
@@ -118,8 +132,10 @@ export default class Log {
118
132
  return;
119
133
  await this.writeToFile(type, `${timestamp} ${content}\n`, overwrite);
120
134
  }
121
- /*
122
- * Direct hardcoded debug method (limited file logging).
135
+ /**
136
+ * Direct hardcoded debug method (limited file logging). Unlike the `defineType` convenience
137
+ * methods this does not route through `logAction`, so it never consults `logToFileByDefault` and
138
+ * only writes to file when `logToFile` is passed explicitly.
123
139
  *
124
140
  * When `logToFile` is true this awaits `writeToFile` and propagates its rejection unchanged, on
125
141
  * exactly the same contract as `log` - see `writeToFile` below.
@@ -130,7 +146,7 @@ export default class Log {
130
146
  return;
131
147
  await this.writeToFile('debug', JSON.stringify(content, null, 2), overwrite);
132
148
  }
133
- /*
149
+ /**
134
150
  * Writes `content` to the resolved target for `type`, creating the target's directory on first
135
151
  * use for that directory.
136
152
  *
@@ -141,11 +157,12 @@ export default class Log {
141
157
  * On `ENOENT`, `EACCES`, `EPERM` or `EROFS` the cached directory entry is dropped and the write
142
158
  * is retried exactly once; every other code rejects on the first attempt without a retry.
143
159
  *
144
- * That retry is why a rejection is terminal rather than transient: by the time one escapes, the
145
- * self-heal has already run and failed. Consumers must not layer their own retry on top - doing
146
- * so races the internal one and delays the disable/back-off the rejection exists to trigger. The
147
- * correct response to the first rejection is to stop writing. See README "Handling write
148
- * failures".
160
+ * A rejection is terminal either way. For those four codes the self-heal has already run and
161
+ * failed by the time one escapes; the rest were never retryable, so repeating the call cannot
162
+ * change the outcome. Consumers must not layer their own retry on top - doing so either races the
163
+ * internal one or repeats a call that already cannot succeed, and delays the disable/back-off the
164
+ * rejection exists to trigger. The correct response to the first rejection is to stop writing.
165
+ * See README "Handling write failures".
149
166
  */
150
167
  async writeToFile(type, content, overwrite) {
151
168
  const path = this.getOptionForType(type, 'path');
@@ -178,7 +195,7 @@ export default class Log {
178
195
  }
179
196
  this.noticeWriteResult(path, targetLog, null);
180
197
  }
181
- /*
198
+ /**
182
199
  * One structured stderr notice per failure episode: an emit on the first failure, silence while
183
200
  * it keeps failing, one recovery emit on the next success. A null payload records a success.
184
201
  *
@@ -207,10 +224,11 @@ export default class Log {
207
224
  });
208
225
  }
209
226
  }
210
- /*
211
- * The write-failure path must never re-enter this logger, so notices go straight to stderr as a
212
- * single JSONL record. Field order is the canonical one mandated by the framework logging
213
- * schema, with the optional `schemaVersion` last.
227
+ /**
228
+ * The write-failure path must never re-enter this logger, so notices go straight out through
229
+ * `console.error` as a single JSONL record, bypassing all of this package's formatting. Field
230
+ * order is the canonical one mandated by the framework logging schema, with the optional
231
+ * `schemaVersion` last.
214
232
  */
215
233
  emitNotice(severity, event, payload) {
216
234
  const ts = new Date().toISOString();
@@ -246,7 +264,7 @@ export default class Log {
246
264
  // sanitize: prevent path traversal and disallow directory separators
247
265
  return resolved.replace(/\.\./g, '').replace(/[/\\]/g, '');
248
266
  }
249
- /*
267
+ /**
250
268
  * Ensures the target's directory exists. Both write paths auto-create the file itself, so no
251
269
  * bootstrap write is needed. `targetLog` is unused but retained for signature compatibility:
252
270
  * resolveFilename strips separators, so the only cached invariant is the directory.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stonyx/logs",
3
- "version": "1.0.1-alpha.18",
3
+ "version": "1.0.1-alpha.19",
4
4
  "description": "Simplified logging for node applications",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",