effect 4.0.0-beta.11 → 4.0.0-beta.13

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 (171) hide show
  1. package/dist/Config.d.ts +157 -0
  2. package/dist/Config.d.ts.map +1 -1
  3. package/dist/Config.js +56 -1
  4. package/dist/Config.js.map +1 -1
  5. package/dist/Effect.d.ts +79 -0
  6. package/dist/Effect.d.ts.map +1 -1
  7. package/dist/Effect.js +26 -0
  8. package/dist/Effect.js.map +1 -1
  9. package/dist/Fiber.d.ts +2 -2
  10. package/dist/Fiber.d.ts.map +1 -1
  11. package/dist/Fiber.js.map +1 -1
  12. package/dist/Graph.d.ts.map +1 -1
  13. package/dist/Graph.js +3 -6
  14. package/dist/Graph.js.map +1 -1
  15. package/dist/ManagedRuntime.d.ts +1 -1
  16. package/dist/ManagedRuntime.js +1 -1
  17. package/dist/Random.d.ts +17 -0
  18. package/dist/Random.d.ts.map +1 -1
  19. package/dist/Random.js +17 -0
  20. package/dist/Random.js.map +1 -1
  21. package/dist/Schedule.js +1 -1
  22. package/dist/Schedule.js.map +1 -1
  23. package/dist/Schema.d.ts +3 -1
  24. package/dist/Schema.d.ts.map +1 -1
  25. package/dist/SchemaAST.js +1 -1
  26. package/dist/SchemaAST.js.map +1 -1
  27. package/dist/index.d.ts +47 -0
  28. package/dist/index.d.ts.map +1 -1
  29. package/dist/index.js +47 -0
  30. package/dist/index.js.map +1 -1
  31. package/dist/internal/effect.js +59 -44
  32. package/dist/internal/effect.js.map +1 -1
  33. package/dist/internal/request.js +2 -2
  34. package/dist/internal/request.js.map +1 -1
  35. package/dist/internal/schema/annotations.js +2 -0
  36. package/dist/internal/schema/annotations.js.map +1 -1
  37. package/dist/unstable/ai/LanguageModel.d.ts.map +1 -1
  38. package/dist/unstable/ai/LanguageModel.js +49 -18
  39. package/dist/unstable/ai/LanguageModel.js.map +1 -1
  40. package/dist/unstable/ai/McpSchema.d.ts +134 -58
  41. package/dist/unstable/ai/McpSchema.d.ts.map +1 -1
  42. package/dist/unstable/ai/McpSchema.js +49 -12
  43. package/dist/unstable/ai/McpSchema.js.map +1 -1
  44. package/dist/unstable/ai/McpServer.d.ts.map +1 -1
  45. package/dist/unstable/ai/McpServer.js +33 -6
  46. package/dist/unstable/ai/McpServer.js.map +1 -1
  47. package/dist/unstable/ai/Tool.d.ts +16 -0
  48. package/dist/unstable/ai/Tool.d.ts.map +1 -1
  49. package/dist/unstable/ai/Tool.js +14 -0
  50. package/dist/unstable/ai/Tool.js.map +1 -1
  51. package/dist/unstable/cli/CliOutput.js +24 -2
  52. package/dist/unstable/cli/CliOutput.js.map +1 -1
  53. package/dist/unstable/cli/Command.d.ts +41 -6
  54. package/dist/unstable/cli/Command.d.ts.map +1 -1
  55. package/dist/unstable/cli/Command.js +64 -31
  56. package/dist/unstable/cli/Command.js.map +1 -1
  57. package/dist/unstable/cli/GlobalFlag.d.ts +162 -0
  58. package/dist/unstable/cli/GlobalFlag.d.ts.map +1 -0
  59. package/dist/unstable/cli/GlobalFlag.js +164 -0
  60. package/dist/unstable/cli/GlobalFlag.js.map +1 -0
  61. package/dist/unstable/cli/HelpDoc.d.ts +10 -0
  62. package/dist/unstable/cli/HelpDoc.d.ts.map +1 -1
  63. package/dist/unstable/cli/index.d.ts +4 -0
  64. package/dist/unstable/cli/index.d.ts.map +1 -1
  65. package/dist/unstable/cli/index.js +4 -0
  66. package/dist/unstable/cli/index.js.map +1 -1
  67. package/dist/unstable/cli/internal/command.d.ts +1 -5
  68. package/dist/unstable/cli/internal/command.d.ts.map +1 -1
  69. package/dist/unstable/cli/internal/command.js +4 -22
  70. package/dist/unstable/cli/internal/command.js.map +1 -1
  71. package/dist/unstable/cli/internal/help.d.ts +19 -0
  72. package/dist/unstable/cli/internal/help.d.ts.map +1 -0
  73. package/dist/unstable/cli/internal/help.js +54 -0
  74. package/dist/unstable/cli/internal/help.js.map +1 -0
  75. package/dist/unstable/cli/internal/parser.js +20 -35
  76. package/dist/unstable/cli/internal/parser.js.map +1 -1
  77. package/dist/unstable/cluster/ClusterWorkflowEngine.d.ts.map +1 -1
  78. package/dist/unstable/cluster/ClusterWorkflowEngine.js +2 -2
  79. package/dist/unstable/cluster/ClusterWorkflowEngine.js.map +1 -1
  80. package/dist/unstable/http/HttpClient.d.ts +28 -4
  81. package/dist/unstable/http/HttpClient.d.ts.map +1 -1
  82. package/dist/unstable/http/HttpClient.js.map +1 -1
  83. package/dist/unstable/http/HttpEffect.d.ts +3 -8
  84. package/dist/unstable/http/HttpEffect.d.ts.map +1 -1
  85. package/dist/unstable/http/HttpEffect.js +13 -24
  86. package/dist/unstable/http/HttpEffect.js.map +1 -1
  87. package/dist/unstable/http/HttpMiddleware.d.ts.map +1 -1
  88. package/dist/unstable/http/HttpMiddleware.js +4 -8
  89. package/dist/unstable/http/HttpMiddleware.js.map +1 -1
  90. package/dist/unstable/http/HttpServerError.d.ts +6 -1
  91. package/dist/unstable/http/HttpServerError.d.ts.map +1 -1
  92. package/dist/unstable/http/HttpServerError.js +26 -17
  93. package/dist/unstable/http/HttpServerError.js.map +1 -1
  94. package/dist/unstable/http/internal/preResponseHandler.d.ts +2 -0
  95. package/dist/unstable/http/internal/preResponseHandler.d.ts.map +1 -0
  96. package/dist/unstable/http/internal/preResponseHandler.js +10 -0
  97. package/dist/unstable/http/internal/preResponseHandler.js.map +1 -0
  98. package/dist/unstable/httpapi/HttpApiBuilder.d.ts +1 -1
  99. package/dist/unstable/httpapi/HttpApiBuilder.d.ts.map +1 -1
  100. package/dist/unstable/reactivity/Atom.d.ts +4 -4
  101. package/dist/unstable/reactivity/Atom.d.ts.map +1 -1
  102. package/dist/unstable/reactivity/Atom.js +1 -1
  103. package/dist/unstable/reactivity/Atom.js.map +1 -1
  104. package/dist/unstable/rpc/Rpc.d.ts +1 -1
  105. package/dist/unstable/rpc/Rpc.d.ts.map +1 -1
  106. package/dist/unstable/rpc/RpcSchema.d.ts +13 -0
  107. package/dist/unstable/rpc/RpcSchema.d.ts.map +1 -1
  108. package/dist/unstable/rpc/RpcSchema.js +14 -0
  109. package/dist/unstable/rpc/RpcSchema.js.map +1 -1
  110. package/dist/unstable/rpc/RpcSerialization.d.ts.map +1 -1
  111. package/dist/unstable/rpc/RpcSerialization.js +34 -9
  112. package/dist/unstable/rpc/RpcSerialization.js.map +1 -1
  113. package/dist/unstable/rpc/RpcServer.d.ts +0 -7
  114. package/dist/unstable/rpc/RpcServer.d.ts.map +1 -1
  115. package/dist/unstable/rpc/RpcServer.js +5 -10
  116. package/dist/unstable/rpc/RpcServer.js.map +1 -1
  117. package/dist/unstable/schema/Model.d.ts +1 -1
  118. package/dist/unstable/schema/Model.d.ts.map +1 -1
  119. package/dist/unstable/schema/VariantSchema.d.ts +3 -3
  120. package/dist/unstable/schema/VariantSchema.d.ts.map +1 -1
  121. package/dist/unstable/schema/VariantSchema.js +3 -3
  122. package/dist/unstable/schema/VariantSchema.js.map +1 -1
  123. package/dist/unstable/sql/SqlError.d.ts +14 -14
  124. package/dist/unstable/sql/SqlError.d.ts.map +1 -1
  125. package/dist/unstable/sql/SqlError.js +9 -3
  126. package/dist/unstable/sql/SqlError.js.map +1 -1
  127. package/package.json +1 -1
  128. package/src/Config.ts +171 -9
  129. package/src/Effect.ts +80 -0
  130. package/src/Fiber.ts +9 -2
  131. package/src/Graph.ts +16 -6
  132. package/src/ManagedRuntime.ts +1 -1
  133. package/src/Random.ts +18 -0
  134. package/src/Schedule.ts +1 -1
  135. package/src/Schema.ts +1 -1
  136. package/src/SchemaAST.ts +1 -1
  137. package/src/index.ts +47 -0
  138. package/src/internal/effect.ts +82 -49
  139. package/src/internal/request.ts +2 -2
  140. package/src/internal/schema/annotations.ts +2 -0
  141. package/src/unstable/ai/LanguageModel.ts +54 -24
  142. package/src/unstable/ai/McpSchema.ts +59 -13
  143. package/src/unstable/ai/McpServer.ts +44 -6
  144. package/src/unstable/ai/Tool.ts +15 -0
  145. package/src/unstable/cli/CliOutput.ts +32 -2
  146. package/src/unstable/cli/Command.ts +95 -38
  147. package/src/unstable/cli/GlobalFlag.ts +342 -0
  148. package/src/unstable/cli/HelpDoc.ts +12 -0
  149. package/src/unstable/cli/index.ts +5 -0
  150. package/src/unstable/cli/internal/command.ts +5 -30
  151. package/src/unstable/cli/internal/help.ts +66 -0
  152. package/src/unstable/cli/internal/parser.ts +23 -52
  153. package/src/unstable/cluster/ClusterWorkflowEngine.ts +2 -2
  154. package/src/unstable/http/HttpClient.ts +45 -10
  155. package/src/unstable/http/HttpEffect.ts +20 -39
  156. package/src/unstable/http/HttpMiddleware.ts +4 -14
  157. package/src/unstable/http/HttpServerError.ts +29 -18
  158. package/src/unstable/http/internal/preResponseHandler.ts +15 -0
  159. package/src/unstable/httpapi/HttpApiBuilder.ts +1 -1
  160. package/src/unstable/reactivity/Atom.ts +7 -7
  161. package/src/unstable/rpc/Rpc.ts +1 -1
  162. package/src/unstable/rpc/RpcSchema.ts +17 -0
  163. package/src/unstable/rpc/RpcSerialization.ts +44 -9
  164. package/src/unstable/rpc/RpcServer.ts +10 -19
  165. package/src/unstable/schema/VariantSchema.ts +6 -6
  166. package/src/unstable/sql/SqlError.ts +11 -9
  167. package/dist/unstable/cli/internal/builtInFlags.d.ts +0 -7
  168. package/dist/unstable/cli/internal/builtInFlags.d.ts.map +0 -1
  169. package/dist/unstable/cli/internal/builtInFlags.js +0 -44
  170. package/dist/unstable/cli/internal/builtInFlags.js.map +0 -1
  171. package/src/unstable/cli/internal/builtInFlags.ts +0 -78
