Skip to content

⬆️ Update dependency effect to v3 [SECURITY]#486

Open
renovate[bot] wants to merge 1 commit intomainfrom
renovate/npm-effect-vulnerability
Open

⬆️ Update dependency effect to v3 [SECURITY]#486
renovate[bot] wants to merge 1 commit intomainfrom
renovate/npm-effect-vulnerability

Conversation

@renovate
Copy link
Copy Markdown
Contributor

@renovate renovate bot commented Mar 21, 2026

ℹ️ Note

This PR body was truncated due to platform limits.

This PR contains the following updates:

Package Change Age Confidence
effect (source) 2.2.53.20.0 age confidence

GitHub Vulnerability Alerts

CVE-2026-32887

Versions

  • effect: 3.19.15
  • @effect/rpc: 0.72.1
  • @effect/platform: 0.94.2
  • Node.js: v22.20.0
  • Vercel runtime with Fluid compute
  • Next.js: 16 (App Router)
  • @clerk/nextjs: 6.x

Root cause

Effect's MixedScheduler batches fiber continuations and drains them inside a single microtask or timer callback. The AsyncLocalStorage context active during that callback belongs to whichever request first triggered the scheduler's drain cycle — not the request that owns the fiber being resumed.

Detailed mechanism

1. Scheduler batching (effect/src/Scheduler.ts, MixedScheduler)

// MixedScheduler.starve() — called once when first task is scheduled
private starve(depth = 0) {
  if (depth >= this.maxNextTickBeforeTimer) {
    setTimeout(() => this.starveInternal(0), 0)       // timer queue
  } else {
    Promise.resolve(void 0).then(() => this.starveInternal(depth + 1)) // microtask queue
  }
}

// MixedScheduler.starveInternal() — drains ALL accumulated tasks in one call
private starveInternal(depth: number) {
  const tasks = this.tasks.buckets
  this.tasks.buckets = []
  for (const [_, toRun] of tasks) {
    for (let i = 0; i < toRun.length; i++) {
      toRun[i]()  // ← Every fiber continuation runs in the SAME ALS context
    }
  }
  // ...
}

scheduleTask only calls starve() when running is false. Subsequent tasks accumulate in this.tasks until starveInternal drains them all. The Promise.then() (or setTimeout) callback inherits the ALS context from whichever call site created it — i.e., whichever request's fiber first set running = true.

Result: Under concurrent load, fiber continuations from Request A and Request B execute inside the same starveInternal call, sharing a single ALS context. If Request A triggered starve(), then Request B's fiber reads Request A's ALS context.

2. toWebHandlerRuntime does not propagate ALS (@effect/platform/src/HttpApp.ts:211-240)

export const toWebHandlerRuntime = <R>(runtime: Runtime.Runtime<R>) => {
  const httpRuntime: Types.Mutable<Runtime.Runtime<R>> = Runtime.make(runtime)
  const run = Runtime.runFork(httpRuntime)
  return <E>(self: Default<E, R | Scope.Scope>, middleware?) => {
    return (request: Request, context?): Promise<Response> =>
      new Promise((resolve) => {
        // Per-request Effect context is correctly set via contextMap:
        const contextMap = new Map<string, any>(runtime.context.unsafeMap)
        const httpServerRequest = ServerRequest.fromWeb(request)
        contextMap.set(ServerRequest.HttpServerRequest.key, httpServerRequest)
        httpRuntime.context = Context.unsafeMake(contextMap)

        // But the fiber is forked without any ALS propagation:
        const fiber = run(httpApp as any)  // ← ALS context is NOT captured or restored
      })
  }
}

Effect's own Context (containing HttpServerRequest) is correctly set per-request. But the Node.js ALS context — which frameworks like Next.js, Clerk, and OpenTelemetry rely on — is not captured at fork time or restored when the fiber's continuations execute.

3. The dangerous pattern this enables

