Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
9 changes: 9 additions & 0 deletions .changeset/effect-4.0.6.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
"effect-app": patch
"@effect-app/infra": patch
"@effect-app/vue": patch
"@effect-app/vue-components": patch
"@effect-app/cli": patch
---

Update Effect dependencies to 4.0.6.
10 changes: 10 additions & 0 deletions .changeset/rpc-parse-options.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
"effect-app": patch
"@effect-app/infra": patch
---

Adopt effect's `Rpc.ParseOptions` for RPC encode and decode concurrency, and drop the per-schema `parseOptions` concurrency annotations.

Effect 4 ignores `parseOptions` annotations on schemas. Client and server RPC groups now carry `DefaultParseOptions` (unbounded unless `SCHEMA_CONCURRENCY` is set) via `annotateRpcParseOptions`, applied after prefix and middleware. `decodeEffectConcurrently` is unchanged for call sites that pass parse options themselves.

Dependencies are on `effect@4.0.6`, which includes `Rpc.ParseOptions`. Peers require `effect@^4.0.6`.
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,15 +50,15 @@
"@effect-app/infra": "workspace:*",
"@effect/language-service": "0.87.5",
"@effect/tsgo": "^0.56.0",
"@effect/platform-node": "4.0.5",
"@effect/vitest": "4.0.5",
"@effect/platform-node": "4.0.6",
"@effect/vitest": "4.0.6",
"@tsconfig/strictest": "^2.0.8",
"@types/node": "25.9.1",
"@typescript-eslint/eslint-plugin": "8.60.0",
"@typescript-eslint/parser": "8.60.0",
"@typescript/native-preview": "7.0.0-dev.20260626.1",
"dprint": "^0.54.0",
"effect": "4.0.5",
"effect": "4.0.6",
"effect-app": "workspace:*",
"eslint": "^10.4.1",
"json5": "^2.2.3",
Expand Down
4 changes: 2 additions & 2 deletions packages/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@
"effect-app-cli": "./bin.js"
},
"dependencies": {
"@effect/platform-node": "4.0.5",
"effect": "4.0.5",
"@effect/platform-node": "4.0.6",
"effect": "4.0.6",
"js-yaml": "4.2.0"
},
"devDependencies": {
Expand Down
8 changes: 4 additions & 4 deletions packages/e2e/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@
"effect-app": "workspace:*"
},
"devDependencies": {
"@effect/atom-vue": "^4.0.5",
"@effect/platform-node": "4.0.5",
"@effect/vitest": "4.0.5",
"@effect/atom-vue": "^4.0.6",
"@effect/platform-node": "4.0.6",
"@effect/vitest": "4.0.6",
"@tanstack/vue-query": "5.96.2",
"@types/node": "25.9.1",
"@vitejs/plugin-vue": "^6.0.7",
"effect": "^4.0.5",
"effect": "^4.0.6",
"typescript": "~6.0.3",
"vitest": "^5.0.5",
"vue": "^3.5.35"
Expand Down
2 changes: 1 addition & 1 deletion packages/effect-app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
"vitest": "^5.0.5"
},
"peerDependencies": {
"effect": "^4.0.5"
"effect": "^4.0.6"
},
"typesVersions": {
"*": {
Expand Down
10 changes: 5 additions & 5 deletions packages/effect-app/src/Schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import type { RequiredKeys } from "effect/Types"
import type { Arbitrary as FastCheckArbitrary } from "fast-check"
import type { NonEmptyReadonlyArray } from "./Array.ts"
import { Email as EmailT, type Email as EmailType } from "./Schema/email.ts"
import { concurrencyUnbounded, withDefaultMake, withDefaultParseOptions } from "./Schema/ext.ts"
import { withDefaultMake, withDefaultParseOptions } from "./Schema/ext.ts"
import type { FC } from "./Schema/FastCheck.ts"
import { PhoneNumber as PhoneNumberT, type PhoneNumber as PhoneNumberType } from "./Schema/phoneNumber.ts"
import { type AST } from "./Schema/schema.ts"
Expand Down Expand Up @@ -156,7 +156,7 @@ export const Void: Void = S.Void
export function Struct<const Fields extends S.Struct.Fields>(
fields: Fields
): Struct<Fields> {
const result = S.Struct(fields).annotate(concurrencyUnbounded)
const result = S.Struct(fields)
const allowVoidMake = (schema: any): any => {
// Normalize omitted input to an empty object so optional/default-only structs can be constructed with make().
// Do not read schema.make first: Effect 4 caches it as a non-writable own property.
Expand Down Expand Up @@ -194,7 +194,7 @@ export function Struct<const Fields extends S.Struct.Fields>(
}
;(result as any).mapFields = function(this: any, f: any, options?: any) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
const mapped = origMapFields.call(this, f, options).annotate(concurrencyUnbounded)
const mapped = origMapFields.call(this, f, options)
return preserveCopyAndMethods(mapped)
}
;(result as any).annotate = function(this: any, annotations?: any) {
Expand Down Expand Up @@ -296,7 +296,7 @@ export type StructNestedEncoded<T> = T extends { fields: infer Fields extends S.
: StructNestedEncodedError<T>

export function NonEmptyArray<Value extends S.Top>(value: Value): S.NonEmptyArray<Value> {
return S.NonEmptyArray(value).annotate(concurrencyUnbounded)
return S.NonEmptyArray(value)
}

export function TaggedStruct<const Tag extends SchemaAST.LiteralValue, const Fields extends S.Struct.Fields>(
Expand Down Expand Up @@ -336,7 +336,7 @@ export function Record<Key extends S.Record.Key, Value extends S.Top>(
key: Key,
value: Value
): S.$Record<Key, Value> {
return S.Record(key, value).annotate(concurrencyUnbounded)
return S.Record(key, value)
}
export declare namespace Record {
export type Key = S.Record.Key
Expand Down
21 changes: 4 additions & 17 deletions packages/effect-app/src/Schema/Class.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import * as SchemaTransformation from "effect/SchemaTransformation"
import type { Struct as EffectAppStruct } from "../Schema.ts"
import * as SchemaAST from "../SchemaAST.ts"
import { copyOrigin } from "../utils.ts"
import { concurrencyUnbounded } from "./ext.ts"
import * as SchemaParser from "./SchemaParser.ts"

type ClassAnnotations<Self> = S.Annotations.Declaration<Self, readonly [any]>
Expand Down Expand Up @@ -130,7 +129,7 @@ export const Class: <Self = never, Encoded = ExtendedSchemaNoEncoded, Brand = {}
> = (identifier) => (fields, annotations, options) => {
const relaxed = options?.strict === false
// Build the original Schema.Class
const Base = (S.Class as any)(identifier)(fields, { ...concurrencyUnbounded, ...annotations })
const Base = (S.Class as any)(identifier)(fields, annotations)
// Get the original ast getter from the base class
const originalAstDescriptor = Object.getOwnPropertyDescriptor(Base, "ast")!

Expand All @@ -157,9 +156,6 @@ export const Class: <Self = never, Encoded = ExtendedSchemaNoEncoded, Brand = {}
astCache.set(this, cached)
return cached
}
static mapFields(f: any, options?: any) {
return Base.mapFields(f, options).annotate(concurrencyUnbounded)
}
} as any
}

Expand Down Expand Up @@ -197,7 +193,7 @@ export const TaggedClass: <Self = never, Encoded = ExtendedSchemaNoEncoded, Bran
Brand
> = (identifier) => (tag, fields, annotations, options) => {
const relaxed = options?.strict === false
const Base = (S.TaggedClass as any)(identifier)(tag, fields, { ...concurrencyUnbounded, ...annotations })
const Base = (S.TaggedClass as any)(identifier)(tag, fields, annotations)
const originalAstDescriptor = Object.getOwnPropertyDescriptor(Base, "ast")!
const astCache = new WeakMap<any, SchemaAST.Declaration>()
const copyCache = new WeakMap<any, ReturnType<typeof copyOrigin>>()
Expand All @@ -219,9 +215,6 @@ export const TaggedClass: <Self = never, Encoded = ExtendedSchemaNoEncoded, Bran
astCache.set(this, cached)
return cached
}
static mapFields(f: any, options?: any) {
return Base.mapFields(f, options).annotate(concurrencyUnbounded)
}
} as any
}

Expand All @@ -242,7 +235,7 @@ export const ErrorClass: <Self = never, Encoded = ExtendedSchemaNoEncoded, Brand
Cause.YieldableError & Brand
> = (identifier) => (fields, annotations, options) => {
const relaxed = options?.strict === false
const Base = (S.Error as any)(identifier)(fields, { ...concurrencyUnbounded, ...annotations })
const Base = (S.Error as any)(identifier)(fields, annotations)
const originalAstDescriptor = Object.getOwnPropertyDescriptor(Base, "ast")!
const astCache = new WeakMap<any, SchemaAST.Declaration>()
const copyCache = new WeakMap<any, ReturnType<typeof copyOrigin>>()
Expand All @@ -264,9 +257,6 @@ export const ErrorClass: <Self = never, Encoded = ExtendedSchemaNoEncoded, Brand
astCache.set(this, cached)
return cached
}
static mapFields(f: any, options?: any) {
return Base.mapFields(f, options).annotate(concurrencyUnbounded)
}
} as any
}

Expand All @@ -288,7 +278,7 @@ export const TaggedErrorClass: <Self = never, Encoded = ExtendedSchemaNoEncoded,
Cause.YieldableError & Brand
> = (identifier) => (tag, fields, annotations, options) => {
const relaxed = options?.strict === false
const Base = (S.TaggedError as any)(identifier)(tag, fields, { ...concurrencyUnbounded, ...annotations })
const Base = (S.TaggedError as any)(identifier)(tag, fields, annotations)
const originalAstDescriptor = Object.getOwnPropertyDescriptor(Base, "ast")!
const astCache = new WeakMap<any, SchemaAST.Declaration>()
const copyCache = new WeakMap<any, ReturnType<typeof copyOrigin>>()
Expand All @@ -310,9 +300,6 @@ export const TaggedErrorClass: <Self = never, Encoded = ExtendedSchemaNoEncoded,
astCache.set(this, cached)
return cached
}
static mapFields(f: any, options?: any) {
return Base.mapFields(f, options).annotate(concurrencyUnbounded)
}
} as any
}

