effect 4.0.0-beta.75 → 4.0.0-beta.77

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 (130) hide show
  1. package/dist/Config.d.ts +25 -1
  2. package/dist/Config.d.ts.map +1 -1
  3. package/dist/Config.js +32 -0
  4. package/dist/Config.js.map +1 -1
  5. package/dist/Schema.d.ts +102 -40
  6. package/dist/Schema.d.ts.map +1 -1
  7. package/dist/Schema.js +149 -78
  8. package/dist/Schema.js.map +1 -1
  9. package/dist/SchemaAST.d.ts.map +1 -1
  10. package/dist/SchemaAST.js +2 -1
  11. package/dist/SchemaAST.js.map +1 -1
  12. package/dist/SchemaRepresentation.d.ts +10 -1
  13. package/dist/SchemaRepresentation.d.ts.map +1 -1
  14. package/dist/SchemaRepresentation.js +12 -5
  15. package/dist/SchemaRepresentation.js.map +1 -1
  16. package/dist/SchemaTransformation.d.ts.map +1 -1
  17. package/dist/SchemaTransformation.js +57 -18
  18. package/dist/SchemaTransformation.js.map +1 -1
  19. package/dist/internal/schema/representation.js +1 -0
  20. package/dist/internal/schema/representation.js.map +1 -1
  21. package/dist/unstable/ai/McpSchema.js +1 -1
  22. package/dist/unstable/ai/McpSchema.js.map +1 -1
  23. package/dist/unstable/cli/CliError.js +1 -1
  24. package/dist/unstable/cli/CliError.js.map +1 -1
  25. package/dist/unstable/cluster/ClusterError.js +2 -2
  26. package/dist/unstable/cluster/ClusterError.js.map +1 -1
  27. package/dist/unstable/cluster/Reply.js +1 -1
  28. package/dist/unstable/cluster/Reply.js.map +1 -1
  29. package/dist/unstable/devtools/DevToolsSchema.js +5 -1
  30. package/dist/unstable/devtools/DevToolsSchema.js.map +1 -1
  31. package/dist/unstable/http/HttpClientError.js +1 -1
  32. package/dist/unstable/http/HttpClientError.js.map +1 -1
  33. package/dist/unstable/httpapi/HttpApiScalar.d.ts +2 -0
  34. package/dist/unstable/httpapi/HttpApiScalar.d.ts.map +1 -1
  35. package/dist/unstable/httpapi/HttpApiScalar.js +12 -6
  36. package/dist/unstable/httpapi/HttpApiScalar.js.map +1 -1
  37. package/dist/unstable/observability/Otlp.d.ts +18 -0
  38. package/dist/unstable/observability/Otlp.d.ts.map +1 -1
  39. package/dist/unstable/observability/Otlp.js +20 -0
  40. package/dist/unstable/observability/Otlp.js.map +1 -1
  41. package/dist/unstable/observability/OtlpLogger.d.ts +17 -1
  42. package/dist/unstable/observability/OtlpLogger.d.ts.map +1 -1
  43. package/dist/unstable/observability/OtlpLogger.js +49 -0
  44. package/dist/unstable/observability/OtlpLogger.js.map +1 -1
  45. package/dist/unstable/observability/OtlpMetrics.d.ts +14 -0
  46. package/dist/unstable/observability/OtlpMetrics.d.ts.map +1 -1
  47. package/dist/unstable/observability/OtlpMetrics.js +45 -0
  48. package/dist/unstable/observability/OtlpMetrics.js.map +1 -1
  49. package/dist/unstable/observability/OtlpResource.d.ts +3 -3
  50. package/dist/unstable/observability/OtlpResource.d.ts.map +1 -1
  51. package/dist/unstable/observability/OtlpResource.js +12 -11
  52. package/dist/unstable/observability/OtlpResource.js.map +1 -1
  53. package/dist/unstable/observability/OtlpTracer.d.ts +15 -0
  54. package/dist/unstable/observability/OtlpTracer.d.ts.map +1 -1
  55. package/dist/unstable/observability/OtlpTracer.js +46 -0
  56. package/dist/unstable/observability/OtlpTracer.js.map +1 -1
  57. package/dist/unstable/observability/internal/otlpEnv.d.ts +8 -0
  58. package/dist/unstable/observability/internal/otlpEnv.d.ts.map +1 -0
  59. package/dist/unstable/observability/internal/otlpEnv.js +30 -0
  60. package/dist/unstable/observability/internal/otlpEnv.js.map +1 -0
  61. package/dist/unstable/persistence/Persistable.js +1 -1
  62. package/dist/unstable/persistence/Persistable.js.map +1 -1
  63. package/dist/unstable/persistence/PersistedQueue.js +1 -1
  64. package/dist/unstable/persistence/PersistedQueue.js.map +1 -1
  65. package/dist/unstable/persistence/Persistence.js +1 -1
  66. package/dist/unstable/persistence/Persistence.js.map +1 -1
  67. package/dist/unstable/persistence/RateLimiter.js +1 -1
  68. package/dist/unstable/persistence/RateLimiter.js.map +1 -1
  69. package/dist/unstable/persistence/Redis.js +1 -1
  70. package/dist/unstable/persistence/Redis.js.map +1 -1
  71. package/dist/unstable/reactivity/AsyncResult.js +1 -1
  72. package/dist/unstable/reactivity/AsyncResult.js.map +1 -1
  73. package/dist/unstable/rpc/Rpc.d.ts +1 -1
  74. package/dist/unstable/rpc/Rpc.js +2 -2
  75. package/dist/unstable/rpc/Rpc.js.map +1 -1
  76. package/dist/unstable/rpc/RpcClient.js +1 -1
  77. package/dist/unstable/rpc/RpcClient.js.map +1 -1
  78. package/dist/unstable/rpc/RpcClientError.js +1 -1
  79. package/dist/unstable/rpc/RpcClientError.js.map +1 -1
  80. package/dist/unstable/rpc/RpcMessage.d.ts +2 -2
  81. package/dist/unstable/rpc/RpcMessage.js +3 -3
  82. package/dist/unstable/rpc/RpcMessage.js.map +1 -1
  83. package/dist/unstable/socket/Socket.js +3 -3
  84. package/dist/unstable/socket/Socket.js.map +1 -1
  85. package/dist/unstable/sql/SqlError.js +1 -1
  86. package/dist/unstable/sql/SqlError.js.map +1 -1
  87. package/dist/unstable/workers/WorkerError.js +4 -4
  88. package/dist/unstable/workers/WorkerError.js.map +1 -1
  89. package/dist/unstable/workflow/Activity.js +1 -1
  90. package/dist/unstable/workflow/Activity.js.map +1 -1
  91. package/dist/unstable/workflow/DurableDeferred.js +1 -1
  92. package/dist/unstable/workflow/DurableDeferred.js.map +1 -1
  93. package/dist/unstable/workflow/Workflow.js +2 -2
  94. package/dist/unstable/workflow/Workflow.js.map +1 -1
  95. package/package.json +1 -1
  96. package/src/Config.ts +44 -0
  97. package/src/Schema.ts +179 -117
  98. package/src/SchemaAST.ts +3 -1
  99. package/src/SchemaRepresentation.ts +12 -4
  100. package/src/SchemaTransformation.ts +75 -25
  101. package/src/internal/schema/representation.ts +1 -0
  102. package/src/unstable/ai/McpSchema.ts +1 -1
  103. package/src/unstable/cli/CliError.ts +1 -1
  104. package/src/unstable/cluster/ClusterError.ts +2 -2
  105. package/src/unstable/cluster/Reply.ts +1 -1
  106. package/src/unstable/devtools/DevToolsSchema.ts +1 -1
  107. package/src/unstable/http/HttpClientError.ts +1 -1
  108. package/src/unstable/httpapi/HttpApiScalar.ts +14 -7
  109. package/src/unstable/observability/Otlp.ts +36 -0
  110. package/src/unstable/observability/OtlpLogger.ts +56 -1
  111. package/src/unstable/observability/OtlpMetrics.ts +51 -0
  112. package/src/unstable/observability/OtlpResource.ts +27 -16
  113. package/src/unstable/observability/OtlpTracer.ts +52 -0
  114. package/src/unstable/observability/internal/otlpEnv.ts +58 -0
  115. package/src/unstable/persistence/Persistable.ts +1 -1
  116. package/src/unstable/persistence/PersistedQueue.ts +1 -1
  117. package/src/unstable/persistence/Persistence.ts +1 -1
  118. package/src/unstable/persistence/RateLimiter.ts +1 -1
  119. package/src/unstable/persistence/Redis.ts +1 -1
  120. package/src/unstable/reactivity/AsyncResult.ts +1 -1
  121. package/src/unstable/rpc/Rpc.ts +3 -3
  122. package/src/unstable/rpc/RpcClient.ts +1 -1
  123. package/src/unstable/rpc/RpcClientError.ts +1 -1
  124. package/src/unstable/rpc/RpcMessage.ts +3 -3
  125. package/src/unstable/socket/Socket.ts +3 -3
  126. package/src/unstable/sql/SqlError.ts +1 -1
  127. package/src/unstable/workers/WorkerError.ts +4 -4
  128. package/src/unstable/workflow/Activity.ts +1 -1
  129. package/src/unstable/workflow/DurableDeferred.ts +1 -1
  130. package/src/unstable/workflow/Workflow.ts +2 -2
