Skip to content
Effect Days 2026 Get your ticket

effect

3.22.2

Patch Changes

  • #6233 291d5a9 Thanks @mvanhorn! - Fix TMap.remove and removeAll erroneously clearing entire bucket on hash collision.

  • #7170 7c6e1e5 Thanks @thewilkybarkid! - Fix use of Schema.NonEmptyArrayEnsure with strings

3.22.1

Patch Changes

  • #6443 7ccbd9c Thanks @coyaSONG! - Clarify that Context.GenericTag requires a key.

  • #6673 ab2af6d Thanks @tim-smart! - Fix zero-capacity Mailbox rendezvous behavior for take and takeN.

  • #6761 3cc3c6e Thanks @tim-smart! - Use a linear matcher when decoding template literals containing only string spans.

  • #6507 3d390f2 Thanks @tim-smart! - Disable unhandled error logging for fibers spawned by Effect.timeout.

  • #6762 fcabf08 Thanks @tim-smart! - Redact errors from wrapped config parsers and avoid JSON key path collisions.

  • #6669 735d81b Thanks @tim-smart! - Fix Stream.asyncPush termination with bounded dropping and sliding buffers.

3.22.0

Minor Changes

  • #6286 fffdee0 Thanks @effect-bot! - Add Graph.successors and Graph.predecessors, deprecate Graph.neighborsDirected, and fix graph algorithm edge cases around reversal, undirected edge queries, shortest-path weight validation, topological sort initials, and strongly connected components.

3.21.5

Patch Changes

  • #6302 307d54a Thanks @fubhy! - Allow cron fields like 5/15 to expand from the starting value through the field maximum.

  • #6303 d95868a Thanks @fubhy! - Fix Schedule.cron when the test clock is adjusted to infinity.

  • #6285 95c7d2e Thanks @chatman-media! - Fix Cron.next skipping earlier matching days when the upcoming day-of-month does not exist in the current month. For an expression like 0 0 1,16,31 * *, advancing from a date past the 16th selected day 31; in a month without 31 days this overflowed into the following month and landed on a later matching day (e.g. the 16th), silently skipping the 1st. Cron.next now wraps to the first matching day of the next month in that case, matching the behaviour of Cron.prev and other cron implementations.

  • #6305 d24511f Thanks @fubhy! - Fix cron parsing and scheduling edge cases for whitespace, Sunday 7, strict numeric tokens, explicit full day ranges, and month-constrained day-of-month / weekday matching.

3.21.4

Patch Changes

  • #6267 8222963 Thanks @fubhy! - Fix Graph traversal and shortest-path algorithms to traverse undirected edges independently of their stored source/target orientation.

3.21.3

Patch Changes

  • #6250 e2126bc Thanks @milkyskies! - Fix $match generic type parameter inference inside arms (#6249)

  • #6257 f7e836e Thanks @gcanti! - Emit additionalProperties: false for records with string keys and Schema.Never values.

3.21.2

Patch Changes

  • #6194 74f3267 Thanks @mikearnaldi! - Fix TestClock.unsafeCurrentTimeNanos() to floor fractional millisecond instants before converting them to BigInt.

3.21.1

Patch Changes

  • #6139 f99048e Thanks @marbemac! - Fix batched request resolver defects causing consumer fibers to hang forever.

    When a RequestResolver.makeBatched resolver died with a defect, the request Deferreds were never completed because the cleanup logic in invokeWithInterrupt used flatMap (which only runs on success). Changed to ensuring so uncompleted request entries are always resolved regardless of exit type.

3.21.0

Minor Changes

  • #5780 f7bb09b Thanks @kitlangton! - Add Cron.prev and reverse iteration support, aligning next/prev lookup tables, fixing DST handling symmetry, and expanding cron backward/forward test coverage.

  • #5780 bd7552a Thanks @mattiamanzati! - Add type-level utils to asserting layer types

  • #5780 ad1a7eb Thanks @schickling! - RcMap: support dynamic idleTimeToLive values per key

    The idleTimeToLive option can now be a function that receives the key and returns a duration, allowing different TTL values for different resources.

    const map =
    yield *
    RcMap.make({
    lookup: (key: string) => acquireResource(key),
    idleTimeToLive: (key: string) => {
    if (key.startsWith("premium:")) return Duration.minutes(10)
    return Duration.minutes(1)
    }
    })
  • #5780 0d32048 Thanks @mikearnaldi! - Fix annotateCurrentSpan, add Effect.currentPropagatedSpan

Patch Changes

  • #5780 0d32048 Thanks @mikearnaldi! - Add logs to first propagated span, in the following case before this fix the log would not be added to the p span because Effect.fn adds a fake span for the purpose of adding a stack frame.

    import { Effect } from "effect"
    const f = Effect.fn(function* () {
    yield* Effect.logWarning("FooBar")
    return yield* Effect.fail("Oops")
    })
    const p = f().pipe(Effect.withSpan("p"))

3.20.1

Patch Changes

  • #6133 add06f4 Thanks @aniravi24! - Fix Equal.equals crash when comparing null values inside structuralRegion. Added null guard before Object.getPrototypeOf calls to prevent TypeError: Cannot convert undefined or null to object.

  • #6093 a03b6a2 Thanks @luchersou! - avoid class for PrettyError to preserve error.name

3.20.0

Minor Changes

  • #6124 8798a84 Thanks @mikearnaldi! - Fix scheduler task draining to isolate AsyncLocalStorage across fibers.

Patch Changes

  • #6107 fc82e81 Thanks @gcanti! - Backport Types.VoidIfEmpty to 3.x

  • #6088 82996bc Thanks @taylorOntologize! - Schema: fix Schema.omit producing wrong result on Struct with optionalWith({ default }) and index signatures

    getIndexSignatures now handles Transformation AST nodes by delegating to ast.to, matching the existing behavior of getPropertyKeys and getPropertyKeyIndexedAccess. Previously, Schema.omit on a struct combining Schema.optionalWith (with { default }, { as: "Option" }, etc.) and Schema.Record would silently take the wrong code path, returning a Transformation with property signatures instead of a TypeLiteral with index signatures.

  • #6086 4d97a61 Thanks @taylorOntologize! - Schema: fix getPropertySignatures crash on Struct with optionalWith({ default }) and other Transformation-producing variants

    SchemaAST.getPropertyKeyIndexedAccess now handles Transformation AST nodes by delegating to ast.to, matching the existing behavior of getPropertyKeys. Previously, calling getPropertySignatures on a Schema.Struct containing Schema.optionalWith with { default }, { as: "Option" }, { nullable: true }, or similar options would throw "Unsupported schema (Transformation)".

  • #6097 f6b0960 Thanks @gcanti! - Fix TupleWithRest post-rest validation to check each tail index sequentially.

3.19.19

Patch Changes

  • #6079 4eb5c00 Thanks @tim-smart! - add short circuit to fiber.await internals

  • #6079 4eb5c00 Thanks @tim-smart! - build ManagedRuntime synchronously if possible

  • #6081 2d2bb13 Thanks @tim-smart! - fix semaphore race condition where permits could be leaked

3.19.18

Patch Changes

  • #6062 12b1f1e Thanks @tim-smart! - prevent Stream.changes from writing empty chunks

3.19.17

Patch Changes

  • #6040 a8c436f Thanks @jacobconley! - Fix Stream.decodeText to correctly handle multi-byte UTF-8 characters split across chunk boundaries.

3.19.16

Patch Changes

  • #6018 e71889f Thanks @codewithkenzo! - fix(Match): handle null/undefined in Match.tag and Match.tagStartsWith

    Added null checks to discriminator and discriminatorStartsWith predicates to prevent crashes when matching nullable union types.

    Fixes #6017

3.19.15

Patch Changes

  • #5981 7e925ea Thanks @bxff! - Fix type inference loss in Array.flatten for complex nested structures like unions of Effects with contravariant requirements. Uses distributive indexed access (T[number][number]) in the Flatten type utility and adds const to the flatten generic parameter.

  • #5970 d7e75d6 Thanks @KhraksMamtsov! - fix Config.orElseIf signature

  • #5996 4860d1e Thanks @parischap! - fix Equal.equals plain object comparisons in structural mode

3.19.14

Patch Changes

  • #5924 488d6e8 Thanks @mikearnaldi! - Fix Effect.retry to respect times: 0 option by using explicit undefined check instead of truthy check.

3.19.13

Patch Changes

  • #5911 77eeb86 Thanks @mattiamanzati! - Add test for ensuring typeConstructor is attached

  • #5910 287c32c Thanks @mattiamanzati! - Add typeConstructor annotation for Schema

3.19.12

Patch Changes

  • #5897 a6dfca9 Thanks @fubhy! - Ensure performance.now is only used if it’s available

3.19.11

Patch Changes

  • #5888 38abd67 Thanks @gcanti! - filter non-JSON values from schema examples and defaults, closes #5884

    Introduce JsonValue type and update JsonSchemaAnnotations to use it for type safety. Add validation to filter invalid values (BigInt, cyclic refs) from examples and defaults, preventing infinite recursion on cycles.

  • #5885 44e0b04 Thanks @gcanti! - feat(JSONSchema): add missing options for target JSON Schema version in make function, closes #5883

3.19.10

Patch Changes

  • #5874 bd08028 Thanks @mattiamanzati! - Fix NoSuchElementException instantiation in fastPath and add corresponding test case

  • #5878 6c5c2ba Thanks @Hoishin! - prevent crash from Hash and Equal with invalid Date object

3.19.9

Patch Changes

  • #5875 3f9bbfe Thanks @gcanti! - Fix the arbitrary generator for BigDecimal to allow negative scales.

3.19.8

Patch Changes

  • #5815 f03b8e5 Thanks @lokhmakov! - Prevent multiple iterations over the same Iterable in Array.intersectionWith and Array.differenceWith

3.19.7

Patch Changes

  • #5813 7ef13d3 Thanks @tim-smart! - fix SqlPersistedQueue batch size

3.19.6

Patch Changes

  • #5778 af7916a Thanks @tim-smart! - add RcRef.invalidate api

3.19.5

Patch Changes

  • #5772 079975c Thanks @tim-smart! - backport Effect.gen optimization

3.19.4

Patch Changes

  • #5752 f445b87 Thanks @janglad! - Fix Types.DeepMutable mapping over functions

  • #5757 d2b68ac Thanks @tim-smart! - add experimental PartitionedSemaphore module

    A PartitionedSemaphore is a concurrency primitive that can be used to control concurrent access to a resource across multiple partitions identified by keys.

    The total number of permits is shared across all partitions, with waiting permits equally distributed among partitions using a round-robin strategy.

    This is useful when you want to limit the total number of concurrent accesses to a resource, while still allowing for fair distribution of access across different partitions.

    import { Effect, PartitionedSemaphore } from "effect"
    Effect.gen(function* () {
    const semaphore = yield* PartitionedSemaphore.make<string>({ permits: 5 })
    // Take the first 5 permits with key "A", then the following permits will be
    // equally distributed between all the keys using a round-robin strategy
    yield* Effect.log("A").pipe(
    Effect.delay(1000),
    semaphore.withPermits("A", 1),
    Effect.replicateEffect(15, { concurrency: "unbounded" }),
    Effect.fork
    )
    yield* Effect.log("B").pipe(
    Effect.delay(1000),
    semaphore.withPermits("B", 1),
    Effect.replicateEffect(10, { concurrency: "unbounded" }),
    Effect.fork
    )
    yield* Effect.log("C").pipe(
    Effect.delay(1000),
    semaphore.withPermits("C", 1),
    Effect.replicateEffect(10, { concurrency: "unbounded" }),
    Effect.fork
    )
    return yield* Effect.never
    }).pipe(Effect.runFork)

3.19.3

Patch Changes

  • #5712 7d28a90 Thanks @gcanti! - Use standard formatting function in Config error messages, closes #5709

3.19.2

Patch Changes

  • #5703 374f58c Thanks @tim-smart! - preserve Layer.mergeAll context order

  • #5703 374f58c Thanks @tim-smart! - ensure FiberHandle.run state transition is atomic

3.19.1

Patch Changes

  • #5695 63f2bf3 Thanks @tim-smart! - allow parallel finalization of merged layers

3.19.0

Minor Changes

  • #5606 3863fa8 Thanks @mikearnaldi! - Add Effect.fn.Return to allow typing returns on Effect.fn

  • #5606 2a03c76 Thanks @fubhy! - Backport Graph module updates

  • #5606 24a1685 Thanks @tim-smart! - add experimental HashRing module

Patch Changes

  • #5679 3c15d5f Thanks @KhraksMamtsov! - Array.window signature has been improved

3.18.5

Patch Changes

  • #5669 a537469 Thanks @fubhy! - Fix Graph.neighbors() returning self-loops in undirected graphs.

    Graph.neighbors() now correctly returns the other endpoint for undirected graphs instead of always returning edge.target, which caused nodes to appear as their own neighbors when queried from the target side of an edge.

  • #5628 52d5963 Thanks @mikearnaldi! - Make sure AsEffect is computed

  • #5671 463345d Thanks @gcanti! - JSON Schema generation: add jsonSchema2020-12 target and fix tuple output for:

    • JSON Schema 2019-09
    • OpenAPI 3.1

3.18.4

Patch Changes

  • #5617 6ae2f5d Thanks @gcanti! - JSONSchema: Fix issue where invalid defaults were included in the output.

    Now they are ignored, similar to invalid examples.

    Before

    import { JSONSchema, Schema } from "effect"
    const schema = Schema.NonEmptyString.annotations({
    default: ""
    })
    const jsonSchema = JSONSchema.make(schema)
    console.log(JSON.stringify(jsonSchema, null, 2))
    /*
    Output:
    {
    "$schema": "http://json-schema.org/draft-07/schema#",
    "type": "string",
    "description": "a non empty string",
    "title": "nonEmptyString",
    "default": "",
    "minLength": 1
    }
    */

    After

    import { JSONSchema, Schema } from "effect"
    const schema = Schema.NonEmptyString.annotations({
    default: ""
    })
    const jsonSchema = JSONSchema.make(schema)
    console.log(JSON.stringify(jsonSchema, null, 2))
    /*
    Output:
    {
    "$schema": "http://json-schema.org/draft-07/schema#",
    "type": "string",
    "description": "a non empty string",
    "title": "nonEmptyString",
    "minLength": 1
    }
    */

3.18.3

Patch Changes

  • #5612 25fab81 Thanks @gcanti! - Fix JSON Schema generation with topLevelReferenceStrategy: "skip", closes #5611

    This patch fixes a bug that occurred when generating JSON Schemas with nested schemas that had identifiers, while using topLevelReferenceStrategy: "skip".

    Previously, the generator would still output $ref entries even though references were supposed to be skipped, leaving unresolved definitions.

    Before

    import { JSONSchema, Schema } from "effect"
    const A = Schema.Struct({ value: Schema.String }).annotations({
    identifier: "A"
    })
    const B = Schema.Struct({ a: A }).annotations({ identifier: "B" })
    const definitions = {}
    console.log(
    JSON.stringify(
    JSONSchema.fromAST(B.ast, {
    definitions,
    topLevelReferenceStrategy: "skip"
    }),
    null,
    2
    )
    )
    /*
    {
    "type": "object",
    "required": ["a"],
    "properties": {
    "a": {
    "$ref": "#/$defs/A"
    }
    },
    "additionalProperties": false
    }
    */
    console.log(definitions)
    /*
    {
    A: {
    type: "object",
    required: ["value"],
    properties: { value: [Object] },
    additionalProperties: false
    }
    }
    */

    After

    import { JSONSchema, Schema } from "effect"
    const A = Schema.Struct({ value: Schema.String }).annotations({
    identifier: "A"
    })
    const B = Schema.Struct({ a: A }).annotations({ identifier: "B" })
    const definitions = {}
    console.log(
    JSON.stringify(
    JSONSchema.fromAST(B.ast, {
    definitions,
    topLevelReferenceStrategy: "skip"
    }),
    null,
    2
    )
    )
    /*
    {
    "type": "object",
    "required": ["a"],
    "properties": {
    "a": {
    "type": "object",
    "required": ["value"],
    "properties": {
    "value": { "type": "string" }
    },
    "additionalProperties": false
    }
    },
    "additionalProperties": false
    }
    */
    console.log(definitions)
    /*
    {}
    */

    Now schemas are correctly inlined, and no leftover $ref entries or unused definitions remain.

3.18.2

Patch Changes

  • #5598 8ba4757 Thanks @cyberixae! - Fix Array Do documentation

3.18.1

Patch Changes

  • #5584 07802f7 Thanks @indietyp! - Enable console.group use in Logger.prettyFormat when using Bun

3.18.0

Minor Changes

  • #5302 1c6ab74 Thanks @schickling! - Add experimental Graph module with comprehensive graph data structure support

    This experimental module provides:

    • Directed and undirected graph support
    • Immutable and mutable graph variants
    • Type-safe node and edge operations
    • Graph algorithms: DFS, BFS, shortest paths, cycle detection, etc.

    Example usage:

    import { Graph } from "effect"
    // Create a graph with mutations
    const graph = Graph.directed<string, number>((mutable) => {
    const nodeA = Graph.addNode(mutable, "Node A")
    const nodeB = Graph.addNode(mutable, "Node B")
    Graph.addEdge(mutable, nodeA, nodeB, 5)
    })
    console.log(
    `Nodes: ${Graph.nodeCount(graph)}, Edges: ${Graph.edgeCount(graph)}`
    )
  • #5302 70fe803 Thanks @mikearnaldi! - Automatically set otel parent when present as external span

  • #5302 c296e32 Thanks @tim-smart! - add Effect.Semaphore.resize

  • #5302 a098ddf Thanks @mikearnaldi! - Introduce ReadonlyTag as the covariant side of a tag, enables:

    import type { Context } from "effect"
    import { Effect } from "effect"
    export class MyRequirement extends Effect.Service<MyRequirement>()(
    "MyRequirement",
    { succeed: () => 42 }
    ) {}
    export class MyUseCase extends Effect.Service<MyUseCase>()("MyUseCase", {
    dependencies: [MyRequirement.Default],
    effect: Effect.gen(function* () {
    const requirement = yield* MyRequirement
    return Effect.fn("MyUseCase.execute")(function* () {
    return requirement()
    })
    })
    }) {}
    export function effectHandler<I, Args extends Array<any>, A, E, R>(
    service: Context.ReadonlyTag<I, (...args: Args) => Effect.Effect<A, E, R>>
    ) {
    return Effect.fn("effectHandler")(function* (...args: Args) {
    const execute = yield* service
    yield* execute(...args)
    })
    }
    export const program = effectHandler(MyUseCase)

3.17.14

Patch Changes

  • #5533 ea95998 Thanks @IMax153! - Preserve the precision of histogram boundary values

3.17.13

Patch Changes

  • #5462 51bfc78 Thanks @tim-smart! - ensure tracerLogger does not drop message items

3.17.12

Patch Changes

  • #5456 b359bdc Thanks @tim-smart! - add preload options to LayerMap

3.17.11

Patch Changes

  • #5449 fb5e414 Thanks @tim-smart! - Simplify Effect.raceAll implementation, ensure children fibers are awaited

  • #5451 018363b Thanks @mikearnaldi! - Fix Predicate.isIterable to allow strings

3.17.10

Patch Changes

  • #5368 3b26094 Thanks @gcanti! - ## Annotation Behavior

    When you call .annotations on a schema, any identifier annotations that were previously set will now be removed. Identifiers are now always tied to the schema’s ast reference (this was the intended behavior).

    Example

    import { JSONSchema, Schema } from "effect"
    const schema = Schema.URL
    console.log(JSON.stringify(JSONSchema.make(schema), null, 2))
    /*
    {
    "$schema": "http://json-schema.org/draft-07/schema#",
    "$defs": {
    "URL": {
    "type": "string",
    "description": "a string to be decoded into a URL"
    }
    },
    "$ref": "#/$defs/URL"
    }
    */
    const annotated = Schema.URL.annotations({ description: "description" })
    console.log(JSON.stringify(JSONSchema.make(annotated), null, 2))
    /*
    {
    "$schema": "http://json-schema.org/draft-07/schema#",
    "type": "string",
    "description": "description"
    }
    */

    OpenAPI 3.1 Compatibility

    OpenAPI 3.1 does not allow nullable: true. Instead, the schema will now correctly use { "type": "null" } inside a union.

    Example

    import { JSONSchema, Schema } from "effect"
    const schema = Schema.NullOr(Schema.String)
    console.log(
    JSON.stringify(
    JSONSchema.fromAST(schema.ast, {
    definitions: {},
    target: "openApi3.1"
    }),
    null,
    2
    )
    )
    /*
    {
    "anyOf": [
    {
    "type": "string"
    },
    {
    "type": "null"
    }
    ]
    }
    */

    Schema Description Deduplication

    Previously, when a schema was reused, only the first description was kept. Now, every property keeps its own description, even if the schema is reused.

    Example

    import { JSONSchema, Schema } from "effect"
    const schemaWithAnIdentifier = Schema.String.annotations({
    identifier: "my-id"
    })
    const schema = Schema.Struct({
    a: schemaWithAnIdentifier.annotations({
    description: "a-description"
    }),
    b: schemaWithAnIdentifier.annotations({
    description: "b-description"
    })
    })
    console.log(JSON.stringify(JSONSchema.make(schema), null, 2))
    /*
    {
    "$schema": "http://json-schema.org/draft-07/schema#",
    "type": "object",
    "required": [
    "a",
    "b"
    ],
    "properties": {
    "a": {
    "type": "string",
    "description": "a-description"
    },
    "b": {
    "type": "string",
    "description": "b-description"
    }
    },
    "additionalProperties": false
    }
    */

    Fragment Detection in Non-Refinement Schemas

    This patch fixes the issue where fragments (e.g. jsonSchema.format) were not detected on non-refinement schemas.

    Example

    import { JSONSchema, Schema } from "effect"
    const schema = Schema.UUID.pipe(
    Schema.compose(Schema.String),
    Schema.annotations({
    identifier: "UUID",
    title: "title",
    description: "description",
    jsonSchema: {
    format: "uuid" // fragment
    }
    })
    )
    console.log(JSON.stringify(JSONSchema.make(schema), null, 2))
    /*
    {
    "$schema": "http://json-schema.org/draft-07/schema#",
    "$defs": {
    "UUID": {
    "type": "string",
    "description": "description",
    "format": "uuid",
    "pattern": "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$",
    "title": "title"
    }
    },
    "$ref": "#/$defs/UUID"
    }
    */

    Nested Unions

    Nested unions are no longer flattened. Instead, they remain as nested anyOf arrays. This is fine because JSON Schema allows nested anyOf.

    Example

    import { JSONSchema, Schema } from "effect"
    const schema = Schema.Union(
    Schema.NullOr(Schema.String),
    Schema.Literal("a", null)
    )
    console.log(JSON.stringify(JSONSchema.make(schema), null, 2))
    /*
    {
    "$schema": "http://json-schema.org/draft-07/schema#",
    "anyOf": [
    {
    "anyOf": [
    {
    "type": "string"
    },
    {
    "type": "null"
    }
    ]
    },
    {
    "anyOf": [
    {
    "type": "string",
    "enum": [
    "a"
    ]
    },
    {
    "type": "null"
    }
    ]
    }
    ]
    }
    */

    Refinements without jsonSchema annotation

    Refinements that don’t provide a jsonSchema annotation no longer cause errors. They are simply ignored, so you can still generate a JSON Schema even when refinements can’t easily be expressed.

  • #5437 a33e491 Thanks @tim-smart! - ensure Effect.promise captures span on defect

3.17.9

Patch Changes

  • #5422 0271f14 Thanks @gcanti! - backport formatUnknown from v4

3.17.8

Patch Changes

  • #5407 84bc300 Thanks @thewilkybarkid! - Fix Schema.Defect when seeing a null-prototype object

3.17.7

Patch Changes

3.17.6

Patch Changes

  • #5322 f187941 Thanks @beezee! - Use non-greedy matching for Schema.String in Schema.TemplateLiteralParser

3.17.5

Patch Changes

  • #5315 5f98388 Thanks @patroza! - improve provide/merge apis to support readonly array inputs.

3.17.4

Patch Changes

  • #5306 7d7c55d Thanks @leonitousconforti! - Align RcMap.keys return type with internal signature

3.17.3

Patch Changes

  • #5275 3504555 Thanks @taylornz! - fix DateTime.makeZoned handling of DST transitions

  • #5282 f6c7ca7 Thanks @beezee! - Improve inference on Metric.trackSuccessWith for use in Effect.pipe(…)

  • #5275 3504555 Thanks @taylornz! - add DateTime.Disambiguation for handling DST edge cases

    Added four disambiguation strategies to DateTime.Zoned constructors for handling DST edge cases:

    • 'compatible' - Maintains backward compatibility
    • 'earlier' - Choose earlier time during ambiguous periods (default)
    • 'later' - Choose later time during ambiguous periods
    • 'reject' - Throw error for ambiguous times

3.17.2

Patch Changes

  • #5277 6309e0a Thanks @tim-smart! - Fix Layer.mock dual detection

3.17.1

Patch Changes

  • #5262 5a0f4f1 Thanks @tim-smart! - remove recursion from Sink fold loop

3.17.0

Minor Changes

  • #4949 40c3c87 Thanks @fubhy! - Added Random.fixed to create a version of the Random service with fixed values for testing.

  • #4949 ed2c74a Thanks @dmaretskyi! - Add Struct.entries function

  • #4949 073a1b8 Thanks @f15u! - Add Layer.mock

    Creates a mock layer for testing purposes. You can provide a partial implementation of the service, and any methods not provided will throw an UnimplementedError defect when called.

    import { Context, Effect, Layer } from "effect"
    class MyService extends Context.Tag("MyService")<
    MyService,
    {
    one: Effect.Effect<number>
    two(): Effect.Effect<number>
    }
    >() {}
    const MyServiceTest = Layer.mock(MyService, {
    two: () => Effect.succeed(2)
    })
  • #4949 f382e99 Thanks @KhraksMamtsov! - Schedule output has been added into CurrentIterationMetadata

  • #4949 e8c7ba5 Thanks @mikearnaldi! - Remove global state index by version, make version mismatch a warning message

  • #4949 7e10415 Thanks @devinjameson! - Array: add findFirstWithIndex function

  • #4949 e9bdece Thanks @vinassefranche! - Add HashMap.countBy

    import { HashMap } from "effect"
    const map = HashMap.make([1, "a"], [2, "b"], [3, "c"])
    const result = HashMap.countBy(map, (_v, key) => key % 2 === 1)
    console.log(result) // 2
  • #4949 8d95eb0 Thanks @tim-smart! - add Effect.ensure{Success,Error,Requirements}Type, for constraining Effect types

3.16.17

Patch Changes

  • #5246 aaa6ad0 Thanks @mikearnaldi! - Copy over apply, bind, call into service proxy

  • #5158 5b74ea5 Thanks @cyberixae! - Clarify Tuple length requirements

3.16.16

Patch Changes

  • #5224 127e602 Thanks @tim-smart! - prevent fiber leak when Stream.toAsyncIterable returns early

3.16.15

Patch Changes

  • #5222 15df9bf Thanks @gcanti! - Schema.attachPropertySignature: simplify signature and fix parameter type to use Schema instead of SchemaClass

3.16.14

Patch Changes

  • #5213 f5dfabf Thanks @gcanti! - Fix incorrect schema ID annotation in Schema.lessThanOrEqualToDate, closes #5212

  • #5192 17a5ea8 Thanks @nikelborm! - Updated deprecated OTel Resource attributes names and values.

    Many of the attributes have undergone the process of deprecation not once, but twice. Most of the constants holding attribute names have been renamed. These are minor changes.

    Additionally, there were numerous changes to the attribute keys themselves. These changes can be considered major.

    In the @opentelemetry/semantic-conventions package, new attributes having ongoing discussion about them are going through a process called incubation, until a consensus about their necessity and form is reached. Otel team recommends devs to copy them directly into their code. Luckily, it’s not necessary because all of the new attribute names and values came out of this process (some of them were changed again) and are now considered stable.

    Reasoning for minor version bump

    PackageMajor attribute changesMajor value changes
    Clickhouse clientdb.system -> db.system.name
    db.name -> db.namespace
    MsSQL clientdb.system -> db.system.name
    db.name -> db.namespace
    mssql -> microsoft.sql_server
    MySQL clientdb.system -> db.system.name
    db.name -> db.namespace
    Pg clientdb.system -> db.system.name
    db.name -> db.namespace
    Bun SQLite clientdb.system -> db.system.name
    Node SQLite clientdb.system -> db.system.name
    React.Native SQLite clientdb.system -> db.system.name
    Wasm SQLite clientdb.system -> db.system.name
    SQLite Do clientdb.system -> db.system.name
    LibSQL clientdb.system -> db.system.name
    D1 clientdb.system -> db.system.name
    Kysely clientdb.statement -> db.query.text
    @effect/sqldb.statement -> db.query.text
    db.operation -> db.operation.name
  • #5211 d25f22b Thanks @mattiamanzati! - Removed some unnecessary single-arg pipe calls

3.16.13

Patch Changes

  • #5097 c1c05a8 Thanks @tim-smart! - remove completion helper overload from Effect.catchTag, to fix Effect.fn inference

  • #5157 81fe4a2 Thanks @cyberixae! - Clarify Array rotate example

3.16.12

Patch Changes

  • #5149 905da99 Thanks @milkyskies! - Fix $match to disallow invalid _tag keys in TaggedEnum handler objects.

3.16.11

Patch Changes

  • #5127 99590a6 Thanks @tim-smart! - fix DateTime zone check to includes zones without ”:”

  • #5123 6c3e24c Thanks @gcanti! - Schema.equivalence: handle non-array and non-record inputs

3.16.10

Patch Changes

  • #5100 faad30e Thanks @tim-smart! - relax Predicate.compose constraint on second refinement

3.16.9

Patch Changes

  • #5081 5137c70 Thanks @tim-smart! - expose Stream.provideSomeContext

  • #5082 c23d25c Thanks @tim-smart! - fix Effect.filterOrFail return type inference

3.16.8

Patch Changes

  • #5047 8cb98d5 Thanks @tim-smart! - ensure Stream.toReadableStream ignores empty chunks

  • #5046 db2dd3c Thanks @tim-smart! - ignore ReadableStream defect in bun due to controller bug

3.16.7

Patch Changes

  • #5033 1bb0d8a Thanks @tim-smart! - ensure DateTime.make interprets strings without zone as UTC

3.16.6

Patch Changes

  • #5026 a5f7595 Thanks @KhraksMamtsov! - Add missing type variances

  • #5031 a02470c Thanks @KhraksMamtsov! - Fix Context.add & Context.make signatures

  • #5003 f891d45 Thanks @beezee! - Ensure binding __proto__ to lexical scope in do notation is preserved by bind and let

3.16.5

Patch Changes

  • #5008 bf418ef Thanks @jdharrisnz! - Record.findFirst: Accept ReadonlyRecord type input and optimise the loop

3.16.4

Patch Changes

  • #4994 74ab9a0 Thanks @tim-smart! - don’t inherit interruption flag in Effect.addFinalizer

  • #4986 770008e Thanks @tim-smart! - ensure Cause.YieldableError extends Error

3.16.3

Patch Changes

  • #4952 87722fc Thanks @tim-smart! - improve Effect.catchTag auto-completion

  • #4950 36217ee Thanks @tim-smart! - remove this type propagation from Effect.fn

3.16.2

Patch Changes

  • #4943 0ddf148 Thanks @gcanti! - relax Schema.brand constraint, closes #4942

3.16.1

Patch Changes

  • #4936 71174d0 Thanks @mattiamanzati! - Escape JSON Schema $id for empty struct

  • #4937 d615e6e Thanks @tim-smart! - adjust ExecutionPlan provides & requirements types

3.16.0

Minor Changes

  • #4891 ee0bd5d Thanks @KhraksMamtsov! - Schedule.CurrentIterationMetadata has been added

    import { Effect, Schedule } from "effect"
    Effect.gen(function* () {
    const currentIterationMetadata = yield* Schedule.CurrentIterationMetadata
    // ^? Schedule.IterationMetadata
    console.log(currentIterationMetadata)
    }).pipe(Effect.repeat(Schedule.recurs(2)))
    // {
    // elapsed: Duration.zero,
    // elapsedSincePrevious: Duration.zero,
    // input: undefined,
    // now: 0,
    // recurrence: 0,
    // start: 0
    // }
    // {
    // elapsed: Duration.zero,
    // elapsedSincePrevious: Duration.zero,
    // input: undefined,
    // now: 0,
    // recurrence: 1,
    // start: 0
    // }
    // {
    // elapsed: Duration.zero,
    // elapsedSincePrevious: Duration.zero,
    // input: undefined,
    // now: 0,
    // recurrence: 2,
    // start: 0
    // }
    Effect.gen(function* () {
    const currentIterationMetadata = yield* Schedule.CurrentIterationMetadata
    console.log(currentIterationMetadata)
    }).pipe(
    Effect.schedule(
    Schedule.intersect(Schedule.fibonacci("1 second"), Schedule.recurs(3))
    )
    )
    // {
    // elapsed: Duration.zero,
    // elapsedSincePrevious: Duration.zero,
    // recurrence: 1,
    // input: undefined,
    // now: 0,
    // start: 0
    // },
    // {
    // elapsed: Duration.seconds(1),
    // elapsedSincePrevious: Duration.seconds(1),
    // recurrence: 2,
    // input: undefined,
    // now: 1000,
    // start: 0
    // },
    // {
    // elapsed: Duration.seconds(2),
    // elapsedSincePrevious: Duration.seconds(1),
    // recurrence: 3,
    // input: undefined,
    // now: 2000,
    // start: 0
    // }
  • #4891 5189800 Thanks @vinassefranche! - Add HashMap.hasBy helper

    import { HashMap } from "effect"
    const hm = HashMap.make([1, "a"])
    HashMap.hasBy(hm, (value, key) => value === "a" && key === 1) // -> true
    HashMap.hasBy(hm, (value) => value === "b") // -> false
  • #4891 58bfeaa Thanks @jrudder! - Add round and sumAll to BigDecimal

  • #4891 194d748 Thanks @tim-smart! - add ExecutionPlan module

    A ExecutionPlan can be used with Effect.withExecutionPlan or Stream.withExecutionPlan, allowing you to provide different resources for each step of execution until the effect succeeds or the plan is exhausted.

    import { type AiLanguageModel } from "@effect/ai"
    import type { Layer } from "effect"
    import { Effect, ExecutionPlan, Schedule } from "effect"
    declare const layerBad: Layer.Layer<AiLanguageModel.AiLanguageModel>
    declare const layerGood: Layer.Layer<AiLanguageModel.AiLanguageModel>
    const ThePlan = ExecutionPlan.make(
    {
    // First try with the bad layer 2 times with a 3 second delay between attempts
    provide: layerBad,
    attempts: 2,
    schedule: Schedule.spaced(3000)
    },
    // Then try with the bad layer 3 times with a 1 second delay between attempts
    {
    provide: layerBad,
    attempts: 3,
    schedule: Schedule.spaced(1000)
    },
    // Finally try with the good layer.
    //
    // If `attempts` is omitted, the plan will only attempt once, unless a schedule is provided.
    {
    provide: layerGood
    }
    )
    declare const effect: Effect.Effect<
    void,
    never,
    AiLanguageModel.AiLanguageModel
    >
    const withPlan: Effect.Effect<void> = Effect.withExecutionPlan(
    effect,
    ThePlan
    )
  • #4891 918c9ea Thanks @thewilkybarkid! - Add Array.removeOption and Chunk.removeOption

  • #4891 9198e6f Thanks @TylorS! - Add parameter support for Effect.Service

    This allows you to pass parameters to the effect & scoped Effect.Service constructors, which will also be reflected in the .Default layer.

    import type { Layer } from "effect"
    import { Effect } from "effect"
    class NumberService extends Effect.Service<NumberService>()("NumberService", {
    // You can now pass a function to the `effect` and `scoped` constructors
    effect: Effect.fn(function* (input: number) {
    return {
    get: Effect.succeed(`The number is: ${input}`)
    } as const
    })
    }) {}
    // Pass the arguments to the `Default` layer
    const CoolNumberServiceLayer: Layer.Layer<NumberService> =
    NumberService.Default(6942)
  • #4891 2a370bf Thanks @vinassefranche! - Add Iterable.countBy and Array.countBy

    import { Array, Iterable } from "effect"
    const resultArray = Array.countBy([1, 2, 3, 4, 5], (n) => n % 2 === 0)
    console.log(resultArray) // 2
    const resultIterable = resultIterable.countBy(
    [1, 2, 3, 4, 5],
    (n) => n % 2 === 0
    )
    console.log(resultIterable) // 2
  • #4891 58ccb91 Thanks @KhraksMamtsov! - The Config.port and Config.branded functions have been added.

    import { Brand, Config } from "effect"
    type DbPort = Brand.Branded<number, "DbPort">
    const DbPort = Brand.nominal<DbPort>()
    const dbPort: Config.Config<DbPort> = Config.branded(
    Config.port("DB_PORT"),
    DbPort
    )
    import { Brand, Config } from "effect"
    type Port = Brand.Branded<number, "Port">
    const Port = Brand.refined<Port>(
    (num) =>
    !Number.isNaN(num) && Number.isInteger(num) && num >= 1 && num <= 65535,
    (n) => Brand.error(`Expected ${n} to be an TCP port`)
    )
    const dbPort: Config.Config<Port> = Config.number("DB_PORT").pipe(
    Config.branded(Port)
    )
  • #4891 fd47834 Thanks @tim-smart! - return a proxy Layer from LayerMap service

    The new usage is:

    import { NodeRuntime } from "@effect/platform-node"
    import { Context, Effect, FiberRef, Layer, LayerMap } from "effect"
    class Greeter extends Context.Tag("Greeter")<
    Greeter,
    {
    greet: Effect.Effect<string>
    }
    >() {}
    // create a service that wraps a LayerMap
    class GreeterMap extends LayerMap.Service<GreeterMap>()("GreeterMap", {
    // define the lookup function for the layer map
    //
    // The returned Layer will be used to provide the Greeter service for the
    // given name.
    lookup: (name: string) =>
    Layer.succeed(Greeter, {
    greet: Effect.succeed(`Hello, ${name}!`)
    }),
    // If a layer is not used for a certain amount of time, it can be removed
    idleTimeToLive: "5 seconds",
    // Supply the dependencies for the layers in the LayerMap
    dependencies: []
    }) {}
    // usage
    const program: Effect.Effect<void, never, GreeterMap> = Effect.gen(
    function* () {
    // access and use the Greeter service
    const greeter = yield* Greeter
    yield* Effect.log(yield* greeter.greet)
    }
    ).pipe(
    // use the GreeterMap service to provide a variant of the Greeter service
    Effect.provide(GreeterMap.get("John"))
    )
    // run the program
    program.pipe(Effect.provide(GreeterMap.Default), NodeRuntime.runMain)

3.15.5

Patch Changes

  • #4924 cc5bb2b Thanks @KhraksMamtsov! - Fix type inference for Effect suptypes in NonGen case

3.15.4

Patch Changes

  • #4869 f570554 Thanks @IGassmann! - Fix summary metric’s min/max values when no samples

  • #4917 78047e8 Thanks @KhraksMamtsov! - Fix Effect.fn inference in case of use with pipe functions

3.15.3

Patch Changes

  • #4907 4577f54 Thanks @mattiamanzati! - Escape JSON-pointers

3.15.2

Patch Changes

  • #4659 b8722b8 Thanks @KhraksMamtsov! - - The HashMap.has/get family has become more type-safe.
    • Fix the related type errors in TestAnnotationsMap.ts.

3.15.1

Patch Changes

  • #4870 787ce70 Thanks @tim-smart! - ensure generic refinements work with Effect.filterOr*

  • #4857 1269641 Thanks @tim-smart! - preserve explicit this in Effect.fn apis

  • #4857 1269641 Thanks @tim-smart! - use span name as function name in Effect.fn

3.15.0

Minor Changes

  • #4641 c654595 Thanks @tim-smart! - Add Layer.setRandom, for over-riding the default Random service

  • #4641 d9f5dea Thanks @KhraksMamtsov! - Brand.unbranded getter has been added

  • #4641 49aa723 Thanks @titouancreach! - Add Either.transposeMapOption

  • #4641 74c14d0 Thanks @vinassefranche! - Add Record.findFirst

  • #4641 e4f49b6 Thanks @KhraksMamtsov! - Default never type has been added to MutableHasMap.empty & MutableList.empty ctors

  • #4641 6f02224 Thanks @tim-smart! - add Stream.toAsyncIterable* apis

    import { Stream } from "effect"
    // Will print:
    // 1
    // 2
    // 3
    const stream = Stream.make(1, 2, 3)
    for await (const result of Stream.toAsyncIterable(stream)) {
    console.log(result)
    }
  • #4641 1dcfd41 Thanks @tim-smart! - improve Effect.filter* types to exclude candidates in fallback functions

  • #4641 b21ab16 Thanks @KhraksMamtsov! - Simplified the creation of pipeable classes.

    class MyClass extends Pipeable.Class() {
    constructor(public a: number) {
    super()
    }
    methodA() {
    return this.a
    }
    }
    console.log(new MyClass(2).pipe((x) => x.methodA())) // 2
    class A {
    constructor(public a: number) {}
    methodA() {
    return this.a
    }
    }
    class B extends Pipeable.Class(A) {
    constructor(private b: string) {
    super(b.length)
    }
    methodB() {
    return [this.b, this.methodA()]
    }
    }
    console.log(new B("pipe").pipe((x) => x.methodB())) // ['pipe', 4]
  • #4641 fcf1822 Thanks @KhraksMamtsov! - property message: string has been added to ConfigError.And & Or members

  • #4641 0061dd1 Thanks @tim-smart! - allow catching multiple different tags in Effect.catchTag

  • #4641 8421e6e Thanks @mlegenhausen! - Expose Cause.isTimeoutException

  • #4641 fa10f56 Thanks @thewilkybarkid! - Support multiple values in Function.apply

3.14.22

Patch Changes

  • #4847 24a9ebb Thanks @gcanti! - Schema: TaggedError no longer crashes when the message field is explicitly defined.

    If you define a message field in your schema, TaggedError will no longer add its own message getter. This avoids a stack overflow caused by infinite recursion.

    Before

    import { Schema } from "effect"
    class Todo extends Schema.TaggedError<Todo>()("Todo", {
    message: Schema.optional(Schema.String)
    }) {}
    // ❌ Throws "Maximum call stack size exceeded"
    console.log(Todo.make({}))

    After

    // ✅ Works correctly
    console.log(Todo.make({}))

3.14.21

Patch Changes

3.14.20

Patch Changes

  • #4832 17e2f30 Thanks @gcanti! - JSONSchema: respect annotations on declarations.

    Previously, annotations added with .annotations(...) on Schema.declare(...) were not included in the generated JSON Schema output.

    Before

    import { JSONSchema, Schema } from "effect"
    class MyType {}
    const schema = Schema.declare<MyType>((x) => x instanceof MyType, {
    jsonSchema: {
    type: "my-type"
    }
    }).annotations({
    title: "My Title",
    description: "My Description"
    })
    console.log(JSON.stringify(JSONSchema.make(schema), null, 2))
    /*
    {
    "$schema": "http://json-schema.org/draft-07/schema#",
    "type": "my-type"
    }
    */

    After

    import { JSONSchema, Schema } from "effect"
    class MyType {}
    const schema = Schema.declare<MyType>((x) => x instanceof MyType, {
    jsonSchema: {
    type: "my-type"
    }
    }).annotations({
    title: "My Title",
    description: "My Description"
    })
    console.log(JSON.stringify(JSONSchema.make(schema), null, 2))
    /*
    {
    "$schema": "http://json-schema.org/draft-07/schema#",
    "description": "My Description",
    "title": "My Title",
    "type": "my-type"
    }
    */

3.14.19

Patch Changes

  • #4822 056a910 Thanks @KhraksMamtsov! - fix Layer.discard jsdoc

  • #4816 3273d57 Thanks @mikearnaldi! - Fix captureStackTrace for bun

3.14.18

Patch Changes

  • #4809 b1164d4 Thanks @tim-smart! - fix refinement narrowing in Match

3.14.17

Patch Changes

  • #4806 0b54681 Thanks @thewilkybarkid! - Match the JS API for locale arguments

  • #4805 41a59d5 Thanks @mikearnaldi! - Implement stack cleaning for Bun

3.14.16

Patch Changes

  • #4800 ee14444 Thanks @tim-smart! - improve Match refinement resolution

3.14.15

Patch Changes

  • #4798 239cc99 Thanks @gcanti! - Schema: respect custom constructors in make for Schema.Class, closes #4797

    Previously, the make method did not support custom constructors defined using Schema.Class or Schema.TaggedError, resulting in type errors when passing custom constructor arguments.

    This update ensures that make now correctly uses the class constructor, allowing custom parameters and initialization logic.

    Before

    import { Schema } from "effect"
    class MyError extends Schema.TaggedError<MyError>()("MyError", {
    message: Schema.String
    }) {
    constructor({ a, b }: { a: string; b: string }) {
    super({ message: `${a}:${b}` })
    }
    }
    // @ts-expect-error: Object literal may only specify known properties, and 'a' does not exist in type '{ readonly message: string; }'.ts(2353)
    MyError.make({ a: "1", b: "2" })

    After

    import { Schema } from "effect"
    class MyError extends Schema.TaggedError<MyError>()("MyError", {
    message: Schema.String
    }) {
    constructor({ a, b }: { a: string; b: string }) {
    super({ message: `${a}:${b}` })
    }
    }
    console.log(MyError.make({ a: "1", b: "2" }).message)
    // Output: "1:2"
  • #4687 8b6c947 Thanks @KhraksMamtsov! - Modify the signatures of Either.liftPredicate and Effect.predicate to make them reusable.

  • #4794 c50a63b Thanks @IGassmann! - Fix summary metric’s quantile value calculation

3.14.14

Patch Changes

  • #4786 6ed8d15 Thanks @tim-smart! - drop use of performance.timeOrigin in clock

3.14.13

Patch Changes

  • #4777 ee77788 Thanks @gcanti! - JSONSchema: apply encodeOption to each example and retain successful results.

    Example

    import { JSONSchema, Schema } from "effect"
    const schema = Schema.Struct({
    a: Schema.propertySignature(Schema.BigInt).annotations({
    examples: [1n, 2n]
    })
    })
    console.log(JSON.stringify(JSONSchema.make(schema), null, 2))
    /*
    {
    "$schema": "http://json-schema.org/draft-07/schema#",
    "$defs": {
    "BigInt": {
    "type": "string",
    "description": "a string to be decoded into a bigint"
    }
    },
    "type": "object",
    "required": [
    "a"
    ],
    "properties": {
    "a": {
    "$ref": "#/$defs/BigInt",
    "examples": [
    "1",
    "2"
    ]
    }
    },
    "additionalProperties": false
    }
    */
  • #4701 5fce6ba Thanks @gcanti! - Fix JSONSchema.make for Exit schemas.

    Before

    import { JSONSchema, Schema } from "effect"
    const schema = Schema.Exit({
    failure: Schema.String,
    success: Schema.Number,
    defect: Schema.Defect
    })
    console.log(JSON.stringify(JSONSchema.make(schema), null, 2))
    /*
    throws
    Error: Missing annotation
    at path: ["cause"]["left"]
    details: Generating a JSON Schema for this schema requires an "identifier" annotation
    schema (Suspend): CauseEncoded<string>
    */

    After

    import { JSONSchema, Schema } from "effect"
    const schema = Schema.Exit({
    failure: Schema.String,
    success: Schema.Number,
    defect: Schema.Defect
    })
    console.log(JSON.stringify(JSONSchema.make(schema), null, 2))
    /*
    Output:
    {
    "$schema": "http://json-schema.org/draft-07/schema#",
    "$defs": {
    "CauseEncoded0": {
    "anyOf": [
    {
    "type": "object",
    "required": [
    "_tag"
    ],
    "properties": {
    "_tag": {
    "type": "string",
    "enum": [
    "Empty"
    ]
    }
    },
    "additionalProperties": false
    },
    {
    "type": "object",
    "required": [
    "_tag",
    "error"
    ],
    "properties": {
    "_tag": {
    "type": "string",
    "enum": [
    "Fail"
    ]
    },
    "error": {
    "type": "string"
    }
    },
    "additionalProperties": false
    },
    {
    "type": "object",
    "required": [
    "_tag",
    "defect"
    ],
    "properties": {
    "_tag": {
    "type": "string",
    "enum": [
    "Die"
    ]
    },
    "defect": {
    "$ref": "#/$defs/Defect"
    }
    },
    "additionalProperties": false
    },
    {
    "type": "object",
    "required": [
    "_tag",
    "fiberId"
    ],
    "properties": {
    "_tag": {
    "type": "string",
    "enum": [
    "Interrupt"
    ]
    },
    "fiberId": {
    "$ref": "#/$defs/FiberIdEncoded"
    }
    },
    "additionalProperties": false
    },
    {
    "type": "object",
    "required": [
    "_tag",
    "left",
    "right"
    ],
    "properties": {
    "_tag": {
    "type": "string",
    "enum": [
    "Sequential"
    ]
    },
    "left": {
    "$ref": "#/$defs/CauseEncoded0"
    },
    "right": {
    "$ref": "#/$defs/CauseEncoded0"
    }
    },
    "additionalProperties": false
    },
    {
    "type": "object",
    "required": [
    "_tag",
    "left",
    "right"
    ],
    "properties": {
    "_tag": {
    "type": "string",
    "enum": [
    "Parallel"
    ]
    },
    "left": {
    "$ref": "#/$defs/CauseEncoded0"
    },
    "right": {
    "$ref": "#/$defs/CauseEncoded0"
    }
    },
    "additionalProperties": false
    }
    ],
    "title": "CauseEncoded<string>"
    },
    "Defect": {
    "$id": "/schemas/unknown",
    "title": "unknown"
    },
    "FiberIdEncoded": {
    "anyOf": [
    {
    "$ref": "#/$defs/FiberIdNoneEncoded"
    },
    {
    "$ref": "#/$defs/FiberIdRuntimeEncoded"
    },
    {
    "$ref": "#/$defs/FiberIdCompositeEncoded"
    }
    ]
    },
    "FiberIdNoneEncoded": {
    "type": "object",
    "required": [
    "_tag"
    ],
    "properties": {
    "_tag": {
    "type": "string",
    "enum": [
    "None"
    ]
    }
    },
    "additionalProperties": false
    },
    "FiberIdRuntimeEncoded": {
    "type": "object",
    "required": [
    "_tag",
    "id",
    "startTimeMillis"
    ],
    "properties": {
    "_tag": {
    "type": "string",
    "enum": [
    "Runtime"
    ]
    },
    "id": {
    "$ref": "#/$defs/Int"
    },
    "startTimeMillis": {
    "$ref": "#/$defs/Int"
    }
    },
    "additionalProperties": false
    },
    "Int": {
    "type": "integer",
    "description": "an integer",
    "title": "int"
    },
    "FiberIdCompositeEncoded": {
    "type": "object",
    "required": [
    "_tag",
    "left",
    "right"
    ],
    "properties": {
    "_tag": {
    "type": "string",
    "enum": [
    "Composite"
    ]
    },
    "left": {
    "$ref": "#/$defs/FiberIdEncoded"
    },
    "right": {
    "$ref": "#/$defs/FiberIdEncoded"
    }
    },
    "additionalProperties": false
    }
    },
    "anyOf": [
    {
    "type": "object",
    "required": [
    "_tag",
    "cause"
    ],
    "properties": {
    "_tag": {
    "type": "string",
    "enum": [
    "Failure"
    ]
    },
    "cause": {
    "$ref": "#/$defs/CauseEncoded0"
    }
    },
    "additionalProperties": false
    },
    {
    "type": "object",
    "required": [
    "_tag",
    "value"
    ],
    "properties": {
    "_tag": {
    "type": "string",
    "enum": [
    "Success"
    ]
    },
    "value": {
    "type": "number"
    }
    },
    "additionalProperties": false
    }
    ],
    "title": "ExitEncoded<number, string, Defect>"
    }
    */
  • #4775 570e45f Thanks @gcanti! - JSONSchema: preserve original key name when using fromKey followed by annotations, closes #4774.

    Before:

    import { JSONSchema, Schema } from "effect"
    const schema = Schema.Struct({
    a: Schema.propertySignature(Schema.String)
    .pipe(Schema.fromKey("b"))
    .annotations({})
    })
    console.log(JSON.stringify(JSONSchema.make(schema), null, 2))
    /*
    {
    "$schema": "http://json-schema.org/draft-07/schema#",
    "type": "object",
    "required": [
    "a"
    ],
    "properties": {
    "a": {
    "type": "string"
    }
    },
    "additionalProperties": false
    }
    */

    After:

    import { JSONSchema, Schema } from "effect"
    const schema = Schema.Struct({
    a: Schema.propertySignature(Schema.String)
    .pipe(Schema.fromKey("b"))
    .annotations({})
    })
    console.log(JSON.stringify(JSONSchema.make(schema), null, 2))
    /*
    {
    "$schema": "http://json-schema.org/draft-07/schema#",
    "type": "object",
    "required": [
    "b"
    ],
    "properties": {
    "b": {
    "type": "string"
    }
    },
    "additionalProperties": false
    }
    */

3.14.12

Patch Changes

  • #4770 c2ad9ee Thanks @gcanti! - Fixes a bug where non existing properties were allowed in the make constructor of a Schema.Class, closes #4767.

    Example

    import { Schema } from "effect"
    class A extends Schema.Class<A>("A")({
    a: Schema.String
    }) {}
    A.make({
    a: "a",
    // @ts-expect-error: Object literal may only specify known properties, and 'b' does not exist in type '{ readonly a: string; }'.ts(2353)
    b: "b"
    })
  • #4735 9c68654 Thanks @suddenlyGiovanni! - Improve Number module with comprehensive TsDocs and type-level tests

3.14.11

Patch Changes

  • #4756 e536127 Thanks @tim-smart! - allow Pool to acquire multiple items at once

3.14.10

Patch Changes

  • #4748 bc7efa3 Thanks @tim-smart! - preserve refinement types in Match.when

3.14.9

Patch Changes

  • #4734 d78249f Thanks @thewilkybarkid! - Allow Match.typeTags to specify a return type

3.14.8

Patch Changes

  • #4708 b3a2d32 Thanks @thewilkybarkid! - Make Match.valueTags dual

3.14.7

Patch Changes

  • #4706 b542a4b Thanks @IGassmann! - Fix summary metric’s quantile values

3.14.6

Patch Changes

  • #4674 47618c1 Thanks @suddenlyGiovanni! - Improved TsDoc documentation for MutableHashSet module.

  • #4699 6077882 Thanks @gcanti! - Fix JSONSchema generation for record values that include undefined, closes #4697.

    Before

    import { JSONSchema, Schema } from "effect"
    const schema = Schema.partial(
    Schema.Struct(
    { foo: Schema.Number },
    {
    key: Schema.String,
    value: Schema.Number
    }
    )
    )
    console.log(JSON.stringify(JSONSchema.make(schema), null, 2))
    // throws

    After

    import { JSONSchema, Schema } from "effect"
    const schema = Schema.partial(
    Schema.Struct(
    { foo: Schema.Number },
    {
    key: Schema.String,
    value: Schema.Number
    }
    )
    )
    console.log(JSON.stringify(JSONSchema.make(schema), null, 2))
    /*
    Output:
    {
    "$schema": "http://json-schema.org/draft-07/schema#",
    "type": "object",
    "required": [],
    "properties": {
    "foo": {
    "type": "number"
    }
    },
    "additionalProperties": {
    "type": "number"
    }
    }
    */

3.14.5

Patch Changes

  • #4676 40dbfef Thanks @tim-smart! - allow Effect.fnUntraced to return non-effects

  • #4682 5a5ebdd Thanks @thewilkybarkid! - ensure Equal considers URL by value

3.14.4

Patch Changes

  • #4667 e4ba2c6 Thanks @suddenlyGiovanni! - Fix: HashSet.md api docs; previously broken by issue with Docgen JsDoc parser.

3.14.3

Patch Changes

  • #4664 37aa8e1 Thanks @suddenlyGiovanni! - Improved TsDoc documentation for HashSet module.

  • #4670 34f03d6 Thanks @tim-smart! - fix Data.TaggedEnum with generics regression

3.14.2

Patch Changes

  • #4646 f87991b Thanks @gcanti! - SchemaAST: add missing getSchemaIdAnnotation API

  • #4646 f87991b Thanks @gcanti! - Arbitrary: fix bug where annotations were ignored.

    Before

    import { Arbitrary, Schema } from "effect"
    const schema = Schema.Int.annotations({
    arbitrary: (_, ctx) => (fc) => {
    console.log("context: ", ctx)
    return fc.integer()
    }
    }).pipe(Schema.greaterThan(0), Schema.lessThan(10))
    Arbitrary.make(schema)
    // No output ❌

    After

    import { Arbitrary, Schema } from "effect"
    const schema = Schema.Int.annotations({
    arbitrary: (_, ctx) => (fc) => {
    console.log("context: ", ctx)
    return fc.integer()
    }
    }).pipe(Schema.greaterThan(0), Schema.lessThan(10))
    Arbitrary.make(schema)
    /*
    context: {
    maxDepth: 2,
    constraints: {
    _tag: 'NumberConstraints',
    constraints: { min: 0, minExcluded: true, max: 10, maxExcluded: true },
    isInteger: true
    }
    }
    */
  • #4648 0a3e3e1 Thanks @gcanti! - Schema: standardSchemaV1 now includes the schema, closes #4494.

    This update fixes an issue where passing Schema.standardSchemaV1(...) directly to JSONSchema.make would throw a TypeError. The schema was missing from the returned object, causing the JSON schema generation to fail.

    Now standardSchemaV1 includes the schema itself, so it can be used with JSONSchema.make without issues.

    Example

    import { JSONSchema, Schema } from "effect"
    const Person = Schema.Struct({
    name: Schema.optionalWith(Schema.NonEmptyString, { exact: true })
    })
    const standardSchema = Schema.standardSchemaV1(Person)
    console.log(JSONSchema.make(standardSchema))
    /*
    {
    '$schema': 'http://json-schema.org/draft-07/schema#',
    '$defs': {
    NonEmptyString: {
    type: 'string',
    description: 'a non empty string',
    title: 'nonEmptyString',
    minLength: 1
    }
    },
    type: 'object',
    required: [],
    properties: { name: { '$ref': '#/$defs/NonEmptyString' } },
    additionalProperties: false
    }
    */

3.14.1

Patch Changes

  • #4620 4a274fe Thanks @tim-smart! - remove Context.ValidTagsById usage

3.14.0

Minor Changes

  • #4469 1f47e4e Thanks @vinassefranche! - Add DateTime.nowAsDate creator

  • #4469 26dd75f Thanks @tim-smart! - expose the Layer.MemoMap via Layer.CurrentMemoMap to the layers being built

  • #4469 04dff2d Thanks @tim-smart! - add Tracer Span.addLinks, for dynamically linking spans

  • #4469 c7fac0c Thanks @LaureRC! - Add HashMap.every

  • #4469 ffaa3f3 Thanks @vinassefranche! - Add Either.transposeOption

  • #4469 ab957c1 Thanks @vinassefranche! - Make TestClock.setTime accept a DateTime.Input

  • #4469 35db9ce Thanks @LaureRC! - Add Effect.transposeMapOption

  • #4469 cf77ea9 Thanks @f15u! - Add Array.window function

  • #4469 26dd75f Thanks @tim-smart! - add LayerMap module

    A LayerMap allows you to create a map of Layer’s that can be used to dynamically access resources based on a key.

    Here is an example of how you can use a LayerMap to create a service that provides access to multiple OpenAI completions services.

    import { Completions } from "@effect/ai"
    import { OpenAiClient, OpenAiCompletions } from "@effect/ai-openai"
    import { FetchHttpClient } from "@effect/platform"
    import { NodeRuntime } from "@effect/platform-node"
    import { Config, Effect, Layer, LayerMap } from "effect"
    // create the openai client layer
    const OpenAiLayer = OpenAiClient.layerConfig({
    apiKey: Config.redacted("OPENAI_API_KEY")
    }).pipe(Layer.provide(FetchHttpClient.layer))
    // create a service that wraps a LayerMap
    class AiClients extends LayerMap.Service<AiClients>()("AiClients", {
    // this LayerMap will provide the ai Completions service
    provides: Completions.Completions,
    // define the lookup function for the layer map
    //
    // The returned Layer will be used to provide the Completions service for the
    // given model.
    lookup: (model: OpenAiCompletions.Model) =>
    OpenAiCompletions.layer({ model }),
    // If a layer is not used for a certain amount of time, it can be removed
    idleTimeToLive: "5 seconds",
    // Supply the dependencies for the layers in the LayerMap
    dependencies: [OpenAiLayer]
    }) {}
    // usage
    Effect.gen(function* () {
    // access and use the generic Completions service
    const ai = yield* Completions.Completions
    const response = yield* ai.create("Hello, world!")
    console.log(response.text)
    }).pipe(
    // use the AiClients service to provide a variant of the Completions service
    AiClients.provide("gpt-4o"),
    // provide the LayerMap service
    Effect.provide(AiClients.Default),
    NodeRuntime.runMain
    )
  • #4469 baaab60 Thanks @vinassefranche! - Make Runtime.run* apis dual

Patch Changes

  • #4469 aba2d1d Thanks @tim-smart! - preserve interruptors in channel executor .runIn

3.13.12

