effect 4.0.0-beta.10 → 4.0.0-beta.11

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 (122) hide show
  1. package/dist/Channel.d.ts +7 -7
  2. package/dist/Effect.d.ts +217 -8
  3. package/dist/Effect.d.ts.map +1 -1
  4. package/dist/Effect.js +46 -0
  5. package/dist/Effect.js.map +1 -1
  6. package/dist/ErrorReporter.d.ts +376 -0
  7. package/dist/ErrorReporter.d.ts.map +1 -0
  8. package/dist/ErrorReporter.js +246 -0
  9. package/dist/ErrorReporter.js.map +1 -0
  10. package/dist/LogLevel.d.ts +5 -0
  11. package/dist/LogLevel.d.ts.map +1 -1
  12. package/dist/LogLevel.js.map +1 -1
  13. package/dist/Logger.d.ts +25 -91
  14. package/dist/Logger.d.ts.map +1 -1
  15. package/dist/Logger.js +2 -3
  16. package/dist/Logger.js.map +1 -1
  17. package/dist/Queue.d.ts.map +1 -1
  18. package/dist/Queue.js +0 -1
  19. package/dist/Queue.js.map +1 -1
  20. package/dist/References.d.ts +3 -3
  21. package/dist/References.d.ts.map +1 -1
  22. package/dist/SchemaAST.d.ts.map +1 -1
  23. package/dist/SchemaAST.js +2 -1
  24. package/dist/SchemaAST.js.map +1 -1
  25. package/dist/Stream.d.ts +5 -5
  26. package/dist/index.d.ts +4 -0
  27. package/dist/index.d.ts.map +1 -1
  28. package/dist/index.js +4 -0
  29. package/dist/index.js.map +1 -1
  30. package/dist/internal/effect.js +54 -4
  31. package/dist/internal/effect.js.map +1 -1
  32. package/dist/internal/hashMap.js +2 -2
  33. package/dist/internal/hashMap.js.map +1 -1
  34. package/dist/unstable/ai/LanguageModel.d.ts.map +1 -1
  35. package/dist/unstable/ai/LanguageModel.js +41 -0
  36. package/dist/unstable/ai/LanguageModel.js.map +1 -1
  37. package/dist/unstable/cli/CliOutput.js +37 -6
  38. package/dist/unstable/cli/CliOutput.js.map +1 -1
  39. package/dist/unstable/cli/Command.d.ts +199 -7
  40. package/dist/unstable/cli/Command.d.ts.map +1 -1
  41. package/dist/unstable/cli/Command.js +116 -6
  42. package/dist/unstable/cli/Command.js.map +1 -1
  43. package/dist/unstable/cli/HelpDoc.d.ts +60 -2
  44. package/dist/unstable/cli/HelpDoc.d.ts.map +1 -1
  45. package/dist/unstable/cli/internal/command.d.ts +11 -1
  46. package/dist/unstable/cli/internal/command.d.ts.map +1 -1
  47. package/dist/unstable/cli/internal/command.js +33 -8
  48. package/dist/unstable/cli/internal/command.js.map +1 -1
  49. package/dist/unstable/cli/internal/completions/CommandDescriptor.js +7 -2
  50. package/dist/unstable/cli/internal/completions/CommandDescriptor.js.map +1 -1
  51. package/dist/unstable/cli/internal/parser.js +10 -2
  52. package/dist/unstable/cli/internal/parser.js.map +1 -1
  53. package/dist/unstable/cluster/ClusterWorkflowEngine.d.ts.map +1 -1
  54. package/dist/unstable/http/Headers.d.ts.map +1 -1
  55. package/dist/unstable/http/Headers.js +27 -10
  56. package/dist/unstable/http/Headers.js.map +1 -1
  57. package/dist/unstable/http/HttpEffect.d.ts.map +1 -1
  58. package/dist/unstable/http/HttpEffect.js +12 -7
  59. package/dist/unstable/http/HttpEffect.js.map +1 -1
  60. package/dist/unstable/http/HttpServerError.d.ts +8 -26
  61. package/dist/unstable/http/HttpServerError.d.ts.map +1 -1
  62. package/dist/unstable/http/HttpServerError.js +11 -27
  63. package/dist/unstable/http/HttpServerError.js.map +1 -1
  64. package/dist/unstable/http/HttpServerRespondable.d.ts +2 -2
  65. package/dist/unstable/http/HttpServerRespondable.d.ts.map +1 -1
  66. package/dist/unstable/http/HttpServerRespondable.js +5 -5
  67. package/dist/unstable/http/HttpServerRespondable.js.map +1 -1
  68. package/dist/unstable/http/HttpServerResponse.d.ts +2 -1
  69. package/dist/unstable/http/HttpServerResponse.d.ts.map +1 -1
  70. package/dist/unstable/http/HttpServerResponse.js +2 -0
  71. package/dist/unstable/http/HttpServerResponse.js.map +1 -1
  72. package/dist/unstable/httpapi/HttpApiBuilder.js +1 -1
  73. package/dist/unstable/httpapi/HttpApiBuilder.js.map +1 -1
  74. package/dist/unstable/httpapi/HttpApiError.d.ts +11 -0
  75. package/dist/unstable/httpapi/HttpApiError.d.ts.map +1 -1
  76. package/dist/unstable/httpapi/HttpApiError.js +29 -9
  77. package/dist/unstable/httpapi/HttpApiError.js.map +1 -1
  78. package/dist/unstable/observability/OtlpLogger.d.ts.map +1 -1
  79. package/dist/unstable/observability/OtlpLogger.js +7 -4
  80. package/dist/unstable/observability/OtlpLogger.js.map +1 -1
  81. package/dist/unstable/reactivity/AtomRegistry.d.ts +6 -0
  82. package/dist/unstable/reactivity/AtomRegistry.d.ts.map +1 -1
  83. package/dist/unstable/reactivity/AtomRegistry.js +22 -1
  84. package/dist/unstable/reactivity/AtomRegistry.js.map +1 -1
  85. package/dist/unstable/rpc/RpcServer.d.ts.map +1 -1
  86. package/dist/unstable/rpc/RpcServer.js +4 -0
  87. package/dist/unstable/rpc/RpcServer.js.map +1 -1
  88. package/dist/unstable/workflow/WorkflowEngine.d.ts +6 -0
  89. package/dist/unstable/workflow/WorkflowEngine.d.ts.map +1 -1
  90. package/dist/unstable/workflow/WorkflowEngine.js +131 -0
  91. package/dist/unstable/workflow/WorkflowEngine.js.map +1 -1
  92. package/package.json +1 -1
  93. package/src/Channel.ts +9 -9
  94. package/src/Effect.ts +235 -8
  95. package/src/ErrorReporter.ts +459 -0
  96. package/src/LogLevel.ts +6 -0
  97. package/src/Logger.ts +28 -95
  98. package/src/Queue.ts +0 -1
  99. package/src/References.ts +4 -4
  100. package/src/SchemaAST.ts +2 -1
  101. package/src/Stream.ts +7 -7
  102. package/src/index.ts +5 -0
  103. package/src/internal/effect.ts +137 -14
  104. package/src/internal/hashMap.ts +2 -2
  105. package/src/unstable/ai/LanguageModel.ts +71 -0
  106. package/src/unstable/cli/CliOutput.ts +45 -6
  107. package/src/unstable/cli/Command.ts +298 -11
  108. package/src/unstable/cli/HelpDoc.ts +68 -2
  109. package/src/unstable/cli/internal/command.ts +47 -11
  110. package/src/unstable/cli/internal/completions/CommandDescriptor.ts +7 -2
  111. package/src/unstable/cli/internal/parser.ts +11 -3
  112. package/src/unstable/http/Headers.ts +28 -13
  113. package/src/unstable/http/HttpEffect.ts +10 -5
  114. package/src/unstable/http/HttpServerError.ts +13 -27
  115. package/src/unstable/http/HttpServerRespondable.ts +6 -6
  116. package/src/unstable/http/HttpServerResponse.ts +3 -1
  117. package/src/unstable/httpapi/HttpApiBuilder.ts +1 -0
  118. package/src/unstable/httpapi/HttpApiError.ts +30 -9
  119. package/src/unstable/observability/OtlpLogger.ts +9 -5
  120. package/src/unstable/reactivity/AtomRegistry.ts +29 -1
  121. package/src/unstable/rpc/RpcServer.ts +4 -0
  122. package/src/unstable/workflow/WorkflowEngine.ts +178 -0