package/src/Config.ts CHANGED
@@ -886,18 +886,19 @@ export function schema<T, E>(codec: Schema.Codec<T, E>, path?: string | ConfigPr
886
886
  const decodeUnknownEffect = Parser.decodeUnknownEffect(toCodecStringTree)
887
887
  const toCodecStringTreeEncoded = AST.toEncoded(toCodecStringTree.ast)
888
888
  const defaultPath = typeof path === "string" ? [path] : path ?? []
889
- return make((provider) =>
890
- recur(toCodecStringTreeEncoded, provider, defaultPath).pipe(
889
+ return make((provider) => {
890
+ const path = provider.prefix ? [...provider.prefix, ...defaultPath] : defaultPath
891
+ return recur(toCodecStringTreeEncoded, provider, defaultPath).pipe(
891
892
  Effect.flatMapEager((tree) =>
892
- decodeUnknownEffect(tree).pipe(Effect.mapErrorEager((issue) =>
893
- new Schema.SchemaError(defaultPath.length > 0 ? new Issue.Pointer(defaultPath, issue) : issue)
894
- ))
893
+ decodeUnknownEffect(tree).pipe(
894
+ Effect.mapErrorEager((issue) =>
895
+ new Schema.SchemaError(path.length > 0 ? new Issue.Pointer(path, issue) : issue)
896
+ )
897
+ )
895
898
  ),
896
- Effect.mapErrorEager((cause) =>
897
- new ConfigError(cause)
898
- )
899
+ Effect.mapErrorEager((cause) => new ConfigError(cause))
899
900
  )
900
- )
901
+ })
901
902
  }
902
903
 
903
904
  /** @internal */
@@ -1446,3 +1447,164 @@ export function url(name?: string) {
1446
1447
  export function date(name?: string) {
1447
1448
  return schema(Schema.DateValid, name)
1448
1449
  }
1450
+
1451
+ /**
1452
+ * Scopes a config under a named prefix.
1453
+ *
1454
+ * When to use:
1455
+ * - Grouping related config keys under a common namespace (e.g.
1456
+ * `"database"`, `"redis"`).
1457
+ * - Building reusable config fragments that callers nest at different paths.
1458
+ *
1459
+ * The prefix is prepended to every key the inner config reads. With
1460
+ * `fromUnknown` this means an extra object level; with `fromEnv` it means
1461
+ * a `_`-separated prefix on env var names.
1462
+ *
1463
+ * Multiple `nested` calls compose: the outermost name becomes the
1464
+ * outermost path segment.
1465
+ *
1466
+ * **Example** (Nesting a struct config under `"database"`)
1467
+ *
1468
+ * ```ts
1469
+ * import { Config, ConfigProvider, Effect } from "effect"
1470
+ *
1471
+ * const dbConfig = Config.all({
1472
+ * host: Config.string("host"),
1473
+ * port: Config.number("port")
1474
+ * }).pipe(Config.nested("database"))
1475
+ *
1476
+ * const provider = ConfigProvider.fromUnknown({
1477
+ * database: { host: "localhost", port: "5432" }
1478
+ * })
1479
+ * // Effect.runSync(dbConfig.parse(provider))
1480
+ * // { host: "localhost", port: 5432 }
1481
+ * ```
1482
+ *
1483
+ * **Example** (Env vars with nested prefix)
1484
+ *
1485
+ * ```ts
1486
+ * import { Config, ConfigProvider, Effect } from "effect"
1487
+ *
1488
+ * const host = Config.string("host").pipe(Config.nested("database"))
1489
+ *
1490
+ * const provider = ConfigProvider.fromEnv({
1491
+ * env: { database_host: "localhost" }
1492
+ * })
1493
+ * // Effect.runSync(host.parse(provider)) // "localhost"
1494
+ * ```
1495
+ *
1496
+ * @see {@link all} – combine multiple configs into a struct
1497
+ * @see {@link schema} – read structured config from a schema
1498
+ *
1499
+ * @category Combinators
1500
+ * @since 4.0.0
1501
+ */
1502
+ export const nested: {
1503
+ /**
1504
+ * Scopes a config under a named prefix.
1505
+ *
1506
+ * When to use:
1507
+ * - Grouping related config keys under a common namespace (e.g.
1508
+ * `"database"`, `"redis"`).
1509
+ * - Building reusable config fragments that callers nest at different paths.
1510
+ *
1511
+ * The prefix is prepended to every key the inner config reads. With
1512
+ * `fromUnknown` this means an extra object level; with `fromEnv` it means
1513
+ * a `_`-separated prefix on env var names.
1514
+ *
1515
+ * Multiple `nested` calls compose: the outermost name becomes the
1516
+ * outermost path segment.
1517
+ *
1518
+ * **Example** (Nesting a struct config under `"database"`)
1519
+ *
1520
+ * ```ts
1521
+ * import { Config, ConfigProvider, Effect } from "effect"
1522
+ *
1523
+ * const dbConfig = Config.all({
1524
+ * host: Config.string("host"),
1525
+ * port: Config.number("port")
1526
+ * }).pipe(Config.nested("database"))
1527
+ *
1528
+ * const provider = ConfigProvider.fromUnknown({
1529
+ * database: { host: "localhost", port: "5432" }
1530
+ * })
1531
+ * // Effect.runSync(dbConfig.parse(provider))
1532
+ * // { host: "localhost", port: 5432 }
1533
+ * ```
1534
+ *
1535
+ * **Example** (Env vars with nested prefix)
1536
+ *
1537
+ * ```ts
1538
+ * import { Config, ConfigProvider, Effect } from "effect"
1539
+ *
1540
+ * const host = Config.string("host").pipe(Config.nested("database"))
1541
+ *
1542
+ * const provider = ConfigProvider.fromEnv({
1543
+ * env: { database_host: "localhost" }
1544
+ * })
1545
+ * // Effect.runSync(host.parse(provider)) // "localhost"
1546
+ * ```
1547
+ *
1548
+ * @see {@link all} – combine multiple configs into a struct
1549
+ * @see {@link schema} – read structured config from a schema
1550
+ *
1551
+ * @category Combinators
1552
+ * @since 4.0.0
1553
+ */
1554
+ (name: string): <A>(self: Config<A>) => Config<A>
1555
+ /**
1556
+ * Scopes a config under a named prefix.
1557
+ *
1558
+ * When to use:
1559
+ * - Grouping related config keys under a common namespace (e.g.
1560
+ * `"database"`, `"redis"`).
1561
+ * - Building reusable config fragments that callers nest at different paths.
1562
+ *
1563
+ * The prefix is prepended to every key the inner config reads. With
1564
+ * `fromUnknown` this means an extra object level; with `fromEnv` it means
1565
+ * a `_`-separated prefix on env var names.
1566
+ *
1567
+ * Multiple `nested` calls compose: the outermost name becomes the
1568
+ * outermost path segment.
1569
+ *
1570
+ * **Example** (Nesting a struct config under `"database"`)
1571
+ *
1572
+ * ```ts
1573
+ * import { Config, ConfigProvider, Effect } from "effect"
1574
+ *
1575
+ * const dbConfig = Config.all({
1576
+ * host: Config.string("host"),
1577
+ * port: Config.number("port")
1578
+ * }).pipe(Config.nested("database"))
1579
+ *
1580
+ * const provider = ConfigProvider.fromUnknown({
1581
+ * database: { host: "localhost", port: "5432" }
1582
+ * })
1583
+ * // Effect.runSync(dbConfig.parse(provider))
1584
+ * // { host: "localhost", port: 5432 }
1585
+ * ```
1586
+ *
1587
+ * **Example** (Env vars with nested prefix)
1588
+ *
1589
+ * ```ts
1590
+ * import { Config, ConfigProvider, Effect } from "effect"
1591
+ *
1592
+ * const host = Config.string("host").pipe(Config.nested("database"))
1593
+ *
1594
+ * const provider = ConfigProvider.fromEnv({
1595
+ * env: { database_host: "localhost" }
1596
+ * })
1597
+ * // Effect.runSync(host.parse(provider)) // "localhost"
1598
+ * ```
1599
+ *
1600
+ * @see {@link all} – combine multiple configs into a struct
1601
+ * @see {@link schema} – read structured config from a schema
1602
+ *
1603
+ * @category Combinators
1604
+ * @since 4.0.0
1605
+ */
1606
+ <A>(self: Config<A>, name: string): Config<A>
1607
+ } = dual(
1608
+ 2,
1609
+ <A>(self: Config<A>, name: string): Config<A> => make((provider) => self.parse(ConfigProvider.nested(provider, name)))
1610
+ )
package/src/Effect.ts CHANGED
@@ -20846,6 +20846,86 @@ export const annotateLogs = dual<
20846
20846
  })