Patch Changes

  • #4610 0c4803f Thanks @gcanti! - Preserve specific annotations (e.g., arbitrary) when using Schema.typeSchema, closes #4609.

    Previously, annotations such as arbitrary were lost when calling Schema.typeSchema on a transformation. This update ensures that certain annotations, which depend only on the “to” side of the transformation, are preserved.

    Annotations that are now retained:

    • examples
    • default
    • jsonSchema
    • arbitrary
    • pretty
    • equivalence

    Example

    Before

    import { Arbitrary, FastCheck, Schema } from "effect"
    const schema = Schema.NumberFromString.annotations({
    arbitrary: () => (fc) => fc.constant(1)
    })
    const to = Schema.typeSchema(schema) // ❌ Annotation is lost
    console.log(FastCheck.sample(Arbitrary.make(to), 5))
    /*
    [
    2.5223372357846707e-44,
    -2.145443957806771e+25,
    -3.4028179901346956e+38,
    5.278086259208735e+29,
    1.8216880036222622e-44
    ]
    */

    After

    import { Arbitrary, FastCheck, Schema } from "effect"
    const schema = Schema.NumberFromString.annotations({
    arbitrary: () => (fc) => fc.constant(1)
    })
    const to = Schema.typeSchema(schema) // ✅ Annotation is now preserved
    console.log(FastCheck.sample(Arbitrary.make(to), 5))
    /*
    [ 1, 1, 1, 1, 1 ]
    */
  • #4607 6f65ac4 Thanks @gcanti! - Add support for jsonSchema annotations on SymbolFromSelf index signatures.

    Before

    import { JSONSchema, Schema } from "effect"
    const schema = Schema.Record({
    key: Schema.SymbolFromSelf.annotations({ jsonSchema: { type: "string" } }),
    value: Schema.Number
    })
    JSONSchema.make(schema)
    /*
    throws:
    Error: Unsupported index signature parameter
    schema (SymbolKeyword): symbol
    */

    After

    import { JSONSchema, Schema } from "effect"
    const schema = Schema.Record({
    key: Schema.SymbolFromSelf.annotations({ jsonSchema: { type: "string" } }),
    value: Schema.Number
    })
    console.log(JSON.stringify(JSONSchema.make(schema), null, 2))
    /*
    Output:
    {
    "$schema": "http://json-schema.org/draft-07/schema#",
    "type": "object",
    "required": [],
    "properties": {},
    "additionalProperties": {
    "type": "number"
    },
    "propertyNames": {
    "type": "string"
    }
    }
    */

3.13.11

Patch Changes

  • #4601 fad8cca Thanks @gcanti! - Schema: enhance the internal formatUnknown function to handle various types including iterables, classes, and additional edge cases.

    Before

    import { Schema } from "effect"
    const schema = Schema.Array(Schema.Number)
    Schema.decodeUnknownSync(schema)(new Set([1, 2]))
    // throws Expected ReadonlyArray<number>, actual {}
    class A {
    constructor(readonly a: number) {}
    }
    Schema.decodeUnknownSync(schema)(new A(1))
    // throws Expected ReadonlyArray<number>, actual {"a":1}

    After

    import { Schema } from "effect"
    const schema = Schema.Array(Schema.Number)
    Schema.decodeUnknownSync(schema)(new Set([1, 2]))
    // throws Expected ReadonlyArray<number>, actual Set([1,2])
    class A {
    constructor(readonly a: number) {}
    }
    Schema.decodeUnknownSync(schema)(new A(1))
    // throws Expected ReadonlyArray<number>, actual A({"a":1})
  • #4606 4296293 Thanks @gcanti! - Fix issue with generic filters when generating arbitraries, closes #4605.

    Previously, applying a filter to a schema when generating arbitraries could cause a TypeError due to missing properties. This fix ensures that arbitraries are generated correctly when filters are used.

    Before

    import { Arbitrary, Schema } from "effect"
    const schema = Schema.BigIntFromSelf.pipe(Schema.filter(() => true))
    Arbitrary.make(schema)
    // TypeError: Cannot read properties of undefined (reading 'min')

    After

    import { Arbitrary, Schema } from "effect"
    const schema = Schema.BigIntFromSelf.pipe(Schema.filter(() => true))
    const result = Arbitrary.make(schema) // Works correctly
  • #4587 9c241ab Thanks @gcanti! - Schema: simplify Struct and Record return types.

  • #4591 082b0c1 Thanks @IMax153! - Improve clarity of the TimeoutException error message

  • #4604 be12983 Thanks @gcanti! - Add support for refinements to Schema.omit, closes #4603.

    Before

    import { Schema } from "effect"
    const schema = Schema.Struct({
    a: Schema.String,
    b: Schema.String
    })
    const omitted = schema.pipe(
    Schema.filter(() => true),
    Schema.omit("a")
    )
    console.log(String(omitted.ast))
    // {} ❌

    After

    import { Schema } from "effect"
    const schema = Schema.Struct({
    a: Schema.String,
    b: Schema.String
    })
    const omitted = schema.pipe(
    Schema.filter(() => true),
    Schema.omit("a")
    )
    console.log(String(omitted.ast))
    // { readonly b: string }
  • #4593 de88127 Thanks @gcanti! - Schema: export Field type.

    Useful for creating a type that can be used to add custom constraints to the fields of a struct.

    import { Schema } from "effect"
    const f = <Fields extends Record<"a" | "b", Schema.Struct.Field>>(
    schema: Schema.Struct<Fields>
    ) => {
    return schema.omit("a")
    }
    // ┌─── Schema.Struct<{ b: typeof Schema.Number; }>
    // ▼
    const result = f(Schema.Struct({ a: Schema.String, b: Schema.Number }))

3.13.10

Patch Changes

  • #4578 527c964 Thanks @gcanti! - Allow toString Method to Be Overridden in Schema Classes, closes #4577.

    Previously, attempting to override the toString method in schema classes caused a TypeError in the browser because the property was set as read-only (writable: false). This fix makes toString writable, allowing developers to override it when needed.

3.13.9

Patch Changes

  • #4579 2976e52 Thanks @giuliobracci! - Fix Match.tags throwing exception on undefined input value

3.13.8

Patch Changes

  • #4567 c65d336 Thanks @rehos! - Schema: standardSchemaV1 now returns all errors by default and supports custom options.

    The standardSchemaV1 now returns all validation errors by default (ParseOptions = { errors: "all" }). Additionally, it now accepts an optional overrideOptions parameter, allowing you to customize the default parsing behavior as needed.

  • #4565 22d2ebb Thanks @gcanti! - ParseResult.ArrayFormatter: correct _tag fields for Refinement and Transformation issues, closes #4564.

    This update fixes an issue where ParseResult.ArrayFormatter incorrectly labeled Refinement and Transformation errors as Type in the output.

    Before

    import { Effect, ParseResult, Schema } from "effect"
    const schema = Schema.Struct({
    a: Schema.NonEmptyString,
    b: Schema.NumberFromString
    })
    const input = { a: "", b: "" }
    const program = Schema.decodeUnknown(schema, { errors: "all" })(input).pipe(
    Effect.catchTag("ParseError", (err) =>
    ParseResult.ArrayFormatter.formatError(err).pipe(
    Effect.map((err) => JSON.stringify(err, null, 2))
    )
    )
    )
    program.pipe(Effect.runPromise).then(console.log)
    /*
    [
    {
    "_tag": "Type", ❌
    "path": [
    "a"
    ],
    "message": "Expected a non empty string, actual \"\""
    },
    {
    "_tag": "Type", ❌
    "path": [
    "b"
    ],
    "message": "Unable to decode \"\" into a number"
    }
    ]
    */

    After

    import { Effect, ParseResult, Schema } from "effect"
    const schema = Schema.Struct({
    a: Schema.NonEmptyString,
    b: Schema.NumberFromString
    })
    const input = { a: "", b: "" }
    const program = Schema.decodeUnknown(schema, { errors: "all" })(input).pipe(
    Effect.catchTag("ParseError", (err) =>
    ParseResult.ArrayFormatter.formatError(err).pipe(
    Effect.map((err) => JSON.stringify(err, null, 2))
    )
    )
    )
    program.pipe(Effect.runPromise).then(console.log)
    /*
    [
    {
    "_tag": "Refinement", ✅
    "path": [
    "a"
    ],
    "message": "Expected a non empty string, actual \"\""
    },
    {
    "_tag": "Transformation", ✅
    "path": [
    "b"
    ],
    "message": "Unable to decode \"\" into a number"
    }
    ]
    */

3.13.7

Patch Changes

  • #4540 840cc73 Thanks @gcanti! - Add additionalPropertiesStrategy option to OpenApi.fromApi, closes #4531.

    This update introduces the additionalPropertiesStrategy option in OpenApi.fromApi, allowing control over how additional properties are handled in the generated OpenAPI schema.

    • When "strict" (default), additional properties are disallowed ("additionalProperties": false).
    • When "allow", additional properties are allowed ("additionalProperties": true), making APIs more flexible.

    The additionalPropertiesStrategy option has also been added to:

    • JSONSchema.fromAST
    • OpenApiJsonSchema.makeWithDefs

    Example

    import {
    HttpApi,
    HttpApiEndpoint,
    HttpApiGroup,
    OpenApi
    } from "@effect/platform"
    import { Schema } from "effect"
    const api = HttpApi.make("api").add(
    HttpApiGroup.make("group").add(
    HttpApiEndpoint.get("get", "/").addSuccess(
    Schema.Struct({ a: Schema.String })
    )
    )
    )
    const schema = OpenApi.fromApi(api, {
    additionalPropertiesStrategy: "allow"
    })
    console.log(JSON.stringify(schema, null, 2))
    /*
    {
    "openapi": "3.1.0",
    "info": {
    "title": "Api",
    "version": "0.0.1"
    },
    "paths": {
    "/": {
    "get": {
    "tags": [
    "group"
    ],
    "operationId": "group.get",
    "parameters": [],
    "security": [],
    "responses": {
    "200": {
    "description": "Success",
    "content": {
    "application/json": {
    "schema": {
    "type": "object",
    "required": [
    "a"
    ],
    "properties": {
    "a": {
    "type": "string"
    }
    },
    "additionalProperties": true
    }
    }
    }
    },
    "400": {
    "description": "The request did not match the expected schema",
    "content": {
    "application/json": {
    "schema": {
    "$ref": "#/components/schemas/HttpApiDecodeError"
    }
    }
    }
    }
    }
    }
    }
    },
    "components": {
    "schemas": {
    "HttpApiDecodeError": {
    "type": "object",
    "required": [
    "issues",
    "message",
    "_tag"
    ],
    "properties": {
    "issues": {
    "type": "array",
    "items": {
    "$ref": "#/components/schemas/Issue"
    }
    },
    "message": {
    "type": "string"
    },
    "_tag": {
    "type": "string",
    "enum": [
    "HttpApiDecodeError"
    ]
    }
    },
    "additionalProperties": true,
    "description": "The request did not match the expected schema"
    },
    "Issue": {
    "type": "object",
    "required": [
    "_tag",
    "path",
    "message"
    ],
    "properties": {
    "_tag": {
    "type": "string",
    "enum": [
    "Pointer",
    "Unexpected",
    "Missing",
    "Composite",
    "Refinement",
    "Transformation",
    "Type",
    "Forbidden"
    ],
    "description": "The tag identifying the type of parse issue"
    },
    "path": {
    "type": "array",
    "items": {
    "$ref": "#/components/schemas/PropertyKey"
    },
    "description": "The path to the property where the issue occurred"
    },
    "message": {
    "type": "string",
    "description": "A descriptive message explaining the issue"
    }
    },
    "additionalProperties": true,
    "description": "Represents an error encountered while parsing a value to match the schema"
    },
    "PropertyKey": {
    "anyOf": [
    {
    "type": "string"
    },
    {
    "type": "number"
    },
    {
    "type": "object",
    "required": [
    "_tag",
    "key"
    ],
    "properties": {
    "_tag": {
    "type": "string",
    "enum": [
    "symbol"
    ]
    },
    "key": {
    "type": "string"
    }
    },
    "additionalProperties": true,
    "description": "an object to be decoded into a globally shared symbol"
    }
    ]
    }
    },
    "securitySchemes": {}
    },
    "security": [],
    "tags": [
    {
    "name": "group"
    }
    ]
    }
    */
  • #4541 9bf8a74 Thanks @fubhy! - Disallowed excess properties for various function options

  • #4554 87ba23c Thanks @gcanti! - ConfigProvider: fromEnv: add missing Partial modifier.

3.13.6

Patch Changes

  • #4551 3154ce4 Thanks @gcanti! - Arbitrary: make called on Schema.Class now respects property annotations, closes #4550.

    Previously, when calling Arbitrary.make on a Schema.Class, property-specific annotations (such as arbitrary) were ignored, leading to unexpected values in generated instances.

    Before

    Even though a had an arbitrary annotation, the generated values were random:

    import { Arbitrary, FastCheck, Schema } from "effect"
    class Class extends Schema.Class<Class>("Class")({
    a: Schema.NumberFromString.annotations({
    arbitrary: () => (fc) => fc.constant(1)
    })
    }) {}
    console.log(FastCheck.sample(Arbitrary.make(Class), 5))
    /*
    Example Output:
    [
    Class { a: 2.6624670822171524e-44 },
    Class { a: 3.4028177873105996e+38 },
    Class { a: 3.402820626847944e+38 },
    Class { a: 3.783505853677006e-44 },
    Class { a: 3243685 }
    ]
    */

    After

    Now, the values respect the arbitrary annotation and return the expected constant:

    import { Arbitrary, FastCheck, Schema } from "effect"
    class Class extends Schema.Class<Class>("Class")({
    a: Schema.NumberFromString.annotations({
    arbitrary: () => (fc) => fc.constant(1)
    })
    }) {}
    console.log(FastCheck.sample(Arbitrary.make(Class), 5))
    /*
    [
    Class { a: 1 },
    Class { a: 1 },
    Class { a: 1 },
    Class { a: 1 },
    Class { a: 1 }
    ]
    */

3.13.5

Patch Changes

  • #4530 367bb35 Thanks @tim-smart! - Match.tag + Match.withReturnType can use literals without as const

  • #4543 6cf11c3 Thanks @gcanti! - Preserve branded primitive types in DeepMutable transformation, closes #4542.

    Previously, applying DeepMutable to branded primitive types (e.g., string & Brand.Brand<"mybrand">) caused unexpected behavior, where String prototype methods were incorrectly inherited.

    This fix ensures that branded types remain unchanged during transformation, preventing type inconsistencies.

    Example

    Before

    import type { Brand, Types } from "effect"
    type T = string & Brand.Brand<"mybrand">
    /*
    type Result = {
    [x: number]: string;
    toString: () => string;
    charAt: (pos: number) => string;
    charCodeAt: (index: number) => number;
    concat: (...strings: string[]) => string;
    indexOf: (searchString: string, position?: number) => number;
    ... 47 more ...;
    [BrandTypeId]: {
    ...;
    };
    }
    */
    type Result = Types.DeepMutable<T>

    After

    import type { Brand, Types } from "effect"
    type T = string & Brand.Brand<"mybrand">
    // type Result = string & Brand.Brand<"mybrand">
    type Result = Types.DeepMutable<T>
  • #4546 a0acec8 Thanks @gcanti! - Schema.extend: add support for Transformation + Struct, closes #4536.

    Example

    Before

    import { Schema } from "effect"
    const A = Schema.Struct({
    a: Schema.String
    })
    const B = Schema.Struct({
    b: Schema.String
    })
    const C = Schema.Struct({
    c: Schema.String
    })
    const AB = Schema.transform(A, B, {
    strict: true,
    decode: (a) => ({ b: a.a }),
    encode: (b) => ({ a: b.b })
    })
    // Transformation + Struct
    const schema = Schema.extend(AB, C)
    /*
    throws:
    Error: Unsupported schema or overlapping types
    details: cannot extend ({ readonly a: string } <-> { readonly b: string }) with { readonly c: string }
    */

    After

    import { Schema } from "effect"
    const A = Schema.Struct({
    a: Schema.String
    })
    const B = Schema.Struct({
    b: Schema.String
    })
    const C = Schema.Struct({
    c: Schema.String
    })
    const AB = Schema.transform(A, B, {
    strict: true,
    decode: (a) => ({ b: a.a }),
    encode: (b) => ({ a: b.b })
    })
    // Transformation + Struct
    const schema = Schema.extend(AB, C)
    console.log(Schema.decodeUnknownSync(schema)({ a: "a", c: "c" }))
    // Output: { b: 'a', c: 'c' }
    console.log(Schema.encodeSync(schema)({ b: "b", c: "c" }))
    // Output: { a: 'b', c: 'c' }

3.13.4

Patch Changes

  • #4533 17d9e89 Thanks @gcanti! - Schema: Export MakeOptions type, closes #4532.

3.13.3

Patch Changes

  • #4502 cc5588d Thanks @gcanti! - Schema: More Accurate Return Types for DataFromSelf and Data.

    This update refines the return types of DataFromSelf and Data, making them clearer and more specific, especially when working with structured schemas.

    Before

    The return types were more generic, making it harder to see the underlying structure:

    import { Schema } from "effect"
    const struct = Schema.Struct({ a: Schema.NumberFromString })
    // ┌─── Schema.DataFromSelf<Schema<{ readonly a: number; }, { readonly a: string; }>>
    // ▼
    const schema1 = Schema.DataFromSelf(struct)
    // ┌─── Schema.Data<Schema<{ readonly a: number; }, { readonly a: string; }>>
    // ▼
    const schema2 = Schema.Data(struct)

    After

    Now, the return types clearly reflect the original schema structure:

    import { Schema } from "effect"
    const struct = Schema.Struct({ a: Schema.NumberFromString })
    // ┌─── Schema.DataFromSelf<Schema.Struct<{ a: typeof Schema.NumberFromString; }>>
    // ▼
    const schema1 = Schema.DataFromSelf(struct)
    // ┌─── Schema.Data<Schema.Struct<{ a: typeof Schema.NumberFromString; }>>
    // ▼
    const schema2 = Schema.Data(struct)
  • #4510 623c8cd Thanks @gcanti! - Schema: More Accurate Return Type for compose.

    Before

    import { Schema } from "effect"
    // ┌─── SchemaClass<number | null, string>
    // ▼
    const schema = Schema.compose(
    Schema.NumberFromString,
    Schema.NullOr(Schema.Number)
    )
    // @ts-expect-error: Property 'from' does not exist
    schema.from
    // @ts-expect-error: Property 'to' does not exist
    schema.to

    After

    import { Schema } from "effect"
    // ┌─── transform<typeof Schema.NumberFromString, Schema.NullOr<typeof Schema.Number>>
    // ▼
    const schema = Schema.compose(
    Schema.NumberFromString,
    Schema.NullOr(Schema.Number)
    )
    // ┌─── typeof Schema.NumberFromString
    // ▼
    schema.from
    // ┌─── Schema.NullOr<typeof Schema.Number>
    // ▼
    schema.to
  • #4488 00b4eb1 Thanks @gcanti! - Schema: more precise return types when filters are involved.

    Example (with Schema.maxLength)

    Before

    import { Schema } from "effect"
    // ┌─── Schema.filter<Schema.Schema<string, string, never>>
    // ▼
    const schema = Schema.String.pipe(Schema.maxLength(10))
    // Schema<string, string, never>
    schema.from

    After

    import { Schema } from "effect"
    // ┌─── Schema.filter<typeof Schema.String>
    // ▼
    const schema = Schema.String.pipe(Schema.maxLength(10))
    // typeof Schema.String
    schema.from

    String filters:

    • maxLength
    • minLength
    • length
    • pattern
    • startsWith
    • endsWith
    • includes
    • lowercased
    • capitalized
    • uncapitalized
    • uppercased
    • nonEmptyString
    • trimmed

    Number filters:

    • finite
    • greaterThan
    • greaterThanOrEqualTo
    • lessThan
    • lessThanOrEqualTo
    • int
    • multipleOf
    • between
    • nonNaN
    • positive
    • negative
    • nonPositive
    • nonNegative

    BigInt filters:

    • greaterThanBigInt
    • greaterThanOrEqualToBigInt
    • lessThanBigInt
    • lessThanOrEqualToBigInt
    • betweenBigInt
    • positiveBigInt
    • negativeBigInt
    • nonNegativeBigInt
    • nonPositiveBigInt

    Duration filters:

    • lessThanDuration
    • lessThanOrEqualToDuration
    • greaterThanDuration
    • greaterThanOrEqualToDuration
    • betweenDuration

    Array filters:

    • minItems
    • maxItems
    • itemsCount

    Date filters:

    • validDate
    • lessThanDate
    • lessThanOrEqualToDate
    • greaterThanDate
    • greaterThanOrEqualToDate
    • betweenDate

    BigDecimal filters:

    • greaterThanBigDecimal
    • greaterThanOrEqualToBigDecimal
    • lessThanBigDecimal
    • lessThanOrEqualToBigDecimal
    • positiveBigDecimal
    • nonNegativeBigDecimal
    • negativeBigDecimal
    • nonPositiveBigDecimal
    • betweenBigDecimal
  • #4508 f2aee98 Thanks @gcanti! - Schema: More Accurate Return Types for ArrayEnsure and NonEmptyArrayEnsure.

    Before

    import { Schema } from "effect"
    const schema1 = Schema.ArrayEnsure(Schema.String)
    // @ts-expect-error: Property 'from' does not exist
    schema1.from
    const schema2 = Schema.NonEmptyArrayEnsure(Schema.String)
    // @ts-expect-error: Property 'from' does not exist
    schema2.from

    After

    import { Schema } from "effect"
    const schema1 = Schema.ArrayEnsure(Schema.String)
    // ┌─── Schema.Union<[typeof Schema.String, Schema.Array$<typeof Schema.String>]>
    // ▼
    schema1.from
    const schema2 = Schema.NonEmptyArrayEnsure(Schema.String)
    // ┌─── Schema.Union<[typeof Schema.String, Schema.NonEmptyArray<typeof Schema.String>]>
    // ▼
    schema2.from
  • #4509 fb798eb Thanks @gcanti! - Schema: More Accurate Return Types for:

    • transformLiteral
    • clamp
    • clampBigInt
    • clampDuration
    • clampBigDecimal
    • head
    • headNonEmpty
    • headOrElse
  • #4524 2251b15 Thanks @gcanti! - Schema: More Accurate Return Type for parseNumber.

    Before

    import { Schema } from "effect"
    const schema = Schema.parseNumber(Schema.String)
    // ┌─── Schema<string>
    // ▼
    schema.from

    After

    import { Schema } from "effect"
    const schema = Schema.parseNumber(Schema.String)
    // ┌─── typeof Schema.String
    // ▼
    schema.from
  • #4483 2e15c1e Thanks @mikearnaldi! - Fix nested batching

  • #4514 a4979db Thanks @gcanti! - Schema: add missing from property to brand interface.

    Before

    import { Schema } from "effect"
    const schema = Schema.String.pipe(Schema.brand("my-brand"))
    // @ts-expect-error: Property 'from' does not exist
    schema.from

    After

    import { Schema } from "effect"
    const schema = Schema.String.pipe(Schema.brand("my-brand"))
    // ┌─── typeof Schema.String
    // ▼
    schema.from
  • #4496 b74255a Thanks @tim-smart! - ensure fibers can’t be added to Fiber{Handle,Set,Map} during closing

  • #4419 d7f6a5c Thanks @KhraksMamtsov! - Fix Context.Tag unification

  • #4495 9dd8979 Thanks @KhraksMamtsov! - Simplify sortWith, sort, reverse, sortBy, unzip, dedupe signatures in Array module

  • #4507 477b488 Thanks @gcanti! - Schema: More Accurate Return Type for parseJson(schema).

    Before

    import { Schema } from "effect"
    // ┌─── Schema.SchemaClass<{ readonly a: number; }, string>
    // ▼
    const schema = Schema.parseJson(
    Schema.Struct({
    a: Schema.NumberFromString
    })
    )
    // @ts-expect-error: Property 'to' does not exist
    schema.to

    After

    import { Schema } from "effect"
    // ┌─── Schema.transform<Schema.SchemaClass<unknown, string, never>, Schema.Struct<{ a: typeof Schema.NumberFromString; }>>
    // ▼
    const schema = Schema.parseJson(
    Schema.Struct({
    a: Schema.NumberFromString
    })
    )
    // ┌─── Schema.Struct<{ a: typeof Schema.NumberFromString; }>
    // ▼
    schema.to
  • #4519 10932cb Thanks @gcanti! - Refactor JSONSchema to use additionalProperties instead of patternProperties for simple records, closes #4518.

    This update improves how records are represented in JSON Schema by replacing patternProperties with additionalProperties, resolving issues in OpenAPI schema generation.

    Why the change?

    • Fixes OpenAPI issues – Previously, records were represented using patternProperties, which caused problems with OpenAPI tools.
    • Better schema compatibility – Some tools, like openapi-ts, struggled with patternProperties, generating Record<string, never> instead of the correct type.
    • Fixes missing example values – When using patternProperties, OpenAPI failed to generate proper response examples, displaying only {}.
    • Simplifies schema modification – Users previously had to manually fix schemas with OpenApi.Transform, which was messy and lacked type safety.

    Before

    import { JSONSchema, Schema } from "effect"
    const schema = Schema.Record({ key: Schema.String, value: Schema.Number })
    console.log(JSON.stringify(JSONSchema.make(schema), null, 2))
    /*
    {
    "$schema": "http://json-schema.org/draft-07/schema#",
    "type": "object",
    "required": [],
    "properties": {},
    "patternProperties": {
    "": { // ❌ Empty string pattern
    "type": "number"
    }
    }
    }
    */

    After

    Now, additionalProperties is used instead, which properly represents an open-ended record:

    import { JSONSchema, Schema } from "effect"
    const schema = Schema.Record({ key: Schema.String, value: Schema.Number })
    console.log(JSON.stringify(JSONSchema.make(schema), null, 2))
    /*
    {
    "$schema": "http://json-schema.org/draft-07/schema#",
    "type": "object",
    "required": [],
    "properties": {},
    "additionalProperties": { // ✅ Represents unrestricted record keys
    "type": "number"
    }
    }
    */
  • #4501 9f6c784 Thanks @gcanti! - Schema: Add Missing declare API Interface to Expose Type Parameters.

    Example

    import { Schema } from "effect"
    const schema = Schema.OptionFromSelf(Schema.String)
    // ┌─── readonly [typeof Schema.String]
    // ▼
    schema.typeParameters
  • #4487 2c639ec Thanks @gcanti! - Schema: more precise return types when transformations are involved.

    • Chunk
    • NonEmptyChunk
    • Redacted
    • Option
    • OptionFromNullOr
    • OptionFromUndefinedOr
    • OptionFromNullishOr
    • Either
    • EitherFromUnion
    • ReadonlyMap
    • Map
    • HashMap
    • ReadonlySet
    • Set
    • HashSet
    • List
    • Cause
    • Exit
    • SortedSet
    • head
    • headNonEmpty
    • headOrElse

    Example (with Schema.Chunk)

    Before

    import { Schema } from "effect"
    const schema = Schema.Chunk(Schema.Number)
    // Property 'from' does not exist on type 'Chunk<typeof Number$>'
    schema.from

    After

    import { Schema } from "effect"
    const schema = Schema.Chunk(Schema.Number)
    // Schema.Array$<typeof Schema.Number>
    schema.from
  • #4492 886aaa8 Thanks @gcanti! - Schema: Improve Literal return type — now returns SchemaClass instead of Schema

3.13.2

Patch Changes

  • #4472 31be72a Thanks @gcanti! - Fix Schema.Enums toString() method to display correct enum values.

    Now, toString() correctly displays the actual enum values instead of internal numeric indices.

    Before

    import { Schema } from "effect"
    enum Fruits {
    Apple = "apple",
    Banana = "banana",
    Cantaloupe = 0
    }
    const schema = Schema.Enums(Fruits)
    console.log(String(schema))
    // Output: <enum 3 value(s): 0 | 1 | 2> ❌ (incorrect)

    After

    import { Schema } from "effect"
    enum Fruits {
    Apple = "apple",
    Banana = "banana",
    Cantaloupe = 0
    }
    const schema = Schema.Enums(Fruits)
    console.log(String(schema))
    // Output: <enum 3 value(s): "apple" | "banana" | 0> ✅ (correct)

3.13.1

Patch Changes

  • #4454 b56a211 Thanks @FizzyElt! - fix Option filterMap example

3.13.0

Minor Changes

  • #4280 8baef83 Thanks @tim-smart! - add Promise based apis to Fiber{Handle,Set,Map} modules

  • #4280 655bfe2 Thanks @gcanti! - Add Effect.transposeOption, closes #3142.

    Converts an Option of an Effect into an Effect of an Option.

    Details

    This function transforms an Option<Effect<A, E, R>> into an Effect<Option<A>, E, R>. If the Option is None, the resulting Effect will immediately succeed with a None value. If the Option is Some, the inner Effect will be executed, and its result wrapped in a Some.

    Example

    import { Effect, Option } from "effect"
    // ┌─── Option<Effect<number, never, never>>
    // ▼
    const maybe = Option.some(Effect.succeed(42))
    // ┌─── Effect<Option<number>, never, never>
    // ▼
    const result = Effect.transposeOption(maybe)
    console.log(Effect.runSync(result))
    // Output: { _id: 'Option', _tag: 'Some', value: 42 }
  • #4280 d90cbc2 Thanks @indietyp! - Add Effect.whenLogLevel, which conditionally executes an effect if the specified log level is enabled

  • #4280 75632bd Thanks @tim-smart! - add RcMap.touch, for reseting the idle timeout for an item

  • #4280 c874a2e Thanks @LaureRC! - Add HashMap.some

  • #4280 bf865e5 Thanks @tim-smart! - allow accessing args in Effect.fn pipe

  • #4280 f98b2b7 Thanks @tim-smart! - add RcMap.invalidate api, for removing a resource from an RcMap

  • #4280 de8ce92 Thanks @mikearnaldi! - Add Layer.updateService mirroring Effect.updateService

  • #4280 db426a5 Thanks @KhraksMamtsov! - Differ implements Pipeable

  • #4280 6862444 Thanks @thewilkybarkid! - Make it easy to convert a DateTime.Zoned to a DateTime.Utc

  • #4280 5fc8a90 Thanks @gcanti! - Add missing Either.void constructor.

  • #4280 546a492 Thanks @vinassefranche! - Add HashMap.toValues and HashSet.toValues getters

  • #4280 65c4796 Thanks @tim-smart! - add {FiberHandle,FiberSet,FiberMap}.awaitEmpty apis

  • #4280 9760fdc Thanks @gcanti! - Schema: Add standardSchemaV1 API to Generate a Standard Schema v1.

    Example

    import { Schema } from "effect"
    const schema = Schema.Struct({
    name: Schema.String
    })
    // ┌─── StandardSchemaV1<{ readonly name: string; }>
    // ▼
    const standardSchema = Schema.standardSchemaV1(schema)
  • #4280 5b471e7 Thanks @fubhy! - Added Duration.formatIso and Duration.fromIso for formatting and parsing ISO8601 durations.

  • #4280 4f810cc Thanks @tim-smart! - add Effect.filterEffect* apis

    Effect.filterEffectOrElse

    Filters an effect with an effectful predicate, falling back to an alternative effect if the predicate fails.

    import { Effect, pipe } from "effect"
    // Define a user interface
    interface User {
    readonly name: string
    }
    // Simulate an asynchronous authentication function
    declare const auth: () => Promise<User | null>
    const program = pipe(
    Effect.promise(() => auth()),
    // Use filterEffectOrElse with an effectful predicate
    Effect.filterEffectOrElse({
    predicate: (user) => Effect.succeed(user !== null),
    orElse: (user) => Effect.fail(new Error(`Unauthorized user: ${user}`))
    })
    )

    Effect.filterEffectOrFail

    Filters an effect with an effectful predicate, failing with a custom error if the predicate fails.

    import { Effect, pipe } from "effect"
    // Define a user interface
    interface User {
    readonly name: string
    }
    // Simulate an asynchronous authentication function
    declare const auth: () => Promise<User | null>
    const program = pipe(
    Effect.promise(() => auth()),
    // Use filterEffectOrFail with an effectful predicate
    Effect.filterEffectOrFail({
    predicate: (user) => Effect.succeed(user !== null),
    orFailWith: (user) => Effect.fail(new Error(`Unauthorized user: ${user}`))
    })
    )