Expand Down
14 changes: 2 additions & 12 deletions packages/effect-app/src/Schema/ext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,15 +63,6 @@ export const DefaultParseOptions: SchemaAST.ParseOptions = {
concurrency: Option.getOrElse(concurrencySetting, () => "unbounded" as const)
}

/**
* Parse-options annotation used on schema constructors for decode paths where callers
* cannot currently pass parse options (notably some RPC / HttpApi integration paths).
*
* Keep this annotation in place so those framework-managed decodes still run with
* unbounded concurrency by default.
*/
export const concurrencyUnbounded = { parseOptions: DefaultParseOptions } as const

type DecodeLike = (schema: any) => (input: any, options?: SchemaAST.ParseOptions) => any

export const withDefaultParseOptions = <Decode extends DecodeLike>(
Expand Down Expand Up @@ -245,7 +236,7 @@ export const Literals = <const Literals extends NonEmptyReadonlyArray<AST.Litera
/** Like the default Schema `Array` but with default helpers. */
export function Array<ValueSchema extends S.Top>(value: ValueSchema) {
return pipe(
S.Array(value).annotate(concurrencyUnbounded),
S.Array(value),
(s) =>
Object.assign(s, {
/**
Expand Down Expand Up @@ -273,7 +264,7 @@ export function Array<ValueSchema extends S.Top>(value: ValueSchema) {
export const ReadonlySetFromArray = <ValueSchema extends S.Top>(value: ValueSchema) => {
const from = S
.Array(value)
.annotate({ ...concurrencyUnbounded, expected: "an array of unique items that will be decoded as a ReadonlySet" })
.annotate({ expected: "an array of unique items that will be decoded as a ReadonlySet" })
const to = S.instanceOf(Set) as S.instanceOf<ReadonlySet<ValueSchema["Type"]>>
const schema = from.pipe(
S.decodeTo(
Expand All @@ -297,7 +288,6 @@ export const ReadonlyMapFromArray = <KeySchema extends S.Top, ValueSchema extend
const from = S
.Array(S.Tuple([pair.key, pair.value]))
.annotate({
...concurrencyUnbounded,
expected: "an array of key-value tuples that will be decoded as a ReadonlyMap"
})
const to = S.instanceOf(Map) as S.instanceOf<
Expand Down
3 changes: 2 additions & 1 deletion packages/effect-app/src/client/apiClientFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import * as DataDependencies from "../DataDependencies.ts"
import * as Effect from "../Effect.ts"
import { HttpClient, HttpClientRequest } from "../http.ts"
import { Invalidation } from "../rpc.ts"
import { annotateRpcParseOptions } from "../rpc/RpcParseOptions.ts"
import type * as S from "../Schema.ts"
import type { Client, ClientForOptions, ExtractModuleName, RequestsAny } from "./clientFor.ts"
import { InvalidationKeysFromServer } from "./InvalidationKeys.ts"
Expand Down Expand Up @@ -155,7 +156,7 @@ export const makeRpcGroupFromRequestsAndModuleName = <M extends RequestsAny, con
const rpcs = (middleware ? baseRpcs.middleware(middleware) : baseRpcs) as unknown as RpcGroup.RpcGroup<
Rpc.Prefixed<RpcHandlers<newM>[keyof newM], `${ModuleName}.`>
>
return rpcs
return annotateRpcParseOptions(rpcs)
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
Expand Down
1 change: 1 addition & 0 deletions packages/effect-app/src/rpc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ export * as Invalidation from "./rpc/Invalidation.ts"
export * as MiddlewareMaker from "./rpc/MiddlewareMaker.ts"
export * as RpcContextMap from "./rpc/RpcContextMap.ts"
export * as RpcMiddleware from "./rpc/RpcMiddleware.ts"
export { annotateRpcParseOptions } from "./rpc/RpcParseOptions.ts"
13 changes: 13 additions & 0 deletions packages/effect-app/src/rpc/RpcParseOptions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { Rpc, type RpcGroup } from "effect/rpc"
import { DefaultParseOptions } from "../Schema/ext.ts"

/**
* Apply {@link DefaultParseOptions} to every RPC in `group`.
*
* `RpcClient` and `RpcServer` read `Rpc.ParseOptions` when encoding and decoding
* payloads, successes, stream chunks, and exits. Schema `parseOptions` annotations
* are not consulted. Call this after `prefix` and `middleware`, which clone RPCs.
*/
export const annotateRpcParseOptions = <R extends Rpc.Any>(
group: RpcGroup.RpcGroup<R>
): RpcGroup.RpcGroup<R> => group.annotateRpcs(Rpc.ParseOptions, DefaultParseOptions)
Loading
Loading