20847
20847
  )
20848
20848
 
20849
+ /**
20850
+ * Adds log annotations to the current scope.
20851
+ *
20852
+ * This differs from `annotateLogs`, which only annotates a specific effect.
20853
+ * `annotateLogsScoped` updates annotations for the entire current `Scope` and
20854
+ * restores the previous annotations when the scope closes.
20855
+ *
20856
+ * @example
20857
+ * ```ts
20858
+ * import { Effect } from "effect"
20859
+ *
20860
+ * const program = Effect.scoped(
20861
+ * Effect.gen(function*() {
20862
+ * yield* Effect.log("before")
20863
+ * yield* Effect.annotateLogsScoped({ requestId: "req-123" })
20864
+ * yield* Effect.log("inside scope")
20865
+ * })
20866
+ * )
20867
+ *
20868
+ * Effect.runPromise(program)
20869
+ * ```
20870
+ *
20871
+ * @since 4.0.0
20872
+ * @category Logging
20873
+ */
20874
+ export const annotateLogsScoped: {
20875
+ /**
20876
+ * Adds log annotations to the current scope.
20877
+ *
20878
+ * This differs from `annotateLogs`, which only annotates a specific effect.
20879
+ * `annotateLogsScoped` updates annotations for the entire current `Scope` and
20880
+ * restores the previous annotations when the scope closes.
20881
+ *
20882
+ * @example
20883
+ * ```ts
20884
+ * import { Effect } from "effect"
20885
+ *
20886
+ * const program = Effect.scoped(
20887
+ * Effect.gen(function*() {
20888
+ * yield* Effect.log("before")
20889
+ * yield* Effect.annotateLogsScoped({ requestId: "req-123" })
20890
+ * yield* Effect.log("inside scope")
20891
+ * })
20892
+ * )
20893
+ *
20894
+ * Effect.runPromise(program)
20895
+ * ```
20896
+ *
20897
+ * @since 4.0.0
20898
+ * @category Logging
20899
+ */
20900
+ (key: string, value: unknown): Effect<void, never, Scope>
20901
+ /**
20902
+ * Adds log annotations to the current scope.
20903
+ *
20904
+ * This differs from `annotateLogs`, which only annotates a specific effect.
20905
+ * `annotateLogsScoped` updates annotations for the entire current `Scope` and
20906
+ * restores the previous annotations when the scope closes.
20907
+ *
20908
+ * @example
20909
+ * ```ts
20910
+ * import { Effect } from "effect"
20911
+ *
20912
+ * const program = Effect.scoped(
20913
+ * Effect.gen(function*() {
20914
+ * yield* Effect.log("before")
20915
+ * yield* Effect.annotateLogsScoped({ requestId: "req-123" })
20916
+ * yield* Effect.log("inside scope")
20917
+ * })
20918
+ * )
20919
+ *
20920
+ * Effect.runPromise(program)
20921
+ * ```
20922
+ *
20923
+ * @since 4.0.0
20924
+ * @category Logging
20925
+ */
20926
+ (values: Record<string, unknown>): Effect<void, never, Scope>
20927
+ } = internal.annotateLogsScoped
20928
+
20849
20929
  /**
20850
20930
  * Adds a span to each log line in this effect.
20851
20931
  *
package/src/Fiber.ts CHANGED
@@ -338,7 +338,10 @@ export const interruptAs: {
338
338
  * @since 2.0.0
339
339
  * @category interruption
340
340
  */