Patch Changes

  • #4280 cf8b2dd Thanks @KhraksMamtsov! - Trie<out A> type annotations have been aligned. The type parameter was made covariant because the structure is immutable.

3.12.12

Patch Changes

  • #4440 4018eae Thanks @gcanti! - Schema: add missing support for tuple annotations in TaggedRequest.

  • #4439 543d36d Thanks @gcanti! - Schedule: fix unsafe tapOutput signature.

    Previously, tapOutput allowed using an output type that wasn’t properly inferred, leading to potential runtime errors. Now, TypeScript correctly detects mismatches at compile time, preventing unexpected crashes.

    Before (Unsafe, Causes Runtime Error)

    import { Effect, Schedule, Console } from "effect"
    const schedule = Schedule.once.pipe(
    Schedule.as<number | string>(1),
    Schedule.tapOutput((s: string) => Console.log(s.trim())) // ❌ Runtime error
    )
    Effect.runPromise(Effect.void.pipe(Effect.schedule(schedule)))
    // throws: TypeError: s.trim is not a function

    After (Safe, Catches Type Error at Compile Time)

    import { Console, Schedule } from "effect"
    const schedule = Schedule.once.pipe(
    Schedule.as<number | string>(1),
    // ✅ Type Error: Type 'number' is not assignable to type 'string'
    Schedule.tapOutput((s: string) => Console.log(s.trim()))
    )
  • #4447 f70a65a Thanks @gcanti! - Preserve function length property in Effect.fn / Effect.fnUntraced, closes #4435

    Previously, functions created with Effect.fn and Effect.fnUntraced always had a .length of 0, regardless of their actual number of parameters. This has been fixed so that the length property correctly reflects the expected number of arguments.

    Before

    import { Effect } from "effect"
    const fn1 = Effect.fn("fn1")(function* (n: number) {
    return n
    })
    console.log(fn1.length)
    // Output: 0 ❌ (incorrect)
    const fn2 = Effect.fnUntraced(function* (n: number) {
    return n
    })
    console.log(fn2.length)
    // Output: 0 ❌ (incorrect)

    After

    import { Effect } from "effect"
    const fn1 = Effect.fn("fn1")(function* (n: number) {
    return n
    })
    console.log(fn1.length)
    // Output: 1 ✅ (correct)
    const fn2 = Effect.fnUntraced(function* (n: number) {
    return n
    })
    console.log(fn2.length)
    // Output: 1 ✅ (correct)
  • #4422 ba409f6 Thanks @mikearnaldi! - Fix Context.Tag inference using explicit generics

  • #4432 3d2e356 Thanks @tim-smart! - use Map for Scope finalizers, to ensure they are always added

3.12.11

Patch Changes

  • #4430 b6a032f Thanks @tim-smart! - ensure Channel executor catches defects in doneHalt

  • #4426 42ddd5f Thanks @gcanti! - Schema: add missing description annotation to BooleanFromString.

  • #4404 2fe447c Thanks @gcanti! - Update forEach function in Chunk to include missing index parameter.

3.12.10

Patch Changes

  • #4412 e30f132 Thanks @KhraksMamtsov! - Fix STM unification

  • #4403 33fa667 Thanks @gcanti! - Duration: fix format output when the input is zero.

    Before

    import { Duration } from "effect"
    console.log(Duration.format(Duration.zero))
    // Output: ""

    After

    import { Duration } from "effect"
    console.log(Duration.format(Duration.zero))
    // Output: "0"
  • #4411 87f5f28 Thanks @gcanti! - Enhance TagClass and ReferenceClass to enforce key type narrowing, closes #4409.

    The key property in TagClass and ReferenceClass now correctly retains its specific string value, just like in Effect.Service

    import { Context, Effect } from "effect"
    // -------------------------------------------------------------------------------------
    // `key` field
    // -------------------------------------------------------------------------------------
    class A extends Effect.Service<A>()("A", { succeed: { a: "value" } }) {}
    // $ExpectType "A"
    A.key
    class B extends Context.Tag("B")<B, { a: "value" }>() {}
    // $ExpectType "B"
    B.key
    class C extends Context.Reference<C>()("C", { defaultValue: () => 0 }) {}
    // $ExpectType "C"
    C.key
  • #4397 4dbd170 Thanks @thewilkybarkid! - Make Array.makeBy dual

3.12.9

Patch Changes

  • #4392 1b4a4e9 Thanks @gcanti! - Fix internal import in Schema.ts, closes #4391

3.12.8

Patch Changes

  • #4341 766113c Thanks @fubhy! - Improve Duration.decode Handling of High-Resolution Time

    • Ensured Immutability: Added the readonly modifier to [seconds: number, nanos: number] in DurationInput to prevent accidental modifications.
    • Better Edge Case Handling: Now correctly processes special values like -Infinity and NaN when they appear in the tuple representation of duration.
  • #4333 712277f Thanks @gcanti! - Cron: unsafeParse now throws a more informative error instead of a generic one

  • #4387 f269122 Thanks @KhraksMamtsov! - A more precise signature has been applied for Effect.schedule

  • #4351 430c846 Thanks @tim-smart! - fix Layer.scope types to correctly use the Scope tag identifier

  • #4344 7b03057 Thanks @IMax153! - Expose Schedule.isSchedule

  • #4313 a9c94c8 Thanks @gcanti! - Schema: Update Duration Encoding to a Tagged Union Format.

    This changeset fixes the Duration schema to support all possible duration types, including finite, infinite, and nanosecond durations. The encoding format has been updated from a tuple (readonly [seconds: number, nanos: number]) to a tagged union.

    This update introduces a change to the encoding format. The previous tuple representation is replaced with a more expressive tagged union, which accommodates all duration types:

    type DurationEncoded =
    | {
    readonly _tag: "Millis"
    readonly millis: number
    }
    | {
    readonly _tag: "Nanos"
    readonly nanos: string
    }
    | {
    readonly _tag: "Infinity"
    }

    Rationale

    The Duration schema is primarily used to encode durations for transmission. The new tagged union format ensures clear and precise encoding for:

    • Finite durations, such as milliseconds.
    • Infinite durations, such as Duration.infinity.
    • Nanosecond durations.

    Example

    import { Duration, Schema } from "effect"
    // Encoding a finite duration in milliseconds
    console.log(Schema.encodeSync(Schema.Duration)(Duration.millis(1000)))
    // Output: { _tag: 'Millis', millis: 1000 }
    // Encoding an infinite duration
    console.log(Schema.encodeSync(Schema.Duration)(Duration.infinity))
    // Output: { _tag: 'Infinity' }
    // Encoding a duration in nanoseconds
    console.log(Schema.encodeSync(Schema.Duration)(Duration.nanos(1000n)))
    // Output: { _tag: 'Nanos', nanos: '1000' }
  • #4331 107e6f0 Thanks @gcanti! - Schema: Improve encoding in Defect and add test for array-based defects.

  • #4329 65c11b9 Thanks @gcanti! - Schema: Update itemsCount to allow 0 as a valid argument, closes #4328.

  • #4330 e386d2f Thanks @gcanti! - Add missing overload for Option.as.

  • #4352 9172efb Thanks @tim-smart! - optimize Stream.toReadableStream

3.12.7

Patch Changes

  • #4320 8dff1d1 Thanks @KhraksMamtsov! - Fix: Cannot find name ‘MissingSelfGeneric’.

3.12.6

Patch Changes

  • #4307 289c13b Thanks @gcanti! - Schema: Enhance error messages for discriminated unions.

    Before

    import { Schema } from "effect"
    const schema = Schema.Union(
    Schema.Tuple(Schema.Literal(-1), Schema.Literal(0)).annotations({
    identifier: "A"
    }),
    Schema.Tuple(Schema.NonNegativeInt, Schema.NonNegativeInt).annotations({
    identifier: "B"
    })
    ).annotations({ identifier: "AB" })
    Schema.decodeUnknownSync(schema)([-500, 0])
    /*
    throws:
    ParseError: AB
    ├─ { readonly 0: -1 }
    │ └─ ["0"]
    │ └─ Expected -1, actual -500
    └─ B
    └─ [0]
    └─ NonNegativeInt
    └─ From side refinement failure
    └─ NonNegative
    └─ Predicate refinement failure
    └─ Expected a non-negative number, actual -500
    */

    After

    import { Schema } from "effect"
    const schema = Schema.Union(
    Schema.Tuple(Schema.Literal(-1), Schema.Literal(0)).annotations({
    identifier: "A"
    }),
    Schema.Tuple(Schema.NonNegativeInt, Schema.NonNegativeInt).annotations({
    identifier: "B"
    })
    ).annotations({ identifier: "AB" })
    Schema.decodeUnknownSync(schema)([-500, 0])
    /*
    throws:
    ParseError: AB
    ├─ { readonly 0: -1 }
    ├─ A
    │ └─ ["0"]
    │ └─ Expected -1, actual -500
    └─ B
    └─ [0]
    └─ NonNegativeInt
    └─ From side refinement failure
    └─ NonNegative
    └─ Predicate refinement failure
    └─ Expected a non-negative number, actual -500
    */
  • #4298 8b4e75d Thanks @KhraksMamtsov! - Added type-level validation for the Effect.Service function to ensure the Self generic parameter is provided. If the generic is missing, the MissingSelfGeneric type will be returned, indicating that the generic parameter must be specified. This improves type safety and prevents misuse of the Effect.Service function.

    type MissingSelfGeneric =
    `Missing \`Self\` generic - use \`class Self extends Service<Self>()...\``
  • #4292 fc5e0f0 Thanks @gcanti! - Improve UnknownException error messages

    UnknownException error messages now include the name of the Effect api that created the error.

    import { Effect } from "effect"
    Effect.tryPromise(() =>
    Promise.reject(new Error("The operation failed"))
    ).pipe(Effect.catchAllCause(Effect.logError), Effect.runFork)
    // timestamp=2025-01-21T00:41:03.403Z level=ERROR fiber=#0 cause="UnknownException: An unknown error occurred in Effect.tryPromise
    // at fail (.../effect/packages/effect/src/internal/core-effect.ts:1654:19)
    // at <anonymous> (.../effect/packages/effect/src/internal/core-effect.ts:1674:26) {
    // [cause]: Error: The operation failed
    // at <anonymous> (.../effect/scratchpad/error.ts:4:24)
    // at .../effect/packages/effect/src/internal/core-effect.ts:1671:7
    // }"
  • #4309 004fd2b Thanks @gcanti! - Schema: Enforce Finite Durations in DurationFromNanos.

    This update ensures that DurationFromNanos only accepts finite durations. Previously, the schema did not explicitly enforce this constraint.

    A filter has been added to validate that the duration is finite.

    DurationFromSelf
    .pipe(
    filter((duration) => duration_.isFinite(duration), {
    description: "a finite duration"
    })
    )
  • #4314 b2a31be Thanks @gcanti! - Duration: make DurationValue properties readonly.

  • #4287 5514d05 Thanks @gcanti! - Array: Fix Either import and correct partition example.

  • #4301 bf5f0ae Thanks @gcanti! - Schema: Fix BigIntFromNumber to enforce upper and lower bounds.

    This update ensures the BigIntFromNumber schema adheres to safe integer limits by applying the following bounds:

    BigIntFromSelf
    .pipe(
    betweenBigInt(
    BigInt(Number.MIN_SAFE_INTEGER),
    BigInt(Number.MAX_SAFE_INTEGER)
    )
    )
  • #4228 3b19bcf Thanks @fubhy! - Fixed conflicting ParseError tags between Cron and Schema

  • #4294 b064b3b Thanks @tim-smart! - ensure cause is rendered in FiberFailure

  • #4307 289c13b Thanks @gcanti! - Schema: Add Support for Infinity in Duration.

    This update adds support for encoding Duration.infinity in Schema.Duration.

    Before

    Attempting to encode Duration.infinity resulted in a ParseError due to the lack of support for Infinity in Schema.Duration:

    import { Duration, Schema } from "effect"
    console.log(Schema.encodeUnknownSync(Schema.Duration)(Duration.infinity))
    /*
    throws:
    ParseError: Duration
    └─ Encoded side transformation failure
    └─ HRTime
    └─ [0]
    └─ NonNegativeInt
    └─ Predicate refinement failure
    └─ Expected an integer, actual Infinity
    */

    After

    The updated behavior successfully encodes Duration.infinity as [ -1, 0 ]:

    import { Duration, Schema } from "effect"
    console.log(Schema.encodeUnknownSync(Schema.Duration)(Duration.infinity))
    // Output: [ -1, 0 ]
  • #4300 f474678 Thanks @gcanti! - Schema: update pluck type signature to respect optional fields.

    Before

    import { Schema } from "effect"
    const schema1 = Schema.Struct({ a: Schema.optional(Schema.String) })
    /*
    const schema2: Schema.Schema<string | undefined, {
    readonly a: string | undefined;
    }, never>
    */
    const schema2 = Schema.pluck(schema1, "a")

    After

    import { Schema } from "effect"
    const schema1 = Schema.Struct({ a: Schema.optional(Schema.String) })
    /*
    const schema2: Schema.Schema<string | undefined, {
    readonly a?: string | undefined;
    }, never>
    */
    const schema2 = Schema.pluck(schema1, "a")
  • #4296 ee187d0 Thanks @gcanti! - fix: update Cause.isCause type from ‘never’ to ‘unknown’

3.12.5

Patch Changes

  • #4273 a8b0ddb Thanks @gcanti! - Arbitrary: Fix bug adjusting array constraints for schemas with fixed and rest elements

    This fix ensures that when a schema includes both fixed elements and a rest element, the constraints for the array are correctly adjusted. The adjustment now subtracts the number of values generated by the fixed elements from the overall constraints.

  • #4259 507d546 Thanks @gcanti! - Schema: improve error messages for invalid transformations

    Before

    import { Schema } from "effect"
    Schema.decodeUnknownSync(Schema.NumberFromString)("a")
    /*
    throws:
    ParseError: NumberFromString
    └─ Transformation process failure
    └─ Expected NumberFromString, actual "a"
    */

    After

    import { Schema } from "effect"
    Schema.decodeUnknownSync(Schema.NumberFromString)("a")
    /*
    throws:
    ParseError: NumberFromString
    └─ Transformation process failure
    └─ Unable to decode "a" into a number
    */
  • #4273 a8b0ddb Thanks @gcanti! - Schema: Extend Support for Array filters, closes #4269.

    Added support for minItems, maxItems, and itemsCount to all schemas where A extends ReadonlyArray, including NonEmptyArray.

    Example

    import { Schema } from "effect"
    // Previously, this would have caused an error
    const schema = Schema.NonEmptyArray(Schema.String).pipe(Schema.maxItems(2))
  • #4257 8db239b Thanks @gcanti! - Schema: Correct BigInt and BigIntFromNumber identifier annotations to follow naming conventions

  • #4276 84a0911 Thanks @tim-smart! - fix formatting of time zone offsets that round to 60 minutes

  • #4276 84a0911 Thanks @tim-smart! - ensure DateTimeZonedFromSelf arbitrary generates in the range supported by the time zone database

  • #4267 3179a9f Thanks @tim-smart! - ensure DateTime.Zoned produces valid dates

  • #4264 6cb9b76 Thanks @gcanti! - Relocate the Issue definition from platform/HttpApiError to Schema (renamed as ArrayFormatterIssue).

  • #4266 1fcbe55 Thanks @gcanti! - Schema: Replace the TimeZoneFromSelf interface with a class definition and fix the arbitraries for DateTimeUtcFromSelf and DateTimeZonedFromSelf (fc.date({ noInvalidDate: true })).

  • #4279 d9a63d9 Thanks @tim-smart! - improve performance of Effect.forkIn

3.12.4

Patch Changes

  • #4231 5b50ea4 Thanks @KhraksMamtsov! - fix Layer.retry and MetricPolling.retry signatures

  • #4253 c170a68 Thanks @sukovanej! - Use non-enumerable properties for mutable fields of DateTime objects.

  • #4255 a66c2eb Thanks @sukovanej! - Improve DateTime type preservation

3.12.3

Patch Changes

  • #4244 d7dac48 Thanks @gcanti! - Improve pattern handling by merging multiple patterns into a union, closes #4243.

    Previously, the algorithm always prioritized the first pattern when multiple patterns were encountered.

    This fix introduces a merging strategy that combines patterns into a union (e.g., (?:${pattern1})|(?:${pattern2})). By doing so, all patterns have an equal chance to generate values when using FastCheck.stringMatching.

    Example

    import { Arbitrary, FastCheck, Schema } from "effect"
    // /^[^A-Z]*$/ (given by Lowercase) + /^0x[0-9a-f]{40}$/
    const schema = Schema.Lowercase.pipe(Schema.pattern(/^0x[0-9a-f]{40}$/))
    const arb = Arbitrary.make(schema)
    // Before this fix, the first pattern would always dominate,
    // making it impossible to generate values
    const sample = FastCheck.sample(arb, { numRuns: 100 })
    console.log(sample)
  • #4252 1d7fd2b Thanks @gcanti! - Fix: Correct Arbitrary.make to support nested TemplateLiterals.

    Previously, Arbitrary.make did not properly handle nested TemplateLiteral schemas, resulting in incorrect or empty outputs. This fix ensures that nested template literals are processed correctly, producing valid arbitrary values.

    Before

    import { Arbitrary, FastCheck, Schema as S } from "effect"
    const schema = S.TemplateLiteral(
    "<",
    S.TemplateLiteral("h", S.Literal(1, 2)),
    ">"
    )
    const arb = Arbitrary.make(schema)
    console.log(FastCheck.sample(arb, { numRuns: 10 }))
    /*
    Output:
    [
    '<>', '<>', '<>',
    '<>', '<>', '<>',
    '<>', '<>', '<>',
    '<>'
    ]
    */

    After

    import { Arbitrary, FastCheck, Schema as S } from "effect"
    const schema = S.TemplateLiteral(
    "<",
    S.TemplateLiteral("h", S.Literal(1, 2)),
    ">"
    )
    const arb = Arbitrary.make(schema)
    console.log(FastCheck.sample(arb, { numRuns: 10 }))
    /*
    Output:
    [
    '<h2>', '<h2>',
    '<h2>', '<h2>',
    '<h2>', '<h1>',
    '<h2>', '<h1>',
    '<h1>', '<h1>'
    ]
    */
  • #4252 1d7fd2b Thanks @gcanti! - Fix: Allow Schema.TemplateLiteral to handle strings with linebreaks, closes #4251.

    Before

    import { Schema } from "effect"
    const schema = Schema.TemplateLiteral("a: ", Schema.String)
    console.log(Schema.decodeSync(schema)("a: b \n c"))
    // throws: ParseError: Expected `a: ${string}`, actual "a: b \n c"

    After

    import { Schema } from "effect"
    const schema = Schema.TemplateLiteral("a: ", Schema.String)
    console.log(Schema.decodeSync(schema)("a: b \n c"))
    /*
    Output:
    a: b
    c
    */

3.12.2

Patch Changes

  • #4220 734af82 Thanks @KhraksMamtsov! - fix inference for contravariant type-parameters

  • #4212 b63c780 Thanks @KhraksMamtsov! - Refine Effect.validateAll return type to use NonEmptyArray for errors.

    This refinement is possible because Effect.validateAll guarantees that when the input iterable is non-empty, any validation failure will produce at least one error. In such cases, the errors are inherently non-empty, making it safe and accurate to represent them using a NonEmptyArray type. This change aligns the return type with the function’s actual behavior, improving type safety and making the API more predictable for developers.

  • #4219 c640d77 Thanks @whoisandy! - fix: ManagedRuntime.Context to work when Context is of type never

  • #4236 0def088 Thanks @tim-smart! - fix color option for Logger.prettyLogger

3.12.1

Patch Changes

  • #4194 302b57d Thanks @KhraksMamtsov! - take concurrentFinalizers option in account in Effect.all combinator

  • #4202 0988083 Thanks @mikearnaldi! - Remove internal EffectError make sure errors are raised with Effect.fail in Effect.try

  • #4185 8b46be6 Thanks @jessekelly881! - fixed incorrect type declaration in LibsqlClient.layer

  • #4189 bfe8027 Thanks @tim-smart! - ensure Effect.timeoutTo sleep is interrupted

  • #4190 16dd657 Thanks @IMax153! - extend IterableIterator instead of Generator in SingleShotGen

  • #4196 39db211 Thanks @mikearnaldi! - Avoid putting symbols in global to fix incompatibility with Temporal Sandbox.

    After speaking with James Watkins-Harvey we realized current Effect escapes the Temporal Worker sandbox that doesn’t look for symbols when restoring global context in the isolate they create leading to memory leaks.

3.12.0

Minor Changes

  • #4068 abb22a4 Thanks @titouancreach! - Added encodeUriComponent/decodeUriComponent for both Encoding and Schema

  • #4068 f369a89 Thanks @vinassefranche! - Add Runtime.Runtime.Context type extractor

  • #4068 642376c Thanks @tim-smart! - add non-traced overload to Effect.fn

  • #4068 3d2b7a7 Thanks @mikearnaldi! - Update fast-check to latest version

  • #4068 73f9c6f Thanks @wewelll! - add DateTimeUtcFromDate schema

  • #4068 17cb451 Thanks @fubhy! - Added support for second granularity to Cron.

  • #4068 d801820 Thanks @fubhy! - Added Cron.unsafeParse and allow passing the Cron.parse time zone parameter as string.

  • #4068 e1eeb2d Thanks @mikearnaldi! - add Effect.fnUntraced - an untraced version of Effect.fn

  • #4068 c11f3a6 Thanks @QuentinJanuel! - Add Context.mergeAll to combine multiple Contexts into one.

  • #4068 618f7e0 Thanks @tim-smart! - add span annotation to disable propagation to the tracer

  • #4068 c0ba834 Thanks @titouancreach! - Add Schema.headNonEmpty for Schema.NonEmptyArray

Patch Changes

  • #4068 e1eeb2d Thanks @mikearnaldi! - Carry both call-site and definition site in Effect.fn, auto-trace to anon

3.11.10

Patch Changes

  • #4176 39457d4 Thanks @mikearnaldi! - Fix Stream.scoped example

  • #4181 a475cc2 Thanks @gcanti! - Schema: Fix withDecodingDefault implementation to align with its signature (now removes undefined from the AST).

    Additionally, a new constraint has been added to the signature to prevent calling withDecodingDefault after withConstructorDefault, which previously led to the following issue:

    import { Schema } from "effect"
    const schema = Schema.Struct({
    a: Schema.optional(Schema.String).pipe(
    Schema.withConstructorDefault(() => undefined), // this is invalidated by the following call to `withDecodingDefault`
    Schema.withDecodingDefault(() => "")
    )
    })
  • #4175 199214e Thanks @gcanti! - Schema: refactor annotations:

    • Export internal Uint8 schema

    • Export internal NonNegativeInt schema

    • Remove title annotations that are identical to identifiers

    • Avoid setting a title annotation when applying branding

    • Add more title annotations to refinements

    • Improve toString output and provide more precise error messages for refinements:

      Before

      import { Schema } from "effect"
      const schema = Schema.Number.pipe(
      Schema.int({ identifier: "MyInt" }),
      Schema.positive()
      )
      console.log(String(schema))
      // Output: a positive number
      Schema.decodeUnknownSync(schema)(1.1)
      /*
      throws:
      ParseError: a positive number
      └─ From side refinement failure
      └─ MyInt
      └─ Predicate refinement failure
      └─ Expected MyInt, actual 1.1
      */

      After

      • toString now combines all refinements with " & " instead of showing only the last one.
      • The last message ("Expected ...") now uses the extended description to make the error message clearer.
      import { Schema } from "effect"
      const schema = Schema.Number.pipe(
      Schema.int({ identifier: "MyInt" }),
      Schema.positive()
      )
      console.log(String(schema))
      // Output: MyInt & positive // <= all the refinements
      Schema.decodeUnknownSync(schema)(1.1)
      /*
      throws:
      ParseError: MyInt & positive
      └─ From side refinement failure
      └─ MyInt
      └─ Predicate refinement failure
      └─ Expected an integer, actual 1.1 // <= extended description
      */
  • #4182 b3c160d Thanks @mikearnaldi! - Replace absolute imports with relative ones

3.11.9

Patch Changes

  • #4113 1c08a0b Thanks @thewilkybarkid! - Schema: Support template literals in Schema.Config.

    Example

    import { Schema } from "effect"
    // const config: Config<`a${string}`>
    const config = Schema.Config(
    "A",
    Schema.TemplateLiteral(Schema.Literal("a"), Schema.String)
    )
  • #4174 1ce703b Thanks @gcanti! - Schema: Add support for TemplateLiteral parameters in TemplateLiteral, closes #4166.

    This update also adds support for TemplateLiteral and TemplateLiteralParser parameters in TemplateLiteralParser.

    Before

    import { Schema } from "effect"
    const schema = Schema.TemplateLiteralParser(
    "<",
    Schema.TemplateLiteralParser("h", Schema.Literal(1, 2)),
    ">"
    )
    /*
    throws:
    Error: Unsupported template literal span
    schema (TemplateLiteral): `h${"1" | "2"}`
    */

    After

    import { Schema } from "effect"
    // Schema<readonly ["<", readonly ["h", 2 | 1], ">"], "<h2>" | "<h1>", never>
    const schema = Schema.TemplateLiteralParser(
    "<",
    Schema.TemplateLiteralParser("h", Schema.Literal(1, 2)),
    ">"
    )
    console.log(Schema.decodeUnknownSync(schema)("<h1>"))
    // Output: [ '<', [ 'h', 1 ], '>' ]
  • #4174 1ce703b Thanks @gcanti! - Schema: Fix bug in TemplateLiteralParser where unions of numeric literals were not coerced correctly.

    Before

    import { Schema } from "effect"
    const schema = Schema.TemplateLiteralParser("a", Schema.Literal(1, 2))
    console.log(Schema.decodeUnknownSync(schema)("a1"))
    /*
    throws:
    ParseError: (`a${"1" | "2"}` <-> readonly ["a", 1 | 2])
    └─ Type side transformation failure
    └─ readonly ["a", 1 | 2]
    └─ [1]
    └─ 1 | 2
    ├─ Expected 1, actual "1"
    └─ Expected 2, actual "1"
    */

    After

    import { Schema } from "effect"
    const schema = Schema.TemplateLiteralParser("a", Schema.Literal(1, 2))
    console.log(Schema.decodeUnknownSync(schema)("a1"))
    // Output: [ 'a', 1 ]
    console.log(Schema.decodeUnknownSync(schema)("a2"))
    // Output: [ 'a', 2 ]
    console.log(Schema.decodeUnknownSync(schema)("a3"))
    /*
    throws:
    ParseError: (`a${"1" | "2"}` <-> readonly ["a", 1 | 2])
    └─ Encoded side transformation failure
    └─ Expected `a${"1" | "2"}`, actual "a3"
    */

3.11.8

Patch Changes

  • #4150 1a6b52d Thanks @gcanti! - Arbitrary: optimize date-based refinements

3.11.7

Patch Changes

  • #4137 2408616 Thanks @gcanti! - Arbitrary: fix bug where refinements in declarations raised an incorrect missing annotation error, closes #4136

  • #4138 cec0b4d Thanks @gcanti! - JSONSchema: ignore never members in unions.

    Before

    import { JSONSchema, Schema } from "effect"
    const schema = Schema.Union(Schema.String, Schema.Never)
    console.log(JSON.stringify(JSONSchema.make(schema), null, 2))
    /*
    {
    "$schema": "http://json-schema.org/draft-07/schema#",
    "anyOf": [
    {
    "type": "string"
    },
    {
    "$id": "/schemas/never",
    "not": {},
    "title": "never"
    }
    ]
    }
    */

    After

    import { JSONSchema, Schema } from "effect"
    const schema = Schema.Union(Schema.String, Schema.Never)
    console.log(JSON.stringify(JSONSchema.make(schema), null, 2))
    /*
    {
    "$schema": "http://json-schema.org/draft-07/schema#",
    "type": "string"
    }
    */
  • #4138 cec0b4d Thanks @gcanti! - JSONSchema: handle the nullable keyword for OpenAPI target, closes #4075.

    Before

    import { OpenApiJsonSchema } from "@effect/platform"
    import { Schema } from "effect"
    const schema = Schema.NullOr(Schema.String)
    console.log(JSON.stringify(OpenApiJsonSchema.make(schema), null, 2))
    /*
    {
    "anyOf": [
    {
    "type": "string"
    },
    {
    "enum": [
    null
    ]
    }
    ]
    }
    */

    After

    import { OpenApiJsonSchema } from "@effect/platform"
    import { Schema } from "effect"
    const schema = Schema.NullOr(Schema.String)
    console.log(JSON.stringify(OpenApiJsonSchema.make(schema), null, 2))
    /*
    {
    "type": "string",
    "nullable": true
    }
    */
  • #4128 8d978c5 Thanks @gcanti! - JSONSchema: add type for homogeneous enum schemas, closes #4127

    Before

    import { JSONSchema, Schema } from "effect"
    const schema = Schema.Literal("a", "b")
    console.log(JSON.stringify(JSONSchema.make(schema), null, 2))
    /*
    {
    "$schema": "http://json-schema.org/draft-07/schema#",
    "enum": [
    "a",
    "b"
    ]
    }
    */

    After

    import { JSONSchema, Schema } from "effect"
    const schema = Schema.Literal("a", "b")
    console.log(JSON.stringify(JSONSchema.make(schema), null, 2))
    /*
    {
    "$schema": "http://json-schema.org/draft-07/schema#",
    "type": "string",
    "enum": [
    "a",
    "b"
    ]
    }
    */
  • #4138 cec0b4d Thanks @gcanti! - JSONSchema: use { "type": "null" } to represent the null literal

    Before

    import { JSONSchema, Schema } from "effect"
    const schema = Schema.NullOr(Schema.String)
    console.log(JSON.stringify(JSONSchema.make(schema), null, 2))
    /*
    {
    "$schema": "http://json-schema.org/draft-07/schema#",
    "anyOf": [
    {
    "type": "string"
    },
    {
    "enum": [
    null
    ]
    }
    ]
    }
    */

    After

    import { JSONSchema, Schema } from "effect"
    const schema = Schema.NullOr(Schema.String)
    console.log(JSON.stringify(JSONSchema.make(schema), null, 2))
    /*
    {
    "$schema": "http://json-schema.org/draft-07/schema#",
    "anyOf": [
    {
    "type": "string"
    },
    {
    "type": "null"
    }
    ]
    }
    */
  • #4138 cec0b4d Thanks @gcanti! - JSONSchema: handle empty native enums.

    Before

    import { JSONSchema, Schema } from "effect"
    enum Empty {}
    const schema = Schema.Enums(Empty)
    console.log(JSON.stringify(JSONSchema.make(schema), null, 2))
    /*
    {
    "$schema": "http://json-schema.org/draft-07/schema#",
    "$comment": "/schemas/enums",
    "anyOf": [] // <= invalid schema!
    }
    */

    After

    import { JSONSchema, Schema } from "effect"
    enum Empty {}
    const schema = Schema.Enums(Empty)
    console.log(JSON.stringify(JSONSchema.make(schema), null, 2))
    /*
    {
    "$schema": "http://json-schema.org/draft-07/schema#",
    "$id": "/schemas/never",
    "not": {}
    }
    */

3.11.6

Patch Changes

  • #4118 662d1ce Thanks @gcanti! - Allow the transformation created by the Class API to be annotated on all its components: the type side, the transformation itself, and the encoded side.

    Example

    import { Schema, SchemaAST } from "effect"
    class A extends Schema.Class<A>("A")(
    {
    a: Schema.NonEmptyString
    },
    [
    { identifier: "TypeID" }, // annotations for the type side
    { identifier: "TransformationID" }, // annotations for the the transformation itself
    { identifier: "EncodedID" } // annotations for the the encoded side
    ]
    ) {}
    console.log(SchemaAST.getIdentifierAnnotation(A.ast.to)) // Some("TypeID")
    console.log(SchemaAST.getIdentifierAnnotation(A.ast)) // Some("TransformationID")
    console.log(SchemaAST.getIdentifierAnnotation(A.ast.from)) // Some("EncodedID")
    A.make({ a: "" })
    /*
    ParseError: TypeID
    └─ ["a"]
    └─ NonEmptyString
    └─ Predicate refinement failure
    └─ Expected NonEmptyString, actual ""
    */
    Schema.encodeSync(A)({ a: "" })
    /*
    ParseError: TransformationID
    └─ Type side transformation failure
    └─ TypeID
    └─ ["a"]
    └─ NonEmptyString
    └─ Predicate refinement failure
    └─ Expected NonEmptyString, actual ""
    */
  • #4126 31c62d8 Thanks @gcanti! - Rewrite the Arbitrary compiler from scratch, closes #2312

3.11.5

Patch Changes

  • #4019 9f5a6f7 Thanks @gcanti! - Add missing jsonSchema annotations to the following filters:

    • lowercased
    • capitalized
    • uncapitalized
    • uppercased

    Before

    import { JSONSchema, Schema } from "effect"
    const schema = Schema.Struct({
    a: Schema.Uppercased
    })
    console.log(JSON.stringify(JSONSchema.make(schema), null, 2))
    /*
    throws:
    Error: Missing annotation
    details: Generating a JSON Schema for this schema requires a "jsonSchema" annotation
    schema (Refinement): Uppercased
    */

    After

    import { JSONSchema, Schema } from "effect"
    const schema = Schema.Uppercased
    console.log(JSON.stringify(JSONSchema.make(schema), null, 2))
    /*
    Output:
    {
    "$ref": "#/$defs/Uppercased",
    "$schema": "http://json-schema.org/draft-07/schema#",
    "$defs": {
    "Uppercased": {
    "type": "string",
    "description": "an uppercase string",
    "title": "Uppercased",
    "pattern": "^[^a-z]*$"
    }
    }
    }
    */
  • #4111 22905cf Thanks @gcanti! - JSONSchema: merge refinement fragments instead of just overwriting them.

    Before

    import { JSONSchema, Schema } from "effect"
    export const schema = Schema.String.pipe(
    Schema.startsWith("a"), // <= overwritten!
    Schema.endsWith("c")
    )
    console.log(JSON.stringify(JSONSchema.make(schema), null, 2))
    /*
    {
    "$schema": "http://json-schema.org/draft-07/schema#",
    "type": "string",
    "description": "a string ending with \"c\"",
    "pattern": "^.*c$" // <= overwritten!
    }
    */

    After

    import { JSONSchema, Schema } from "effect"
    export const schema = Schema.String.pipe(
    Schema.startsWith("a"), // <= preserved!
    Schema.endsWith("c")
    )
    console.log(JSON.stringify(JSONSchema.make(schema), null, 2))
    /*
    {
    "type": "string",
    "description": "a string ending with \"c\"",
    "pattern": "^.*c$",
    "allOf": [
    {
    "pattern": "^a" // <= preserved!
    }
    ],
    "$schema": "http://json-schema.org/draft-07/schema#"
    }
    */
  • #4019 9f5a6f7 Thanks @gcanti! - JSONSchema: Correct the output order when generating a JSON Schema from a Union that includes literals and primitive schemas.

    Before

    import { JSONSchema, Schema } from "effect"
    const schema = Schema.Union(Schema.Literal(1, 2), Schema.String)
    console.log(JSON.stringify(JSONSchema.make(schema), null, 2))
    /*
    {
    "$schema": "http://json-schema.org/draft-07/schema#",
    "anyOf": [
    {
    "type": "string"
    },
    {
    "enum": [
    1,
    2
    ]
    }
    ]
    }
    */

    After

    import { JSONSchema, Schema } from "effect"
    const schema = Schema.Union(Schema.Literal(1, 2), Schema.String)
    console.log(JSON.stringify(JSONSchema.make(schema), null, 2))
    /*
    {
    "$schema": "http://json-schema.org/draft-07/schema#",
    "anyOf": [
    {
    "enum": [
    1,
    2
    ]
    },
    {
    "type": "string"
    }
    ]
    }
    */
  • #4107 1e59e4f Thanks @tim-smart! - remove FnEffect type to improve return type of Effect.fn

  • #4108 8d914e5 Thanks @gcanti! - JSONSchema: represent never as {"not":{}}

    Before

    import { JSONSchema, Schema } from "effect"
    const schema = Schema.Never
    console.log(JSON.stringify(JSONSchema.make(schema), null, 2))
    /*
    throws:
    Error: Missing annotation
    details: Generating a JSON Schema for this schema requires a "jsonSchema" annotation
    schema (NeverKeyword): never
    */

    After

    import { JSONSchema, Schema } from "effect"
    const schema = Schema.Never
    console.log(JSON.stringify(JSONSchema.make(schema), null, 2))
    /*
    {
    "$id": "/schemas/never",
    "not": {},
    "title": "never",
    "$schema": "http://json-schema.org/draft-07/schema#"
    }
    */
  • #4115 03bb00f Thanks @tim-smart! - avoid using non-namespaced “async” internally

  • #4019 9f5a6f7 Thanks @gcanti! - JSONSchema: fix special case in parseJson handling to target the “to” side of the transformation only at the top level.

    Before

    import { JSONSchema, Schema } from "effect"
    const schema = Schema.parseJson(
    Schema.Struct({
    a: Schema.parseJson(
    Schema.Struct({
    b: Schema.String
    })
    )
    })
    )
    console.log(JSON.stringify(JSONSchema.make(schema), null, 2))
    /*
    {
    "$schema": "http://json-schema.org/draft-07/schema#",
    "type": "object",
    "required": [
    "a"
    ],
    "properties": {
    "a": {
    "type": "object",
    "required": [
    "b"
    ],
    "properties": {
    "b": {
    "type": "string"
    }
    },
    "additionalProperties": false
    }
    },
    "additionalProperties": false
    }
    */

    After

    import { JSONSchema, Schema } from "effect"
    const schema = Schema.parseJson(
    Schema.Struct({
    a: Schema.parseJson(
    Schema.Struct({
    b: Schema.String
    })
    )
    })
    )
    console.log(JSON.stringify(JSONSchema.make(schema), null, 2))
    /*
    {
    "type": "object",
    "required": [
    "a"
    ],
    "properties": {
    "a": {
    "type": "string",
    "contentMediaType": "application/json"
    }
    },
    "additionalProperties": false,
    "$schema": "http://json-schema.org/draft-07/schema#"
    }
    */
  • #4101 14e1149 Thanks @gcanti! - Schema: align the make constructor of structs with the behavior of the Class API constructors when all fields have a default.

    Before

    import { Schema } from "effect"
    const schema = Schema.Struct({
    a: Schema.propertySignature(Schema.Number).pipe(
    Schema.withConstructorDefault(() => 0)
    )
    })
    // TypeScript error: Expected 1-2 arguments, but got 0.ts(2554)
    console.log(schema.make())

    After

    import { Schema } from "effect"
    const schema = Schema.Struct({
    a: Schema.propertySignature(Schema.Number).pipe(
    Schema.withConstructorDefault(() => 0)
    )
    })
    console.log(schema.make())
    // Output: { a: 0 }
  • #4019 9f5a6f7 Thanks @gcanti! - JSONSchema: Fix issue where identifier is ignored when a refinement is applied to a schema, closes #4012

    Before

    import { JSONSchema, Schema } from "effect"
    const schema = Schema.NonEmptyString
    console.log(JSON.stringify(JSONSchema.make(schema), null, 2))
    /*
    {
    "$schema": "http://json-schema.org/draft-07/schema#",
    "type": "string",
    "description": "a non empty string",
    "title": "NonEmptyString",
    "minLength": 1
    }
    */

    After

    import { JSONSchema, Schema } from "effect"
    const schema = Schema.NonEmptyString
    console.log(JSON.stringify(JSONSchema.make(schema), null, 2))
    /*
    {
    "$schema": "http://json-schema.org/draft-07/schema#",
    "$ref": "#/$defs/NonEmptyString",
    "$defs": {
    "NonEmptyString": {
    "type": "string",
    "description": "a non empty string",
    "title": "NonEmptyString",
    "minLength": 1
    }
    }
    }
    */
  • #4019 9f5a6f7 Thanks @gcanti! - JSONSchema: Use identifier with Class APIs to create a $ref instead of inlining the schema.

    Before

    import { JSONSchema, Schema } from "effect"
    class A extends Schema.Class<A>("A")({
    a: Schema.String
    }) {}
    console.log(JSON.stringify(JSONSchema.make(A), null, 2))
    /*
    {
    "$schema": "http://json-schema.org/draft-07/schema#",
    "type": "object",
    "required": [
    "a"
    ],
    "properties": {
    "a": {
    "type": "string"
    }
    },
    "additionalProperties": false
    }
    */

    After

    import { JSONSchema, Schema } from "effect"
    class A extends Schema.Class<A>("A")({
    a: Schema.String
    }) {}
    console.log(JSON.stringify(JSONSchema.make(A), null, 2))
    /*
    {
    "$ref": "#/$defs/A",
    "$schema": "http://json-schema.org/draft-07/schema#",
    "$defs": {
    "A": {
    "type": "object",
    "required": [
    "a"
    ],
    "properties": {
    "a": {
    "type": "string"
    }
    },
    "additionalProperties": false
    }
    }
    }
    */

3.11.4

Patch Changes

  • #4087 518b258 Thanks @tim-smart! - remove use of .unsafeAsync in non-suspended contexts

  • #4010 6e323a3 Thanks @fubhy! - Add support for daylight savings time transitions

  • #4010 6e323a3 Thanks @fubhy! - Improved efficiency of Cron.next lookup

3.11.3

Patch Changes

  • #4080 90906f7 Thanks @gcanti! - Fix the Schema.TemplateLiteral output type when the arguments include a branded type.

    Before

    import { Schema } from "effect"
    const schema = Schema.TemplateLiteral(
    "a ",
    Schema.String.pipe(Schema.brand("MyBrand"))
    )
    // type Type = `a ${Schema.brand<typeof Schema.String, "MyBrand"> & string}`
    // | `a ${Schema.brand<typeof Schema.String, "MyBrand"> & number}`
    // | `a ${Schema.brand<typeof Schema.String, "MyBrand"> & bigint}`
    // | `a ${Schema.brand<...> & false}`
    // | `a ${Schema.brand<...> & true}`
    type Type = typeof schema.Type

    After

    import { Schema } from "effect"
    const schema = Schema.TemplateLiteral(
    "a ",
    Schema.String.pipe(Schema.brand("MyBrand"))
    )
    // type Type = `a ${string & Brand<"MyBrand">}`
    type Type = typeof schema.Type
  • #4076 3862cd3 Thanks @gcanti! - Schema: fix bug in Schema.TemplateLiteralParser resulting in a runtime error.

    Before

    import { Schema } from "effect"
    const schema = Schema.TemplateLiteralParser("a", "b")
    // throws TypeError: Cannot read properties of undefined (reading 'replace')

    After

    import { Schema } from "effect"
    const schema = Schema.TemplateLiteralParser("a", "b")
    console.log(Schema.decodeUnknownSync(schema)("ab"))
    // Output: [ 'a', 'b' ]
  • #4076 3862cd3 Thanks @gcanti! - SchemaAST: fix TemplateLiteral model.

    Added Literal and Union as valid types.

  • #4083 343b6aa Thanks @gcanti! - Preserve MissingMessageAnnotations on property signature declarations when another field is a property signature transformation.

    Before

    import { Console, Effect, ParseResult, Schema } from "effect"
    const schema = Schema.Struct({
    a: Schema.propertySignature(Schema.String).annotations({
    missingMessage: () => "message1"
    }),
    b: Schema.propertySignature(Schema.String)
    .annotations({ missingMessage: () => "message2" })
    .pipe(Schema.fromKey("c")), // <= transformation
    d: Schema.propertySignature(Schema.String).annotations({
    missingMessage: () => "message3"
    })
    })
    Effect.runPromiseExit(
    Schema.decodeUnknown(schema, { errors: "all" })({}).pipe(
    Effect.tapError((error) =>
    Console.log(ParseResult.ArrayFormatter.formatErrorSync(error))
    )
    )
    )
    /*
    Output:
    [
    { _tag: 'Missing', path: [ 'a' ], message: 'is missing' }, // <= wrong
    { _tag: 'Missing', path: [ 'c' ], message: 'message2' },
    { _tag: 'Missing', path: [ 'd' ], message: 'is missing' } // <= wrong
    ]
    */

    After

    import { Console, Effect, ParseResult, Schema } from "effect"
    const schema = Schema.Struct({
    a: Schema.propertySignature(Schema.String).annotations({
    missingMessage: () => "message1"
    }),
    b: Schema.propertySignature(Schema.String)
    .annotations({ missingMessage: () => "message2" })
    .pipe(Schema.fromKey("c")), // <= transformation
    d: Schema.propertySignature(Schema.String).annotations({
    missingMessage: () => "message3"
    })
    })
    Effect.runPromiseExit(
    Schema.decodeUnknown(schema, { errors: "all" })({}).pipe(
    Effect.tapError((error) =>
    Console.log(ParseResult.ArrayFormatter.formatErrorSync(error))
    )
    )
    )
    /*
    Output:
    [
    { _tag: 'Missing', path: [ 'a' ], message: 'message1' },
    { _tag: 'Missing', path: [ 'c' ], message: 'message2' },
    { _tag: 'Missing', path: [ 'd' ], message: 'message3' }
    ]
    */
  • #4081 afba339 Thanks @gcanti! - Fix the behavior of Schema.TemplateLiteralParser when the arguments include literals other than string literals.

    Before

    import { Schema } from "effect"
    const schema = Schema.TemplateLiteralParser(Schema.String, 1)
    console.log(Schema.decodeUnknownSync(schema)("a1"))
    /*
    throws
    ParseError: (`${string}1` <-> readonly [string, 1])
    └─ Type side transformation failure
    └─ readonly [string, 1]
    └─ [1]
    └─ Expected 1, actual "1"
    */

    After

    import { Schema } from "effect"
    const schema = Schema.TemplateLiteralParser(Schema.String, 1)
    console.log(Schema.decodeUnknownSync(schema)("a1"))
    // Output: [ 'a', 1 ]

3.11.2

Patch Changes

  • #4063 01cee56 Thanks @tim-smart! - Micro adjustments
    • rename Fiber to MicroFiber
    • add Micro.fiberJoin api
    • adjust output when inspecting Micro data types

3.11.1

Patch Changes

  • #4052 dd8a2d8 Thanks @tim-smart! - ensure pool.get is interrupted on shutdown

  • #4059 a71bfef Thanks @IMax153! - Ensure that the current time zone context tag type is properly exported

3.11.0

Minor Changes

  • #3835 147434b Thanks @IMax153! - Ensure scopes are preserved by stream / sink / channel operations

    NOTE: This change does modify the public signature of several Stream / Sink / Channel methods. Namely, certain run methods that previously removed a Scope from the environment will no longer do so. This was a bug with the previous implementation of how scopes were propagated, and is why this change is being made in a minor release.

  • #3835 6e69493 Thanks @tim-smart! - add Context.Reference - a Tag with a default value

  • #3835 147434b Thanks @IMax153! - Add Effect.scopedWith to run an effect that depends on a Scope, and then closes the Scope after the effect has completed

    import { Effect, Scope } from "effect"
    const program: Effect.Effect<void> = Effect.scopedWith((scope) =>
    Effect.acquireRelease(Effect.log("Acquiring..."), () =>
    Effect.log("Releasing...")
    ).pipe(Scope.extend(scope))
    )
    Effect.runPromise(program)
    // Output:
    // timestamp=2024-11-26T16:44:54.158Z level=INFO fiber=#0 message=Acquiring...
    // timestamp=2024-11-26T16:44:54.165Z level=INFO fiber=#0 message=Releasing...
  • #3835 d9fe79b Thanks @tim-smart! - remove Env, EnvRef & FiberFlags from Micro

  • #3835 251d189 Thanks @KhraksMamtsov! - Config.url constructor has been added, which parses a string using new URL()

  • #3835 5a259f3 Thanks @tim-smart! - use fiber based runtime for Micro module

    • Improved performance
    • Improved interruption model
    • Consistency with the Effect data type
  • #3835 b4ce4ea Thanks @SandroMaglione! - New methods extractAll and extractSchema to UrlParams (added Schema.BooleanFromString).

  • #3835 15fcc5a Thanks @fubhy! - Integrated DateTime with Cron to add timezone support for cron expressions.

  • #3835 9bc9a47 Thanks @KhraksMamtsov! - URL and URLFromSelf schemas have been added

  • #3835 aadb8a4 Thanks @fubhy! - Added BigDecimal.toExponential for scientific notation formatting of BigDecimal values.

    The implementation of BigDecimal.format now uses scientific notation for values with at least 16 decimal places or trailing zeroes. Previously, extremely large or small values could cause OutOfMemory errors when formatting.

  • #3835 1e2747c Thanks @KhraksMamtsov! - - JSONSchema module

    • add format?: string optional field to JsonSchema7String interface
    • Schema module
      • add custom json schema annotation to UUID schema including format: "uuid"
    • OpenApiJsonSchema module
      • add format?: string optional field to String and Numeric interfaces
  • #3835 e0b9b09 Thanks @mikearnaldi! - Implement Effect.fn to define traced functions.

    import { Effect } from "effect"
    const logExample = Effect.fn("example")(function* <N extends number>(n: N) {
    yield* Effect.annotateCurrentSpan("n", n)
    yield* Effect.logInfo(`got: ${n}`)
    yield* Effect.fail(new Error())
    }, Effect.delay("1 second"))
    Effect.runFork(logExample(100).pipe(Effect.catchAllCause(Effect.logError)))
  • #3835 c36f3b9 Thanks @KhraksMamtsov! - Config.redacted has been made more flexible and can now wrap any other config. This allows to transform or validate config values before it’s hidden.

    import { Config } from "effect"
    Effect.gen(function* () {
    // can be any string including empty
    const pass1 = yield* Config.redacted("PASSWORD")
    // ^? Redacted<string>
    // can't be empty string
    const pass2 = yield* Config.redacted(Config.nonEmptyString("PASSWORD"))
    // ^? Redacted<string>
    const pass2 = yield* Config.redacted(Config.number("SECRET_NUMBER"))
    // ^? Redacted<number>
    })
  • #3835 aadb8a4 Thanks @fubhy! - Added BigDecimal.unsafeFromNumber and BigDecimal.safeFromNumber.

    Deprecated BigDecimal.fromNumber in favour of BigDecimal.unsafeFromNumber.

    The current implementation of BigDecimal.fromNumber and BigDecimal.unsafeFromNumber now throws a RangeError for numbers that are not finite such as NaN, +Infinity or -Infinity.

Patch Changes

  • #3835 5eff3f6 Thanks @tim-smart! - fix multipart support for bun http server

  • #3835 9264162 Thanks @IMax153! - inherit child fibers created by merged streams

3.10.20

Patch Changes

  • #4042 3069614 Thanks @tim-smart! - catch logger defects from calling .toJSON on data types

  • #4041 09a5e52 Thanks @tim-smart! - fix docs for Stream.partition

3.10.19

Patch Changes

  • #4007 944025b Thanks @gcanti! - Wrap JSDoc @example tags with a TypeScript fence, closes #4002

  • #4013 54addee Thanks @thewilkybarkid! - Remove reference to non-existent function

3.10.18

Patch Changes

  • #4004 af409cf Thanks @tim-smart! - fix behavour of Stream.partition to match the types

3.10.17

Patch Changes

  • #3998 42c4ce6 Thanks @tim-smart! - ensure fiber observers are cleared after exit to prevent memory leaks

3.10.16

Patch Changes

  • #3918 4dca30c Thanks @gcanti! - Use a specific annotation (AutoTitleAnnotationId) to add automatic titles (added by Struct and Class APIs), instead of TitleAnnotationId, to avoid interfering with user-defined titles.

  • #3981 1d99867 Thanks @gcanti! - Stable filters such as minItems, maxItems, and itemsCount should be applied only if the from part fails with a Composite issue, closes #3980

    Before

    import { Schema } from "effect"
    const schema = Schema.Struct({
    a: Schema.Array(Schema.String).pipe(Schema.minItems(1))
    })
    Schema.decodeUnknownSync(schema)({}, { errors: "all" })
    // throws: TypeError: Cannot read properties of undefined (reading 'length')

    After

    import { Schema } from "effect"
    const schema = Schema.Struct({
    a: Schema.Array(Schema.String).pipe(Schema.minItems(1))
    })
    Schema.decodeUnknownSync(schema)({}, { errors: "all" })
    /*
    throws:
    ParseError: { readonly a: an array of at least 1 items }
    └─ ["a"]
    └─ is missing
    */
  • #3972 6dae414 Thanks @tim-smart! - add support for 0 capacity to Mailbox

  • #3959 6b0d737 Thanks @gcanti! - Remove Omit from the Class interface definition to align type signatures with runtime behavior. This fix addresses the issue of being unable to override base class methods in extended classes without encountering type errors, closes #3958

    Before

    import { Schema } from "effect"
    class Base extends Schema.Class<Base>("Base")({
    a: Schema.String
    }) {
    f() {
    console.log("base")
    }
    }
    class Extended extends Base.extend<Extended>("Extended")({}) {
    // Class '{ readonly a: string; } & Omit<Base, "a">' defines instance member property 'f',
    // but extended class 'Extended' defines it as instance member function.ts(2425)
    // @ts-expect-error
    override f() {
    console.log("extended")
    }
    }

    After

    import { Schema } from "effect"
    class Base extends Schema.Class<Base>("Base")({
    a: Schema.String
    }) {
    f() {
    console.log("base")
    }
    }
    class Extended extends Base.extend<Extended>("Extended")({}) {
    // ok
    override f() {
    console.log("extended")
    }
    }
  • #3971 d8356aa Thanks @gcanti! - Refactor JSON Schema Generation to Include Transformation Annotations, closes #3016

    When generating a JSON Schema, treat TypeLiteralTransformations (such as when Schema.optionalWith is used) as a special case. Annotations from the transformation itself will now be applied, unless there are user-defined annotations on the form side. This change ensures that the user’s intended annotations are properly included in the schema.

    Before

    Annotations set on the transformation are ignored. However while using Schema.optionalWith internally generates a transformation schema, this is considered a technical detail. The user’s intention is to add annotations to the “struct” schema, not to the transformation.

    import { JSONSchema, Schema } from "effect"
    const schema = Schema.Struct({
    a: Schema.optionalWith(Schema.String, { default: () => "" })
    }).annotations({
    identifier: "MyID",
    description: "My description",
    title: "My title"
    })
    console.log(JSONSchema.make(schema))
    /*
    Output:
    {
    '$schema': 'http://json-schema.org/draft-07/schema#',
    type: 'object',
    required: [],
    properties: { a: { type: 'string' } },
    additionalProperties: false
    }
    */

    After

    Annotations set on the transformation are now considered during JSON Schema generation:

    import { JSONSchema, Schema } from "effect"
    const schema = Schema.Struct({
    a: Schema.optionalWith(Schema.String, { default: () => "" })
    }).annotations({
    identifier: "MyID",
    description: "My description",
    title: "My title"
    })
    console.log(JSONSchema.make(schema))
    /*
    Output:
    {
    '$schema': 'http://json-schema.org/draft-07/schema#',
    '$ref': '#/$defs/MyID',
    '$defs': {
    MyID: {
    type: 'object',
    required: [],
    properties: [Object],
    additionalProperties: false,
    description: 'My description',
    title: 'My title'
    }
    }
    }
    */

3.10.15

Patch Changes

  • #3936 8398b32 Thanks @tim-smart! - allow DateTime.makeZoned to default to the local time zone

  • #3917 72e55b7 Thanks @SuttonKyle! - Allow Stream.split to use refinement for better type inference

3.10.14

Patch Changes

  • #3920 f983946 Thanks @gcanti! - remove redundant check in JSONNumber declaration

  • #3924 2d8a750 Thanks @tim-smart! - ensure a ManagedRuntime can be built synchronously

3.10.13

Patch Changes

  • #3907 995bbdf Thanks @arijoon! - Schema.BigDecimal Arbitrary’s scale limited to the range 0-18

3.10.12

Patch Changes

  • #3904 dd14efe Thanks @tim-smart! - allow pool items te be used while being acquired

3.10.11

Patch Changes

  • #3903 5eef499 Thanks @tim-smart! - cache Schema.Class AST once generated

3.10.10

Patch Changes

  • #3893 cd720ae Thanks @tim-smart! - support “dropping” & “sliding” strategies in Mailbox

  • #3893 cd720ae Thanks @tim-smart! - add Mailbox.fromStream api

  • #3886 b631f40 Thanks @fubhy! - Optimized Base64.decode by not capturing the padding characters in the underlying array buffer.

    Previously, the implementation first captured the padding characters in the underlying array buffer and then returned a new subarray view of the buffer with the padding characters removed.

    By not capturing the padding characters, we avoid the creation of another typed array instance for the subarray view.

3.10.9

Patch Changes

  • #3883 a123e80 Thanks @tim-smart! - add FromIterator primitive to improve Effect.gen performance

  • #3880 bd5fcd3 Thanks @tim-smart! - refactor Effect.gen to improve performance

  • #3881 0289d3b Thanks @tim-smart! - implement Effect.suspend using OP_COMMIT

  • #3862 7386b71 Thanks @furrycatherder! - fix the type signature of use in Effect.Service

  • #3879 4211a23 Thanks @IMax153! - Return a sequential cause when both the use and release fail in Effect.acquireUseRelease

3.10.8

Patch Changes

  • #3868 68b5c9e Thanks @tim-smart! - move _op check out of the fiber hot path

  • #3849 9c9928d Thanks @patroza! - improve: use literal key on Service

  • #3872 6306e66 Thanks @KhraksMamtsov! - Fix Config.integer & Config.number

  • #3869 361c7f3 Thanks @KhraksMamtsov! - jsdoc-examples for class-based APIs have been added, e.g. Schema.TaggedError, Effect.Service and others

3.10.7

Patch Changes

  • #3867 33f5b9f Thanks @tim-smart! - ensure Channel.mergeWith fibers can be interrupted

  • #3865 50f0281 Thanks @tim-smart! - fix memory leak in Stream.retry

3.10.6

Patch Changes

3.10.5

Patch Changes

  • #3841 3a6d757 Thanks @KhraksMamtsov! - Support union of parameters in functions in Effect.Tag.Proxy type

  • #3845 59d813a Thanks @tim-smart! - ensure fiber refs are not inherited by ManagedRuntime

3.10.4

Patch Changes

  • #3842 2367708 Thanks @gcanti! - add support for Schema.OptionFromUndefinedOr in JSON Schema generation, closes #3839

    Before

    import { JSONSchema, Schema } from "effect"
    const schema = Schema.Struct({
    a: Schema.OptionFromUndefinedOr(Schema.Number)
    })
    console.log(JSON.stringify(JSONSchema.make(schema), null, 2))
    /*
    throws:
    Error: Missing annotation
    at path: ["a"]
    details: Generating a JSON Schema for this schema requires a "jsonSchema" annotation
    schema (UndefinedKeyword): undefined
    */

    After

    import { JSONSchema, Schema } from "effect"
    const schema = Schema.Struct({
    a: Schema.OptionFromUndefinedOr(Schema.Number)
    })
    console.log(JSON.stringify(JSONSchema.make(schema), null, 2))
    /*
    Output:
    {
    "$schema": "http://json-schema.org/draft-07/schema#",
    "type": "object",
    "required": [],
    "properties": {
    "a": {
    "type": "number"
    }
    },
    "additionalProperties": false
    }
    */

3.10.3

Patch Changes

  • #3833 b9423d8 Thanks @IMax153! - Ensure undefined JSON values are not coerced to empty string

3.10.2

Patch Changes

  • #3820 714e119 Thanks @tim-smart! - simplify Match fail keys types

  • #3825 c1afd55 Thanks @KhraksMamtsov! - - Make MergeRight, MergeLeft and MergeRecord in Types module homomorphic (preserve original readonly and optionality modifiers)

    • MergeRecord now is alias for MergeLeft

3.10.1

Patch Changes

  • #3818 9604d6b Thanks @tim-smart! - fix Channel.embedInput halting in uninterruptible region

3.10.0

