effect 3.5.0 → 3.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/Effect.ts CHANGED
@@ -4798,9 +4798,25 @@ export const matchEffect: {
4798
4798
  // -------------------------------------------------------------------------------------
4799
4799
 
4800
4800
  /**
4801
- * Logs the specified message or cause at the current log level.
4801
+ * Logs one or more messages or error causes at the current log level, which is INFO by default.
4802
+ * This function allows logging multiple items at once and can include detailed error information using `Cause` instances.
4802
4803
  *
4803
- * You can set the current log level using `FiberRef.currentLogLevel`.
4804
+ * To adjust the log level, use the `Logger.withMinimumLogLevel` function.
4805
+ *
4806
+ * @example
4807
+ * import { Cause, Effect } from "effect"
4808
+ *
4809
+ * const program = Effect.log(
4810
+ * "message1",
4811
+ * "message2",
4812
+ * Cause.die("Oh no!"),
4813
+ * Cause.die("Oh uh!")
4814
+ * )
4815
+ *
4816
+ * // Effect.runFork(program)
4817
+ * // Output:
4818
+ * // timestamp=... level=INFO fiber=#0 message=message1 message=message2 cause="Error: Oh no!
4819
+ * // Error: Oh uh!"
4804
4820
  *
4805
4821
  * @since 2.0.0
4806
4822
  * @category logging
@@ -4827,7 +4843,19 @@ export const logWithLevel = (
4827
4843
  export const logTrace: (...message: ReadonlyArray<any>) => Effect<void, never, never> = effect.logTrace
4828
4844
 
4829
4845
  /**
4830
- * Logs the specified message or cause at the Debug log level.
4846
+ * Logs the specified messages at the DEBUG log level.
4847
+ * DEBUG messages are not shown by default.
4848
+ *
4849
+ * To view DEBUG messages, adjust the logging settings using
4850
+ * `Logger.withMinimumLogLevel` and set the log level to `LogLevel.Debug`.
4851
+ *
4852
+ * @example
4853
+ * import { Effect, Logger, LogLevel } from "effect"
4854
+ *
4855
+ * const program = Effect.logDebug("message1").pipe(Logger.withMinimumLogLevel(LogLevel.Debug))
4856
+ *
4857
+ * // Effect.runFork(program)
4858
+ * // timestamp=... level=DEBUG fiber=#0 message=message1
4831
4859
  *
4832
4860
  * @since 2.0.0
4833
4861
  * @category logging
@@ -4867,7 +4895,20 @@ export const logError: (...message: ReadonlyArray<any>) => Effect<void, never, n
4867
4895
  export const logFatal: (...message: ReadonlyArray<any>) => Effect<void, never, never> = effect.logFatal
4868
4896
 
4869
4897
  /**
4870
- * Adjusts the label for the current logging span.
4898
+ * Adds a log span to your effects, which tracks and logs the duration of
4899
+ * operations or tasks. This is useful for performance monitoring and debugging
4900
+ * time-sensitive processes.
4901
+ *
4902
+ * @example
4903
+ * import { Effect } from "effect"
4904
+ *
4905
+ * const program = Effect.gen(function*() {
4906
+ * yield* Effect.sleep("1 second")
4907
+ * yield* Effect.log("The job is finished!")
4908
+ * }).pipe(Effect.withLogSpan("myspan"))
4909
+ *
4910
+ * // Effect.runFork(program)
4911
+ * // timestamp=... level=INFO fiber=#0 message="The job is finished!" myspan=1011ms
4871
4912
  *
4872
4913
  * @since 2.0.0
4873
4914
  * @category logging
@@ -4878,7 +4919,21 @@ export const withLogSpan: {
4878
4919
  } = effect.withLogSpan
4879
4920
 
4880
4921
  /**
4881
- * Annotates each log in this effect with the specified log annotation.
4922
+ * Augments log outputs by appending custom annotations to log entries generated
4923
+ * within an effect. This function provides a way to add more context and detail
4924
+ * to log messages, making them more informative and easier to trace.
4925
+ *
4926
+ * @example
4927
+ * import { Effect } from "effect"
4928
+ *
4929
+ * const program = Effect.gen(function*() {
4930
+ * yield* Effect.log("message1")
4931
+ * yield* Effect.log("message2")
4932
+ * }).pipe(Effect.annotateLogs("key", "value")) // Annotation as key/value pair
4933
+ *
4934
+ * // Effect.runFork(program)
4935
+ * // timestamp=... level=INFO fiber=#0 message=message1 key=value
4936
+ * // timestamp=... level=INFO fiber=#0 message=message2 key=value
4882
4937
  *
4883
4938
  * @since 2.0.0
4884
4939
  * @category logging
@@ -4891,21 +4946,29 @@ export const annotateLogs: {
4891
4946
  } = effect.annotateLogs
4892
4947
 
4893
4948
  /**
4894
- * Annotates each log with the specified log annotation(s), until the Scope is closed.
4949
+ * Applies log annotations with a limited scope, restricting their appearance to
4950
+ * specific sections of your effect computations. Use
4951
+ * `Effect.annotateLogsScoped` to add metadata to logs that only appear within a
4952
+ * defined `Scope`, making it easier to manage context-specific logging.
4895
4953
  *
4896
- * @since 3.1.0
4897
- * @category logging
4898
4954
  * @example
4899
4955
  * import { Effect } from "effect"
4900
4956
  *
4901
- * Effect.gen(function*() {
4957
+ * const program = Effect.gen(function*() {
4902
4958
  * yield* Effect.log("no annotations")
4903
- * yield* Effect.annotateLogsScoped({ foo: "bar" })
4904
- * yield* Effect.log("annotated with foo=bar")
4905
- * }).pipe(
4906
- * Effect.scoped,
4907
- * Effect.andThen(Effect.log("no annotations again"))
4908
- * )
4959
+ * yield* Effect.annotateLogsScoped({ key: "value" })
4960
+ * yield* Effect.log("message1") // Annotation is applied to this log
4961
+ * yield* Effect.log("message2") // Annotation is applied to this log
4962
+ * }).pipe(Effect.scoped, Effect.andThen(Effect.log("no annotations again")))
4963
+ *
4964
+ * // Effect.runFork(program)
4965
+ * // timestamp=... level=INFO fiber=#0 message="no annotations"
4966
+ * // timestamp=... level=INFO fiber=#0 message=message1 key=value
4967
+ * // timestamp=... level=INFO fiber=#0 message=message2 key=value
4968
+ * // timestamp=... level=INFO fiber=#0 message="no annotations again"
4969
+ *
4970
+ * @since 3.1.0
4971
+ * @category logging
4909
4972
  */
4910
4973
  export const annotateLogsScoped: {
4911
4974
  (key: string, value: unknown): Effect<void, never, Scope.Scope>
package/src/Logger.ts CHANGED
@@ -73,6 +73,35 @@ export declare namespace Logger {
73
73
  }
74
74
 
75
75
  /**
76
+ * Creates a custom logger that formats log messages according to the provided
77
+ * function.
78
+ *
79
+ * @example
80
+ * import { Effect, Logger, LogLevel } from "effect"
81
+ *
82
+ * const logger = Logger.make(({ logLevel, message }) => {
83
+ * globalThis.console.log(`[${logLevel.label}] ${message}`)
84
+ * })
85
+ *
86
+ * const task1 = Effect.logDebug("task1 done")
87
+ * const task2 = Effect.logDebug("task2 done")
88
+ *
89
+ * const program = Effect.gen(function*() {
90
+ * yield* Effect.log("start")
91
+ * yield* task1
92
+ * yield* task2
93
+ * yield* Effect.log("done")
94
+ * }).pipe(
95
+ * Logger.withMinimumLogLevel(LogLevel.Debug),
96
+ * Effect.provide(Logger.replace(Logger.defaultLogger, logger))
97
+ * )
98
+ *
99
+ * // Effect.runFork(program)
100
+ * // [INFO] start
101
+ * // [DEBUG] task1 done
102
+ * // [DEBUG] task2 done
103
+ * // [INFO] done
104
+ *
76
105
  * @category constructors
77
106
  * @since 2.0.0
78
107
  */
@@ -160,25 +189,36 @@ export const map: {
160
189
  } = internal.map
161
190
 
162
191
  /**
163
- * @since 2.0.0
164
- * @category mapping
192
+ * Creates a batched logger that groups log messages together and processes them
193
+ * in intervals.
194
+ *
195
+ * @param window - The time window in which to batch log messages.
196
+ *
165
197
  * @example
166
- * import { Console, Effect, Logger } from "effect";
198
+ * import { Console, Effect, Logger } from "effect"
167
199
  *
168
200
  * const LoggerLive = Logger.replaceScoped(
169
201
  * Logger.defaultLogger,
170
202
  * Logger.logfmtLogger.pipe(
171
- * Logger.batched("500 millis", (messages) =>
172
- * Console.log("BATCH", messages.join("\n"))
173
- * )
203
+ * Logger.batched("500 millis", (messages) => Console.log("BATCH", `[\n${messages.join("\n")}\n]`))
174
204
  * )
175
- * );
205
+ * )
206
+ *
207
+ * const program = Effect.gen(function*() {
208
+ * yield* Effect.log("one")
209
+ * yield* Effect.log("two")
210
+ * yield* Effect.log("three")
211
+ * }).pipe(Effect.provide(LoggerLive))
212
+ *
213
+ * // Effect.runFork(program)
214
+ * // BATCH [
215
+ * // timestamp=... level=INFO fiber=#0 message=one
216
+ * // timestamp=... level=INFO fiber=#0 message=two
217
+ * // timestamp=... level=INFO fiber=#0 message=three
218
+ * // ]
176
219
  *
177
- * Effect.gen(function* (_) {
178
- * yield* _(Effect.log("one"));
179
- * yield* _(Effect.log("two"));
180
- * yield* _(Effect.log("three"));
181
- * }).pipe(Effect.provide(LoggerLive), Effect.runFork);
220
+ * @since 2.0.0
221
+ * @category mapping
182
222
  */
183
223
  export const batched: {
184
224
  <Output, R>(
@@ -278,6 +318,17 @@ export const test: {
278
318
  } = internalCircular.test
279
319
 
280
320
  /**
321
+ * Sets the minimum log level for subsequent logging operations, allowing
322
+ * control over which log messages are displayed based on their severity.
323
+ *
324
+ * @example
325
+ * import { Effect, Logger, LogLevel } from "effect"
326
+ *
327
+ * const program = Effect.logDebug("message1").pipe(Logger.withMinimumLogLevel(LogLevel.Debug))
328
+ *
329
+ * // Effect.runFork(program)
330
+ * // timestamp=... level=DEBUG fiber=#0 message=message1
331
+ *
281
332
  * @since 2.0.0
282
333
  * @category context
283
334
  */
@@ -345,12 +396,40 @@ export const zipRight: {
345
396
  export const defaultLogger: Logger<unknown, void> = fiberRuntime.defaultLogger
346
397
 
347
398
  /**
399
+ * The `jsonLogger` logger formats log entries as JSON objects, making them easy to
400
+ * integrate with logging systems that consume JSON data.
401
+ *
402
+ * @example
403
+ * import { Effect, Logger } from "effect"
404
+ *
405
+ * const program = Effect.log("message1", "message2").pipe(
406
+ * Effect.annotateLogs({ key1: "value1", key2: "value2" }),
407
+ * Effect.withLogSpan("myspan")
408
+ * )
409
+ *
410
+ * // Effect.runFork(program.pipe(Effect.provide(Logger.json)))
411
+ * // {"message":["message1","message2"],"logLevel":"INFO","timestamp":"...","annotations":{"key2":"value2","key1":"value1"},"spans":{"myspan":0},"fiberId":"#0"}
412
+ *
348
413
  * @since 2.0.0
349
414
  * @category constructors
350
415
  */
351
416
  export const jsonLogger: Logger<unknown, string> = internal.jsonLogger
352
417
 
353
418
  /**
419
+ * This logger outputs logs in a human-readable format that is easy to read
420
+ * during development or in a production console.
421
+ *
422
+ * @example
423
+ * import { Effect, Logger } from "effect"
424
+ *
425
+ * const program = Effect.log("message1", "message2").pipe(
426
+ * Effect.annotateLogs({ key1: "value1", key2: "value2" }),
427
+ * Effect.withLogSpan("myspan")
428
+ * )
429
+ *
430
+ * // Effect.runFork(program.pipe(Effect.provide(Logger.logFmt)))
431
+ * // timestamp=... level=INFO fiber=#0 message=message1 message=message2 myspan=0ms key2=value2 key1=value1
432
+ *
354
433
  * @since 2.0.0
355
434
  * @category constructors
356
435
  */
@@ -363,6 +442,27 @@ export const logfmtLogger: Logger<unknown, string> = internal.logfmtLogger
363
442
  export const stringLogger: Logger<unknown, string> = internal.stringLogger
364
443
 
365
444
  /**
445
+ * The pretty logger utilizes the capabilities of the console API to generate
446
+ * visually engaging and color-enhanced log outputs. This feature is
447
+ * particularly useful for improving the readability of log messages during
448
+ * development and debugging processes.
449
+ *
450
+ * @example
451
+ * import { Effect, Logger } from "effect"
452
+ *
453
+ * const program = Effect.log("message1", "message2").pipe(
454
+ * Effect.annotateLogs({ key1: "value1", key2: "value2" }),
455
+ * Effect.withLogSpan("myspan")
456
+ * )
457
+ *
458
+ * // Effect.runFork(program.pipe(Effect.provide(Logger.pretty)))
459
+ * // green --v v-- bold and cyan
460
+ * // [07:51:54.434] INFO (#0) myspan=1ms: message1
461
+ * // message2
462
+ * // v-- bold
463
+ * // key2: value2
464
+ * // key1: value1
465
+ *
366
466
  * @since 3.5.0
367
467
  * @category constructors
368
468
  */
@@ -376,6 +476,29 @@ export const prettyLogger: (
376
476
  ) => Logger<unknown, void> = internal.prettyLogger
377
477
 
378
478
  /**
479
+ * The structured logger provides detailed log outputs, structured in a way that
480
+ * retains comprehensive traceability of the events, suitable for deeper
481
+ * analysis and troubleshooting.
482
+ *
483
+ * @example
484
+ * import { Effect, Logger } from "effect"
485
+ *
486
+ * const program = Effect.log("message1", "message2").pipe(
487
+ * Effect.annotateLogs({ key1: "value1", key2: "value2" }),
488
+ * Effect.withLogSpan("myspan")
489
+ * )
490
+ *
491
+ * // Effect.runFork(program.pipe(Effect.provide(Logger.structured)))
492
+ * // {
493
+ * // message: [ 'message1', 'message2' ],
494
+ * // logLevel: 'INFO',
495
+ * // timestamp: '2024-07-09T14:05:41.623Z',
496
+ * // cause: undefined,
497
+ * // annotations: { key2: 'value2', key1: 'value1' },
498
+ * // spans: { myspan: 0 },
499
+ * // fiberId: '#0'
500
+ * // }
501
+ *
379
502
  * @since 2.0.0
380
503
  * @category constructors
381
504
  */
@@ -399,30 +522,118 @@ export const structuredLogger: Logger<
399
522
  export const tracerLogger: Logger<unknown, void> = fiberRuntime.tracerLogger
400
523
 
401
524
  /**
525
+ * The `json` logger formats log entries as JSON objects, making them easy to
526
+ * integrate with logging systems that consume JSON data.
527
+ *
528
+ * @example
529
+ * import { Effect, Logger } from "effect"
530
+ *
531
+ * const program = Effect.log("message1", "message2").pipe(
532
+ * Effect.annotateLogs({ key1: "value1", key2: "value2" }),
533
+ * Effect.withLogSpan("myspan")
534
+ * )
535
+ *
536
+ * // Effect.runFork(program.pipe(Effect.provide(Logger.json)))
537
+ * // {"message":["message1","message2"],"logLevel":"INFO","timestamp":"...","annotations":{"key2":"value2","key1":"value1"},"spans":{"myspan":0},"fiberId":"#0"}
538
+ *
402
539
  * @since 2.0.0
403
540
  * @category constructors
404
541
  */
405
542
  export const json: Layer.Layer<never> = replace(fiberRuntime.defaultLogger, fiberRuntime.jsonLogger)
406
543
 
407
544
  /**
545
+ * This logger outputs logs in a human-readable format that is easy to read
546
+ * during development or in a production console.
547
+ *
548
+ * @example
549
+ * import { Effect, Logger } from "effect"
550
+ *
551
+ * const program = Effect.log("message1", "message2").pipe(
552
+ * Effect.annotateLogs({ key1: "value1", key2: "value2" }),
553
+ * Effect.withLogSpan("myspan")
554
+ * )
555
+ *
556
+ * // Effect.runFork(program.pipe(Effect.provide(Logger.logFmt)))
557
+ * // timestamp=... level=INFO fiber=#0 message=message1 message=message2 myspan=0ms key2=value2 key1=value1
558
+ *
408
559
  * @since 2.0.0
409
560
  * @category constructors
410
561
  */
411
562
  export const logFmt: Layer.Layer<never> = replace(fiberRuntime.defaultLogger, fiberRuntime.logFmtLogger)
412
563
 
413
564
  /**
565
+ * The pretty logger utilizes the capabilities of the console API to generate
566
+ * visually engaging and color-enhanced log outputs. This feature is
567
+ * particularly useful for improving the readability of log messages during
568
+ * development and debugging processes.
569
+ *
570
+ * @example
571
+ * import { Effect, Logger } from "effect"
572
+ *
573
+ * const program = Effect.log("message1", "message2").pipe(
574
+ * Effect.annotateLogs({ key1: "value1", key2: "value2" }),
575
+ * Effect.withLogSpan("myspan")
576
+ * )
577
+ *
578
+ * // Effect.runFork(program.pipe(Effect.provide(Logger.pretty)))
579
+ * // green --v v-- bold and cyan
580
+ * // [07:51:54.434] INFO (#0) myspan=1ms: message1
581
+ * // message2
582
+ * // v-- bold
583
+ * // key2: value2
584
+ * // key1: value1
585
+ *
414
586
  * @since 3.5.0
415
587
  * @category constructors
416
588
  */
417
589
  export const pretty: Layer.Layer<never> = replace(fiberRuntime.defaultLogger, fiberRuntime.prettyLogger)
418
590
 
419
591
  /**
592
+ * The structured logger provides detailed log outputs, structured in a way that
593
+ * retains comprehensive traceability of the events, suitable for deeper
594
+ * analysis and troubleshooting.
595
+ *
596
+ * @example
597
+ * import { Effect, Logger } from "effect"
598
+ *
599
+ * const program = Effect.log("message1", "message2").pipe(
600
+ * Effect.annotateLogs({ key1: "value1", key2: "value2" }),
601
+ * Effect.withLogSpan("myspan")
602
+ * )
603
+ *
604
+ * // Effect.runFork(program.pipe(Effect.provide(Logger.structured)))
605
+ * // {
606
+ * // message: [ 'message1', 'message2' ],
607
+ * // logLevel: 'INFO',
608
+ * // timestamp: '2024-07-09T14:05:41.623Z',
609
+ * // cause: undefined,
610
+ * // annotations: { key2: 'value2', key1: 'value1' },
611
+ * // spans: { myspan: 0 },
612
+ * // fiberId: '#0'
613
+ * // }
614
+ *
420
615
  * @since 2.0.0
421
616
  * @category constructors
422
617
  */
423
618
  export const structured: Layer.Layer<never> = replace(fiberRuntime.defaultLogger, fiberRuntime.structuredLogger)
424
619
 
425
620
  /**
621
+ * Sets the minimum log level for logging operations, allowing control over
622
+ * which log messages are displayed based on their severity.
623
+ *
624
+ * @example
625
+ * import { Effect, Logger, LogLevel } from "effect"
626
+ *
627
+ * const program = Effect.gen(function*() {
628
+ * yield* Effect.log("Executing task...")
629
+ * yield* Effect.sleep("100 millis")
630
+ * console.log("task done")
631
+ * })
632
+ *
633
+ * // Logging disabled using a layer
634
+ * // Effect.runFork(program.pipe(Effect.provide(Logger.minimumLogLevel(LogLevel.None))))
635
+ * // task done
636
+ *
426
637
  * @since 2.0.0
427
638
  * @category context
428
639
  */
package/src/Random.ts CHANGED
@@ -122,6 +122,21 @@ export const Random: Context.Tag<Random, Random> = internal.randomTag
122
122
  /**
123
123
  * Constructs the `Random` service, seeding the pseudo-random number generator
124
124
  * with an hash of the specified seed.
125
+ * This constructor is useful for generating predictable sequences of random values for specific use cases.
126
+ *
127
+ * Example uses:
128
+ * - Generating random UI data for visual tests.
129
+ * - Creating data that needs to change daily but remain the same throughout a single day, such as using a date as the seed.
130
+ *
131
+ * @param seed - The seed value used to initialize the generator.
132
+ *
133
+ * @example
134
+ * import { Effect, Random } from "effect"
135
+ *
136
+ * const random1 = Random.make("myseed")
137
+ * const random2 = Random.make("myseed")
138
+ *
139
+ * assert.equal(Effect.runSync(random1.next), Effect.runSync(random2.next))
125
140
  *
126
141
  * @since 3.5.0
127
142
  * @category constructors
@@ -369,9 +369,6 @@ export const isLogger = (u: unknown): u is Logger.Logger<unknown, unknown> => {
369
369
  return typeof u === "object" && u != null && LoggerTypeId in u
370
370
  }
371
371
 
372
- const processStdoutIsTTY = typeof process === "object" && "stdout" in process && process.stdout.isTTY === true
373
- const hasWindow = typeof window === "object"
374
-
375
372
  const withColor = (text: string, ...colors: ReadonlyArray<string>) => {
376
373
  let out = ""
377
374
  for (let i = 0; i < colors.length; i++) {
@@ -409,6 +406,10 @@ const defaultDateFormat = (date: Date): string =>
409
406
  date.getSeconds().toString().padStart(2, "0")
410
407
  }.${date.getMilliseconds().toString().padStart(3, "0")}`
411
408
 
409
+ const processStdoutIsTTY = typeof process === "object" && "stdout" in process && process.stdout.isTTY === true
410
+ const processIsBun = typeof process === "object" && "isBun" in process && process.isBun === true
411
+ const hasWindow = typeof window === "object"
412
+
412
413
  /** @internal */
413
414
  export const prettyLogger = (options?: {
414
415
  readonly colors?: "auto" | boolean | undefined
@@ -457,7 +458,7 @@ export const prettyLogger = (options?: {
457
458
  console.groupCollapsed(firstLine)
458
459
  } else {
459
460
  log(firstLine)
460
- console.group()
461
+ if (!processIsBun) console.group()
461
462
  }
462
463
  if (!Cause.isEmpty(cause)) {
463
464
  if (isBrowser) {
@@ -478,7 +479,8 @@ export const prettyLogger = (options?: {
478
479
  log(color(`${key}:`, colors.bold, colors.white), value)
479
480
  }
480
481
  }
481
- console.groupEnd()
482
+
483
+ if (!processIsBun) console.groupEnd()
482
484
  }
483
485
  )
484
486
  }
@@ -1,4 +1,4 @@
1
- let moduleVersion = "3.5.0"
1
+ let moduleVersion = "3.5.1"
2
2
 
3
3
  export const getCurrentVersion = () => moduleVersion
4
4