@@ -0,0 +1,459 @@
1
+ /**
2
+ * Pluggable error reporting for Effect programs.
3
+ *
4
+ * Reporting is triggered by `Effect.withErrorReporting`,
5
+ * `ErrorReporter.report`, or built-in reporting boundaries in the HTTP and
6
+ * RPC server modules.
7
+ *
8
+ * Each reporter receives a structured callback with the failing `Cause`, a
9
+ * pretty-printed `Error`, severity, and any extra attributes attached to the
10
+ * original error — making it straightforward to forward failures to Sentry,
11
+ * Datadog, or a custom logging backend.
12
+ *
13
+ * Use the annotation symbols (`ignore`, `severity`, `attributes`) on your
14
+ * error classes to control reporting behavior per-error.
15
+ *
16
+ * @example
17
+ * ```ts
18
+ * import { Data, Effect, ErrorReporter } from "effect"
19
+ *
20
+ * // A reporter that logs to the console
21
+ * const consoleReporter = ErrorReporter.make(({ error, severity }) => {
22
+ * console.error(`[${severity}]`, error.message)
23
+ * })
24
+ *
25
+ * // An error that should be ignored by reporters
26
+ * class NotFoundError extends Data.TaggedError("NotFoundError")<{}> {
27
+ * readonly [ErrorReporter.ignore] = true
28
+ * }
29
+ *
30
+ * // An error with custom severity and attributes
31
+ * class RateLimitError extends Data.TaggedError("RateLimitError")<{
32
+ * readonly retryAfter: number
33
+ * }> {
34
+ * readonly [ErrorReporter.severity] = "Warn" as const
35
+ * readonly [ErrorReporter.attributes] = {
36
+ * retryAfter: this.retryAfter
37
+ * }
38
+ * }
39
+ *
40
+ * // Opt in to error reporting with Effect.withErrorReporting
41
+ * const program = Effect.gen(function*() {
42
+ * yield* new RateLimitError({ retryAfter: 60 })
43
+ * }).pipe(
44
+ * Effect.withErrorReporting,
45
+ * Effect.provide(ErrorReporter.layer([consoleReporter]))
46
+ * )
47
+ * ```
48
+ *
49
+ * @since 4.0.0
50
+ */
51
+ import type * as Cause from "./Cause.ts"
52
+ import * as Effect from "./Effect.ts"
53
+ import type * as Fiber from "./Fiber.ts"
54
+ import * as effect from "./internal/effect.ts"
55
+ import * as Layer from "./Layer.ts"
56
+ import * as LogLevel from "./LogLevel.ts"
57
+ import type { Severity } from "./LogLevel.ts"
58
+ import type { ReadonlyRecord } from "./Record.ts"
59
+ import type * as Scope from "./Scope.ts"
60
+ import type * as ServiceMap from "./ServiceMap.ts"
61
+
62
+ /**
63
+ * @since 4.0.0
64
+ * @category Type Identifiers
65
+ */
66
+ export type TypeId = "~effect/ErrorReporter"
67
+
68
+ /**
69
+ * @since 4.0.0
70
+ * @category Type Identifiers
71
+ */
72
+ export const TypeId: TypeId = "~effect/ErrorReporter"
73
+
74
+ /**
75
+ * An `ErrorReporter` receives reported failures and forwards them to an
76
+ * external system (logging service, error tracker, etc.).
77
+ *
78
+ * Reporting is triggered by `Effect.withErrorReporting`,
79
+ * `ErrorReporter.report`, or built-in boundaries in the HTTP and RPC server
80
+ * modules. Use {@link make} to create a reporter — it handles deduplication
81
+ * and per-error annotation extraction automatically.
82
+ *
83
+ * @since 4.0.0
84
+ * @category Models
85
+ */
86
+ export interface ErrorReporter {
87
+ readonly [TypeId]: TypeId
88
+ report(options: {
89
+ readonly cause: Cause.Cause<unknown>
90
+ readonly fiber: Fiber.Fiber<unknown, unknown>
91
+ readonly timestamp: bigint
92
+ }): void
93
+ }
94
+
95
+ /**
96
+ * Creates an `ErrorReporter` from a callback.
97
+ *
98
+ * The returned reporter automatically deduplicates causes and individual
99
+ * errors (the same object is never reported twice), skips interruptions,
100
+ * and resolves the `ignore`, `severity`, and `attributes` annotations on
101
+ * each error before invoking your callback.
102
+ *
103
+ * @example
104
+ * ```ts
105
+ * import { ErrorReporter } from "effect"
106
+ *
107
+ * // Forward every failure to the console
108
+ * const consoleReporter = ErrorReporter.make(
109
+ * ({ error, severity, attributes }) => {
110
+ * console.error(`[${severity}]`, error.message, attributes)
111
+ * }
112
+ * )
113
+ * ```
114
+ *
115
+ * @since 4.0.0
116
+ * @category Constructors
117
+ */
118
+ export const make = (
119
+ report: (options: {
120
+ readonly cause: Cause.Cause<unknown>
121
+ readonly error: Error
122
+ readonly attributes: ReadonlyRecord<string, unknown>
123
+ readonly severity: Severity
124
+ readonly fiber: Fiber.Fiber<unknown, unknown>
125
+ readonly timestamp: bigint
126
+ }) => void
127
+ ): ErrorReporter => {
128
+ const reported = new WeakSet<Cause.Cause<unknown> | object>()
129
+ return {
130
+ [TypeId]: TypeId,
131
+ report(options) {
132
+ if (reported.has(options.cause)) return
133
+ reported.add(options.cause)
134
+ for (let i = 0; i < options.cause.reasons.length; i++) {
135
+ const reason = options.cause.reasons[i]
136
+ if (reason._tag === "Interrupt") continue
137
+ const original = reason._tag === "Fail" ? reason.error : reason.defect
138
+ const isObject = typeof original === "object" && original !== null
139
+ if (isObject) {
140
+ if (reported.has(original)) continue
141
+ reported.add(original)
142
+ }
143
+ if (isIgnored(original)) continue
144
+ const pretty = effect.causePrettyError(original as any, reason.annotations)
145
+ report({
146
+ ...options,
147
+ error: pretty,
148
+ severity: isObject ? getSeverity(original) : "Error",
149
+ attributes: isObject ? getAttributes(original) : emptyAttributes
150
+ })
151
+ }
152
+ }
153
+ }
154
+ }
155
+
156
+ /**
157
+ * A `ServiceMap.Reference` holding the set of active `ErrorReporter`s for the
158
+ * current fiber. Defaults to an empty set (no reporting).
159
+ *
160
+ * Prefer {@link layer} to configure reporters via the `Layer` API. Use this
161
+ * reference directly only when you need low-level control (e.g. reading the
162
+ * current reporters or swapping them inside a `FiberRef`).
163
+ *
164
+ * @since 4.0.0
165
+ * @category References
166
+ */
167
+ export const CurrentErrorReporters: ServiceMap.Reference<ReadonlySet<ErrorReporter>> = effect.CurrentErrorReporters
168
+
169
+ /**
170
+ * Creates a `Layer` that registers one or more `ErrorReporter`s.
171
+ *
172
+ * Reporters can be plain `ErrorReporter` values or effectful
173
+ * `Effect<ErrorReporter>` values that are resolved when the layer is built.
174
+ *
175
+ * By default the provided reporters **replace** any previously registered
176
+ * reporters. Set `mergeWithExisting: true` to add them alongside existing
177
+ * ones.
178
+ *
179
+ * @example
180
+ * ```ts
181
+ * import { Effect, ErrorReporter } from "effect"
182
+ *
183
+ * const consoleReporter = ErrorReporter.make(({ error, severity }) => {
184
+ * console.error(`[${severity}]`, error.message)
185
+ * })
186
+ *
187
+ * const metricsReporter = ErrorReporter.make(({ severity }) => {
188
+ * // increment an error counter by severity
189
+ * })
190
+ *
191
+ * // Replace all existing reporters
192
+ * const ReporterLive = ErrorReporter.layer([
193
+ * consoleReporter,
194
+ * metricsReporter
195
+ * ])
196
+ *
197
+ * // Add to existing reporters instead of replacing
198
+ * const ReporterMerged = ErrorReporter.layer(
199
+ * [metricsReporter],
200
+ * { mergeWithExisting: true }
201
+ * )
202
+ *
203
+ * const program = Effect.gen(function*() {
204
+ * yield* Effect.fail("boom")
205
+ * }).pipe(
206
+ * Effect.withErrorReporting,
207
+ * Effect.provide(ReporterLive)
208
+ * )
209
+ * ```
210
+ *
211
+ * @since 4.0.0
212
+ * @category Layers
213
+ */
214
+ export const layer = <
215
+ const Reporters extends ReadonlyArray<ErrorReporter | Effect.Effect<ErrorReporter, any, any>>
216
+ >(
217
+ reporters: Reporters,
218
+ options?: { readonly mergeWithExisting?: boolean | undefined } | undefined
219
+ ): Layer.Layer<
220
+ never,
221
+ Reporters extends readonly [] ? never : Effect.Error<Reporters[number]>,
222
+ Exclude<
223
+ Reporters extends readonly [] ? never : Effect.Services<Reporters[number]>,
224
+ Scope.Scope
225
+ >
226
+ > =>
227
+ Layer.effect(
228
+ CurrentErrorReporters,
229
+ Effect.withFiber(Effect.fnUntraced(function*(fiber) {
230
+ const currentReporters = new Set(
231
+ options?.mergeWithExisting === true ? fiber.getRef(effect.CurrentErrorReporters) : []
232
+ )
233
+ for (const reporter of reporters) {
234
+ currentReporters.add(Effect.isEffect(reporter) ? yield* reporter : reporter)
235
+ }
236
+ return currentReporters
237
+ }))
238
+ )
239
+
240
+ /**
241
+ * Manually report a `Cause` to all registered `ErrorReporter`s on the
242
+ * current fiber.
243
+ *
244
+ * This is useful when you want to report an error for observability without
245
+ * actually failing the fiber.
246
+ *
247
+ * @example
248
+ * ```ts
249
+ * import { Cause, Effect, ErrorReporter } from "effect"
250
+ *
251
+ * // Log the cause for monitoring, then continue with a fallback
252
+ * const program = Effect.gen(function*() {
253
+ * const cause = Cause.fail("something went wrong")
254
+ * yield* ErrorReporter.report(cause)
255
+ * return "fallback value"
256
+ * })
257
+ * ```
258
+ *
259
+ * @since 4.0.0
260
+ * @category Reporting
261
+ */
262
+ export const report = <E>(cause: Cause.Cause<E>): Effect.Effect<void> =>
263
+ Effect.withFiber((fiber) => {
264
+ effect.reportCauseUnsafe(fiber, cause)
265
+ return Effect.void
266
+ })
267
+
268
+ /**
269
+ * Interface that errors can implement to control reporting behavior.
270
+ *
271
+ * All three annotation properties are optional:
272
+ * - `[ErrorReporter.ignore]` — when `true`, the error is never reported
273
+ * - `[ErrorReporter.severity]` — overrides the default `"Error"` severity
274
+ * - `[ErrorReporter.attributes]` — extra key/value pairs forwarded to reporters
275
+ *
276
+ * The global `Error` interface is augmented with `Reportable`, so these
277
+ * properties are available on all `Error` instances.
278
+ *
279
+ * @since 4.0.0
280
+ * @category Annotations
281
+ */
282
+ export interface Reportable {
283
+ readonly [ignore]?: boolean
284
+ readonly [severity]?: Severity
285
+ readonly [attributes]?: ReadonlyRecord<string, unknown>
286
+ }
287
+
288
+ declare global {
289
+ interface Error extends Reportable {}
290
+ }
291
+
292
+ /**
293
+ * Symbol key used to mark an error as unreportable.
294
+ *
295
+ * Set this property to `true` on any error class to prevent it from being
296
+ * forwarded to reporters. Useful for expected errors such as HTTP 404s.
297
+ *
298
+ * @example
299
+ * ```ts
300
+ * import { Data, ErrorReporter } from "effect"
301
+ *
302
+ * class NotFoundError extends Data.TaggedError("NotFoundError")<{}> {
303
+ * readonly [ErrorReporter.ignore] = true
304
+ * }
305
+ * ```
306
+ *
307
+ * @since 4.0.0
308
+ * @category Annotations
309
+ */
310
+ export type ignore = "~effect/ErrorReporter/ignore"
311
+
312
+ /**
313
+ * Symbol key used to mark an error as unreportable.
314
+ *
315
+ * Set this property to `true` on any error class to prevent it from being
316
+ * forwarded to reporters. Useful for expected errors such as HTTP 404s.
317
+ *
318
+ * @example
319
+ * ```ts
320
+ * import { Data, ErrorReporter } from "effect"
321
+ *
322
+ * class NotFoundError extends Data.TaggedError("NotFoundError")<{}> {
323
+ * readonly [ErrorReporter.ignore] = true
324
+ * }
325
+ * ```
326
+ *
327
+ * @since 4.0.0
328
+ * @category Annotations
329
+ */
330
+ export const ignore: ignore = "~effect/ErrorReporter/ignore"
331
+
332
+ /**
333
+ * Returns `true` if the given value has the `ErrorReporter.ignore` annotation
334
+ * set to `true`.
335
+ *
336
+ * @since 4.0.0
337
+ * @category Annotations
338
+ */
339
+ export const isIgnored = (u: unknown): boolean =>
340
+ typeof u === "object" && u !== null && ignore in u && u[ignore] === true
341
+
342
+ /**
343
+ * Symbol key used to override the severity level of an error.
344
+ *
345
+ * When set, the reporter callback receives this value as `severity` instead
346
+ * of the default `"Error"`. Accepted values are the `LogLevel.Severity`
347
+ * literals: `"Trace"`, `"Debug"`, `"Info"`, `"Warn"`, `"Error"`, `"Fatal"`.
348
+ *
349
+ * @example
350
+ * ```ts
351
+ * import { Data, ErrorReporter } from "effect"
352
+ *
353
+ * class DeprecationWarning extends Data.TaggedError("DeprecationWarning")<{}> {
354
+ * readonly [ErrorReporter.severity] = "Warn" as const
355
+ * }
356
+ * ```
357
+ *
358
+ * @since 4.0.0
359
+ * @category Annotations
360
+ */
361
+ export type severity = "~effect/ErrorReporter/severity"
362
+
363
+ /**
364
+ * Symbol key used to override the severity level of an error.
365
+ *
366
+ * When set, the reporter callback receives this value as `severity` instead
367
+ * of the default `"Error"`. Accepted values are the `LogLevel.Severity`
368
+ * literals: `"Trace"`, `"Debug"`, `"Info"`, `"Warn"`, `"Error"`, `"Fatal"`.
369
+ *
370
+ * @example
371
+ * ```ts
372
+ * import { Data, ErrorReporter } from "effect"
373
+ *
374
+ * class DeprecationWarning extends Data.TaggedError("DeprecationWarning")<{}> {
375
+ * readonly [ErrorReporter.severity] = "Warn" as const
376
+ * }
377
+ * ```
378
+ *
379
+ * @since 4.0.0
380
+ * @category Annotations
381
+ */
382
+ export const severity: severity = "~effect/ErrorReporter/severity"
383
+
384
+ /**
385
+ * Reads the `ErrorReporter.severity` annotation from an error object,
386
+ * falling back to `"Error"` when unset or invalid.
387
+ *
388
+ * @since 4.0.0
389
+ * @category Annotations
390
+ */
391
+ export const getSeverity = (error: object): Severity => {
392
+ if (severity in error && LogLevel.values.includes(error[severity] as Severity)) {
393
+ return error[severity] as Severity
394
+ }
395
+ return "Error"
396
+ }
397
+
398
+ /**
399
+ * Symbol key used to attach extra key/value metadata to an error report.
400
+ *
401
+ * Reporters receive these attributes alongside the error, making it easy to
402
+ * include contextual information such as user IDs, request IDs, or any
403
+ * domain-specific data useful for debugging.
404
+ *
405
+ * @example
406
+ * ```ts
407
+ * import { Data, ErrorReporter } from "effect"
408
+ *
409
+ * class PaymentError extends Data.TaggedError("PaymentError")<{
410
+ * readonly orderId: string
411
+ * }> {
412
+ * readonly [ErrorReporter.attributes] = {
413
+ * orderId: this.orderId
414
+ * }
415
+ * }
416
+ * ```
417
+ *
418
+ * @since 4.0.0
419
+ * @category Annotations
420
+ */
421
+ export type attributes = "~effect/ErrorReporter/attributes"
422
+
423
+ /**
424
+ * Symbol key used to attach extra key/value metadata to an error report.
425
+ *
426
+ * Reporters receive these attributes alongside the error, making it easy to
427
+ * include contextual information such as user IDs, request IDs, or any
428
+ * domain-specific data useful for debugging.
429
+ *
430
+ * @example
431
+ * ```ts
432
+ * import { Data, ErrorReporter } from "effect"
433
+ *
434
+ * class PaymentError extends Data.TaggedError("PaymentError")<{
435
+ * readonly orderId: string
436
+ * }> {
437
+ * readonly [ErrorReporter.attributes] = {
438
+ * orderId: this.orderId
439
+ * }
440
+ * }
441
+ * ```
442
+ *
443
+ * @since 4.0.0
444
+ * @category Annotations
445
+ */
446
+ export const attributes: attributes = "~effect/ErrorReporter/attributes"
447
+
448
+ /**
449
+ * Reads the `ErrorReporter.attributes` annotation from an error object,
450
+ * returning an empty record when unset.
451
+ *
452
+ * @since 4.0.0
453
+ * @category Annotations
454
+ */
455
+ export const getAttributes = (error: object): ReadonlyRecord<string, unknown> => {
456
+ return attributes in error ? error[attributes] as any : emptyAttributes
457
+ }
458
+
459
+ const emptyAttributes: ReadonlyRecord<string, unknown> = {}
package/src/LogLevel.ts CHANGED
@@ -142,6 +142,12 @@ import * as References from "./References.ts"
142
142
  */