341
- (fiberId: number): <A, E>(self: Fiber<A, E>) => Effect<void>
341
+ (
342
+ fiberId: number | undefined,
343
+ annotations?: ServiceMap.ServiceMap<never> | undefined
344
+ ): <A, E>(self: Fiber<A, E>) => Effect<void>
342
345
  /**
343
346
  * Interrupts a fiber with a specific fiber ID as the interruptor. This allows
344
347
  * tracking which fiber initiated the interruption.
@@ -361,7 +364,11 @@ export const interruptAs: {
361
364
  * @since 2.0.0
362
365
  * @category interruption
363
366
  */
364
- <A, E>(self: Fiber<A, E>, fiberId: number): Effect<void>
367
+ <A, E>(
368
+ self: Fiber<A, E>,
369
+ fiberId: number | undefined,
370
+ annotations?: ServiceMap.ServiceMap<never> | undefined
371
+ ): Effect<void>
365
372
  } = effect.fiberInterruptAs
366
373
 
367
374
  /**
package/src/Graph.ts CHANGED
@@ -2529,13 +2529,23 @@ export interface MermaidOptions<N, E> {
2529
2529
  * Escapes special characters in labels for Mermaid syntax compatibility.
2530
2530
  */
2531
2531
  const escapeMermaidLabel = (label: string): string => {
2532
+ // Escape special characters for Mermaid using HTML entity codes
2533
+ // According to: https://mermaid.js.org/syntax/flowchart.html#special-characters-that-break-syntax
2532
2534
  return label
2533
- .replace(/\\/g, "\\\\") // Escape backslashes first
2534
- .replace(/"/g, "\\\"") // Escape quotes
2535
- .replace(/\[/g, "\\[") // Escape square brackets
2536
- .replace(/\]/g, "\\]") // Escape square brackets
2537
- .replace(/\|/g, "\\|") // Escape pipes
2538
- .replace(/\n/g, "<br/>"); // Convert newlines to HTML breaks
2535
+ .replace(/#/g, "#35;")
2536
+ .replace(/"/g, "#quot;")
2537
+ .replace(/</g, "#lt;")
2538
+ .replace(/>/g, "#gt;")
2539
+ .replace(/&/g, "#amp;")
2540
+ .replace(/\[/g, "#91;")
2541
+ .replace(/\]/g, "#93;")
2542
+ .replace(/\{/g, "#123;")
2543
+ .replace(/\}/g, "#125;")
2544
+ .replace(/\(/g, "#40;")
2545
+ .replace(/\)/g, "#41;")
2546
+ .replace(/\|/g, "#124;")
2547
+ .replace(/\\/g, "#92;")
2548
+ .replace(/\n/g, "<br/>");
2539
2549
  }
2540
2550
 
2541
2551
  /**
@@ -140,7 +140,7 @@ export interface ManagedRuntime<in R, out ER> {
140
140
  * class Notifications extends ServiceMap.Service<Notifications, {
141
141
  * readonly notify: (message: string) => Effect.Effect<void>
142
142
  * }>()("Notifications") {
143
- * static layer = Layer.succeed(this)({
143
+ * static readonly layer = Layer.succeed(this)({
144
144
  * notify: (message) => Console.log(message)
145
145
  * })
146
146
  * }
package/src/Random.ts CHANGED
@@ -76,6 +76,24 @@ const randomWith = <A>(f: (random: typeof Random["Service"]) => A): Effect.Effec
76
76
  */
77
77
  export const next: Effect.Effect<number> = randomWith((r) => r.nextDoubleUnsafe())
78
78
 
79
+ /**
80
+ * Generates a random boolean value.
81
+ *
82
+ * @example
83
+ * ```ts
84
+ * import { Effect, Random } from "effect"
85
+ *
86
+ * const program = Effect.gen(function*() {
87
+ * const value = yield* Random.nextBoolean
88
+ * console.log("Random boolean:", value)
89
+ * })
90
+ * ```
91
+ *
92
+ * @since 4.0.0
93
+ * @category Random Number Generators
94
+ */
95
+ export const nextBoolean: Effect.Effect<boolean> = randomWith((r) => r.nextDoubleUnsafe() > 0.5)
96
+
79
97
  /**
80
98
  * Generates a random integer between `Number.MIN_SAFE_INTEGER` (inclusive)
81
99
  * and `Number.MAX_SAFE_INTEGER` (inclusive).
package/src/Schedule.ts CHANGED
@@ -4015,7 +4015,7 @@ export const fixed = (interval: Duration.Input): Schedule<number> => {
4015
4015
  return fromStepWithMetadata(effect.succeed((meta) =>
4016
4016
  effect.succeed([
4017
4017
  meta.attempt - 1,
4018
- window === 0 || meta.elapsedSincePrevious > window
4018
+ window === 0
4019
4019
  ? Duration.zero
4020
4020
  : Duration.millis(window - (meta.elapsed % window))
4021
4021
  ])
package/src/Schema.ts CHANGED
@@ -1717,7 +1717,7 @@ export function fieldsAssign<const NewFields extends Struct.Fields>(fields: NewF
1717
1717
  * @since 4.0.0
1718
1718
  */
1719
1719
  export function encodeKeys<
1720
- S extends Struct<Struct.Fields>,
1720
+ S extends Top & { readonly fields: Struct.Fields },
1721
1721
  const M extends { readonly [K in keyof S["fields"]]?: PropertyKey }
1722
1722
  >(mapping: M) {
1723
1723
  return function(
package/src/SchemaAST.ts CHANGED
@@ -594,7 +594,7 @@ export class Declaration extends Base {
594
594
  }
595
595
  /** @internal */
596
596
  getExpected(): string {
597
- const expected = this.annotations?.identifier ?? this.annotations?.expected
597
+ const expected = this.annotations?.expected
598
598
  if (typeof expected === "string") return expected
599
599
  return "<Declaration>"
600
600
  }
package/src/index.ts CHANGED
@@ -1120,6 +1120,53 @@ export * as Equal from "./Equal.ts"
1120
1120
  export * as Equivalence from "./Equivalence.ts"
1121
1121
 
1122
1122
  /**
1123
+ * Pluggable error reporting for Effect programs.
1124
+ *
1125
+ * Reporting is triggered by `Effect.withErrorReporting`,
1126
+ * `ErrorReporter.report`, or built-in reporting boundaries in the HTTP and
1127
+ * RPC server modules.
1128
+ *
1129
+ * Each reporter receives a structured callback with the failing `Cause`, a
1130
+ * pretty-printed `Error`, severity, and any extra attributes attached to the
1131
+ * original error — making it straightforward to forward failures to Sentry,
1132
+ * Datadog, or a custom logging backend.
1133
+ *
1134
+ * Use the annotation symbols (`ignore`, `severity`, `attributes`) on your
1135
+ * error classes to control reporting behavior per-error.
1136
+ *
1137
+ * @example
1138
+ * ```ts
1139
+ * import { Data, Effect, ErrorReporter } from "effect"
1140
+ *
1141
+ * // A reporter that logs to the console
1142
+ * const consoleReporter = ErrorReporter.make(({ error, severity }) => {
1143
+ * console.error(`[${severity}]`, error.message)
1144
+ * })
1145
+ *
1146
+ * // An error that should be ignored by reporters
1147
+ * class NotFoundError extends Data.TaggedError("NotFoundError")<{}> {
1148
+ * readonly [ErrorReporter.ignore] = true
1149
+ * }
1150
+ *
1151
+ * // An error with custom severity and attributes
1152
+ * class RateLimitError extends Data.TaggedError("RateLimitError")<{
1153
+ * readonly retryAfter: number
1154
+ * }> {
1155
+ * readonly [ErrorReporter.severity] = "Warn" as const
1156
+ * readonly [ErrorReporter.attributes] = {
1157
+ * retryAfter: this.retryAfter
1158
+ * }
1159
+ * }
1160
+ *
1161
+ * // Opt in to error reporting with Effect.withErrorReporting
1162
+ * const program = Effect.gen(function*() {
1163
+ * yield* new RateLimitError({ retryAfter: 60 })
1164
+ * }).pipe(
1165
+ * Effect.withErrorReporting,
1166
+ * Effect.provide(ErrorReporter.layer([consoleReporter]))
1167
+ * )
1168
+ * ```
1169
+ *
1123
1170
  * @since 4.0.0
1124
1171
  */
1125
1172
  export * as ErrorReporter from "./ErrorReporter.ts"
@@ -492,33 +492,33 @@ const fiberIdStore = { id: 0 }
492
492
  export const getCurrentFiber = (): Fiber.Fiber<any, any> | undefined => (globalThis as any)[currentFiberTypeId]
493
493
 
494
494
  const keepAlive = (() => {
495
- const start = (() => {
496
- const setInterval = globalThis.setInterval
497
- const clearInterval = globalThis.clearInterval
495
+ let isAvailable: boolean | undefined
496
+ const start = () => {
497
+ if (isAvailable === true) return setInterval(constVoid, 2_147_483_647)
498
+ else if (isAvailable === false) return undefined
499
+
498
500
  try {
499
501
  const running = setInterval(constVoid, 2_147_483_647)
500
- clearInterval(running)
501
- return {
502
- setInterval,
503
- clearInterval
504
- }
502
+ isAvailable = true
503
+ return running
505
504
  } catch {
505
+ isAvailable = false
506
506
  return undefined
507
507
  }
508
- })()
508
+ }
509
509
  let count = 0
510
510
  let running: ReturnType<typeof globalThis.setInterval> | undefined = undefined
511
511
  return ({
512
512
  increment() {
513
513
  count++
514
- if (start !== undefined && running === undefined) {
515
- running = start.setInterval(constVoid, 2_147_483_647)
514
+ if (running === undefined) {
515
+ running = start()
516
516
  }
517
517
  },
518
518
  decrement() {
519
519
  count--
520
- if (count === 0 && start !== undefined && running !== undefined) {
521
- start.clearInterval(running)
520
+ if (count === 0 && running !== undefined) {
521
+ clearInterval(running)
522
522
  running = undefined
523
523
  }
524
524
  }
@@ -832,13 +832,29 @@ export const fiberInterrupt = <A, E>(
832
832
 
833
833
  /** @internal */
834
834
  export const fiberInterruptAs: {
835
- (fiberId: number): <A, E>(self: Fiber.Fiber<A, E>) => Effect.Effect<void>
836
- <A, E>(self: Fiber.Fiber<A, E>, fiberId: number): Effect.Effect<void>
837
- } = dual(2, <A, E>(self: Fiber.Fiber<A, E>, fiberId: number): Effect.Effect<void> =>
838
- withFiber((parent) => {
839
- self.interruptUnsafe(fiberId, fiberStackAnnotations(parent))
840
- return asVoid(fiberAwait(self))
841
- }))
835
+ (
836
+ fiberId: number | undefined,
837
+ annotations?: ServiceMap.ServiceMap<never> | undefined
838
+ ): <A, E>(self: Fiber.Fiber<A, E>) => Effect.Effect<void>
839
+ <A, E>(
840
+ self: Fiber.Fiber<A, E>,
841
+ fiberId: number | undefined,
842
+ annotations?: ServiceMap.ServiceMap<never> | undefined
843
+ ): Effect.Effect<void>
844
+ } = dual(
845
+ (args) => hasProperty(args[0], FiberTypeId),
846
+ <A, E>(
847
+ self: Fiber.Fiber<A, E>,
848
+ fiberId: number | undefined,
849
+ annotations?: ServiceMap.ServiceMap<never> | undefined
850
+ ): Effect.Effect<void> =>
851
+ withFiber((parent) => {
852
+ let ann = fiberStackAnnotations(parent)
853
+ ann = ann && annotations ? ServiceMap.merge(ann, annotations) : ann ?? annotations
854
+ self.interruptUnsafe(fiberId, ann)
855
+ return asVoid(fiberAwait(self))
856
+ })
857
+ )
842
858
 
843
859
  /** @internal */
844
860
  export const fiberInterruptAll = <A extends Iterable<Fiber.Fiber<any, any>>>(
@@ -2003,23 +2019,8 @@ export const updateServices: {
2003
2019
  const nextServices = f(prev)
2004
2020
  if (prev === nextServices) return self as any
2005
2021
  fiber.setServices(nextServices)
2006
- const newServices = new Map<string, unknown>()
2007
- for (const [key, value] of fiber.services.mapUnsafe) {
2008
- if (!prev.mapUnsafe.has(key) || value !== prev.mapUnsafe.get(key)) {
2009
- newServices.set(key, value)
2010
- }
2011
- }
2012
- return onExitPrimitive(self as any, () => {
2013
- const map = new Map(fiber.services.mapUnsafe)
2014
- for (const [key, value] of newServices) {
2015
- if (value !== map.get(key)) continue
2016
- if (prev.mapUnsafe.has(key)) {
2017
- map.set(key, prev.mapUnsafe.get(key))
2018
- } else {
2019
- map.delete(key)
2020
- }
2021
- }
2022
- fiber.setServices(ServiceMap.makeUnsafe(map))
2022
+ return onExitPrimitive(self, () => {
2023
+ fiber.setServices(prev)
2023
2024
  return undefined
2024
2025
  })
2025
2026
  })
@@ -2043,12 +2044,11 @@ export const updateService: {
2043
2044
  service: ServiceMap.Service<I, A>,
2044
2045
  f: (value: A) => A
2045
2046
  ): Effect.Effect<XA, E, R | I> =>
2046
- withFiber((fiber) => {
2047
- const prev = ServiceMap.getUnsafe(fiber.services, service)
2047
+ updateServices(self, (s) => {
2048
+ const prev = ServiceMap.getUnsafe(s, service)
2048
2049
  const next = f(prev)
2049
- if (prev === next) return self
2050
- fiber.setServices(ServiceMap.add(fiber.services, service, next))
2051
- return onExit(self, () => sync(() => fiber.setServices(ServiceMap.add(fiber.services, service, prev))))
2050
+ if (prev === next) return s
2051
+ return ServiceMap.add(s, service, next)
2052
2052
  })
2053
2053
  )
2054
2054
 
@@ -2115,11 +2115,10 @@ const provideServiceImpl = <A, E, R, I, S>(
2115
2115
  service: ServiceMap.Service<I, S>,
2116
2116
  implementation: S
2117
2117
  ): Effect.Effect<A, E, Exclude<R, I>> =>
2118
- withFiber((fiber) => {
2119
- const prev = ServiceMap.getOption(fiber.services, service)
2120
- if (prev._tag === "Some" && prev.value === implementation) return self
2121
- fiber.setServices(ServiceMap.add(fiber.services, service, implementation))
2122
- return onExit(self, () => sync(() => fiber.setServices(ServiceMap.addOrOmit(fiber.services, service, prev))))
2118
+ updateServices(self, (s) => {
2119
+ const prev = s.mapUnsafe.get(service.key)
2120
+ if (prev === implementation) return s
2121
+ return ServiceMap.add(s, service, implementation)
2123
2122
  }) as any
2124
2123
 
2125
2124
  /** @internal */
@@ -3666,11 +3665,11 @@ export const provideScope: {
3666
3665
  /** @internal */
3667
3666
  export const scoped = <A, E, R>(self: Effect.Effect<A, E, R>): Effect.Effect<A, E, Exclude<R, Scope.Scope>> =>
3668
3667
  withFiber((fiber) => {
3669
- const prev = ServiceMap.getOption(fiber.services, scopeTag)
3668
+ const prev = fiber.services
3670
3669
  const scope = scopeMakeUnsafe()
3671
3670
  fiber.setServices(ServiceMap.add(fiber.services, scopeTag, scope))
3672
3671
  return onExitPrimitive(self, (exit) => {
3673
- fiber.setServices(ServiceMap.addOrOmit(fiber.services, scopeTag, prev))
3672
+ fiber.setServices(prev)
3674
3673
  return scopeCloseUnsafe(scope, exit)
3675
3674
  })
3676
3675
  }) as any
@@ -5555,6 +5554,40 @@ export const LogToStderr = ServiceMap.Reference<boolean>("effect/Logger/LogToStd
5555
5554
  defaultValue: constFalse
5556
5555
  })
5557
5556
 
5557
+ /** @internal */
5558
+ export const annotateLogsScoped: {
5559
+ (key: string, value: unknown): Effect.Effect<void, never, Scope.Scope>
5560
+ (values: Record<string, unknown>): Effect.Effect<void, never, Scope.Scope>
5561
+ } = function() {
5562
+ const entries = typeof arguments[0] === "string" ?
5563
+ [[arguments[0], arguments[1]]] :
5564
+ Object.entries(arguments[0])
5565
+ return uninterruptible(withFiber((fiber) => {
5566
+ const prev = fiber.getRef(CurrentLogAnnotations)
5567
+ const next = { ...prev }
5568
+ for (let i = 0; i < entries.length; i++) {
5569
+ const [key, value] = entries[i]
5570
+ next[key] = value
5571
+ }
5572
+ fiber.setServices(ServiceMap.add(fiber.services, CurrentLogAnnotations, next))
5573
+ return scopeAddFinalizerExit(ServiceMap.getUnsafe(fiber.services, scopeTag), (_) => {
5574
+ const current = fiber.getRef(CurrentLogAnnotations)
5575
+ const next = { ...current }
5576
+ for (let i = 0; i < entries.length; i++) {
5577
+ const [key, value] = entries[i]
5578
+ if (current[key] !== value) continue
5579
+ if (key in prev) {
5580
+ next[key] = prev[key]
5581
+ } else {
5582
+ delete next[key]
5583
+ }
5584
+ }
5585
+ fiber.setServices(ServiceMap.add(fiber.services, CurrentLogAnnotations, next))
5586
+ return void_
5587
+ })
5588
+ }))
5589
+ }
5590
+
5558
5591
  /** @internal */
5559
5592
  export const LoggerTypeId = "~effect/Logger"
5560
5593
 
@@ -159,7 +159,7 @@ const addEntry = <A extends Request.Any>(
159
159
  batch = newBatch
160
160
  }
161
161
  batchMap.set(key, batch)
162
- batch.fiber = effect.runFork(batch.delayEffect, { scheduler: fiber.currentScheduler })
162
+ batch.fiber = effect.runForkWith(fiber.services)(batch.delayEffect, { scheduler: fiber.currentScheduler })
163
163
  }
164
164
 
165
165
  batch.entrySet.add(entry)
@@ -167,7 +167,7 @@ const addEntry = <A extends Request.Any>(
167
167
  if (batch.resolver.collectWhile(batch.entries)) return entry
168
168
 
169
169
  batch.fiber!.interruptUnsafe(fiber.id)
170
- batch.fiber = effect.runFork(runBatch(batch), { scheduler: fiber.currentScheduler })
170
+ batch.fiber = effect.runForkWith(fiber.services)(runBatch(batch), { scheduler: fiber.currentScheduler })
171
171
  return entry
172
172
  }
173
173
 
@@ -26,6 +26,8 @@ export const resolveBrands = resolveAt<ReadonlyArray<string>>("brands")
26
26
 
27
27
  /** @internal */
28
28
  export const getExpected = memoize((ast: AST.AST): string => {
29
+ const identifier = resolveIdentifier(ast)
30
+ if (typeof identifier === "string") return identifier
29
31
  return ast.getExpected(getExpected)
30
32
  })
31
33