Minor Changes

  • #3764 4a01828 Thanks @evelant! - add TSubscriptionRef

  • #3764 4a01828 Thanks @evelant! - add Stream.fromTQueue & Stream.fromTPubSub

  • #3764 c79c4c1 Thanks @gcanti! - Merge Schema into Effect.

    Modules

    Before

    import {
    Arbitrary,
    AST,
    FastCheck,
    JSONSchema,
    ParseResult,
    Pretty,
    Schema
    } from "@effect/schema"

    After

    import {
    Arbitrary,
    SchemaAST, // changed
    FastCheck,
    JSONSchema,
    ParseResult,
    Pretty,
    Schema
    } from "effect"

    Formatters

    ArrayFormatter / TreeFormatter merged into ParseResult module.

    Before

    import { ArrayFormatter, TreeFormatter } from "@effect/schema"

    After

    import { ArrayFormatter, TreeFormatter } from "effect/ParseResult"

    Serializable

    Merged into Schema module.

    Equivalence

    Merged into Schema module.

    Before

    import { Equivalence } from "@effect/schema"
    Equivalence.make(myschema)

    After

    import { Schema } from "@effect/schema"
    Schema.equivalence(myschema)
  • #3764 38d30f0 Thanks @tim-smart! - add option to .releaseLock a ReadableStream on finalization

  • #3764 5821ce3 Thanks @patroza! - feat: implement Redactable. Used by Headers to not log sensitive information

3.9.2

Patch Changes

  • #3768 61a99b2 Thanks @tim-smart! - allow tacit usage with do notation apis (.bind / .let)

3.9.1

Patch Changes

  • #3740 3b2ad1d Thanks @tim-smart! - revert deno Inspectable changes

3.9.0

Minor Changes

  • #3620 ff3d1aa Thanks @vinassefranche! - Adds HashMap.HashMap.Entry type helper

  • #3620 0ba66f2 Thanks @tim-smart! - add deno support to Inspectable

  • #3620 bf77f51 Thanks @KhraksMamtsov! - Latch implements Effect<void> with .await semantic

  • #3620 0779681 Thanks @KhraksMamtsov! - Effect.mapAccum & Array.mapAccum preserve non-emptiness

  • #3620 534129f Thanks @KhraksMamtsov! - Pool is now a subtype of Effect, equivalent to Pool.get

  • #3620 d75140c Thanks @mikearnaldi! - Support providing an array of layers via Effect.provide and Layer.provide

  • #3620 be0451c Thanks @leonitousconforti! - support ManagedRuntime in Effect.provide

  • #3620 be0451c Thanks @leonitousconforti! - ManagedRuntime<R, E> is subtype of Effect<Runtime<R>, E, never>

  • #3620 5b36494 Thanks @KhraksMamtsov! - Tuple.map transforms each element of tuple using the given function, treating tuple homomorphically

    import { pipe, Tuple } from "effect"
    const result = pipe(
    // ^? [string, string, string]
    ["a", 1, false] as const,
    T.map((el) => {
    //^? "a" | 1 | false
    return el.toString().toUppercase()
    })
    )
    assert.deepStrictEqual(result, ["A", "1", "FALSE"])
  • #3620 c716adb Thanks @AlexGeb! - Add Array.pad function

  • #3620 4986391 Thanks @ianbollinger! - Add an isRegExp type guard

  • #3620 d75140c Thanks @mikearnaldi! - Implement Effect.Service as a Tag and Layer with Opaque Type.

    Namely the following is now possible:

    class Prefix extends Effect.Service<Prefix>()("Prefix", {
    sync: () => ({
    prefix: "PRE"
    })
    }) {}
    class Postfix extends Effect.Service<Postfix>()("Postfix", {
    sync: () => ({
    postfix: "POST"
    })
    }) {}
    const messages: Array<string> = []
    class Logger extends Effect.Service<Logger>()("Logger", {
    accessors: true,
    effect: Effect.gen(function* () {
    const { prefix } = yield* Prefix
    const { postfix } = yield* Postfix
    return {
    info: (message: string) =>
    Effect.sync(() => {
    messages.push(`[${prefix}][${message}][${postfix}]`)
    })
    }
    }),
    dependencies: [Prefix.Default, Postfix.Default]
    }) {}
    describe("Effect", () => {
    it.effect("Service correctly wires dependencies", () =>
    Effect.gen(function* () {
    const { _tag } = yield* Logger
    expect(_tag).toEqual("Logger")
    yield* Logger.info("Ok")
    expect(messages).toEqual(["[PRE][Ok][POST]"])
    const { prefix } = yield* Prefix
    expect(prefix).toEqual("PRE")
    const { postfix } = yield* Postfix
    expect(postfix).toEqual("POST")
    }).pipe(Effect.provide([Logger.Default, Prefix.Default, Postfix.Default]))
    )
    })
  • #3620 d1387ae Thanks @KhraksMamtsov! - Resource<A, E> is subtype of Effect<A, E>. ScopedRed<A> is subtype of Effect<A>.

Patch Changes

  • #3620 016f9ad Thanks @tim-smart! - fix Unify for Deferred

  • #3620 9237ac6 Thanks @leonitousconforti! - move ManagedRuntime.TypeId to fix circular imports

3.8.5

Patch Changes

  • #3734 88e85db Thanks @mikearnaldi! - Ensure random numbers are correctly distributed

  • #3717 83887ca Thanks @mikearnaldi! - Consider async operation in runSync as a defect, add span based stack

  • #3731 5266b6c Thanks @patroza! - Improve DX of type errors from inside pipe and flow

  • #3699 cdead5c Thanks @jessekelly881! - added Stream.mergeWithTag

    Combines a struct of streams into a single stream of tagged values where the tag is the key of the struct.

    import { Stream } from "effect"
    // Stream.Stream<{ _tag: "a"; value: number; } | { _tag: "b"; value: string; }>
    const stream = Stream.mergeWithTag(
    {
    a: Stream.make(0),
    b: Stream.make("")
    },
    { concurrency: 1 }
    )
  • #3706 766a8af Thanks @fubhy! - Made BigDecimal.scale dual.

3.8.4

Patch Changes

  • #3661 4509656 Thanks @KhraksMamtsov! - Micro.EnvRef and Micro.Handle is subtype of Micro

3.8.3

Patch Changes

  • #3644 bb5ec6b Thanks @tim-smart! - fix encoding of logs to tracer span events

3.8.2

Patch Changes

  • #3627 f0d8ef1 Thanks @fubhy! - Revert cron schedule regression

3.8.1

Patch Changes

  • #3624 10bf621 Thanks @fubhy! - Fixed double firing of cron schedules in cases where the current time matched the initial interval.

  • #3623 ae36fa6 Thanks @fubhy! - Allow CRLF characters in base64 encoded strings.

3.8.0

Minor Changes

  • #3541 fcfa6ee Thanks @Schniz! - add Logger.withLeveledConsole

    In browsers and different platforms, console.error renders differently than console.info. This helps to distinguish between different levels of logging. Logger.withLeveledConsole takes any logger and calls the respective Console method based on the log level. For instance, Effect.logError will call Console.error and Effect.logInfo will call Console.info.

    To use it, you can replace the default logger with a Logger.withLeveledConsole logger:

    import { Logger, Effect } from "effect"
    const loggerLayer = Logger.withLeveledConsole(Logger.stringLogger)
    Effect.gen(function* () {
    yield* Effect.logError("an error")
    yield* Effect.logInfo("an info")
    }).pipe(Effect.provide(loggerLayer))
  • #3541 bb9931b Thanks @KhraksMamtsov! - Made Ref, SynchronizedRed and SubscriptionRef a subtype of Effect

  • #3541 5798f76 Thanks @tim-smart! - add Semaphore.withPermitsIfAvailable

    You can now use Semaphore.withPermitsIfAvailable to run an Effect only if the Semaphore has enough permits available. This is useful when you want to run an Effect only if you can acquire a permit without blocking.

    It will return an Option.Some with the result of the Effect if the permits were available, or None if they were not.

    import { Effect } from "effect"
    Effect.gen(function* () {
    const semaphore = yield* Effect.makeSemaphore(1)
    semaphore.withPermitsIfAvailable(1)(Effect.void)
    })
  • #3541 5f0bfa1 Thanks @KhraksMamtsov! - The Deferred<A> is now a subtype of Effect<A>. This change simplifies handling of deferred values, removing the need for explicit call Deffer.await.

    import { Effect, Deferred } from "effect"
    Effect.gen(function* () {
    const deferred = yield* Deferred.make<string>()
    const before = yield* Deferred.await(deferred)
    const after = yield* deferred
    })
  • #3541 812a4e8 Thanks @tim-smart! - add Logger.prettyLoggerDefault, to prevent duplicate pretty loggers

  • #3541 273565e Thanks @tim-smart! - add Effect.makeLatch, for creating a simple async latch

    import { Effect } from "effect"
    Effect.gen(function* () {
    // Create a latch, starting in the closed state
    const latch = yield* Effect.makeLatch(false)
    // Fork a fiber that logs "open sesame" when the latch is opened
    const fiber = yield* Effect.log("open sesame").pipe(
    latch.whenOpen,
    Effect.fork
    )
    // Open the latch
    yield* latch.open
    yield* fiber.await
    })
  • #3541 569a801 Thanks @KhraksMamtsov! - Dequeue<A> and Queue<A> is subtype of Effect<A>. This means that now it can be used as an Effect, and when called, it will automatically extract and return an item from the queue, without having to explicitly use the Queue.take function.

    Effect.gen(function* () {
    const queue = yield* Queue.unbounded<number>()
    yield* Queue.offer(queue, 1)
    yield* Queue.offer(queue, 2)
    const oldWay = yield* Queue.take(queue)
    const newWay = yield* queue
    })
  • #3541 aa1fa53 Thanks @vinassefranche! - Add Number.round

  • #3541 02f6b06 Thanks @fubhy! - Add additional Duration conversion apis

    • Duration.toMinutes
    • Duration.toHours
    • Duration.toDays
    • Duration.toWeeks
  • #3541 12b893e Thanks @KhraksMamtsov! - The Fiber<A, E> is now a subtype of Effect<A, E>. This change removes the need for explicit call Fiber.join.

    import { Effect, Fiber } from "effect"
    Effect.gen(function*() {
    const fiber = yield* Effect.fork(Effect.succeed(1))
    const oldWay = yield* Fiber.join(fiber)
    const now = yield* fiber
    }))
  • #3541 bbad27e Thanks @dilame! - add Stream.share api

    The Stream.share api is a ref counted variant of the broadcast apis.

    It allows you to share a stream between multiple consumers, and will close the upstream when the last consumer ends.

  • #3541 adf7d7a Thanks @tim-smart! - add Mailbox module, a queue which can have done or failure signals

    import { Chunk, Effect, Mailbox } from "effect"
    import * as assert from "node:assert"
    Effect.gen(function* () {
    const mailbox = yield* Mailbox.make<number, string>()
    // add messages to the mailbox
    yield* mailbox.offer(1)
    yield* mailbox.offer(2)
    yield* mailbox.offerAll([3, 4, 5])
    // take messages from the mailbox
    const [messages, done] = yield* mailbox.takeAll
    assert.deepStrictEqual(Chunk.toReadonlyArray(messages), [1, 2, 3, 4, 5])
    assert.strictEqual(done, false)
    // signal that the mailbox is done
    yield* mailbox.end
    const [messages2, done2] = yield* mailbox.takeAll
    assert.deepStrictEqual(messages2, Chunk.empty())
    assert.strictEqual(done2, true)
    // signal that the mailbox is failed
    yield* mailbox.fail("boom")
    })
  • #3541 007289a Thanks @mikearnaldi! - Cache some fiber references in the runtime to optimize reading in hot-paths

  • #3541 42a8f99 Thanks @fubhy! - Added RcMap.keys and MutableHashMap.keys.

    These functions allow you to get a list of keys currently stored in the underlying hash map.

    const map = MutableHashMap.make([
    ["a", "a"],
    ["b", "b"],
    ["c", "c"]
    ])
    const keys = MutableHashMap.keys(map) // ["a", "b", "c"]
    Effect.gen(function* () {
    const map = yield* RcMap.make({
    lookup: (key) => Effect.succeed(key)
    })
    yield* RcMap.get(map, "a")
    yield* RcMap.get(map, "b")
    yield* RcMap.get(map, "c")
    const keys = yield* RcMap.keys(map) // ["a", "b", "c"]
    })
  • #3541 eebfd29 Thanks @fubhy! - Add Duration.parts api

    const parts = Duration.parts(Duration.sum("5 minutes", "20 seconds"))
    assert.equal(parts.minutes, 5)
    assert.equal(parts.seconds, 20)
  • #3541 040703d Thanks @KhraksMamtsov! - The FiberRef<A> is now a subtype of Effect<A>. This change simplifies handling of deferred values, removing the need for explicit call FiberRef.get.

    import { Effect, FiberRef } from "effect"
    Effect.gen(function* () {
    const fiberRef = yield* FiberRef.make("value")
    const before = yield* FiberRef.get(fiberRef)
    const after = yield* fiberRef
    })

3.7.3

Patch Changes

  • #3592 35a0f81 Thanks @mikearnaldi! - TestClock yield with setTimeout(0)

3.7.2

Patch Changes

  • #3548 8a601d7 Thanks @tim-smart! - remove console.log statements from Micro

  • #3546 353ba19 Thanks @tim-smart! - fix exported Stream types for broadcast* and toPubSub

3.7.1

Patch Changes

  • #3536 79859e7 Thanks @mikearnaldi! - Optimize Array.sortWith to avoid calling the map function excesively

  • #3516 f6a469c Thanks @KhraksMamtsov! - support tacit usage for Effect.tapErrorTag and Effect.catchTag

  • #3543 dcb9ec0 Thanks @datner! - Align behavior of Stream.empty to act like Stream.make() to fix behavior with NodeStream.toReadable

  • #3545 79aa6b1 Thanks @tim-smart! - fix Micro.forEach for empty iterables

3.7.0

Minor Changes

  • #3410 2f456cc Thanks @vinassefranche! - preserve Array.modify Array.modifyOption non emptiness

  • #3410 8745e41 Thanks @patroza! - improve: type Fiber.awaitAll as Exit<A, E>[].

  • #3410 e557838 Thanks @titouancreach! - New constructor Config.nonEmptyString

  • #3410 d6e7e40 Thanks @KhraksMamtsov! - preserve Array.replace Array.replaceOption non emptiness

  • #3410 8356321 Thanks @KhraksMamtsov! - add Effect.bindAll api

    This api allows you to combine Effect.all with Effect.bind. It is useful when you want to concurrently run multiple effects and then combine their results in a Do notation pipeline.

    import { Effect } from "effect"
    const result = Effect.Do.pipe(
    Effect.bind("x", () => Effect.succeed(2)),
    Effect.bindAll(
    ({ x }) => ({
    a: Effect.succeed(x + 1),
    b: Effect.succeed("foo")
    }),
    { concurrency: 2 }
    )
    )
    assert.deepStrictEqual(Effect.runSync(result), {
    x: 2,
    a: 3,
    b: "foo"
    })
  • #3410 192f2eb Thanks @tim-smart! - add propagateInterruption option to Fiber{Handle,Set,Map}

    This option will send any external interrupts to the .join result.

  • #3410 718cb70 Thanks @dilame! - feat(Stream): implement race operator, which accepts two upstreams and returns a stream that mirrors the first upstream to emit an item and interrupts the other upstream.

    import { Stream, Schedule, Console, Effect } from "effect"
    const stream = Stream.fromSchedule(Schedule.spaced("2 millis")).pipe(
    Stream.race(Stream.fromSchedule(Schedule.spaced("1 millis"))),
    Stream.take(6),
    Stream.tap((n) => Console.log(n))
    )
    Effect.runPromise(Stream.runDrain(stream))
    // Output each millisecond from the first stream, the rest streams are interrupted
    // 0
    // 1
    // 2
    // 3
    // 4
    // 5
  • #3410 e9d0310 Thanks @mikearnaldi! - Avoid automatic propagation of finalizer concurrency, closes #3440

  • #3410 6bf28f7 Thanks @tim-smart! - add Context.getOrElse api, for gettings a Tag’s value with a fallback

Patch Changes

  • #3410 db89601 Thanks @juliusmarminge! - add Micro.isMicroCause guard

3.6.8

Patch Changes

  • #3510 e809286 Thanks @fubhy! - Detect environment in Logger.pretty using process.stdout

3.6.7

Patch Changes

  • #3504 50ec889 Thanks @datner! - improve the performance of Effect.partitionMap

3.6.6

Patch Changes

  • #3306 f960bf4 Thanks @dilame! - Introduce left / right naming for Stream apis

  • #3499 46a575f Thanks @tim-smart! - fix nested Config.array, by ensuring path patches aren’t applied twice in sequences

3.6.5

Patch Changes

  • #3474 14a47a8 Thanks @IMax153! - Add support for incrementing and decrementing a gauge based on its prior value

  • #3490 0c09841 Thanks @tim-smart! - fix type error when .pipe() has no arguments

3.6.4

Patch Changes

  • #3404 8295281 Thanks @KhraksMamtsov! - Fix Cache<_, Value, _> type parameter variance (covariant -> invariant)

  • #3452 c940df6 Thanks @tim-smart! - ensure Scheduler tasks are added to a matching priority bucket

  • #3459 00b6c6d Thanks @tim-smart! - ensure defects are caught in Effect.tryPromise

  • #3458 f8d95a6 Thanks @thomasvargiu! - fix DateTime.makeZonedFromString for 0 offset

3.6.3

Patch Changes

  • #3444 04adcac Thanks @tim-smart! - ensure Stream.toReadableStream pulls always result in a enqueue

3.6.2

Patch Changes

  • #3435 fd4b2f6 Thanks @Andarist! - ensure fiber is properly cleared in FiberHandle.unsafeSet

3.6.1

Patch Changes

  • #3405 510a34d Thanks @KhraksMamtsov! - Fix Effect.repeat with times option returns wrong value

  • #3398 45dbb9f Thanks @sukovanej! - Fix Stream.asyncPush type signature - allow the register effect to fail.

3.6.0

Minor Changes

  • #3380 1e0fe80 Thanks @tim-smart! - make List.Cons extend NonEmptyIterable

  • #3380 8135294 Thanks @tim-smart! - add DateTime module

    The DateTime module provides functionality for working with time, including support for time zones and daylight saving time.

    It has two main data types: DateTime.Utc and DateTime.Zoned.

    A DateTime.Utc represents a time in Coordinated Universal Time (UTC), and a DateTime.Zoned contains both a UTC timestamp and a time zone.

    There is also a CurrentTimeZone service, for setting a time zone contextually.

    import { DateTime, Effect } from "effect"
    Effect.gen(function* () {
    // Get the current time in the current time zone
    const now = yield* DateTime.nowInCurrentZone
    // Math functions are included
    const tomorrow = DateTime.add(now, 1, "day")
    // Convert to a different time zone
    // The UTC portion of the `DateTime` is preserved and only the time zone is
    // changed
    const sydneyTime = tomorrow.pipe(
    DateTime.unsafeSetZoneNamed("Australia/Sydney")
    )
    }).pipe(DateTime.withCurrentZoneNamed("America/New_York"))
  • #3380 cd255a4 Thanks @tim-smart! - add Stream.asyncPush api

    This api creates a stream from an external push-based resource.

    You can use the emit helper to emit values to the stream. You can also use the emit helper to signal the end of the stream by using apis such as emit.end or emit.fail.

    By default it uses an “unbounded” buffer size. You can customize the buffer size and strategy by passing an object as the second argument with the bufferSize and strategy fields.

    import { Effect, Stream } from "effect"
    Stream.asyncPush<string>(
    (emit) =>
    Effect.acquireRelease(
    Effect.gen(function* () {
    yield* Effect.log("subscribing")
    return setInterval(() => emit.single("tick"), 1000)
    }),
    (handle) =>
    Effect.gen(function* () {
    yield* Effect.log("unsubscribing")
    clearInterval(handle)
    })
    ),
    { bufferSize: 16, strategy: "dropping" }
    )
  • #3380 3845646 Thanks @mikearnaldi! - Implement Struct.keys as a typed alternative to Object.keys

    import { Struct } from "effect"
    const symbol: unique symbol = Symbol()
    const value = {
    a: 1,
    b: 2,
    [symbol]: 3
    }
    const keys: Array<"a" | "b"> = Struct.keys(value)
  • #3380 2d09078 Thanks @sukovanej! - Add Random.choice.

    import { Random } from "effect"
    Effect.gen(function* () {
    const randomItem = yield* Random.choice([1, 2, 3])
    console.log(randomItem)
    })
  • #3380 4bce5a0 Thanks @vinassefranche! - Add onlyEffect option to Effect.tap

  • #3380 4ddbff0 Thanks @KhraksMamtsov! - Support Refinement in Predicate.tuple and Predicate.struct

  • #3380 e74cc38 Thanks @dilame! - Implement Stream.onEnd that adds an effect to be executed at the end of the stream.

    import { Console, Effect, Stream } from "effect"
    const stream = Stream.make(1, 2, 3).pipe(
    Stream.map((n) => n * 2),
    Stream.tap((n) => Console.log(`after mapping: ${n}`)),
    Stream.onEnd(Console.log("Stream ended"))
    )
    Effect.runPromise(Stream.runCollect(stream)).then(console.log)
    // after mapping: 2
    // after mapping: 4
    // after mapping: 6
    // Stream ended
    // { _id: 'Chunk', values: [ 2, 4, 6 ] }
  • #3380 bb069b4 Thanks @dilame! - Implement Stream.onStart that adds an effect to be executed at the start of the stream.

    import { Console, Effect, Stream } from "effect"
    const stream = Stream.make(1, 2, 3).pipe(
    Stream.onStart(Console.log("Stream started")),
    Stream.map((n) => n * 2),
    Stream.tap((n) => Console.log(`after mapping: ${n}`))
    )
    Effect.runPromise(Stream.runCollect(stream)).then(console.log)
    // Stream started
    // after mapping: 2
    // after mapping: 4
    // after mapping: 6
    // { _id: 'Chunk', values: [ 2, 4, 6 ] }
  • #3380 cd255a4 Thanks @tim-smart! - add bufferSize option to Stream.fromEventListener

  • #3380 7d02174 Thanks @fubhy! - Changed various function signatures to return Array instead of ReadonlyArray

3.5.9

Patch Changes

  • #3377 6359644 Thanks @tim-smart! - add MicroScheduler to Micro module

  • #3362 7f41e42 Thanks @IMax153! - Add Service and Identifier to Context.Tag.

    These helpers can be used, for example, to extract the service shape from a tag:

    import * as Context from "effect/Context"
    export class Foo extends Context.Tag("Foo")<
    Foo,
    {
    readonly foo: Effect.Effect<void>
    }
    >() {}
    type ServiceShape = typeof Foo.Service
  • #3373 f566fd1 Thanks @KhraksMamtsov! - Add test for Hash.number(0.1) !== Has.number(0)

3.5.8

Patch Changes

  • #3345 1ba640c Thanks @mikearnaldi! - Fix typo propety to property

  • #3349 c8c71bd Thanks @tim-smart! - ensure all Data.Error arguments are preserved in .toJSON

  • #3355 a26ce58 Thanks @tim-smart! - fix Hash.number not returning unique values

3.5.7

Patch Changes

  • #3288 3afcc93 Thanks @mikearnaldi! - Forbid usage of property “name” in Effect.Tag

  • #3310 99bddcf Thanks @fubhy! - Added additional pure annotations to improve tree-shakeability

3.5.6

Patch Changes

  • #3294 cc327a1 Thanks @tim-smart! - correctly exclude symbols from Record.keys

  • #3289 4bfe4fb Thanks @dilame! - Changed Stream.groupByKey/Stream.grouped/Stream.groupedWithin JSDoc category from utils to grouping

  • #3295 2b14d18 Thanks @tim-smart! - fix YieldableError rendering on bun

3.5.5

Patch Changes

  • #3266 a9d7800 Thanks @tim-smart! - use “unbounded” buffer for Stream.fromEventListener

3.5.4

Patch Changes

  • #3253 ed0dde4 Thanks @tim-smart! - update dependencies

  • #3247 ca775ce Thanks @tim-smart! - if performance.timeOrigin is 0, use performance.now() directly in Clock

    This is a workaround for cloudflare, where performance.now() cannot be used in the global scope to calculate the origin.

  • #3259 5be9cc0 Thanks @IMax153! - expose Channel.isChannel

  • #3250 203658f Thanks @gcanti! - add support for Refinements to Predicate.or, closes #3243

    import { Predicate } from "effect"
    // Refinement<unknown, string | number>
    const isStringOrNumber = Predicate.or(Predicate.isString, Predicate.isNumber)
  • #3246 eb1c4d4 Thanks @tim-smart! - render nested causes in Cause.pretty

3.5.3

Patch Changes

  • #3234 edb0da3 Thanks @tim-smart! - do not add a error “cause” if the upstream error does not contain one

  • #3236 c8d3fb0 Thanks @tim-smart! - set Logger.pretty message color to deepskyblue on browsers

  • #3240 dabd028 Thanks @tim-smart! - fix process .isTTY detection

  • #3230 786b2ab Thanks @KhraksMamtsov! - support heterogenous argument in Option.firstSomeOf

  • #3238 fc57354 Thanks @leonitousconforti! - Align Stream.run public function signatures

3.5.2

Patch Changes

  • #3228 639208e Thanks @IMax153! - Render a more helpful error message when timing out an effect

  • #3235 6684b4c Thanks @tim-smart! - improve safari support for Logger.pretty

  • #3235 6684b4c Thanks @tim-smart! - fix span stack rendering when stack function returns undefined

  • #3235 6684b4c Thanks @tim-smart! - align UnsafeConsole group types with web apis

3.5.1

Patch Changes

  • #3220 55fdd76 Thanks @tim-smart! - fix Logger.pretty on bun

3.5.0

Minor Changes

  • #3048 a1f5b83 Thanks @tim-smart! - add renderErrorCause option to Cause.pretty

  • #3048 60bc3d0 Thanks @tim-smart! - add RcRef module

    An RcRef wraps a reference counted resource that can be acquired and released multiple times.

    The resource is lazily acquired on the first call to get and released when the last reference is released.

    import { Effect, RcRef } from "effect"
    Effect.gen(function* () {
    const ref = yield* RcRef.make({
    acquire: Effect.acquireRelease(Effect.succeed("foo"), () =>
    Effect.log("release foo")
    )
    })
    // will only acquire the resource once, and release it
    // when the scope is closed
    yield* RcRef.get(ref).pipe(Effect.andThen(RcRef.get(ref)), Effect.scoped)
    })
  • #3048 5ab348f Thanks @tim-smart! - allowing customizing Stream pubsub strategy

    import { Schedule, Stream } from "effect"
    // toPubSub
    Stream.fromSchedule(Schedule.spaced(1000)).pipe(
    Stream.toPubSub({
    capacity: 16, // or "unbounded"
    strategy: "dropping" // or "sliding" / "suspend"
    })
    )
    // also for the broadcast apis
    Stream.fromSchedule(Schedule.spaced(1000)).pipe(
    Stream.broadcastDynamic({
    capacity: 16,
    strategy: "dropping"
    })
    )
  • #3048 60bc3d0 Thanks @tim-smart! - add Duration.isZero, for checking if a Duration is zero

  • #3048 3e04bf8 Thanks @sukovanej! - Add Success type util for Config.

  • #3048 e7fc45f Thanks @tim-smart! - add Logger.prettyLogger and Logger.pretty

    Logger.pretty is a new logger that leverages the features of the console APIs to provide a more visually appealing output.

    To try it out, provide it to your program:

    import { Effect, Logger } from "effect"
    Effect.log("Hello, World!").pipe(Effect.provide(Logger.pretty))
  • #3048 a1f5b83 Thanks @tim-smart! - add .groupCollapsed to UnsafeConsole

  • #3048 4626de5 Thanks @giacomoran! - export Random.make taking hashable values as seed

  • #3048 f01e7db Thanks @tim-smart! - add replay option to PubSub constructors

    This option adds a replay buffer in front of the given PubSub. The buffer will replay the last n messages to any new subscriber.

    Effect.gen(function*() {
    const messages = [1, 2, 3, 4, 5]
    const pubsub = yield* PubSub.bounded<number>({ capacity: 16, replay: 3 })
    yield* PubSub.publishAll(pubsub, messages)
    const sub = yield* PubSub.subscribe(pubsub)
    assert.deepStrictEqual(Chunk.toReadonlyArray(yield* Queue.takeAll(sub)), [3, 4, 5])
    }))
  • #3048 60bc3d0 Thanks @tim-smart! - add RcMap module

    An RcMap can contain multiple reference counted resources that can be indexed by a key. The resources are lazily acquired on the first call to get and released when the last reference is released.

    Complex keys can extend Equal and Hash to allow lookups by value.

    import { Effect, RcMap } from "effect"
    Effect.gen(function* () {
    const map = yield* RcMap.make({
    lookup: (key: string) =>
    Effect.acquireRelease(Effect.succeed(`acquired ${key}`), () =>
    Effect.log(`releasing ${key}`)
    )
    })
    // Get "foo" from the map twice, which will only acquire it once
    // It will then be released once the scope closes.
    yield* RcMap.get(map, "foo").pipe(
    Effect.andThen(RcMap.get(map, "foo")),
    Effect.scoped
    )
    })
  • #3048 ac71f37 Thanks @dilame! - Ensure Scope is excluded from R in the Channel / Stream run* functions.

    This fix ensures that Scope is now properly excluded from the resulting effect environment. The affected functions include run, runCollect, runCount, runDrain and other non-scoped run* in both Stream and Channel modules. This fix brings the type declaration in line with the runtime implementation.

  • #3048 8432360 Thanks @dilame! - refactor(Stream/mergeLeft): rename self/that argument names to left/right for clarity

    refactor(Stream/mergeRight): rename self/that argument names to left/right for clarity

  • #3048 e4bf1bf Thanks @dilame! - feat(Stream): implement “raceAll” operator, which returns a stream that mirrors the first source stream to emit an item.

    import { Stream, Schedule, Console, Effect } from "effect"
    const stream = Stream.raceAll(
    Stream.fromSchedule(Schedule.spaced("1 millis")),
    Stream.fromSchedule(Schedule.spaced("2 millis")),
    Stream.fromSchedule(Schedule.spaced("4 millis"))
    ).pipe(Stream.take(6), Stream.tap(Console.log))
    Effect.runPromise(Stream.runDrain(stream))
    // Output only from the first stream, the rest streams are interrupted
    // 0
    // 1
    // 2
    // 3
    // 4
    // 5
  • #3048 13cb861 Thanks @dilame! - refactor(Stream): use new built-in Types.TupleOf instead of Stream.DynamicTuple and deprecate it

  • #3048 79d2d91 Thanks @tim-smart! - support ErrorOptions in YieldableError constructor

  • #3048 9f66825 Thanks @tim-smart! - allow customizing the output buffer for the Stream.async* apis

    import { Stream } from "effect"
    Stream.async<string>(
    (emit) => {
    // ...
    },
    {
    bufferSize: 16,
    strategy: "dropping" // you can also use "sliding" or "suspend"
    }
    )