143
143
  export type LogLevel = "All" | "Fatal" | "Error" | "Warn" | "Info" | "Debug" | "Trace" | "None"
144
144
 
145
+ /**
146
+ * @since 4.0.0
147
+ * @category models
148
+ */
149
+ export type Severity = "Fatal" | "Error" | "Warn" | "Info" | "Debug" | "Trace"
150
+
145
151
  /**
146
152
  * @since 4.0.0
147
153
  * @category models
package/src/Logger.ts CHANGED
@@ -115,8 +115,7 @@ import type { PlatformError } from "./PlatformError.ts"
115
115
  import * as Predicate from "./Predicate.ts"
116
116
  import { CurrentLogAnnotations, CurrentLogSpans } from "./References.ts"
117
117
  import type * as Scope from "./Scope.ts"
118
- import * as ServiceMap from "./ServiceMap.ts"
119
- import type * as Types from "./Types.ts"
118
+ import type * as ServiceMap from "./ServiceMap.ts"
120
119
 
121
120
  const TypeId = "~effect/Logger"
122
121
 
@@ -144,8 +143,9 @@ const TypeId = "~effect/Logger"
144
143
  * @since 2.0.0
145
144
  * @category models
146
145
  */
147
- export interface Logger<in Message, out Output> extends Logger.Variance<Message, Output>, Pipeable {
148
- log: (options: Logger.Options<Message>) => Output
146
+ export interface Logger<in Message, out Output> extends Pipeable {
147
+ readonly [TypeId]: typeof TypeId
148
+ log(options: Options<Message>): Output
149
149
  }
150
150
 
151
151
  /**
@@ -153,100 +153,32 @@ export interface Logger<in Message, out Output> extends Logger.Variance<Message,
153
153
  * ```ts
154
154
  * import { Effect, Logger } from "effect"
155
155
  *
156
- * // Access Logger namespace types and functionality
157
- * const customLogger = Logger.make((options) => {
158
- * console.log(`Message: ${options.message}`)
159
- * console.log(`Level: ${options.logLevel}`)
160
- * console.log(`Date: ${options.date.toISOString()}`)
161
- * console.log(`Fiber ID: ${options.fiber.id}`)
156
+ * // Options interface provides all logging context
157
+ * const detailedLogger = Logger.make((options) => {
158
+ * const output = {
159
+ * message: options.message,
160
+ * level: options.logLevel,
161
+ * timestamp: options.date.toISOString(),
162
+ * fiberId: options.fiber.id,
163
+ * hasCause: options.cause !== undefined
164
+ * }
165
+ * console.log(JSON.stringify(output))
162
166
  * })
163
167
  *
164
- * // The Logger namespace contains types like Options, Variance, etc.
165
- * const program = Effect.log("Hello World").pipe(
166
- * Effect.provide(Logger.layer([customLogger]))
168
+ * const program = Effect.log("Processing request").pipe(
169
+ * Effect.provide(Logger.layer([detailedLogger]))
167
170
  * )
168
171
  * ```
169
172
  *
170
173
  * @since 2.0.0
171
174
  * @category models
172
175
  */
173
- export declare namespace Logger {
174
- /**
175
- * @example
176
- * ```ts
177
- * import { Logger } from "effect"
178
- *
179
- * // Variance interface defines contravariance for Message and covariance for Output
180
- * const customLogger = Logger.make<unknown, void>((options) => {
181
- * console.log(options.message)
182
- * })
183
- *
184
- * // The logger can accept more specific message types (contravariance)
185
- * // and can be used where less specific output types are expected (covariance)
186
- * ```
187
- *
188
- * @since 2.0.0
189
- * @category models
190
- */
191
- export interface Variance<in Message, out Output> {
192
- readonly [TypeId]: VarianceStruct<Message, Output>
193
- }
194
-
195
- /**
196
- * @example
197
- * ```ts
198
- * import { Logger } from "effect"
199
- *
200
- * // VarianceStruct defines the actual variance structure used internally
201
- * // This structure ensures proper typing and variance behavior
202
- * const logger = Logger.make<unknown, void>((options) => {
203
- * console.log(options.message)
204
- * })
205
- *
206
- * // The variance structure handles contravariance for input Message type
207
- * // and covariance for output Output type
208
- * ```
209
- *
210
- * @since 4.0.0
211
- * @category models
212
- */
213
- export interface VarianceStruct<in Message, out Output> {
214
- _Message: Types.Contravariant<Message>
215
- _Output: Types.Covariant<Output>
216
- }
217
-
218
- /**
219
- * @example
220
- * ```ts
221
- * import { Effect, Logger } from "effect"
222
- *
223
- * // Options interface provides all logging context
224
- * const detailedLogger = Logger.make((options) => {
225
- * const output = {
226
- * message: options.message,
227
- * level: options.logLevel,
228
- * timestamp: options.date.toISOString(),
229
- * fiberId: options.fiber.id,
230
- * hasCause: options.cause !== undefined
231
- * }
232
- * console.log(JSON.stringify(output))
233
- * })
234
- *
235
- * const program = Effect.log("Processing request").pipe(
236
- * Effect.provide(Logger.layer([detailedLogger]))
237
- * )
238
- * ```
239
- *
240
- * @since 2.0.0
241
- * @category models
242
- */
243
- export interface Options<out Message> {
244
- readonly message: Message
245
- readonly logLevel: LogLevel.LogLevel
246
- readonly cause: Cause.Cause<unknown>
247
- readonly fiber: Fiber.Fiber<unknown, unknown>
248
- readonly date: Date
249
- }
176
+ export interface Options<out Message> {
177
+ readonly message: Message
178
+ readonly logLevel: LogLevel.LogLevel
179
+ readonly cause: Cause.Cause<unknown>
180
+ readonly fiber: Fiber.Fiber<unknown, unknown>
181
+ readonly date: Date
250
182
  }
251
183
 
252
184
  /**
@@ -562,7 +494,7 @@ const format = (
562
494
  quoteValue: (s: string) => string,
563
495
  space?: number | string | undefined
564
496
  ) =>
565
- ({ cause, date, fiber, logLevel, message }: Logger.Options<unknown>): string => {
497
+ ({ cause, date, fiber, logLevel, message }: Options<unknown>): string => {
566
498
  const formatValue = (value: string): string => value.match(textOnly) ? value : quoteValue(value)
567
499
  const format = (label: string, value: string): string => `${effect.formatLabel(label)}=${formatValue(value)}`
568
500
  const append = (label: string, value: string): string => " " + format(label, value)
@@ -636,7 +568,7 @@ const format = (
636
568
  * @category constructors
637
569
  */
638
570
  export const make: <Message, Output>(
639
- log: (options: Logger.Options<Message>) => Output
571
+ log: (options: Options<Message>) => Output
640
572
  ) => Logger<Message, Output> = effect.loggerMake
641
573
 
642
574
  /**
@@ -1389,7 +1321,7 @@ export const layer = <
1389
1321
  const Loggers extends ReadonlyArray<Logger<unknown, unknown> | Effect.Effect<Logger<unknown, unknown>, any, any>>
1390
1322
  >(
1391
1323
  loggers: Loggers,
1392
- options?: { mergeWithExisting: boolean }
1324
+ options?: { readonly mergeWithExisting?: boolean | undefined } | undefined
1393
1325
  ): Layer.Layer<
1394
1326
  never,
1395
1327
  Loggers extends readonly [] ? never : Effect.Error<Loggers[number]>,
@@ -1398,13 +1330,14 @@ export const layer = <
1398
1330
  Scope.Scope
1399
1331
  >
1400
1332
  > =>
1401
- Layer.effectServices(
1333
+ Layer.effect(
1334
+ CurrentLoggers,
1402
1335
  withFiber(effect.fnUntraced(function*(fiber) {
1403
1336
  const currentLoggers = new Set(options?.mergeWithExisting === true ? fiber.getRef(effect.CurrentLoggers) : [])
1404
1337
  for (const logger of loggers) {
1405
1338
  currentLoggers.add(isEffect(logger) ? yield* logger : logger)
1406
1339
  }
1407
- return ServiceMap.make(effect.CurrentLoggers, currentLoggers)
1340
+ return currentLoggers
1408
1341
  }))
1409
1342
  )
1410
1343
 
package/src/Queue.ts CHANGED
@@ -1113,7 +1113,6 @@ export const collect = <A, E>(self: Dequeue<A, E | Done>): Effect<Array<A>, Pull
1113
1113
  while: constTrue,
1114
1114
  body: constant(takeAll(self)),
1115
1115
  step(items: Arr.NonEmptyArray<A>) {
1116
- out.push(...items)
1117
1116
  for (let i = 0; i < items.length; i++) {
1118
1117
  out.push(items[i])
1119
1118
  }
package/src/References.ts CHANGED
@@ -11,7 +11,7 @@
11
11
  * @since 4.0.0
12
12
  */
13
13
  import { constTrue, constUndefined } from "./Function.ts"
14
- import type { LogLevel } from "./LogLevel.ts"
14
+ import type { LogLevel, Severity } from "./LogLevel.ts"
15
15
  import type { ReadonlyRecord } from "./Record.ts"
16
16
  import { MaxOpsBeforeYield } from "./Scheduler.ts"
17
17
  import * as ServiceMap from "./ServiceMap.ts"
@@ -451,7 +451,7 @@ export const CurrentLogAnnotations = ServiceMap.Reference<ReadonlyRecord<string,
451
451
  * @category references
452
452
  * @since 4.0.0
453
453
  */
454
- export const CurrentLogLevel: ServiceMap.Reference<LogLevel> = ServiceMap.Reference<LogLevel>(
454
+ export const CurrentLogLevel: ServiceMap.Reference<Severity> = ServiceMap.Reference<Severity>(
455
455
  "effect/References/CurrentLogLevel",
456
456
  { defaultValue: () => "Info" }
457
457
  )
@@ -516,9 +516,9 @@ export const MinimumLogLevel = ServiceMap.Reference<
516
516
  * @category references
517
517
  * @since 4.0.0
518
518
  */
519
- export const UnhandledLogLevel: ServiceMap.Reference<LogLevel | undefined> = ServiceMap.Reference(
519
+ export const UnhandledLogLevel: ServiceMap.Reference<Severity | undefined> = ServiceMap.Reference(
520
520
  "effect/References/UnhandledLogLevel",
521
- { defaultValue: (): LogLevel | undefined => "Error" }
521
+ { defaultValue: (): Severity | undefined => "Error" }
522
522
  )
523
523
 
524
524
  /**