@@ -80,6 +80,8 @@ export type ScalarConfig = {
80
80
  layout?: "modern" | "classic"
81
81
  /** URL to a request proxy for the API client */
82
82
  proxyUrl?: string
83
+ /** Browser JavaScript function expression used by Scalar for documents and test requests */
84
+ customFetch?: string
83
85
  /** Whether to show the sidebar */
84
86
  showSidebar?: boolean
85
87
  /**
@@ -179,9 +181,10 @@ const makeHandler = <Id extends string, Groups extends HttpApiGroup.Any>(options
179
181
  readonly scalar: ScalarConfig | undefined
180
182
  }) => {
181
183
  const spec = OpenApi.fromApi(options.api)
184
+ const { customFetch, ...scalar } = options.scalar ?? {}
182
185
  const scalarConfig = {
183
186
  _integration: "html",
184
- ...options.scalar
187
+ ...scalar
185
188
  }
186
189
  const response = HttpServerResponse.html(`<!doctype html>
187
190
  <html>
@@ -203,12 +206,7 @@ const makeHandler = <Id extends string, Groups extends HttpApiGroup.Any>(options
203
206
  content="width=device-width, initial-scale=1" />
204
207
  </head>
205
208
  <body>
206
- <script id="api-reference" type="application/json">
207
- ${Html.escapeJson(spec)}
208
- </script>
209
- <script>
210
- document.getElementById('api-reference').dataset.configuration = JSON.stringify(${Html.escapeJson(scalarConfig)})
211
- </script>
209
+ <div id="api-reference-container"></div>
212
210
  ${
213
211
  options.source._tag === "Cdn"
214
212
  ? `<script src="${`https://cdn.jsdelivr.net/npm/@scalar/api-reference@${
@@ -216,6 +214,15 @@ const makeHandler = <Id extends string, Groups extends HttpApiGroup.Any>(options
216
214
  }/dist/browser/standalone.min.js`}" crossorigin></script>`
217
215
  : `<script>${options.source.source}</script>`
218
216
  }
217
+ <script>
218
+ window.Scalar.createApiReference(document.getElementById('api-reference-container'), {
219
+ ...${Html.escapeJson(scalarConfig)},
220
+ content: ${Html.escapeJson(spec)}${
221
+ customFetch === undefined ? "" : `,
222
+ customFetch: ${customFetch}`
223
+ }
224
+ })
225
+ </script>
219
226
  </body>
220
227
  </html>`)
221
228
  return Effect.succeed(response)
@@ -110,6 +110,42 @@ export const layer = (options: {
110
110
  )
111
111
  }
112
112
 
113
+ /**
114
+ * Creates a combined OTLP layer for logs, metrics, and traces from
115
+ * OpenTelemetry configuration.
116
+ *
117
+ * @category layers
118
+ * @since 4.0.0
119
+ */
120
+ export const layerFromConfig = (options?: {
121
+ readonly resource?: {
122
+ readonly serviceName?: string | undefined
123
+ readonly serviceVersion?: string | undefined
124
+ readonly attributes?: Record<string, unknown>
125
+ } | undefined
126
+ readonly headers?: Headers.Input | undefined
127
+ readonly tracerContext?: (<X>(primitive: Tracer.EffectPrimitive<X>, span: Tracer.AnySpan) => X) | undefined
128
+ readonly loggerExcludeLogSpans?: boolean | undefined
129
+ readonly loggerMergeWithExisting?: boolean | undefined
130
+ }): Layer.Layer<never, never, HttpClient.HttpClient | OtlpSerialization.OtlpSerialization> =>
131
+ Layer.mergeAll(
132
+ OtlpLogger.layerFromConfig({
133
+ resource: options?.resource,
134
+ headers: options?.headers,
135
+ excludeLogSpans: options?.loggerExcludeLogSpans,
136
+ mergeWithExisting: options?.loggerMergeWithExisting
137
+ }),
138
+ OtlpMetrics.layerFromConfig({
139
+ resource: options?.resource,
140
+ headers: options?.headers
141
+ }),
142
+ OtlpTracer.layerFromConfig({
143
+ resource: options?.resource,
144
+ headers: options?.headers,
145
+ context: options?.tracerContext
146
+ })
147
+ )
148
+
113
149
  /**
114
150
  * Creates the combined OTLP logs, metrics, and traces layer using JSON
115
151
  * serialization.
@@ -37,15 +37,18 @@
37
37
  import * as Arr from "../../Array.ts"
38
38
  import * as Cause from "../../Cause.ts"
39
39
  import { Clock } from "../../Clock.ts"
40
+ import * as Config from "../../Config.ts"
40
41
  import * as Duration from "../../Duration.ts"
41
42
  import * as Effect from "../../Effect.ts"
42
- import type * as Layer from "../../Layer.ts"
43
+ import * as Layer from "../../Layer.ts"
43
44
  import * as Logger from "../../Logger.ts"
44
45
  import type * as LogLevel from "../../LogLevel.ts"
46
+ import * as Option from "../../Option.ts"
45
47
  import { CurrentLogAnnotations, CurrentLogSpans } from "../../References.ts"
46
48
  import type * as Scope from "../../Scope.ts"
47
49
  import type * as Headers from "../http/Headers.ts"
48
50
  import type * as HttpClient from "../http/HttpClient.ts"
51
+ import * as OtlpEnv from "./internal/otlpEnv.ts"
49
52
  import * as Exporter from "./OtlpExporter.ts"
50
53
  import type { AnyValue, Fixed64, KeyValue, Resource } from "./OtlpResource.ts"
51
54
  import * as OtlpResource from "./OtlpResource.ts"
@@ -144,6 +147,58 @@ export const layer = (options: {
144
147
  mergeWithExisting: options.mergeWithExisting ?? true
145
148
  })
146
149
 
150
+ /**
151
+ * Creates an OTLP logs layer from OpenTelemetry configuration.
152
+ *
153
+ * @category layers
154
+ * @since 4.0.0
155
+ */
156
+ export const layerFromConfig = (options?: {
157
+ readonly resource?: {
158
+ readonly serviceName?: string | undefined
159
+ readonly serviceVersion?: string | undefined
160
+ readonly attributes?: Record<string, unknown>
161
+ } | undefined
162
+ readonly headers?: Headers.Input | undefined
163
+ readonly excludeLogSpans?: boolean | undefined
164
+ readonly mergeWithExisting?: boolean | undefined
165
+ }): Layer.Layer<never, never, HttpClient.HttpClient | OtlpSerialization> =>
166
+ Effect.gen(function*() {
167
+ const { disabled, endpoint, exporters } = yield* Config.all({
168
+ disabled: Config.boolean("OTEL_SDK_DISABLED").pipe(Config.withDefault(false)),
169
+ endpoint: OtlpEnv.endpoint("LOGS"),
170
+ exporters: OtlpEnv.exporters("LOGS")
171
+ })
172
+
173
+ if (disabled || !endpoint || !exporters.includes("otlp")) {
174
+ return Layer.empty
175
+ }
176
+
177
+ const { baseTimeout, logsTimeout, exportTimeout, scheduleDelay, maxBatchSize } = yield* Config.all({
178
+ baseTimeout: Config.option(Config.int("OTEL_EXPORTER_OTLP_TIMEOUT")),
179
+ logsTimeout: Config.option(Config.int("OTEL_EXPORTER_OTLP_LOGS_TIMEOUT")),
180
+ exportTimeout: Config.option(Config.int("OTEL_BLRP_EXPORT_TIMEOUT")),
181
+ scheduleDelay: Config.option(Config.int("OTEL_BLRP_SCHEDULE_DELAY")),
182
+ maxBatchSize: Config.option(Config.int("OTEL_BLRP_MAX_EXPORT_BATCH_SIZE"))
183
+ })
184
+
185
+ const shutdownTimeout = Option.firstSomeOf([logsTimeout, baseTimeout, exportTimeout]).pipe(
186
+ Option.map((_) => Duration.millis(_))
187
+ )
188
+ const exportInterval = Option.map(scheduleDelay, (_) => Duration.millis(_))
189
+
190
+ return layer({
191
+ url: endpoint,
192
+ resource: options?.resource,
193
+ headers: options?.headers ?? (yield* OtlpEnv.headers("LOGS")),
194
+ exportInterval: Option.getOrUndefined(exportInterval),
195
+ maxBatchSize: Option.getOrUndefined(maxBatchSize),
196
+ shutdownTimeout: Option.getOrUndefined(shutdownTimeout),
197
+ excludeLogSpans: options?.excludeLogSpans,
198
+ mergeWithExisting: options?.mergeWithExisting
199
+ })
200
+ }).pipe(Effect.orDie, Layer.unwrap)
201
+
147
202
  /**
148
203
  * OTLP logs payload serialized by `OtlpLogger`.
149
204
  *
@@ -25,14 +25,17 @@
25
25
  */
26
26
  import * as Arr from "../../Array.ts"
27
27
  import { Clock } from "../../Clock.ts"
28
+ import * as Config from "../../Config.ts"
28
29
  import * as Duration from "../../Duration.ts"
29
30
  import * as Effect from "../../Effect.ts"
30
31
  import * as Layer from "../../Layer.ts"
31
32
  import * as Metric from "../../Metric.ts"
33
+ import * as Option from "../../Option.ts"
32
34
  import type * as Scope from "../../Scope.ts"
33
35
  import type * as Headers from "../http/Headers.ts"
34
36
  import type { HttpBody } from "../http/HttpBody.ts"
35
37
  import type * as HttpClient from "../http/HttpClient.ts"
38
+ import * as OtlpEnv from "./internal/otlpEnv.ts"
36
39
  import * as Exporter from "./OtlpExporter.ts"
37
40
  import type { Fixed64, KeyValue } from "./OtlpResource.ts"
38
41
  import * as OtlpResource from "./OtlpResource.ts"
@@ -475,6 +478,54 @@ export const layer = (options: {
475
478
  readonly temporality?: AggregationTemporality | undefined
476
479
  }): Layer.Layer<never, never, HttpClient.HttpClient | OtlpSerialization> => Layer.effectDiscard(make(options))
477
480
 
481
+ /**
482
+ * Creates an OTLP metrics layer from OpenTelemetry configuration.
483
+ *
484
+ * @category layers
485
+ * @since 4.0.0
486
+ */
487
+ export const layerFromConfig = (options?: {
488
+ readonly resource?: {
489
+ readonly serviceName?: string | undefined
490
+ readonly serviceVersion?: string | undefined
491
+ readonly attributes?: Record<string, unknown>
492
+ } | undefined
493
+ readonly headers?: Headers.Input | undefined
494
+ }): Layer.Layer<never, never, HttpClient.HttpClient | OtlpSerialization> =>
495
+ Effect.gen(function*() {
496
+ const { disabled, endpoint, exporters } = yield* Config.all({
497
+ disabled: Config.boolean("OTEL_SDK_DISABLED").pipe(Config.withDefault(false)),
498
+ endpoint: OtlpEnv.endpoint("METRICS"),
499
+ exporters: OtlpEnv.exporters("METRICS")
500
+ })
501
+ if (disabled || !endpoint || !exporters.includes("otlp")) {
502
+ return Layer.empty
503
+ }
504
+
505
+ const { baseTimeout, metricsTimeout, exportTimeout, exportInterval, temporalityPreference } = yield* Config.all({
506
+ baseTimeout: Config.option(Config.int("OTEL_EXPORTER_OTLP_TIMEOUT")),
507
+ metricsTimeout: Config.option(Config.int("OTEL_EXPORTER_OTLP_METRICS_TIMEOUT")),
508
+ exportTimeout: Config.option(Config.int("OTEL_METRIC_EXPORT_TIMEOUT")),
509
+ exportInterval: Config.option(Config.int("OTEL_METRIC_EXPORT_INTERVAL")),
510
+ temporalityPreference: Config.option(
511
+ Config.literals(["delta", "cumulative"], "OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE")
512
+ )
513
+ })
514
+
515
+ const shutdownTimeout = Option.firstSomeOf([metricsTimeout, baseTimeout, exportTimeout]).pipe(
516
+ Option.map((_) => Duration.millis(_))
517
+ )
518
+
519
+ return layer({
520
+ url: endpoint,
521
+ resource: options?.resource,
522
+ headers: options?.headers ?? (yield* OtlpEnv.headers("METRICS")),
523
+ exportInterval: Option.getOrUndefined(Option.map(exportInterval, (_) => Duration.millis(_))),
524
+ shutdownTimeout: Option.getOrUndefined(shutdownTimeout),
525
+ temporality: Option.getOrUndefined(temporalityPreference)
526
+ })
527
+ }).pipe(Effect.orDie, Layer.unwrap)
528
+
478
529
  /**
479
530
  * OTLP metrics payload serialized by `OtlpMetrics`.
480
531
  *
@@ -26,10 +26,10 @@
26
26
  * **Gotchas**
27
27
  *
28
28
  * `service.name` is required because the signal exporters also use it as the
29
- * instrumentation scope name. Explicit options take precedence over
30
- * environment variables. `service.name` and `service.version` are normalized
31
- * into canonical OTLP attributes instead of being left in the custom attribute
32
- * map. Unsupported runtime values are formatted as strings.
29
+ * instrumentation scope name. OpenTelemetry environment variables take
30
+ * precedence over explicit options. `service.name` and `service.version` are
31
+ * normalized into canonical OTLP attributes instead of being left in the custom
32
+ * attribute map. Unsupported runtime values are formatted as strings.
33
33
  *
34
34
  * **See also**
35
35
  *
@@ -102,9 +102,9 @@ export const make = (options: {
102
102
  *
103
103
  * **Details**
104
104
  *
105
- * Explicit options override `OTEL_RESOURCE_ATTRIBUTES`, `OTEL_SERVICE_NAME`,
106
- * and `OTEL_SERVICE_VERSION`; missing required configuration is converted to a
107
- * defect.
105
+ * `OTEL_RESOURCE_ATTRIBUTES`, `OTEL_SERVICE_NAME`, and
106
+ * `OTEL_SERVICE_VERSION` override explicit options; missing required
107
+ * configuration is converted to a defect.
108
108
  *
109
109
  * @category constructors
110
110
  * @since 4.0.0
@@ -120,19 +120,30 @@ export const fromConfig: (
120
120
  readonly serviceVersion?: string | undefined
121
121
  readonly attributes?: Record<string, unknown> | undefined
122
122
  }) {
123
+ const env = yield* Config.schema(
124
+ Schema.UndefinedOr(Config.Record(Schema.String, Schema.String)),
125
+ "OTEL_RESOURCE_ATTRIBUTES"
126
+ )
127
+
128
+ const serviceName = (yield* Config.schema(Schema.UndefinedOr(Schema.String), "OTEL_SERVICE_NAME"))
129
+ ?? env?.["service.name"] as string | undefined
130
+ ?? options?.attributes?.["service.name"] as string | undefined
131
+ ?? options?.serviceName
132
+ ?? (yield* Config.string("OTEL_SERVICE_NAME"))
133
+
134
+ const serviceVersion = (yield* Config.schema(Schema.UndefinedOr(Schema.String), "OTEL_SERVICE_VERSION"))
135
+ ?? env?.["service.version"] as string | undefined
136
+ ?? options?.attributes?.["service.version"] as string | undefined
137
+ ?? options?.serviceVersion
138
+
123
139
  const attributes = {
124
- ...(yield* Config.schema(
125
- Schema.UndefinedOr(Config.Record(Schema.String, Schema.String)),
126
- "OTEL_RESOURCE_ATTRIBUTES"
127
- )),
128
- ...options?.attributes
140
+ ...options?.attributes,
141
+ ...env
129
142
  }
130
- const serviceName = options?.serviceName ?? attributes["service.name"] as string ??
131
- (yield* Config.schema(Schema.String, "OTEL_SERVICE_NAME"))
143
+
132
144
  delete attributes["service.name"]
133
- const serviceVersion = options?.serviceVersion ?? attributes["service.version"] as string ??
134
- (yield* Config.schema(Schema.UndefinedOr(Schema.String), "OTEL_SERVICE_VERSION"))
135
145
  delete attributes["service.version"]
146
+
136
147
  return make({
137
148
  serviceName,
138
149
  serviceVersion,
@@ -24,6 +24,7 @@
24
24
  * @since 4.0.0
25
25
  */
26
26
  import * as Cause from "../../Cause.ts"
27
+ import * as Config from "../../Config.ts"
27
28
  import type * as Context from "../../Context.ts"
28
29
  import * as Duration from "../../Duration.ts"
29
30
  import * as Effect from "../../Effect.ts"
@@ -36,6 +37,7 @@ import * as Tracer from "../../Tracer.ts"
36
37
  import type { ExtractTag, Mutable } from "../../Types.ts"
37
38
  import type * as Headers from "../http/Headers.ts"
38
39
  import type * as HttpClient from "../http/HttpClient.ts"
40
+ import * as OtlpEnv from "./internal/otlpEnv.ts"
39
41
  import * as Exporter from "./OtlpExporter.ts"
40
42
  import type { KeyValue, Resource } from "./OtlpResource.ts"
41
43
  import { entriesToAttributes } from "./OtlpResource.ts"
@@ -147,6 +149,56 @@ export const layer: (options: {
147
149
  readonly shutdownTimeout?: Duration.Input | undefined
148
150
  }) => Layer.Layer<never, never, OtlpSerialization | HttpClient.HttpClient> = flow(make, Layer.effect(Tracer.Tracer))
149
151
 
152
+ /**
153
+ * Creates an OTLP traces layer from OpenTelemetry configuration.
154
+ *
155
+ * @category layers
156
+ * @since 4.0.0
157
+ */
158
+ export const layerFromConfig = (options?: {
159
+ readonly resource?: {
160
+ readonly serviceName?: string | undefined
161
+ readonly serviceVersion?: string | undefined
162
+ readonly attributes?: Record<string, unknown>
163
+ } | undefined
164
+ readonly headers?: Headers.Input | undefined
165
+ readonly context?: (<X>(primitive: Tracer.EffectPrimitive<X>, span: Tracer.AnySpan) => X) | undefined
166
+ }): Layer.Layer<never, never, HttpClient.HttpClient | OtlpSerialization> =>
167
+ Effect.gen(function*() {
168
+ const { disabled, endpoint, exporters } = yield* Config.all({
169
+ disabled: Config.boolean("OTEL_SDK_DISABLED").pipe(Config.withDefault(false)),
170
+ endpoint: OtlpEnv.endpoint("TRACES"),
171
+ exporters: OtlpEnv.exporters("TRACES")
172
+ })
173
+
174
+ if (disabled || !endpoint || !exporters.includes("otlp")) {
175
+ return Layer.empty
176
+ }
177
+
178
+ const { baseTimeout, tracesTimeout, exportTimeout, scheduleDelay, maxBatchSize } = yield* Config.all({
179
+ baseTimeout: Config.option(Config.int("OTEL_EXPORTER_OTLP_TIMEOUT")),
180
+ tracesTimeout: Config.option(Config.int("OTEL_EXPORTER_OTLP_TRACES_TIMEOUT")),
181
+ exportTimeout: Config.option(Config.int("OTEL_BSP_EXPORT_TIMEOUT")),
182
+ scheduleDelay: Config.option(Config.int("OTEL_BSP_SCHEDULE_DELAY")),
183
+ maxBatchSize: Config.option(Config.int("OTEL_BSP_MAX_EXPORT_BATCH_SIZE"))
184
+ })
185
+
186
+ const shutdownTimeout = Option.firstSomeOf([tracesTimeout, baseTimeout, exportTimeout]).pipe(
187
+ Option.map((_) => Duration.millis(_))
188
+ )
189
+ const exportInterval = Option.map(scheduleDelay, (_) => Duration.millis(_))
190
+
191
+ return layer({
192
+ url: endpoint,
193
+ resource: options?.resource,
194
+ headers: options?.headers ?? (yield* OtlpEnv.headers("TRACES")),
195
+ exportInterval: Option.getOrUndefined(exportInterval),
196
+ maxBatchSize: Option.getOrUndefined(maxBatchSize),
197
+ context: options?.context,
198
+ shutdownTimeout: Option.getOrUndefined(shutdownTimeout)
199
+ })
200
+ }).pipe(Effect.orDie, Layer.unwrap)
201
+
150
202
  // internal
151
203
 
152
204
  interface SpanImpl extends Tracer.Span {
@@ -0,0 +1,58 @@
1
+ import * as Config from "../../../Config.ts"
2
+ import * as Effect from "../../../Effect.ts"
3
+ import * as Option from "../../../Option.ts"
4
+ import * as Schema from "../../../Schema.ts"
5
+ import * as SchemaGetter from "../../../SchemaGetter.ts"
6
+ import * as SchemaTransformation from "../../../SchemaTransformation.ts"
7
+ import * as HttpClientRequest from "../../http/HttpClientRequest.ts"
8
+
9
+ export type Signal = "LOGS" | "METRICS" | "TRACES"
10
+
11
+ const ExporterList = Config.Array(
12
+ Schema.String.pipe(
13
+ Schema.decode(SchemaTransformation.trim()),
14
+ Schema.decode(SchemaTransformation.toLowerCase())
15
+ )
16
+ ).pipe(
17
+ Schema.decode({
18
+ decode: SchemaGetter.transform((_: ReadonlyArray<string>) => _.filter((_) => _ !== "")),
19
+ encode: SchemaGetter.passthrough()
20
+ })
21
+ )
22
+
23
+ const HeadersRecord = Config.Record(Schema.String, Schema.String)
24
+
25
+ export const headers = (signal: Signal) =>
26
+ Config.make((provider) =>
27
+ Effect.gen(function*() {
28
+ const headers = yield* Config.option(Config.schema(HeadersRecord, `OTEL_EXPORTER_OTLP_${signal}_HEADERS`)).parse(
29
+ provider
30
+ )
31
+ if (Option.isSome(headers)) {
32
+ return headers.value
33
+ }
34
+
35
+ const fallback = yield* Config.option(Config.schema(HeadersRecord, "OTEL_EXPORTER_OTLP_HEADERS")).parse(provider)
36
+ return Option.getOrUndefined(fallback)
37
+ })
38
+ )
39
+
40
+ export const endpoint = (signal: Signal) =>
41
+ Config.make((provider) =>
42
+ Effect.gen(function*() {
43
+ const endpoint = yield* Config.option(Config.string(`OTEL_EXPORTER_OTLP_${signal}_ENDPOINT`)).parse(provider)
44
+ if (Option.isSome(endpoint)) {
45
+ return endpoint.value
46
+ }
47
+
48
+ const fallback = yield* Config.option(Config.string("OTEL_EXPORTER_OTLP_ENDPOINT")).parse(provider)
49
+ return Option.isSome(fallback) && fallback.value !== ""
50
+ ? HttpClientRequest.appendUrl(HttpClientRequest.get(fallback.value), `/v1/${signal.toLowerCase()}`).url
51
+ : undefined
52
+ })
53
+ )
54
+
55
+ export const exporters = (signal: Signal) =>
56
+ Config.option(Config.schema(ExporterList, `OTEL_${signal}_EXPORTER`)).pipe(
57
+ Config.map(Option.getOrElse<ReadonlyArray<string>>(() => []))
58
+ )
@@ -224,7 +224,7 @@ export const exitSchema = <A extends Schema.Top, E extends Schema.Top>(
224
224
  ): Schema.Exit<A, E, Schema.Defect> => {
225
225
  let schema = exitSchemaCache.get(self)
226
226
  if (schema) return schema as Schema.Exit<A, E, Schema.Defect>
227
- schema = Schema.Exit(self[symbol].success, self[symbol].error, Schema.Defect)
227
+ schema = Schema.Exit(self[symbol].success, self[symbol].error, Schema.Defect())
228
228
  exitSchemaCache.set(self, schema)
229
229
  return schema as Schema.Exit<A, E, Schema.Defect>
230
230
  }
@@ -244,7 +244,7 @@ export class PersistedQueueError extends Schema.ErrorClass<PersistedQueueError>(
244
244
  )({
245
245
  _tag: Schema.tag("PersistedQueueError"),
246
246
  message: Schema.String,
247
- cause: Schema.optional(Schema.Defect)
247
+ cause: Schema.optional(Schema.Defect())
248
248
  }) {
249
249
  /**
250
250
  * Marks this value as a persisted queue error for runtime guards.
@@ -52,7 +52,7 @@ const ErrorTypeId = "~effect/persistence/Persistence/PersistenceError" as const
52
52
  export class PersistenceError extends Schema.ErrorClass<PersistenceError>(ErrorTypeId)({
53
53
  _tag: Schema.tag("PersistenceError"),
54
54
  message: Schema.String,
55
- cause: Schema.optional(Schema.Defect)
55
+ cause: Schema.optional(Schema.Defect())
56
56
  }) {
57
57
  /**
58
58
  * Marks this value as a persistence error for runtime guards.
@@ -396,7 +396,7 @@ export class RateLimitStoreError extends Schema.ErrorClass<RateLimitStoreError>(
396
396
  )({
397
397
  _tag: Schema.tag("RateLimitStoreError"),
398
398
  message: Schema.String,
399
- cause: Schema.optional(Schema.Defect)
399
+ cause: Schema.optional(Schema.Defect())
400
400
  }) {}
401
401
 
402
402
  /**
@@ -115,7 +115,7 @@ const ErrorTypeId: ErrorTypeId = "~effect/persistence/Redis/RedisError"
115
115
  */
116
116
  export class RedisError extends Schema.ErrorClass<RedisError>(ErrorTypeId)({
117
117
  _tag: Schema.tag("RedisError"),
118
- cause: Schema.Defect
118
+ cause: Schema.Defect()
119
119
  }) {
120
120
  /**
121
121
  * Marks this value as a Redis persistence error for runtime guards.
@@ -1129,7 +1129,7 @@ export const Schema = <
1129
1129
  AsyncResult<A["Type"], E["Type"]>,
1130
1130
  AsyncResult<A["Encoded"], E["Encoded"]>
1131
1131
  >()(
1132
- [success_, Schema_.Cause(error, Schema_.Defect)],
1132
+ [success_, Schema_.Cause(error, Schema_.Defect())],
1133
1133
  ([value, cause]) => (input, ast, options) => {
1134
1134
  if (!isAsyncResult(input)) {
1135
1135
  return Effect.fail(new SchemaIssue.InvalidType(ast, Option.some(input)))
@@ -35,7 +35,7 @@
35
35
  * - Streaming RPCs store element and stream-error schemas in
36
36
  * `RpcSchema.Stream`; the immediate exit success is `void` and the ordinary
37
37
  * RPC error schema is `Schema.Never`
38
- * - Defects use a separate defect schema, defaulting to `Schema.Defect`; custom
38
+ * - Defects use a separate defect schema, defaulting to `Schema.Defect()`; custom
39
39
  * defect schemas must not require decoding or encoding services
40
40
  * - Schema services are directional: clients encode payloads and decode
41
41
  * responses, while servers decode payloads and encode responses
@@ -956,7 +956,7 @@ export const make = <
956
956
  > => {
957
957
  const successSchema = options?.success ?? Schema.Void
958
958
  const errorSchema = options?.error ?? Schema.Never
959
- const defectSchema = options?.defect ?? Schema.Defect
959
+ const defectSchema = options?.defect ?? Schema.Defect()
960
960
  let payloadSchema: any
961
961
  if (options?.primaryKey) {
962
962
  payloadSchema = class Payload extends Schema.Class<Payload>(`effect/rpc/Rpc/${tag}`)(options.payload as any) {
@@ -1058,7 +1058,7 @@ export const custom = <Def extends Custom>(
1058
1058
  > => {
1059
1059
  const success = options?.success ?? Schema.Void
1060
1060
  const error = options?.error ?? Schema.Never
1061
- const defect = options?.defect ?? Schema.Defect
1061
+ const defect = options?.defect ?? Schema.Defect()
1062
1062
  const out = f({
1063
1063
  success,
1064
1064
  error,
@@ -1407,4 +1407,4 @@ export class ConnectionHooks extends Context.Service<ConnectionHooks, {
1407
1407
 
1408
1408
  // internal
1409
1409
 
1410
- const decodeDefect = Schema.decodeSync(Schema.Defect)
1410
+ const decodeDefect = Schema.decodeSync(Schema.Defect())
@@ -48,7 +48,7 @@ const TypeId = "~effect/rpc/RpcClientError"
48
48
  export class RpcClientDefect extends Schema.ErrorClass<RpcClientDefect>("effect/rpc/RpcClientError/RpcClientDefect")({
49
49
  _tag: Schema.tag("RpcClientDefect"),
50
50
  message: Schema.String,
51
- cause: Schema.Defect
51
+ cause: Schema.Defect()
52
52
  }) {}
53
53
 
54
54
  /**
@@ -350,11 +350,11 @@ export interface ResponseDefectEncoded {
350
350
  readonly defect: unknown
351
351
  }
352
352
 
353
- const encodeDefect = Schema.encodeSync(Schema.Defect)
353
+ const encodeDefect = Schema.encodeSync(Schema.Defect())
354
354
 
355
355
  /**
356
356
  * Creates an encoded terminal response for a request whose exit is a defect
357
- * encoded with `Schema.Defect`.
357
+ * encoded with `Schema.Defect()`.
358
358
  *
359
359
  * @category response
360
360
  * @since 4.0.0
@@ -376,7 +376,7 @@ export const ResponseExitDieEncoded = (options: {
376
376
 
377
377
  /**
378
378
  * Creates a transport-encoded defect response by encoding the input with
379
- * `Schema.Defect`.
379
+ * `Schema.Defect()`.
380
380
  *
381
381
  * @category response
382
382
  * @since 4.0.0
@@ -237,7 +237,7 @@ export const isSocketError = (u: unknown): u is SocketError => Predicate.hasProp
237
237
  */
238
238
  export class SocketReadError extends Schema.ErrorClass<SocketReadError>("effect/socket/Socket/SocketReadError")({
239
239
  _tag: Schema.tag("SocketReadError"),
240
- cause: Schema.Defect
240
+ cause: Schema.Defect()
241
241
  }) {
242
242
  /**
243
243
  * Default message used for socket read failures.
@@ -255,7 +255,7 @@ export class SocketReadError extends Schema.ErrorClass<SocketReadError>("effect/
255
255
  */
256
256
  export class SocketWriteError extends Schema.ErrorClass<SocketWriteError>("effect/socket/Socket/SocketWriteError")({
257
257
  _tag: Schema.tag("SocketWriteError"),
258
- cause: Schema.Defect
258
+ cause: Schema.Defect()
259
259
  }) {
260
260
  /**
261
261
  * Default message used for socket write failures.
@@ -275,7 +275,7 @@ export class SocketWriteError extends Schema.ErrorClass<SocketWriteError>("effec
275
275
  export class SocketOpenError extends Schema.ErrorClass<SocketOpenError>("effect/socket/Socket/SocketOpenError")({
276
276
  _tag: Schema.tag("SocketOpenError"),
277
277
  kind: Schema.Literals(["Unknown", "Timeout"]),
278
- cause: Schema.Defect
278
+ cause: Schema.Defect()
279
279
  }) {
280
280
  /**
281
281
  * Formats timeout and unknown open failures for display.
@@ -42,7 +42,7 @@ const TypeId = "~effect/sql/SqlError" as const
42
42
  const ReasonTypeId = "~effect/sql/SqlError/Reason" as const
43
43
 
44
44
  const ReasonFields = {
45
- cause: Schema.Defect,
45
+ cause: Schema.Defect(),
46
46
  message: Schema.optional(Schema.String),
47
47
  operation: Schema.optional(Schema.String)
48
48
  }
@@ -68,7 +68,7 @@ export class WorkerSpawnError extends Schema.ErrorClass<WorkerSpawnError>(
68
68
  )({
69
69
  _tag: Schema.tag("WorkerSpawnError"),
70
70
  message: Schema.String,
71
- cause: Schema.optional(Schema.Defect)
71
+ cause: Schema.optional(Schema.Defect())
72
72
  }) {}
73
73
 
74
74
  /**
@@ -82,7 +82,7 @@ export class WorkerSendError extends Schema.ErrorClass<WorkerSendError>(
82
82
  )({
83
83
  _tag: Schema.tag("WorkerSendError"),
84
84
  message: Schema.String,
85
- cause: Schema.optional(Schema.Defect)
85
+ cause: Schema.optional(Schema.Defect())
86
86
  }) {}
87
87
 
88
88
  /**
@@ -97,7 +97,7 @@ export class WorkerReceiveError extends Schema.ErrorClass<WorkerReceiveError>(
97
97
  )({
98
98
  _tag: Schema.tag("WorkerReceiveError"),
99
99
  message: Schema.String,
100
- cause: Schema.optional(Schema.Defect)
100
+ cause: Schema.optional(Schema.Defect())
101
101
  }) {}
102
102
 
103
103
  /**
@@ -111,7 +111,7 @@ export class WorkerUnknownError extends Schema.ErrorClass<WorkerUnknownError>(
111
111
  )({
112
112
  _tag: Schema.tag("WorkerUnknownError"),
113
113
  message: Schema.String,
114
- cause: Schema.optional(Schema.Defect)
114
+ cause: Schema.optional(Schema.Defect())
115
115
  }) {}
116
116
 
117
117
  /**
@@ -165,7 +165,7 @@ export const make = <
165
165
  name: options.name,
166
166
  successSchema,
167
167
  errorSchema,
168
- exitSchema: Schema.Exit(successSchemaJson, errorSchemaJson, Schema.Defect),
168
+ exitSchema: Schema.Exit(successSchemaJson, errorSchemaJson, Schema.Defect()),
169
169
  annotations: options.annotations ?? Context.empty(),
170
170
  annotate(tag: Context.Key<any, any>, value: any) {
171
171
  return make({