Patch Changes

  • #3048 a1f5b83 Thanks @tim-smart! - include Error.cause stack in log output

  • #3048 a1f5b83 Thanks @tim-smart! - set stackTraceLimit to 1 in PrettyError to address performance issues

  • #3048 79d2d91 Thanks @tim-smart! - ensure “cause” is rendered in Data.Error output

  • #3048 e7fc45f Thanks @tim-smart! - fix types of UnsafeConsole.group

3.4.9

Patch Changes

  • #3210 7af137c Thanks @tim-smart! - prevent reclaim of manually invalidated pool items

  • #3204 ee4b3dc Thanks @gcanti! - Updated the JSDocs for the Stream module by adding examples to key functions.

  • #3202 097d25c Thanks @tim-smart! - allow invalidated Pool items to be reclaimed with usage strategy

3.4.8

Patch Changes

  • #3181 a435e0f Thanks @KhraksMamtsov! - refactor TrimEnd & TrimStart

  • #3176 b5554db Thanks @tim-smart! - allow Stream run fiber to close before trying to interrupt it

  • #3175 a9c4fb3 Thanks @tim-smart! - ensure fibers are interrupted in Stream.mergeWith

3.4.7

Patch Changes

  • #3161 a5737d6 Thanks @tim-smart! - ensure PubSub.publishAll does not increase size while there are no subscribers

3.4.6

Patch Changes

  • #3096 5c0ceb0 Thanks @gcanti! - Micro: align with Effect module (renamings and new combinators).

    General naming convention rule: <reference module (start with lowercase)><api (start with Uppercase)>.

    • Failure -> MicroCause
      • Failure.Expected<E> -> MicroCause.Fail<E>
      • Failure.Unexpected -> MicroCause.Die
      • Failure.Aborted -> MicroCause.Interrupt
      • FailureExpected -> causeFail
      • FailureUnexpected -> causeDie
      • FailureAborted -> causeInterrupt
      • failureIsExpected -> causeIsFail
      • failureIsExpected -> causeIsFail
      • failureIsUnexpected -> causeIsDie
      • failureIsAborted -> causeIsInterrupt
      • failureSquash -> causeSquash
      • failureWithTrace -> causeWithTrace
    • Result -> MicroExit
      • ResultAborted -> exitInterrupt
      • ResultSuccess -> exitSucceed
      • ResultFail -> exitFail
      • ResultFailUnexpected -> exitDie
      • ResultFailWith -> exitFailCause
      • resultIsSuccess -> exitIsSuccess
      • resultIsFailure -> exitIsFailure
      • resultIsAborted -> exitIsInterrupt
      • resultIsFailureExpected -> exitIsFail
      • resultIsFailureUnexpected -> exitIsDie
      • resultVoid -> exitVoid
    • DelayFn -> MicroSchedule
      • delayExponential -> scheduleExponential
      • delaySpaced -> scheduleSpaced
      • delayWithMax -> scheduleWithMaxDelay
      • delayWithMaxElapsed -> scheduleWithMaxElapsed
      • delayWithRecurs -> scheduleRecurs and make it a constructor
      • add scheduleAddDelay combinator
      • add scheduleUnion combinator
      • add scheduleIntersect combinator
    • Handle
      • abort -> interrupt
      • unsafeAbort -> unsafeInterrupt
    • provideServiceMicro -> provideServiceEffect
    • fromResult -> fromExit
    • fromResultSync -> fromExitSync
    • failWith -> failCause
    • failWithSync -> failCauseSync
    • asResult -> exit
    • filterOrFailWith -> filterOrFailCause
    • repeatResult -> repeatExit
    • catchFailure -> catchAllCause
    • catchFailureIf -> catchCauseIf
    • catchExpected -> catchAll
    • catchUnexpected -> catchAllDefect
    • tapFailure -> tapErrorCause
    • tapFailureIf -> tapErrorCauseIf
    • tapExpected -> tapError
    • tapUnexpected -> tapDefect
    • mapFailure -> mapErrorCause
    • matchFailureMicro -> matchCauseEffect
    • matchFailure -> matchCause
    • matchMicro -> matchEffect
    • onResult -> onExit
    • onResultIf -> onExitIf
    • onFailure -> onError
    • onAbort -> onInterrupt
    • abort -> interrupt
    • runPromiseResult -> runPromiseExit
    • runSyncResult -> runSyncExit
    • rename delay option to schedule
  • #3096 5c0ceb0 Thanks @gcanti! - Micro: rename timeout to timeoutOption, and add a timeout that fails with a TimeoutException

  • #3121 33735b1 Thanks @KhraksMamtsov! - Support for the tacit usage of external handlers for Match.tag and Match.tagStartsWith functions

    type Value = { _tag: "A"; a: string } | { _tag: "B"; b: number }
    const handlerA = (_: { _tag: "A"; a: number }) => _.a
    // $ExpectType string | number
    pipe(
    M.type<Value>(),
    M.tag("A", handlerA), // <-- no type issue
    M.orElse((_) => _.b)
    )(value)
  • #3096 5c0ceb0 Thanks @gcanti! - Micro: move MicroExit types to a namespace

  • #3134 139d4b3 Thanks @tim-smart! - use Channel.acquireUseRelease for Channel.withSpan

3.4.5

Patch Changes

  • #3099 a047af9 Thanks @tim-smart! - fix using unions with Match.withReturnType

3.4.4

Patch Changes

  • #3083 72638e3 Thanks @gcanti! - Micro: add NoSuchElementException error and update fromOption to change the failure type from Option.None<never> to NoSuchElementException

  • #3095 d7dde2b Thanks @tim-smart! - remove global AbortController from Micro

  • #3085 9b2fc3b Thanks @gcanti! - Micro: add zipWith

3.4.3

Patch Changes

  • #3065 c342739 Thanks @KhraksMamtsov! - Support this argument for Micro.gen

  • #3067 8898e5e Thanks @KhraksMamtsov! - Cleanup signal “abort” event handler in Micro.runFork

  • #3082 ff78636 Thanks @gcanti! - Align the Micro.catchIf signature with Effect.catchIf

  • #3078 c86bd4e Thanks @KhraksMamtsov! - Support unification for Micro module

  • #3079 bbdd365 Thanks @tim-smart! - update to typescript 5.5

3.4.2

Patch Changes

  • #3062 3da1497 Thanks @KhraksMamtsov! - Reuse centralized do-notation code

3.4.1

Patch Changes

  • #3056 66a1910 Thanks @gcanti! - add missing TypeLambda to Micro module

3.4.0

Minor Changes

  • #2938 c0ce180 Thanks @LaureRC! - Make Option.liftPredicate dual

  • #2938 61707b6 Thanks @LaureRC! - Add Effect.liftPredicate

    Effect.liftPredicate transforms a Predicate function into an Effect returning the input value if the predicate returns true or failing with specified error if the predicate fails.

    import { Effect } from "effect"
    const isPositive = (n: number): boolean => n > 0
    // succeeds with `1`
    Effect.liftPredicate(1, isPositive, (n) => `${n} is not positive`)
    // fails with `"0 is not positive"`
    Effect.liftPredicate(0, isPositive, (n) => `${n} is not positive`)
  • #2938 9c1b5b3 Thanks @tim-smart! - add EventListener type to Stream to avoid use of dom lib

  • #2938 a35faf8 Thanks @gcanti! - Add lastNonEmpty function to Chunk module, closes #2946

  • #2938 ff73c0c Thanks @dilame! - feat(Stream): implement Success, Error, Context type accessors

  • #2938 984d516 Thanks @tim-smart! - add Micro module

    A lightweight alternative to Effect, for when bundle size really matters.

    At a minimum, Micro adds 5kb gzipped to your bundle, and scales with the amount of features you use.

  • #2938 8c3b8a2 Thanks @gcanti! - add ManagedRuntime type utils (Context, and Error)

  • #2938 017e2f9 Thanks @LaureRC! - Add Either.liftPredicate

  • #2938 91bf8a2 Thanks @msensys! - Add Tuple.at api, to retrieve an element at a specified index from a tuple.

    import { Tuple } from "effect"
    assert.deepStrictEqual(Tuple.at([1, "hello", true], 1), "hello")
  • #2938 c6a4a26 Thanks @datner! - add ensure util for Array, used to normalize A | ReadonlyArray<A>

    import { ensure } from "effect/Array"
    // lets say you are not 100% sure if it's a member or a collection
    declare const someValue: { foo: string } | Array<{ foo: string }>
    // $ExpectType ({ foo: string })[]
    const normalized = ensure(someValue)

3.3.5

Patch Changes

  • #3012 6c89408 Thanks @tim-smart! - ensure Config.Wrap only destructures plain objects

3.3.4

Patch Changes

  • #3001 a67b8fe Thanks @tim-smart! - use Math.random for Hash.random

3.3.3

Patch Changes

  • #2999 06ede85 Thanks @KhraksMamtsov! - Added tests for Chunk.toArray and Chunk.toReadonlyArray with use cases in the pipe

  • #3000 7204ca5 Thanks @tim-smart! - fix support for Predicates in Predicate.compose

3.3.2

Patch Changes

  • #2981 3572646 Thanks @tim-smart! - ensure multiline error messages are preserved in cause rendering

  • #2970 1aed347 Thanks @gcanti! - Updated Chunk.toArray and Chunk.toReadonlyArray. Improved function signatures to preserve non-empty status of chunks during conversion.

  • #2977 df4bf4b Thanks @tim-smart! - fix discard option in Effect.all

  • #2917 f085f92 Thanks @mikearnaldi! - Fix Unify for Stream

3.3.1

Patch Changes

  • #2952 eb98c5b Thanks @KhraksMamtsov! - Change Config.array to return Array<A> instead of ReadonlyArray<A>

  • #2950 184fed8 Thanks @gcanti! - Ensure Chunk.reverse preserves NonEmpty status, closes #2947

  • #2954 6068e07 Thanks @jessekelly881! - Fix runtime error in Struct.evolve by enhancing compile-time checks, closes #2953

  • #2948 3a77e20 Thanks @gcanti! - Remove unnecessary === comparison in getEquivalence functions

    In some getEquivalence functions that use make, there is an unnecessary === comparison. The make function already handles this comparison.

3.3.0

Minor Changes

  • #2837 1f4ac00 Thanks @dilame! - add Stream.zipLatestAll api

  • #2837 9305b76 Thanks @mattrossman! - Add queuing strategy option for Stream.toReadableStream

  • #2837 0f40d98 Thanks @tim-smart! - add timeToLiveStrategy to Pool options

    The timeToLiveStrategy determines how items are invalidated. If set to “creation”, then items are invalidated based on their creation time. If set to “usage”, then items are invalidated based on pool usage.

    By default, the timeToLiveStrategy is set to “usage”.

  • #2837 b761ef0 Thanks @tim-smart! - add Layer.annotateLogs & Layer.annotateSpans

    This allows you to add log & span annotation to a Layer.

    import { Effect, Layer } from "effect"
    Layer.effectDiscard(Effect.log("hello")).pipe(
    Layer.annotateLogs({
    service: "my-service"
    })
    )
  • #2837 b53f69b Thanks @dilame! - Types: implement TupleOf and TupleOfAtLeast types

    Predicate: implement isTupleOf and isTupleOfAtLeast type guards

  • #2837 0f40d98 Thanks @tim-smart! - add concurrency & targetUtilization option to Pool.make & Pool.makeWithTTL

    This option allows you to specify the level of concurrent access per pool item. I.e. setting concurrency: 2 will allow each pool item to be in use by 2 concurrent tasks.

    targetUtilization determines when to create new pool items. It is a value between 0 and 1, where 1 means only create new pool items when all the existing items are fully utilized.

    A targetUtilization of 0.5 will create new pool items when the existing items are 50% utilized.

  • #2837 5bd549e Thanks @KhraksMamtsov! - Support this argument for {STM, Either, Option}.gen

  • #2837 67f160a Thanks @KhraksMamtsov! - Introduced Redacted<out T = string> module - Secret generalization Secret extends Redacted The use of the Redacted has been replaced by the use of the Redacted in packages with version 0.*.*

3.2.9

Patch Changes

  • #2921 8c5d280 Thanks @tim-smart! - remove usage of performance.timeOrigin

  • #2912 6ba6d26 Thanks @mikearnaldi! - Remove toJSON from PrettyError and fix message generation

  • #2923 3f28bf2 Thanks @tim-smart! - only wrap objects with string keys in Config.Wrap

  • #2914 5817820 Thanks @mikearnaldi! - Fix id extraction in Context.Tag.Identifier

3.2.8

Patch Changes

  • #2894 fb91f17 Thanks @mikearnaldi! - ensure Equal considers Date by value

3.2.7

Patch Changes

  • #2887 6801fca Thanks @mikearnaldi! - Ensure provide of runtime is additive on context

3.2.6

Patch Changes

  • #2879 cc8ac50 Thanks @TylorS! - Support tuples in Types.DeepMutable

3.2.5

Patch Changes

  • #2823 608b01f Thanks @gcanti! - Array: simplify signatures (ReadonlyArray<any> | Iterable<any> = Iterable<any>)

  • #2834 031c712 Thanks @tim-smart! - attach Stream.toReadableStream fibers to scope

  • #2744 a44e532 Thanks @KhraksMamtsov! - make Array.separate, Array.getRights, Array.getLefts, Array.getSomes heterogeneous

3.2.4

Patch Changes

  • #2801 1af94df Thanks @tim-smart! - ensure pool calls finalizer for failed acquisitions

  • #2808 e313a01 Thanks @gcanti! - Array: fix flatMapNullable implementation and add descriptions / examples

3.2.3

Patch Changes

  • #2805 45578e8 Thanks @tim-smart! - fix internal cutpoint name preservation

3.2.2

Patch Changes

  • #2787 5d9266e Thanks @mikearnaldi! - Prohibit name clashes in Effect.Tag

    The following now correctly flags a type error given that the property context exists already in Tag:

    import { Effect } from "effect"
    class LoaderArgs extends Effect.Tag("@services/LoaderContext")<
    LoaderArgs,
    { context: number }
    >() {}
  • #2797 9f8122e Thanks @mikearnaldi! - Improve internalization of functions to clean stack traces

  • #2798 6a6f670 Thanks @mikearnaldi! - Avoid eager read of the stack when captured by a span

3.2.1

Patch Changes

3.2.0

Minor Changes

  • #2778 146cadd Thanks @tim-smart! - Add Stream.toReadableStreamEffect / .toReadableStreamRuntime

  • #2778 7135748 Thanks @tim-smart! - add Cause.prettyErrors api

    You can use this to extract Error instances from a Cause, that have clean stack traces and have had span information added to them.

  • #2778 963b4e7 Thanks @tim-smart! - add Chunk.difference & Chunk.differenceWith

  • #2778 64c9414 Thanks @tim-smart! - Improve causal rendering in vitest by rethrowing pretty errors

  • #2778 7135748 Thanks @tim-smart! - add Effect.functionWithSpan

    Allows you to define an effectful function that is wrapped with a span.

    import { Effect } from "effect"
    const getTodo = Effect.functionWithSpan({
    body: (id: number) => Effect.succeed(`Got todo ${id}!`),
    options: (id) => ({
    name: `getTodo-${id}`,
    attributes: { id }
    })
    })
  • #2778 2cbb76b Thanks @tim-smart! - Add do notation for Array

  • #2778 870c5fa Thanks @tim-smart! - support $is & $match for Data.TaggedEnum with generics

  • #2778 7135748 Thanks @tim-smart! - capture stack trace for tracing spans

Patch Changes

3.1.6

Patch Changes

3.1.5

Patch Changes

3.1.4

Patch Changes

3.1.3

Patch Changes

3.1.2

Patch Changes

3.1.1

Patch Changes

3.1.0

Minor Changes

  • #2543 c3c12c6 Thanks @github-actions! - add SortedMap.lastOption & partition apis

  • #2543 ba64ea6 Thanks @github-actions! - add Types.DeepMutable, an alternative to Types.Mutable that makes all properties recursively mutable

  • #2543 b5de2d2 Thanks @github-actions! - add Effect.annotateLogsScoped

    This api allows you to annotate logs until the Scope has been closed.

    import { Effect } from "effect"
    Effect.gen(function* () {
    yield* Effect.log("no annotations")
    yield* Effect.annotateLogsScoped({ foo: "bar" })
    yield* Effect.log("annotated with foo=bar")
    }).pipe(Effect.scoped, Effect.andThen(Effect.log("no annotations again")))
  • #2543 a1c7ab8 Thanks @github-actions! - added Stream.fromEventListener, and BrowserStream.{fromEventListenerWindow, fromEventListenerDocument} for constructing a stream from addEventListener

  • #2543 a023f28 Thanks @github-actions! - add kind property to Tracer.Span

    This can be used to specify what kind of service created the span.

  • #2543 1c9454d Thanks @github-actions! - add Effect.timeoutOption

    Returns an effect that will return None if the effect times out, otherwise it will return Some of the produced value.

    import { Effect } from "effect"
    // will return `None` after 500 millis
    Effect.succeed("hello").pipe(
    Effect.delay(1000),
    Effect.timeoutOption("500 millis")
    )
  • #2543 92d56db Thanks @github-actions! - add $is & $match helpers to Data.TaggedEnum constructors

    import { Data } from "effect"
    type HttpError = Data.TaggedEnum<{
    NotFound: {}
    InternalServerError: { reason: string }
    }>
    const { $is, $match, InternalServerError, NotFound } =
    Data.taggedEnum<HttpError>()
    // create a matcher
    const matcher = $match({
    NotFound: () => 0,
    InternalServerError: () => 1
    })
    // true
    $is("NotFound")(NotFound())
    // false
    $is("NotFound")(InternalServerError({ reason: "fail" }))

3.0.8

Patch Changes

3.0.7

Patch Changes

3.0.6

Patch Changes

3.0.5

Patch Changes

3.0.4

Patch Changes

  • #2602 9a24667 Thanks @mikearnaldi! - allow use of generators (Effect.gen) without the adapter

    Effect’s data types now implement a Iterable that can be yield*’ed directly.

    Effect.gen(function* () {
    const a = yield* Effect.success(1)
    const b = yield* Effect.success(2)
    return a + b
    })

3.0.3

Patch Changes

  • #2568 a7b4b84 Thanks @tim-smart! - add Match.withReturnType api

    Which can be used to constrain the return type of a match expression.

    import { Match } from "effect"
    Match.type<string>().pipe(
    Match.withReturnType<string>(),
    Match.when("foo", () => "foo"), // valid
    Match.when("bar", () => 123), // type error
    Match.else(() => "baz")
    )

3.0.2

Patch Changes

3.0.1

Patch Changes

3.0.0

Major Changes

Minor Changes

  • #2207 1b5f0c7 Thanks @github-actions! - close FiberHandle/FiberSet/FiberMap when it is released

    When they are closed, fibers can no longer be added to them.

  • #2207 d50a652 Thanks @github-actions! - add preregisteredWords option to frequency metric key type

    You can use this to register a list of words to pre-populate the value of the metric.

    import { Metric } from "effect"
    const counts = Metric.frequency("counts", {
    preregisteredWords: ["a", "b", "c"]
    }).register()
  • #2207 9a3bd47 Thanks @github-actions! - Bump TypeScript min requirement to version 5.4

  • #2207 be9d025 Thanks @github-actions! - add unique identifier to Tracer.ParentSpan tag

  • #2529 78b767c Thanks @fubhy! - Renamed ReadonlyArray and ReadonlyRecord modules for better discoverability.

  • #2207 5c2b561 Thanks @github-actions! - The signatures of the HaltStrategy.match StreamHaltStrategy.match functions have been changed to the generally accepted ones

  • #2207 a18f594 Thanks @github-actions! - support variadic arguments in Effect.log

    This makes Effect.log more similar to console.log:

    Effect.log("hello", { foo: "bar" }, Cause.fail("error"))
  • #2207 2f96d93 Thanks @github-actions! - Fix ConfigError _tag, with the previous implementation catching the ConfigError with Effect.catchTag would show And, Or, etc.

  • #2207 5a2314b Thanks @github-actions! - replace use of unit terminology with void

    For all the data types.

    Effect.unit // => Effect.void
    Stream.unit // => Stream.void
    // etc
  • #2207 271b79f Thanks @github-actions! - Either: fix getEquivalence parameter order from Either.getEquivalence(left, right) to Either.getEquivalence({ left, right })

  • #2207 53d1c2a Thanks @github-actions! - use LazyArg for Effect.if branches

    Instead of:

    Effect.if(true, {
    onTrue: Effect.succeed("true"),
    onFalse: Effect.succeed("false")
    })

    You should now write:

    Effect.if(true, {
    onTrue: () => Effect.succeed("true"),
    onFalse: () => Effect.succeed("false")
    })
  • #2207 e7e1bbe Thanks @github-actions! - Replaced custom NoInfer type with the native NoInfer type from TypeScript 5.4

  • #2207 10c169e Thanks @github-actions! - Cache<Key, Error, Value> has been changed to Cache<Key, Value, Error = never>. ScopedCache<Key, Error, Value> has been changed to ScopedCache<Key, Value, Error = never>. Lookup<Key, Environment, Error, Value> has been changed to Lookup<Key, Value, Error = never, Environment = never>

Patch Changes

  • #2104 1499974 Thanks @IMax153! - don’t run resolver if there are no incomplete requests

  • #2207 1b5f0c7 Thanks @github-actions! - add FiberMap.has/unsafeHas api

  • #2104 1499974 Thanks @IMax153! - add String casing transformation apis

    • snakeToCamel
    • snakeToPascal
    • snakeToKebab
    • camelToSnake
    • pascalToSnake
    • kebabToSnake
  • #2207 1b5f0c7 Thanks @github-actions! - add FiberHandle module, for holding a reference to a running fiber

    import { Effect, FiberHandle } from "effect"
    Effect.gen(function* (_) {
    const handle = yield* _(FiberHandle.make())
    // run some effects
    yield* _(FiberHandle.run(handle, Effect.never))
    // this will interrupt the previous fiber
    yield* _(FiberHandle.run(handle, Effect.never))
    // this will not run, as a fiber is already running
    yield* _(FiberHandle.run(handle, Effect.never, { onlyIfMissing: true }))
    yield* _(Effect.sleep(1000))
    }).pipe(
    Effect.scoped // The fiber will be interrupted when the scope is closed
    )
  • #2521 6424181 Thanks @patroza! - change return type of Fiber.joinAll to return an array

2.4.19

Patch Changes

  • #2503 41c8102 Thanks @gcanti! - Centralize error messages for bugs

  • #2493 776ef2b Thanks @gcanti! - add a RegExp module to packages/effect, closes #2488

  • #2499 217147e Thanks @tim-smart! - ensure FIFO ordering when a Deferred is resolved

  • #2502 90776ec Thanks @tim-smart! - make tracing spans cheaper to construct

  • #2472 8709856 Thanks @tim-smart! - add Subscribable trait / module

    Subscribable represents a resource that has a current value and can be subscribed to for updates.

    The following data types are subscribable:

    • A SubscriptionRef
    • An Actor from the experimental Machine module
  • #2500 232c353 Thanks @tim-smart! - simplify scope internals

  • #2507 0ca835c Thanks @gcanti! - ensure correct value is passed to mapping function in mapAccum loop, closes #2506

  • #2472 8709856 Thanks @tim-smart! - add Readable module / trait

    Readable is a common interface for objects that can be read from using a get Effect.

    For example, Ref’s implement Readable:

    import { Effect, Readable, Ref } from "effect"
    import assert from "assert"
    Effect.gen(function* (_) {
    const ref = yield* _(Ref.make(123))
    assert(Readable.isReadable(ref))
    const result = yield* _(ref.get)
    assert(result === 123)
    })
  • #2498 e983740 Thanks @jessekelly881! - added {Readable, Subscribable}.unwrap

  • #2494 e3e0924 Thanks @thewilkybarkid! - Add Duration.divide and Duration.unsafeDivide.

    import { Duration, Option } from "effect"
    import assert from "assert"
    assert.deepStrictEqual(
    Duration.divide("10 seconds", 2),
    Option.some(Duration.decode("5 seconds"))
    )
    assert.deepStrictEqual(Duration.divide("10 seconds", 0), Option.none())
    assert.deepStrictEqual(Duration.divide("1 nano", 1.5), Option.none())
    assert.deepStrictEqual(
    Duration.unsafeDivide("10 seconds", 2),
    Duration.decode("5 seconds")
    )
    assert.deepStrictEqual(
    Duration.unsafeDivide("10 seconds", 0),
    Duration.infinity
    )
    assert.throws(() => Duration.unsafeDivide("1 nano", 1.5))

2.4.18

Patch Changes

  • #2473 dadc690 Thanks @tim-smart! - add Logger.withConsoleLog/withConsoleError apis

    These apis send a Logger’s output to console.log/console.error respectively.

    import { Logger } from "effect"
    // send output to stderr
    const stderrLogger = Logger.withConsoleError(Logger.stringLogger)

2.4.17

Patch Changes

  • #2461 8fdfda6 Thanks @tim-smart! - add Inspectable.toStringUnknown/stringifyCircular

  • #2462 607b2e7 Thanks @tim-smart! - remove handled errors from Effect.retryOrElse

  • #2461 8fdfda6 Thanks @tim-smart! - improve formatting of Runtime failures

  • #2415 8206caf Thanks @tim-smart! - add Iterable module

    This module shares many apis compared to “effect/ReadonlyArray”, but is fully lazy.

    import { Iterable, pipe } from "effect"
    // Only 5 items will be generated & transformed
    pipe(
    Iterable.range(1, 100),
    Iterable.map((i) => `item ${i}`),
    Iterable.take(5)
    )
  • #2438 7ddd654 Thanks @mikearnaldi! - Support Heterogeneous Effects in Effect Iterable apis

    Including:

    • Effect.allSuccesses
    • Effect.firstSuccessOf
    • Effect.mergeAll
    • Effect.reduceEffect
    • Effect.raceAll
    • Effect.forkAll

    For example:

    import { Effect } from "effect"
    class Foo extends Effect.Tag("Foo")<Foo, 3>() {}
    class Bar extends Effect.Tag("Bar")<Bar, 4>() {}
    // const program: Effect.Effect<(1 | 2 | 3 | 4)[], never, Foo | Bar>
    export const program = Effect.allSuccesses([
    Effect.succeed(1 as const),
    Effect.succeed(2 as const),
    Foo,
    Bar
    ])

    The above is now possible while before it was expecting all Effects to conform to the same type

  • #2438 7ddd654 Thanks @mikearnaldi! - add Effect.filterMap api

    Which allows you to filter and map an Iterable of Effects in one step.

    import { Effect, Option } from "effect"
    // resolves with `["even: 2"]
    Effect.filterMap(
    [Effect.succeed(1), Effect.succeed(2), Effect.succeed(3)],
    (i) => (i % 2 === 0 ? Option.some(`even: ${i}`) : Option.none())
    )
  • #2461 8fdfda6 Thanks @tim-smart! - use Inspectable.toStringUnknown for absurd runtime errors

  • #2460 f456ba2 Thanks @tim-smart! - use const type parameter for Config.withDefault

    Which ensures that the fallback value type is not widened for literals.

2.4.16

Patch Changes

2.4.15

Patch Changes

  • #2407 d7688c0 Thanks @thewilkybarkid! - Add Config.duration

    This can be used to parse Duration’s from environment variables:

    import { Config, Effect } from "effect"
    Config.duration("CACHE_TTL").pipe(
    Effect.andThen((duration) => ...)
    )
  • #2416 b3a4fac Thanks @mikearnaldi! - Collect exits on forEach interrupt of residual requests

2.4.14

Patch Changes

2.4.13

Patch Changes

2.4.12

Patch Changes

2.4.11

Patch Changes

  • #2384 2f488c4 Thanks @tim-smart! - update dependencies

  • #2381 37ca592 Thanks @tim-smart! - add fiber ref for disabling the tracer

    You can use it with the Effect.withTracerEnabled api:

    import { Effect } from "effect"
    Effect.succeed(42).pipe(
    Effect.withSpan("my-span"),
    // the span will not be registered with the tracer
    Effect.withTracerEnabled(false)
    )
  • #2383 317b5b8 Thanks @tim-smart! - add Duration.isFinite api, to determine if a duration is not Infinity

2.4.10

Patch Changes

2.4.9

Patch Changes

2.4.8

Patch Changes

  • #2354 bb0b69e Thanks @tim-smart! - add overload to Effect.filterOrFail that fails with NoSuchElementException

    This allows you to perform a filterOrFail without providing a fallback failure function.

    Example:

    import { Effect } from "effect"
    // fails with NoSuchElementException
    Effect.succeed(1).pipe(Effect.filterOrFail((n) => n === 0))
  • #2336 6b20bad Thanks @jessekelly881! - added Predicate.isTruthy

  • #2351 4e64e9b Thanks @tim-smart! - fix metrics not using labels from fiber ref

  • #2266 3851a02 Thanks @patroza! - fix Effect.Tag generated proxy functions to work with andThen/tap, or others that do function/isEffect checks

  • #2353 5f5fcd9 Thanks @tim-smart! - Types: add Has helper

  • #2299 814e5b8 Thanks @alex-dixon! - Prevent Effect.if from crashing when first argument is not an Effect

2.4.7

Patch Changes

2.4.6

Patch Changes

  • #2290 4f35a7e Thanks @mikearnaldi! - Remove function renaming from internals, introduce new cutpoint strategy

  • #2311 9971186 Thanks @tim-smart! - add Channel.splitLines api

    It splits strings on newlines. Handles both Windows newlines (\r\n) and UNIX newlines (\n).

2.4.5

Patch Changes

2.4.4

Patch Changes

  • #2172 5d47ee0 Thanks @gcanti! - Brand: add refined overload

    export function refined<A extends Brand<any>>(
    f: (unbranded: Brand.Unbranded<A>) => Option.Option<Brand.BrandErrors>
    ): Brand.Constructor<A>
  • #2285 817a04c Thanks @tim-smart! - add support for AbortSignal’s to runPromise

    If the signal is aborted, the effect execution will be interrupted.

    import { Effect } from "effect"
    const controller = new AbortController()
    Effect.runPromise(Effect.never, { signal: controller.signal })
    // abort after 1 second
    setTimeout(() => controller.abort(), 1000)
  • #2293 d90a99d Thanks @tim-smart! - add AbortSignal support to ManagedRuntime

  • #2288 dd05faa Thanks @tim-smart! - optimize addition of blocked requests to parallel collection

  • #2288 dd05faa Thanks @tim-smart! - use Chunk for request block collections

  • #2280 802674b Thanks @jessekelly881! - added support for PromiseLike

2.4.3

Patch Changes

  • #2211 20e63fb Thanks @tim-smart! - add ManagedRuntime module, to make incremental adoption easier

    You can use a ManagedRuntime to run Effect’s that can use the dependencies from the given Layer. For example:

    import { Console, Effect, Layer, ManagedRuntime } from "effect"
    class Notifications extends Effect.Tag("Notifications")<
    Notifications,
    { readonly notify: (message: string) => Effect.Effect<void> }
    >() {
    static Live = Layer.succeed(this, {
    notify: (message) => Console.log(message)
    })
    }
    async function main() {
    const runtime = ManagedRuntime.make(Notifications.Live)
    await runtime.runPromise(Notifications.notify("Hello, world!"))
    await runtime.dispose()
    }
    main()
  • #2211 20e63fb Thanks @tim-smart! - add Layer.toRuntimeWithMemoMap api

    Similar to Layer.toRuntime, but allows you to share a Layer.MemoMap between layer builds.

    By sharing the MemoMap, layers are shared between each build - ensuring layers are only built once between multiple calls to Layer.toRuntimeWithMemoMap.

2.4.2

Patch Changes

  • #2264 e03811e Thanks @patroza! - fix: unmatched function fallthrough in andThen and tap

  • #2225 ac41d84 Thanks @mikearnaldi! - Add Effect.Tag to simplify access to service.

    This change allows to define tags in the following way:

    class DemoTag extends Effect.Tag("DemoTag")<
    DemoTag,
    {
    readonly getNumbers: () => Array<number>
    readonly strings: Array<string>
    }
    >() {}

    And use them like:

    DemoTag.getNumbers()
    DemoTag.strings

    This fuses together serviceFunctions and serviceConstants in the static side of the tag.

    Additionally it allows using the service like:

    DemoTag.use((_) => _.getNumbers())

    This is especially useful when having functions that contain generics in the service given that those can’t be reliably transformed at the type level and because of that we can’t put them on the tag.

  • #2238 6137533 Thanks @JJayet! - Request: swap Success and Error params

  • #2270 f373529 Thanks @tim-smart! - add structured logging apis

    • Logger.json / Logger.jsonLogger
    • Logger.structured / Logger.structuredLogger

    Logger.json logs JSON serialized strings to the console.

    Logger.structured logs structured objects, which is useful in the browser where you can inspect objects logged to the console.

  • #2257 1bf9f31 Thanks @mikearnaldi! - Make sure Effect.Tag works on primitives.

    This change allows the following to work just fine:

    import { Effect, Layer } from "effect"
    class DateTag extends Effect.Tag("DateTag")<DateTag, Date>() {
    static date = new Date(1970, 1, 1)
    static Live = Layer.succeed(this, this.date)
    }
    class MapTag extends Effect.Tag("MapTag")<MapTag, Map<string, string>>() {
    static Live = Layer.effect(
    this,
    Effect.sync(() => new Map())
    )
    }
    class NumberTag extends Effect.Tag("NumberTag")<NumberTag, number>() {
    static Live = Layer.succeed(this, 100)
    }
  • #2244 e3ff789 Thanks @tim-smart! - add FiberMap/FiberSet.join api

    This api can be used to propogate failures back to a parent fiber, in case any of the fibers added to the FiberMap/FiberSet fail with an error.

    Example:

    import { Effect, FiberSet } from "effect"
    Effect.gen(function* (_) {
    const set = yield* _(FiberSet.make())
    yield* _(FiberSet.add(set, Effect.runFork(Effect.fail("error"))))
    // parent fiber will fail with "error"
    yield* _(FiberSet.join(set))
    })
  • #2238 6137533 Thanks @JJayet! - make Effect.request dual

  • #2263 507ba40 Thanks @thewilkybarkid! - Allow duration inputs to be singular

  • #2255 e466afe Thanks @jessekelly881! - added Either.Either.{Left,Right} and Option.Option.Value type utils

  • #2270 f373529 Thanks @tim-smart! - add Logger.batched, for batching logger output

    It takes a duration window and an effectful function that processes the batched output.

    Example:

    import { Console, Effect, Logger } from "effect"
    const LoggerLive = Logger.replaceScoped(
    Logger.defaultLogger,
    Logger.logfmtLogger.pipe(
    Logger.batched("500 millis", (messages) =>
    Console.log("BATCH", messages.join("\n"))
    )
    )
    )
    Effect.gen(function* (_) {
    yield* _(Effect.log("one"))
    yield* _(Effect.log("two"))
    yield* _(Effect.log("three"))
    }).pipe(Effect.provide(LoggerLive), Effect.runFork)
  • #2233 de74eb8 Thanks @gcanti! - Struct: make pick / omit dual

2.4.1

Patch Changes

2.4.0

Minor Changes

  • #2101 5de7be5 Thanks @github-actions! - remove ReadonlyRecord.fromIterable (duplicate of fromEntries)

  • #2101 489fcf3 Thanks @github-actions! - - swap Schedule type parameters from Schedule<out Env, in In, out Out> to Schedule<out Out, in In = unknown, out R = never>, closes #2154

    • swap ScheduleDriver type parameters from ScheduleDriver<out Env, in In, out Out> to ScheduleDriver<out Out, in In = unknown, out R = never>
  • #2101 7d9c3bf Thanks @github-actions! - Consolidate Effect.asyncOption, Effect.asyncEither, Stream.asyncOption, Stream.asyncEither, and Stream.asyncInterrupt

    This PR removes Effect.asyncOption and Effect.asyncEither as their behavior can be entirely implemented with the new signature of Effect.async, which optionally returns a cleanup Effect from the registration callback.

    declare const async: <A, E = never, R = never>(
    register: (
    callback: (_: Effect<A, E, R>) => void,
    signal: AbortSignal
    ) => void | Effect<void, never, R>,
    blockingOn?: FiberId
    ) => Effect<A, E, R>

    Additionally, this PR removes Stream.asyncOption, Stream.asyncEither, and Stream.asyncInterrupt as their behavior can be entirely implemented with the new signature of Stream.async, which can optionally return a cleanup Effect from the registration callback.

    declare const async: <A, E = never, R = never>(
    register: (emit: Emit<R, E, A, void>) => Effect<void, never, R> | void,
    outputBuffer?: number
    ) => Stream<A, E, R>
  • #2101 d8d278b Thanks @github-actions! - swap GroupBy type parameters from GroupBy<out R, out E, out K, out V> to GroupBy<out K, out V, out E = never, out R = never>

  • #2101 14c5711 Thanks @github-actions! - Remove Effect.unified and Effect.unifiedFn in favour of Unify.unify.

    The Unify module fully replaces the need for specific unify functions, when before you did:

    import { Effect } from "effect"
    const effect = Effect.unified(
    Math.random() > 0.5 ? Effect.succeed("OK") : Effect.fail("NO")
    )
    const effectFn = Effect.unifiedFn((n: number) =>
    Math.random() > 0.5 ? Effect.succeed("OK") : Effect.fail("NO")
    )

    You can now do:

    import { Effect, Unify } from "effect"
    const effect = Unify.unify(
    Math.random() > 0.5 ? Effect.succeed("OK") : Effect.fail("NO")
    )
    const effectFn = Unify.unify((n: number) =>
    Math.random() > 0.5 ? Effect.succeed("OK") : Effect.fail("NO")
    )
  • #2101 5de7be5 Thanks @github-actions! - add key type to ReadonlyRecord

  • #2101 585fcce Thanks @github-actions! - add support for optional property keys to pick, omit and get

    Before:

    import { pipe } from "effect/Function"
    import * as S from "effect/Struct"
    const struct: {
    a?: string
    b: number
    c: boolean
    } = { b: 1, c: true }
    // error
    const x = pipe(struct, S.pick("a", "b"))
    const record: Record<string, number> = {}
    const y = pipe(record, S.pick("a", "b"))
    console.log(y) // => { a: undefined, b: undefined }
    // error
    console.log(pipe(struct, S.get("a")))

    Now

    import { pipe } from "effect/Function"
    import * as S from "effect/Struct"
    const struct: {
    a?: string
    b: number
    c: boolean
    } = { b: 1, c: true }
    const x = pipe(struct, S.pick("a", "b"))
    console.log(x) // => { b: 1 }
    const record: Record<string, number> = {}
    const y = pipe(record, S.pick("a", "b"))
    console.log(y) // => {}
    console.log(pipe(struct, S.get("a"))) // => undefined
  • #2101 a025b12 Thanks @github-actions! - Swap type params of Either from Either<E, A> to Either<R, L = never>.

    Along the same line of the other changes this allows to shorten the most common types such as:

    import { Either } from "effect"
    const right: Either.Either<string> = Either.right("ok")

Patch Changes

2.3.8

Patch Changes

  • #2167 5ad2eec Thanks @tim-smart! - add Hash.cached

    This api assists with adding a layer of caching, when hashing immutable data structures.

    import { Data, Hash } from "effect"
    class User extends Data.Class<{
    id: number
    name: string
    }> {
    [Hash.symbol]() {
    return Hash.cached(this, Hash.string(`${this.id}-${this.name}`))
    }
    }
  • #2187 e6d36c0 Thanks @tim-smart! - update development dependencies

2.3.7

Patch Changes

  • #2142 bc8404d Thanks @mikearnaldi! - Expose version control via ModuleVersion.

    This enables low level framework authors to run their own effect version which won’t conflict with any other effect versions running on the same process.

    Imagine cases where for example a function runtime is built on effect, we don’t want lifecycle of the runtime to clash with lifecycle of user-land provided code.

    To manually control the module version one can use:

    import * as ModuleVersion from "effect/ModuleVersion"
    ModuleVersion.setCurrentVersion(
    `my-effect-runtime-${ModuleVersion.getCurrentVersion()}`
    )

    Note that this code performs side effects and should be executed before any module is imported ideally via an init script.

    The resulting order of execution has to be:

    import * as ModuleVersion from "effect/ModuleVersion"
    ModuleVersion.setCurrentVersion(
    `my-effect-runtime-${ModuleVersion.getCurrentVersion()}`
    )
    import { Effect } from "effect"
    // rest of code
  • #2159 2c5cbcd Thanks @IMax153! - Avoid incrementing cache hits for expired entries

  • #2165 6565916 Thanks @tim-smart! - fix Hash implemention for Option.none

2.3.6

Patch Changes

  • #2145 b1163b2 Thanks @tim-smart! - add RequestResolver.aroundRequests api

    This can be used to run side effects that introspect the requests being executed.

    Example:

    import { Effect, Request, RequestResolver } from "effect"
    interface GetUserById extends Request.Request<unknown> {
    readonly id: number
    }
    declare const resolver: RequestResolver.RequestResolver<GetUserById>
    RequestResolver.aroundRequests(
    resolver,
    (requests) => Effect.log(`got ${requests.length} requests`),
    (requests, _) => Effect.log(`finised running ${requests.length} requests`)
    )
  • #2148 b46b869 Thanks @riordanpawley! - Flipped scheduleForked types to match new <A, E, R> signature

  • #2139 de1b226 Thanks @mikearnaldi! - Introduce FiberId.Single, make FiberId.None behave like FiberId.Runtime, relax FiberRefs to use Single instead of Runtime.

    This change is a precursor to enable easier APIs to modify the Runtime when patching FiberRefs.

  • #2137 a663390 Thanks @mikearnaldi! - Expose Random Tag and functions to use a specific random service implementation

  • #2143 ff88f80 Thanks @mikearnaldi! - Fix Cause.pretty when toString is invalid

    import { Cause } from "effect"
    console.log(Cause.pretty(Cause.fail([{ toString: "" }])))

    The code above used to throw now it prints:

    Terminal window
    Error: [{"toString":""}]
  • #2080 11be07b Thanks @KhraksMamtsov! - Add functional analogue of satisfies operator. This is a convenient operator to use in the pipe chain to localize type errors closer to their source.

    import { satisfies } from "effect/Function"
    const test1 = satisfies<number>()(5 as const)
    // ^? const test: 5
    // @ts-expect-error
    const test2 = satisfies<string>()(5)
    // ^? Argument of type 'number' is not assignable to parameter of type 'string'
  • #2147 c568645 Thanks @tim-smart! - generate a random span id for the built-in tracer

    This ensures the same span id isn’t used between application runs.

  • #2144 88835e5 Thanks @mikearnaldi! - Fix withRandom and withClock types

  • #2138 b415577 Thanks @mikearnaldi! - Fix internals of TestAnnotationsMap making it respect equality

  • #2149 ff8046f Thanks @tim-smart! - add Runtime.updateFiberRefs/setFiberRef/deleteFiberRef

    This change allows you to update fiber ref values inside a Runtime object.

    Example:

    import { Effect, FiberRef, Runtime } from "effect"
    const ref = FiberRef.unsafeMake(0)
    const updatedRuntime = Runtime.defaultRuntime.pipe(
    Runtime.setFiberRef(ref, 1)
    )
    // returns 1
    const result = Runtime.runSync(updatedRuntime)(FiberRef.get(ref))

2.3.5

Patch Changes

2.3.4

Patch Changes

2.3.3

Patch Changes

  • #2090 efd41d8 Thanks @hsubra89! - Update RateLimiter to support passing in a custom cost per effect. This is really useful for API(s) that have a “credit cost” per endpoint.

    Usage Example :

    import { Effect, RateLimiter } from "effect"
    import { compose } from "effect/Function"
    const program = Effect.scoped(
    Effect.gen(function* ($) {
    // Create a rate limiter that has an hourly limit of 1000 credits
    const rateLimiter = yield* $(RateLimiter.make(1000, "1 hours"))
    // Query API costs 1 credit per call ( 1 is the default cost )
    const queryAPIRL = compose(rateLimiter, RateLimiter.withCost(1))
    // Mutation API costs 5 credits per call
    const mutationAPIRL = compose(rateLimiter, RateLimiter.withCost(5))
    // ...
    // Use the pre-defined rate limiters
    yield* $(queryAPIRL(Effect.log("Sample Query")))
    yield* $(mutationAPIRL(Effect.log("Sample Mutation")))
    // Or set a cost on-the-fly
    yield* $(
    rateLimiter(Effect.log("Another query with a different cost")).pipe(
    RateLimiter.withCost(3)
    )
    )
    })
    )
  • #2097 0f83515 Thanks @IMax153! - Updates the RateLimiter.make constructor to take an object of RateLimiter.Options, which allows for specifying the rate-limiting algorithm to utilize:

    You can choose from either the token-bucket or the fixed-window algorithms for rate-limiting.

    export declare namespace RateLimiter {
    export interface Options {
    /**
    * The maximum number of requests that should be allowed.
    */
    readonly limit: number
    /**
    * The interval to utilize for rate-limiting requests. The semantics of the
    * specified `interval` vary depending on the chosen `algorithm`:
    *
    * `token-bucket`: The maximum number of requests will be spread out over
    * the provided interval if no tokens are available.
    *
    * For example, for a `RateLimiter` using the `token-bucket` algorithm with
    * a `limit` of `10` and an `interval` of `1 seconds`, `1` request can be
    * made every `100 millis`.
    *
    * `fixed-window`: The maximum number of requests will be reset during each
    * interval. For example, for a `RateLimiter` using the `fixed-window`
    * algorithm with a `limit` of `10` and an `interval` of `1 seconds`, a
    * maximum of `10` requests can be made each second.
    */
    readonly interval: DurationInput
    /**
    * The algorithm to utilize for rate-limiting requests.
    *
    * Defaults to `token-bucket`.
    */
    readonly algorithm?: "fixed-window" | "token-bucket"
    }
    }
  • #2097 0f83515 Thanks @IMax153! - return the resulting available permits from Semaphore.release

2.3.2

Patch Changes

  • #2096 6654f5f Thanks @tim-smart! - default to never for Runtime returning functions

    This includes:

    • Effect.runtime
    • FiberSet.makeRuntime

    It prevents unknown from creeping into types, as well as never being a useful default type for propogating Fiber Refs and other context.

  • #2094 2eb11b4 Thanks @tim-smart! - revert some type param adjustments in FiberSet

    makeRuntime now has the R parameter first again.

    Default to unknown for the A and E parameters instead of never.

  • #2103 56c09bd Thanks @patroza! - Expand Either and Option andThen to support the map case like Effects’ andThen

    For example:

    expect(pipe(Either.right(1), Either.andThen(2))).toStrictEqual(
    Either.right(2)
    )
    expect(
    pipe(
    Either.right(1),
    Either.andThen(() => 2)
    )
    ).toStrictEqual(Either.right(2))
    expect(pipe(Option.some(1), Option.andThen(2))).toStrictEqual(Option.some(2))
    expect(
    pipe(
    Option.some(1),
    Option.andThen(() => 2)
    )
    ).toStrictEqual(Option.some(2))
  • #2098 71aa5b1 Thanks @ethanniser! - removed ./internal/timeout and replaced all usages with setTimeout directly

    previously it was required to abstract away conditionally solving an bun had an issue with setTimeout, that caused incorrect behavior that bug has since been fixed, and the isBun check is no longer needed as such the timeout module is also no longer needed

  • #2099 1700af8 Thanks @tim-smart! - optimize Effect.zip{Left,Right}

    for the sequential case, avoid using Effect.all internally

2.3.1

Patch Changes

  • #2085 b5a8215 Thanks @gcanti! - Fix Schedule typings (some APIs didn’t have Effect parameters swapped).

2.3.0

Minor Changes

  • #2006 96bcee2 Thanks @github-actions! - change Runtime.AsyncFiberException type parameters order from AsyncFiberException<E, A> to AsyncFiberException<A, E = never>

  • #2006 96bcee2 Thanks @github-actions! - change Runtime.Cancel type parameters order from Cancel<E, A> to Cancel<A, E = never>

  • #2006 c77f635 Thanks @github-actions! - change Exit type parameter order from Exit<E, A> to Exit<A, E = never>

  • #2006 e343a74 Thanks @github-actions! - change Resource type parameters order from Resource<E, A> to Resource<A, E = never>

  • #2006 acf1894 Thanks @github-actions! - change FiberMap type parameters order from FiberMap<K, E = unknown, A = unknown> to FiberMap<K, A = unknown, E = unknown>

  • #2006 9a2d1c1 Thanks @github-actions! - With this change we now require a string key to be provided for all tags and renames the dear old Tag to GenericTag, so when previously you could do:

    import { Effect, Context } from "effect"
    interface Service {
    readonly _: unique symbol
    }
    const Service = Context.Tag<
    Service,
    {
    number: Effect.Effect<never, never, number>
    }
    >()

    you are now mandated to do:

    import { Effect, Context } from "effect"
    interface Service {
    readonly _: unique symbol
    }
    const Service = Context.GenericTag<
    Service,
    {
    number: Effect.Effect<never, never, number>
    }
    >("Service")

    This makes by default all tags globals and ensures better debuggaility when unexpected errors arise.

    Furthermore we introduce a new way of constructing tags that should be considered the new default:

    import { Effect, Context } from "effect"
    class Service extends Context.Tag("Service")<
    Service,
    {
    number: Effect.Effect<never, never, number>
    }
    >() {}
    const program = Effect.flatMap(Service, ({ number }) => number).pipe(
    Effect.flatMap((_) => Effect.log(`number: ${_}`))
    )

    this will use “Service” as the key and will create automatically an opaque identifier (the class) to be used at the type level, it does something similar to the above in a single shot.

  • #2006 1a77f72 Thanks @github-actions! - change Effect type parameters order from Effect<R, E, A> to Effect<A, E = never, R = never>

  • #2006 c986f0e Thanks @github-actions! - change FiberSet type parameters order from FiberSet<E, A> to FiberSet<A, E = never>

  • #2006 96bcee2 Thanks @github-actions! - change Runtime.RunCallbackOptions type parameters order from RunCallbackOptions<E, A> to RunCallbackOptions<A, E = never>

  • #2006 70dde23 Thanks @github-actions! - change TDeferred type parameters order from TDeferred<E, A> to TDeferred<A, E = never>

  • #2006 81b7425 Thanks @github-actions! - change Streamable.Class and Effectable.Class type parameters order from Class<R, E, A> to Class<A, E = never, R = never>

  • #2006 02c3461 Thanks @github-actions! - With this change we remove the Data.Data type and we make Equal.Equal & Hash.Hash implicit traits.

    The main reason is that Data.Data<A> was structurally equivalent to A & Equal.Equal but extending Equal.Equal doesn’t mean that the equality is implemented by-value, so the type was simply adding noise without gaining any level of safety.

    The module Data remains unchanged at the value level, all the functions previously available are supposed to work in exactly the same manner.

    At the type level instead the functions return Readonly variants, so for example we have:

    import { Data } from "effect"
    const obj = Data.struct({
    a: 0,
    b: 1
    })

    will have the obj typed as:

    declare const obj: {
    readonly a: number
    readonly b: number
    }
  • #2006 0e56e99 Thanks @github-actions! - change Deferred type parameters order from Deferred<E, A> to Deferred<A, E>

  • #2006 8b0ded9 Thanks @github-actions! - change Fiber type parameters order from Fiber<E, A> to Fiber<A, E = never>

  • #2006 8dd83e8 Thanks @github-actions! - change Channel type parameters order from Channel<out Env, in InErr, in InElem, in InDone, out OutErr, out OutElem, out OutDone> to Channel<OutElem, InElem = unknown, OutErr = never, InErr = unknown, OutDone = void, InDone = unknown, Env = never>

  • #2006 d75f6fe Thanks @github-actions! - change Take type parameters order from Take<E, A> to Take<A, E = never>

  • #2006 7356e5c Thanks @github-actions! - change STM type parameters order from STM<R, E, A> to STM<A, E = never, R = never>

  • #2006 3077cde Thanks @github-actions! - change Stream type parameters order from Stream<R, E, A> to Stream<A, E = never, R = never>

  • #2006 78f47ab Thanks @github-actions! - change Pool type parameters order from Pool<E, A> to Pool<A, E = never>, and KeyedPool from KeyedPool<E, A> to KeyedPool<A, E = never>

  • #2006 52e5d20 Thanks @github-actions! - change Request type parameters order from Request<E, A> to Request<A, E = never>

  • #2006 c6137ec Thanks @github-actions! - change RuntimeFiber type parameters order from RuntimeFiber<E, A> to RuntimeFiber<A, E = never>

  • #2006 f5ae081 Thanks @github-actions! - Use TimeoutException instead of NoSuchElementException for timeout.

  • #2006 60686f5 Thanks @github-actions! - change Layer type parameters order from Layer<RIn, E, ROut> to Layer<ROut, E = never, RIn = never>

  • #2006 9a2d1c1 Thanks @github-actions! - This change enables Effect.serviceConstants and Effect.serviceMembers to access any constant in the service, not only the effects, namely it is now possible to do:

    import { Effect, Context } from "effect"
    class NumberRepo extends Context.TagClass("NumberRepo")<
    NumberRepo,
    {
    readonly numbers: Array<number>
    }
    >() {
    static numbers = Effect.serviceConstants(NumberRepo).numbers
    }
  • #2006 5127afe Thanks @github-actions! - Rename ReadonlyRecord.update to .replace

  • #2006 8ee2931 Thanks @github-actions! - enhance DX by swapping type parameters and adding defaults to:

    • Effect
      • async
      • asyncOption
      • asyncEither
    • Stream
      • asyncEffect
      • asyncInterrupt
      • asyncOption
      • asyncScoped
      • identity
  • #2006 6727474 Thanks @github-actions! - change Sink type parameters order from Sink<out R, out E, in In, out L, out Z> to Sink<out A, in In = unknown, out L = never, out E = never, out R = never>

  • #2006 5127afe Thanks @github-actions! - rename ReadonlyRecord.upsert to .set

Patch Changes

  • #2006 5127afe Thanks @github-actions! - add ReadonlyRecord.modify

  • #2083 be19ce0 Thanks @mikearnaldi! - Add Ratelimiter which limits the number of calls to a resource within a time window using the token bucket algorithm.

    Usage Example:

    import { Effect, RateLimiter } from "effect"
    // we need a scope because the rate limiter needs to allocate a state and a background job
    const program = Effect.scoped(
    Effect.gen(function* ($) {
    // create a rate limiter that executes up to 10 requests within 2 seconds
    const rateLimit = yield* $(RateLimiter.make(10, "2 seconds"))
    // simulate repeated calls
    for (let n = 0; n < 100; n++) {
    // wrap the effect we want to limit with rateLimit
    yield* $(rateLimit(Effect.log("Calling RateLimited Effect")))
    }
    })
    )
    // will print 10 calls immediately and then throttle
    program.pipe(Effect.runFork)

    Or, in a more real world scenario, with a dedicated Service + Layer:

    import { Context, Effect, Layer, RateLimiter } from "effect"
    class ApiLimiter extends Context.Tag("@services/ApiLimiter")<
    ApiLimiter,
    RateLimiter.RateLimiter
    >() {
    static Live = RateLimiter.make(10, "2 seconds").pipe(
    Layer.scoped(ApiLimiter)
    )
    }
    const program = Effect.gen(function* ($) {
    const rateLimit = yield* $(ApiLimiter)
    for (let n = 0; n < 100; n++) {
    yield* $(rateLimit(Effect.log("Calling RateLimited Effect")))
    }
    })
    program.pipe(Effect.provide(ApiLimiter.Live), Effect.runFork)
  • #2084 4a5d01a Thanks @tim-smart! - simplify RateLimiter implementation using semaphore

  • #2084 4a5d01a Thanks @tim-smart! - add Number.nextPow2

    This function returns the next power of 2 from the given number.

    import { nextPow2 } from "effect/Number"
    assert.deepStrictEqual(nextPow2(5), 8)
    assert.deepStrictEqual(nextPow2(17), 32)

2.2.5

Patch Changes

  • #2075 3ddfdbf Thanks @tim-smart! - add apis for manipulating context to the Runtime module

    These include:

    • Runtime.updateContext for modifying the Context directly
    • Runtime.provideService for adding services to an existing Runtime

    Example:

    import { Context, Runtime } from "effect"
    interface Name {
    readonly _: unique symbol
    }
    const Name = Context.Tag<Name, string>("Name")
    const runtime: Runtime.Runtime<Name> = Runtime.defaultRuntime.pipe(
    Runtime.provideService(Name, "John")
    )
  • #2075 3ddfdbf Thanks @tim-smart! - add apis for patching runtime flags to the Runtime module

    The apis include:

    • Runtime.updateRuntimeFlags for updating all the flags at once
    • Runtime.enableRuntimeFlag for enabling a single runtime flag
    • Runtime.disableRuntimeFlag for disabling a single runtime flag

2.2.4

Patch Changes

  • #2067 d0b911c Thanks @tim-smart! - add releaseAll api to Semaphore

    You can use semphore.releaseAll to atomically release all the permits of a Semaphore.

  • #2071 330e1a4 Thanks @tim-smart! - add Option.orElseSome

    Allows you to specify a default value for an Option, similar to Option.getOrElse, except the return value is still an Option.

    import * as O from "effect/Option"
    import { pipe } from "effect/Function"
    assert.deepStrictEqual(
    pipe(
    O.none(),
    O.orElseSome(() => "b")
    ),
    O.some("b")
    )
    assert.deepStrictEqual(
    pipe(
    O.some("a"),
    O.orElseSome(() => "b")
    ),
    O.some("a")
    )
  • #2057 6928a2b Thanks @joepjoosten! - Fix for possible stack overflow errors when using Array.push with spread operator arguments

  • #2033 296bc1c Thanks @rehos! - Add toJSON for Secret

2.2.3

Patch Changes

2.2.2

Patch Changes

2.2.1

Patch Changes

2.2.0

Minor Changes

Patch Changes

2.1.2

Patch Changes

2.1.1

Patch Changes

2.1.0

Minor Changes

Patch Changes

2.0.5

Patch Changes

2.0.4

Patch Changes

2.0.3

Patch Changes

2.0.2

Patch Changes

2.0.1

Patch Changes

2.0.0

Minor Changes

Patch Changes

2.0.0-next.62

Minor Changes

Patch Changes

2.0.0-next.61

Patch Changes

2.0.0-next.60

Minor Changes

Patch Changes

2.0.0-next.59

Minor Changes

Patch Changes

2.0.0-next.58

Patch Changes

2.0.0-next.57

Minor Changes

Patch Changes

2.0.0-next.56

Minor Changes

Patch Changes

2.0.0-next.55

Patch Changes

2.0.0-next.54

Patch Changes

2.0.0-next.53

Minor Changes

Patch Changes

2.0.0-next.52

Patch Changes

2.0.0-next.51

Minor Changes

Patch Changes

2.0.0-next.50

Minor Changes

Patch Changes

  • #1537 9bd70154b Thanks @patroza! - fix: Either/Option gen when no yield executes, just a plain return

  • #1526 656955944 Thanks @gcanti! - ReadonlyRecord: add missing APIs:

    • keys
    • values
    • upsert
    • update
    • isSubrecord
    • isSubrecordBy
    • reduce
    • every
    • some
    • union
    • intersection
    • difference
    • getEquivalence
    • singleton
  • #1536 80800bfb0 Thanks @fubhy! - avoid use of bigint literals

2.0.0-next.49

Patch Changes

2.0.0-next.48

Minor Changes

Patch Changes

2.0.0-next.47

Minor Changes

2.0.0-next.46

Minor Changes

Patch Changes

2.0.0-next.45

Patch Changes

2.0.0-next.44

Patch Changes

2.0.0-next.43

Patch Changes

2.0.0-next.42

Patch Changes

2.0.0-next.41

Patch Changes

2.0.0-next.40

Patch Changes

2.0.0-next.39

Patch Changes

2.0.0-next.38

Patch Changes

2.0.0-next.37

Minor Changes

Patch Changes

2.0.0-next.36

Patch Changes

2.0.0-next.35

Patch Changes

2.0.0-next.34

Patch Changes

2.0.0-next.33

Patch Changes

2.0.0-next.32

Patch Changes

2.0.0-next.31

Patch Changes

2.0.0-next.30

Patch Changes

2.0.0-next.29

Patch Changes

2.0.0-next.28

Patch Changes

2.0.0-next.27

Patch Changes

2.0.0-next.26

Patch Changes

2.0.0-next.25

Patch Changes

2.0.0-next.24

Minor Changes

Patch Changes

2.0.0-next.23

Patch Changes

2.0.0-next.22

Patch Changes

2.0.0-next.21

Patch Changes

2.0.0-next.20

Patch Changes

2.0.0-next.19

Minor Changes

2.0.0-next.18

Patch Changes

2.0.0-next.17

Patch Changes

2.0.0-next.16

Patch Changes

2.0.0-next.15

Patch Changes

2.0.0-next.14

Patch Changes

2.0.0-next.13

Patch Changes

2.0.0-next.12

Patch Changes

2.0.0-next.11

Patch Changes

2.0.0-next.10

Patch Changes

2.0.0-next.9

Patch Changes

2.0.0-next.8

Patch Changes

2.0.0-next.7

Patch Changes

2.0.0-next.6

Patch Changes

2.0.0-next.5

Patch Changes

2.0.0-next.4

Patch Changes

2.0.0-next.3

Patch Changes

2.0.0-next.2

Patch Changes

2.0.0-next.1

Patch Changes

2.0.0-next.0

Major Changes

Patch Changes