// RPC handler — runs inside an Effect fiber
const handler = Effect.gen(function*() {
  // This calls auth() from @&#8203;clerk/nextjs/server, which reads from ALS
  const { userId } = yield* Effect.tryPromise({
    try: async () => auth(),  // ← may read WRONG user's session
    catch: () => new UnauthorizedError({ message: "Auth failed" })
  })
  return yield* repository.getUser(userId)
})

The async () => auth() thunk executes when the fiber continuation is scheduled by MixedScheduler. At that point, the ALS context belongs to an arbitrary concurrent request.

Reproduction scenario

Timeline (two concurrent requests to the same toWebHandler endpoint):

T0: Request A arrives → POST handler → webHandler(requestA)
    → Promise executor runs synchronously
    → httpRuntime.context set to A's context
    → fiber A forked, runs first ops synchronously
    → fiber A yields (e.g., at Effect.tryPromise boundary)
    → scheduler.scheduleTask(fiberA_continuation)
    → running=false → starve() called → Promise.resolve().then(drain)
       ↑ ALS context captured = Request A's context

T1: Request B arrives → POST handler → webHandler(requestB)
    → Promise executor runs synchronously
    → httpRuntime.context set to B's context
    → fiber B forked, runs first ops synchronously
    → fiber B yields
    → scheduler.scheduleTask(fiberB_continuation)
    → running=true → task queued, no new starve()

T2: Microtask fires → starveInternal() runs
    → Drains fiberA_continuation → auth() reads ALS → gets A's context ✓
    → Drains fiberB_continuation → auth() reads ALS → gets A's context ✗ ← WRONG USER

Minimal reproduction

import { AsyncLocalStorage } from "node:async_hooks"
import { Effect, Layer } from "effect"
import { RpcServer, RpcSerialization, Rpc, RpcGroup } from "@&#8203;effect/rpc"
import { HttpServer } from "@&#8203;effect/platform"
import * as S from "effect/Schema"

// Simulate a framework's ALS (like Next.js / Clerk)
const requestStore = new AsyncLocalStorage<{ userId: string }>()

class GetUser extends Rpc.make("GetUser", {
  success: S.Struct({ userId: S.String, alsUserId: S.String }),
  failure: S.Never,
  payload: {}
}) {}

const MyRpc = RpcGroup.make("MyRpc").add(GetUser)

const MyRpcLive = MyRpc.toLayer(
  RpcGroup.toHandlers(MyRpc, {
    GetUser: () =>
      Effect.gen(function*() {
        // Simulate calling an ALS-dependent API inside an Effect fiber
        const alsResult = yield* Effect.tryPromise({
          try: async () => {
            const store = requestStore.getStore()
            return store?.userId ?? "NONE"
          },
          catch: () => { throw new Error("impossible") }
        })
        return { userId: "from-effect-context", alsUserId: alsResult }
      })
  })
)

const RpcLayer = MyRpcLive.pipe(
  Layer.provideMerge(RpcSerialization.layerJson),
  Layer.provideMerge(HttpServer.layerContext)
)

const { handler } = RpcServer.toWebHandler(MyRpc, { layer: RpcLayer })

// Simulate two concurrent requests with different ALS contexts
async function main() {
  const results = await Promise.all([
    requestStore.run({ userId: "user-A" }, () => handler(makeRpcRequest("GetUser"))),
    requestStore.run({ userId: "user-B" }, () => handler(makeRpcRequest("GetUser"))),
  ])

  // Parse responses and check if alsUserId matches the expected user
  // Under the bug: both responses may show "user-A" (or one shows the other's)
  for (const res of results) {
    console.log(await res.json())
  }
}

Impact

Symptom Severity
auth() returns wrong user's session Critical — authentication bypass
cookies() / headers() from Next.js read wrong request High — data leakage
OpenTelemetry trace context crosses requests Medium — incorrect traces
Works locally, fails in production Hard to diagnose — only manifests under concurrent load

Workaround

Capture ALS-dependent values before entering the Effect runtime and pass them via Effect's own context system:

// In the route handler — OUTSIDE the Effect fiber (ALS is correct here)
export const POST = async (request: Request) => {
  const { userId } = await auth()  // ← Safe: still in Next.js ALS context

  // Inject into request headers or use the `context` parameter
  const headers = new Headers(request.headers)
  headers.set("x-clerk-auth-user-id", userId ?? "")
  const enrichedRequest = new Request(request.url, {
    method: request.method,
    headers,
    body: request.body,
    duplex: "half" as any,
  })

  return webHandler(enrichedRequest)
}

// In Effect handlers — read from HttpServerRequest headers instead of calling auth()
const getAuthenticatedUserId = Effect.gen(function*() {
  const req = yield* HttpServerRequest.HttpServerRequest
  const userId = req.headers["x-clerk-auth-user-id"]
  if (!userId) return yield* Effect.fail(new UnauthorizedError({ message: "Auth required" }))
  return userId
})

Suggested fix (for Effect maintainers)

Option A: Propagate ALS context through the scheduler

Capture the AsyncLocalStorage snapshot when a fiber continuation is scheduled, and restore it when the continuation executes:

// In MixedScheduler or the fiber runtime
import { AsyncLocalStorage } from "node:async_hooks"

scheduleTask(task: Task, priority: number) {
  // Capture current ALS context
  const snapshot = AsyncLocalStorage.snapshot()
  this.tasks.scheduleTask(() => snapshot(task), priority)
  // ...
}

AsyncLocalStorage.snapshot() (Node.js 20.5+) returns a function that, when called, restores the ALS context from the point of capture. This ensures each fiber continuation runs with its originating request's ALS context.

Trade-off: Adds one closure allocation per scheduled task. Could be opt-in via a FiberRef or scheduler option.

Option B: Capture ALS at runFork and restore per fiber step

When Runtime.runFork is called, capture the ALS snapshot and associate it with the fiber. Before each fiber step (in the fiber runtime's evaluateEffect loop), restore the snapshot.

Trade-off: More invasive but provides correct ALS propagation for the fiber's entire lifetime, including across flatMap chains and Effect.tryPromise thunks.

Option C: Document the limitation and provide a context injection API

If ALS propagation is intentionally not supported, document this prominently and provide a first-class API for toWebHandler to accept per-request context. The existing context?: Context.Context<never> parameter on the handler function partially addresses this, but it requires callers to know about the issue and manually extract values before entering Effect.

Related

POC replica of my setup

// Create web handler from Effect RPC
// sharedMemoMap ensures all RPC routes share the same connection pool
const { handler: webHandler, dispose } = RpcServer.toWebHandler(DemoRpc, {
  layer: RpcLayer,
  memoMap: sharedMemoMap,
});

/**
 * POST /api/rpc/demo
 */
export const POST = async (request: Request) => {
  return webHandler(request);
};

registerDispose(dispose);

Used util functions


/**
 * Creates a dispose registry that collects dispose callbacks and runs them
 * when `runAll` is invoked. Handles both sync and async dispose functions,
 * catching errors to prevent one failing dispose from breaking others.
 *
 * @&#8203;internal Exported for testing — use `registerDispose` in application code.
 */
export const makeDisposeRegistry = () => {
  const disposeFns: Array<() => void | Promise<void>> = []

  const runAll = () => {
    for (const fn of disposeFns) {
      try {
        const result = fn()
        if (result && typeof result.then === "function") {
          result.then(undefined, (err: unknown) => console.error("Dispose error:", err))
        }
      } catch (err) {
        console.error("Dispose error:", err)
      }
    }
  }

  const register = (dispose: () => void | Promise<void>) => {
    disposeFns.push(dispose)
  }

  return { register, runAll }
}

export const registerDispose: (dispose: () => void | Promise<void>) => void = globalValue(
  Symbol.for("@&#8203;global/RegisterDispose"),
  () => {
    const registry = makeDisposeRegistry()

    if (typeof process !== "undefined") {
      process.once("beforeExit", registry.runAll)
    }

    return registry.register
  }
)

The actual effect that was run within the RPC context that the bug was found

export const getAuthenticatedUserId: Effect.Effect<string, UnauthorizedError> =
  Effect.gen(function*() {
    const authResult = yield* Effect.tryPromise({
      try: async () => auth(),
      catch: () =>
        new UnauthorizedError({
          message: "Failed to get auth session"
        })
    })

    if (!authResult.userId) {
      return yield* Effect.fail(
        new UnauthorizedError({
          message: "Authentication required"
        })
      )
    }

    return authResult.userId
  })

Release Notes

Effect-TS/effect (effect)

v3.20.0

Compare Source

Minor Changes
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.

v3.19.19

Compare Source

Patch Changes

v3.19.18

Compare Source

Patch Changes

v3.19.17

Compare Source

Patch Changes

v3.19.16

Compare Source

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

v3.19.15

Compare Source

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

v3.19.14

Compare Source

Patch Changes

v3.19.13

Compare Source

Patch Changes

v3.19.12

Compare Source

Patch Changes

v3.19.11

Compare Source

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

v3.19.10

Compare Source

Patch Changes

v3.19.9

Compare Source

Patch Changes

v3.19.8

Compare Source

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

v3.19.7

Compare Source

Patch Changes

v3.19.6

Compare Source

Patch Changes

v3.19.5

Compare Source

Patch Changes

v3.19.4

Compare Source

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)

v3.19.3

Compare Source

Patch Changes

v3.19.2

Compare Source

Patch Changes

v3.19.1

Compare Source

Patch Changes

v3.19.0

Compare Source

Minor Changes
Patch Changes

v3.18.5

Compare Source

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

v3.18.4

Compare Source

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
    }
    */

v3.18.3

Compare Source

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.

v3.18.2

Compare Source

Patch Changes

v3.18.1

Compare Source

Patch Changes

v3.18.0

Compare Source

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)

v3.17.14

Compare Source

Patch Changes

v3.17.13

Compare Source

Patch Changes

v3.17.12

Compare Source

Patch Changes

v3.17.11

Compare Source

Patch Changes

v3.17.10

Compare Source

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"
    }
    */

v3.17.9

Compare Source

Patch Changes

v3.17.8

Compare Source

Patch Changes

v3.17.7

Compare Source

Patch Changes

v3.17.6

Compare Source

Patch Changes

v3.17.5

Compare Source

Patch Changes

v3.17.4

Compare Source

Patch Changes

v3.17.3

Compare Source

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

v3.17.2

Compare Source

Patch Changes

v3.17.1

Compare Source

Patch Changes

v3.17.0

Compare Source

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

v3.16.17

Compare Source

Patch Changes

v3.16.16

Compare Source

Patch Changes

v3.16.15

Compare Source

Patch Changes

Configuration

📅 Schedule: Branch creation - "" (UTC), Automerge - At any time (no schedule defined).

🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.

Rebasing: Whenever PR is behind base branch, or you tick the rebase/retry checkbox.

🔕 Ignore: Close this PR and you won't be reminded about this update again.


  • If you want to rebase/retry this PR, check this box

This PR was generated by Mend Renovate. View the repository job log.

@renovate renovate bot added renovate upgrade Any kind of dependency updates labels Mar 21, 2026
@renovate renovate bot force-pushed the renovate/npm-effect-vulnerability branch 2 times, most recently from 5dd6b04 to 6993fe5 Compare March 23, 2026 05:34
@renovate renovate bot changed the title ⬆️ Update dependency effect to v3 [SECURITY] ⬆️ Update dependency effect to v3 [SECURITY] - autoclosed Mar 27, 2026
@renovate renovate bot closed this Mar 27, 2026
@renovate renovate bot deleted the renovate/npm-effect-vulnerability branch March 27, 2026 01:05
@renovate renovate bot changed the title ⬆️ Update dependency effect to v3 [SECURITY] - autoclosed ⬆️ Update dependency effect to v3 [SECURITY] Mar 30, 2026
@renovate renovate bot reopened this Mar 30, 2026
@renovate renovate bot force-pushed the renovate/npm-effect-vulnerability branch 2 times, most recently from 6993fe5 to afdc67b Compare March 30, 2026 17:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

renovate upgrade Any kind of dependency updates

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants